面向对象编程(高级)
本章内容
类变量
引入
package com.bbedu.static_;
public class ChildGame {
public static void main(String[] args) {
// 定义一个count
int count = 0;
Child ming = new Child("ming");
ming.join();
count++;
Child qiang = new Child("qiang");
qiang.join();
count++;
Child li = new Child("li");
li.join();
count++;
System.out.println("共有" + count + "个人加入游戏");
}
}
class Child {
private String name;
public Child(String name) {
this.name = name;
}
public void join() {
System.out.println(name + " 加入了游戏...");
}
}
分析:
- count 是一个独立于对象,很尴尬
- 以后我们访问 count 很麻烦, 没有使用到 OOP
- 因此,我们引出 类变量/静态变量
快速入门
package com.bbedu.static_;
public class ChildGame {
public static void main(String[] args) {
Child ming = new Child("ming");
ming.join();
Child qiang = new Child("qiang");
qiang.join();
Child li = new Child("li");
li.join();
// 类变量可以通过类名直接访问
System.out.println("共有" + Child.count + "个人加入游戏");
}
}
class Child {
private String name;
// 类变量,又叫静态变量
// 该变量最大的特点是会被所有的 Child 对象实例共享
public static int count = 0;
public Child(String name) {
this.name = name;
}
public void join() {
System.out.println(name + " 加入了游戏...");
count++;
}
}
理解
- 静态变量被对象共享,在类加载中就生成了,JDK7以上版本,静态域存储于定义类型的 Class 对象中,Class 对象如同堆中其他对象一样,存在于 GC 堆中。
- 因此不影响对静态变量的使用
类变量又叫静态变量/静态属性,是该类的所有对象共享的变量,任何一个该类的独享去访问它,取到的都是相同的值,同样任何一个该类的对象去修改它时,修改的都是同一个变量
定义类变量:
访问修饰符 static 数据类型 变量名; [推荐]
static 访问修饰符 数据类型 变量名;
访问类变量:
类名.类变量 [推荐]
或者对象名.类变量
细节
什么时候使用类变量
当我们需要让某个类的所有对象共享一个变量时,就可以考虑使用类变量
类变量与实例变量(普通属性)的区别
类变量是该类的所有对象共享的,而实例变量是每个对象独享的
- 加上 static 称为类变量或静态变量,否则称为实例变量/普通变量/非静态变量
- 类变量可以通过 类名.类变量名 或者 对象名.类变量名 来访问,但 java 设计者推荐使用 类名.类变量名 的方式来访问
- 实例变量不能通过 类名.变量名 的方式来访问
- 类变量是在类创建时就初始化了,也就是说,即使没有创建对象,只要类加载了,就可以使用类变量了
- 类变量的生命周期是随着类的加载开始,随着类的消亡而销毁
类方法
基本介绍
类方法也叫静态方法,形式如下:
访问修饰符 static 返回数据类型 方法名() [推荐]
static 访问修饰符 返回数据类型 方法名()
类方法的调用:
类名.类方法名 或 对象名.类方法名 (需要满足访问修饰符的访问权限和范围)
类方法的使用场景:
当方法中不涉及到任何和对象相关的成员,则可以讲方法设计成静态方法,提高开发效率
如果我们希望不创建对象,也可以调用某个方法(即当作工具来使用),这时,把方法设计成静态方法非常合适
比如,工具类中的方法 utils Math类、Arrays类、Collections集合类
细节
- 类方法和普通方法都是随着类的加载而加载,讲结构信息存储在方法区
- 类方法可以通过类名调用,也可以通过对象名调用
- 普通方法和对象有关,需要通过对象名调用,比如 对象名.方法(参数),不能通过类名调用
- 类方法中不允许使用和对象有关的关键字,如 super 和 this,普通方法可以
- 类方法中只能访问 静态变量 或 静态方法(静态方法只能访问静态成员)
- 普通成员方法,既可以访问 非静态方法/普通变量(方法),也可以访问 静态方法/静态变量(方法)
练习
count=9
count=10
11
id++; 错误
输出:
0
1
4
main 方法
理解 main 方法
public static void main(String[] args){}
- main 方法是虚拟机调用
- java 虚拟机需要调用类的 main() 方法,所以该方法的访问权限必须是 public
- java 虚拟机在执行 main() 方法时,不必创建对象,所以方法必须是 static
- 该方法接收 String 类型的数组参数,该数组中保存执行 java 命令时传递给所运行的类的参数
- java 执行的程序 参数1 参数2 参数3
特别说明
- 在 main() 方法中,我们可以直接调用 main 方法所在类的静态方法或静态属性
- 但是,不能直接访问该类中的非静态成员,必须创建该类的有个实例对象后,才能通过这个对象去访问类中的非静态成员
package com.bbedu.main_;
public class Main01 {
// 静态的属性
private static String name = "秋高气爽";
public static void hi(){
System.out.println("Main01 的 hi 方法");
}
// 非静态成员
private int n1 = 10000;
public void ok(){
System.out.println("Main01 的 ok 方法");
}
public static void main(String[] args) {
// 可以直接使用 name
// 静态方法可以直接访问本类的静态成员
System.out.println("name=" + name);
hi();
// 静态方法main不可以访问非静态成员
//System.out.println("n1=" + n1); // 错误
Main01 main01 = new Main01();
System.out.println("n1=" + main01.n1);
main01.ok();
}
}
idea 中传值
代码块
代码化块又称为初始化块,属于类中的成员[即是类的一部分],类似于方法,将逻辑语句封装在方法体中,通过包围起来。
但和方法不同,没有方法名,没有返回,没有参数,只有方法体,而且不用通过对象或类显式调用,而是加载类时,或创建对象时隐式调用。
基本语法
[修饰符]{
代码
}
- 修饰符可选,要写的话,也只能写 static
- 代码块分为两类,使用 static 修饰的叫静态代码块,没有 static 修饰的,叫普通代码块
- 逻辑语句可以为任意逻辑语句(输入、输出、方法调用、循环、判断等)
- ; 号可以写,也可以忽略
细节
- static 代码块也叫静态代码块,作用就是对类进行初始化,而且它随着类的加载而执行,并且只会执行一次。如果是普通代码块,每创建一个对象,就执行
类声明时候被加载
(1) 创建对象实例时 (new)
(2) 创建子类对象实例,父类也会被加载
(3) 使用类的静态成员时 (静态属性、静态方法)
- 普通的代码块,在创建对象实例是,会被隐式地调用。被创建一次,就会调用一次。如果只是使用类的静态成员,普通代码块并不会执行。可以把普通代码块简单的理解为构造器的补充
创建一个对象时,在一个类中调用顺序是:
- 调用静态代码块和静态属性初始化(若有多个静态代码块和多个静态变量初始化,则按顺序依次调用)
- 调用普通代码块和普通属性的初始化(同上,若有多个,则按顺序)
- 调用构造器
- 构造方法(构造器) 的最前面隐含了 super() 和 调用普通代码块,静态相关的代码块,属性初始化,在类加载时,就执行完毕,因此是优先于构造器和普通代码块执行的
创建子类对象时(继承关系),同名的静态代码块,静态属性初始化,普通代码块,普通属性初始化,构造方法的调用顺序如下:/面试题
- 父类的静态代码块和静态属性(优先级一样,按定义顺序执行)
- 子类的静态代码块和静态属性(优先级一样,按定义顺序执行)
- 父类的普通代码块和普通属性初始化(优先级一样。按定义顺序执行)
- 父类的构造方法
- 子类的普通代码块和普通属性初始化(优先级一样,按定义顺序执行)
- 子类的构造方法
总结:父子静,父普构,子普构
- 静态代码块只能调用静态成员,普通代码块可以调用所有成员
练习
输出:
in static block!
total = 100
total = 100
输出:
静态成员sam初始化
static块执行
sam1成员初始化
Test默认构造函数被调用
单例设计模式
什么是设计模式
- 静态方法和属性的经典使用
- 设计模式是在大量的实践中总结和理论化之后优选的代码结构、编程风格以及解决问题的思考方式。设计模式就像是经典的棋谱,不同的棋局,我们用不同的棋谱,免去我们自己在思考和摸索
什么是单例模式
- 所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法
- 单例模式有两种方式:饿汉式,懒汉式
饿汉式
- 构造器私有化 => 防止直接 new
- 类的内部创建对象
- 向外暴露一个静态的公共方法
package com.bbedu.single_;
public class SingleTon01 {
public static void main(String[] args) {
GirlFriend instance = GirlFriend.getInstance();
System.out.println(instance);
GirlFriend instance1 = GirlFriend.getInstance();
System.out.println(instance1);
System.out.println(instance == instance1);
}
}
class GirlFriend {
private String name;
// 静态方法只能访问静态成员,因此 gf 需要为静态
private static GirlFriend gf = new GirlFriend("Mary");
// 保证我们只能创建一个 GirlFriend 对象
//1. 构造器私有化 => 防止直接 new
//2. 类的内部创建对象
//3. 向外暴露一个静态的公共方法
private GirlFriend(String name){
this.name = name;
}
public static GirlFriend getInstance(){
return gf;
}
@Override
public String toString() {
return "GirlFriend{" +
"name='" + name + '\'' +
'}';
}
}
懒汉式
- 构造器私有化 => 防止直接 new
- 类的内部创建对象
- 向外暴露一个静态的公共方法
- 只有当用户调用 getInstance 方法时,才创建对象实例
package com.bbedu.single_;
/**
* 演示懒汉式的单例模式
*/
public class SingleTon02 {
public static void main(String[] args) {
System.out.println(Cat.n1);
Cat instance = Cat.getInstance();
System.out.println(instance);
}
}
// 希望只能创建一个 Cat 对象
class Cat {
public static int n1 = 888;
private String name;
private static Cat cat;
private Cat(String name) {
System.out.println("构造器被调用...");
this.name = name;
}
public static Cat getInstance(){
if (cat == null){
cat = new Cat("小花");
}
return cat;
}
@Override
public String toString() {
return "Cat{" +
"name='" + name + '\'' +
'}';
}
}
区别:饿汉式 VS 懒汉式
- 二者最主要的区别在于创建对象的时机不同:饿汉式是在类加载就创建了对象实例,而懒汉式是在使用时才创建。
- 饿汉式不存在线程安全问题,懒汉式存在线程安全问题。(后面学习线程后,会完善)
3. 饿汉式存在浪费资源的可能。因为如果程序员一个对象实例都没有使用,那么饿汉式创建的对象就浪费了,懒汉式是使用时才创建,就不存在这个问题。
4. 在我们javaSE标准类中,java.lang.Runtime就是经典的单例模式。
final 关键字
final 可以修饰类、属性、方法和局部变量
在某些情况下,程序员可能有以下需求,就会用到 final:
- 当不希望类被继承时
- 当不希望父类的某个方法被子类重写时
- 当不希望类的某个属性的值被修改时
- 当不希望某个局部变量被修改时
细节
- final 修饰的属性又叫常量,一般用 XX_XX_XX来命名
final 修饰的属性在定义时,必须赋初值,并且以后不能在修改,赋值可以在下面位置之一:
- 定义时
- 构造器中
- 代码块中
如果 final 修饰的属性是静态的,贼初始化的位置只能是:
- 定义时
- 在静态代码块,不能再构造器中赋值
- final 类不能继承,但是可以实例化对象
- 如果类不是 final 类,但是含有 final 方法,则该方法虽然不能重写,但是可以被继承,即仍然遵守继承规则
- 一般来说,如果一个类已经是 final 类了,就没必要再将方法修饰成 final 方法
- final 不能修饰构造方法
- final 和 static 往往搭配使用,效率更高,不会导致类的加载,底层编译器做了相关优化
- 包装类(Integer,String,Double)是 final 类,不能被继承
注意
final 关键字只是表示存储在变量中的对象不会再指示另一个不同的对象,不过这个对象可以更改,例如:
final char[] a = {'a', 'b', 'c'};
a[0] = 'd';
System.out.println(a);
换句话说,final 修饰基本数据类型的话值是不能进行改变的,修饰引用类型的话就是指地址不能被改变而值可以进行修改,这个需要区分一下
练习
package com.bbedu.final_;
public class FinalExercise01 {
public static void main(String[] args) {
Circle circle = new Circle(5);
System.out.println(circle.area());
}
}
class Circle {
private double radius;
private final double PI_1 = 3.14;
private final double PI_2;
private final double PI_3;
{
PI_2 = 3.14;
}
public Circle(double radius){
this.radius = radius;
PI_3 = 3.14;
}
public double area(){
return PI_1 * radius * radius;
}
}
有误,x不可自增
抽象类
当父类的某些方法,需要声明,但是又不确定如何实现时,可以将其声明为抽象方法,那么这个类就是抽象类
所谓抽象方法,就是没有实现的方法 => 没有方法体
当一个类中存在抽象方法时,需要将该类声明为 abstract 类
一般来说,抽象类会被继承,由其子类来实现抽象方法
介绍
- 用abstract关键字来修饰一个类时,这个类就叫抽象类访问修饰符abstract类名{}
- 用abstract关键字来修饰一个方法时,这个方法就是抽象方法
访问修饰符 abstract 返回类型方法名(参数列表);//没有方法体
- 抽象类的价值更多作用是在于设计,是设计者设计好后,让子类继承并实现抽象类()
- 抽象类,是考官比较爱问的知识点,在框架和设计模式使用较多
细节
- 抽象类不能实例化
- 抽象类不一定要包含 abstract 方法
- 一旦包含了 abstract 方法,则这个类必须声明为 abstract
- abstract 只能修饰类和方法,不能修饰属性和其他
- 抽象类可以拥有任意成员(抽象类本质还是类),如:非抽象方法、构造方法、静态属性...
- 抽象方法不能有主体,即不能实现
- 如果一个类继承了抽象类,则它必须实现抽象类的所有抽象方法,除非它自己也声明为 abstract 类
- 抽象方法不能使用 private final static 来修饰,因为这些关键字都是和重写违背的
练习
package com.bbedu.abstract_;
abstract public class Employee {
private String name;
private int age;
private double sal;
public Employee(String name, int age, double sal) {
this.name = name;
this.age = age;
this.sal = sal;
}
// 将 work 做成一个抽象方法
abstract public void work();
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public double getSal() {
return sal;
}
public void setSal(double sal) {
this.sal = sal;
}
}
package com.bbedu.abstract_;
public class Manager extends Employee{
private double bonus;
public Manager(String name, int age, double sal, double bonus) {
super(name, age, sal);
this.bonus = bonus;
}
public double getBonus() {
return bonus;
}
public void setBonus(double bonus) {
this.bonus = bonus;
}
@Override
public void work() {
System.out.println("经理" + getName() + "工作中...");
}
}
package com.bbedu.abstract_;
public class CommonEmployee extends Employee{
public CommonEmployee(String name, int age, double sal) {
super(name, age, sal);
}
@Override
public void work() {
System.out.println("普通员工" + getName() + "工作中...");
}
}
package com.bbedu.abstract_;
public class AbstractExercise01 {
public static void main(String[] args) {
Manager tim = new Manager("Tim", 32, 600000, 5000);
tim.work();
CommonEmployee rick = new CommonEmployee("Rick", 40, 50000);
rick.work();
}
}
抽象类最佳实践-模板设计模式
需求
- 有多个类,完成不同的任务
- 能够统计得到各自完成任务的时间
package com.bbedu.abstract_;
/**
* 模板设计模式
*/
abstract public class Template {
public abstract void job();
public void calculateTime(){
long start = System.currentTimeMillis();
job();
long end = System.currentTimeMillis();
System.out.println("执行时间:" + (end - start));
}
}
package com.bbedu.abstract_;
public class AA extends Template{
// 实现Template的抽象方法
public void job(){
long num = 0;
for (long i = 1; i <= 1000000000; i++) {
num += i;
}
System.out.println(num);
}
}
package com.bbedu.abstract_;
public class BB extends Template{
public void job(){
long num = 0;
for (long i = 1; i <= 80000; i++) {
num *= i;
}
System.out.println(num);
}
}
package com.bbedu.abstract_;
public class TestTemplate {
public static void main(String[] args) {
AA aa = new AA();
aa.calculateTime();
BB bb = new BB();
bb.calculateTime();
}
}
接口
基本介绍
就看就是给出一些没有实现的方法,封装到一起,到某个类要使用的时候,再根据具体情况把这些方法写出来。语法:
interface 接口名{
// 属性
// 方法
}
class 类名 implements 接口 {
自己属性;
自己方法;
必须实现的接口的抽象方法;
}
在接口中,抽象方法可以省略 abstract 关键字
在jdk8后,可以有默认实现方法,需要使用 default 关键字修饰;可以有静态方法
细节
- 接口不能被实例化
- 接口中的所有方法都是 public 方法,接口中的抽象方法,可以不用 abstract 修饰
- 一个普通类实现接口,就必须将该接口的所有方法都实现
- 抽象类实现接口,可以不用实现接口的方法
- 一个类可以同时实现多个接口
- 接口中的属性,只能是 final 的,而且是 public static final 修饰符,比如 int a = 1; 实际上等价于 public static final int a = 1;(必须初始化)
- 接口中属性的访问形式:接口名.属性名
- 接口不能继承其他的类,但是可以继承多个别的接口
练习
都正确,输出:
23
23
23
实现接口 vs 继承类
当子类继承了父类,就自动的拥有了父类的功能,如果子类需要扩展功能,可以通过实现接口的方式来扩展
可以理解:实现接口是对 java 单继承的一种补充
接口和继承解决的问题不同
继承的价值主要在于:解决代码的复用性和可维护性
接口的价值主要在于:设计,设计好各种规范(方法),让其他类去实现这些方法,更加灵活
继承是满足 is-a 的关系,而接口只需满足 like - a的关系
接口在一定程度上实现代码解耦(即 接口规范性+动态绑定)
接口的多态特性
- 多态参数,接口类型的变量 可以指向 实现了此接口的对象实例 => 类比继承的向上转型
- 多态数组,动态绑定 + 使用 instanceof 判断 + 向下转型
多态传递,接口间继承,类实现时遵守继承规则
练习
pX() 中 x 不明确,A.x, super.x,输出为:
0
1
小结
类的五大成员:
- 属性
- 方法
- 构造器
- 代码块
- *内部类
内部类
一个类的内部又完整地嵌套了另一个类结构,被嵌套的类成为内部类(inner class),嵌套其他类的类成为外部类(outer class)。内部类是java 类的第五大成员,内部类的最大特点是可以直接访问私有属性,并且可以体现类鱼类的包含关系。内部类是学习的重难点,底层源码中有大量的内部类
基本语法
class Outer{ // 外部类
class Inner{ // 内部类
}
}
class Other{ // 外部其他类
}
分类
定义在外部类局部位置上:
- 局部内部类(有类名)
- 匿名内部类(没有类名,重点)
定义在外部类的成员位置上:
- 成员内部类(没用 static 修饰)
- 静态内部类(使用 static 修饰)
局部内部类
- 局部内部类是定义在外部类的局部位置,通常在方法
- 可以直接访问外部类的所有成员,包含私有的
- 不能添加访问修饰符,但是可以使用 final 修饰
- 作用域:仅仅在定义它的方法或代码块中
- 局部内部类可以直接访问外部类的成员
- 外部类在方法中,可以创建 Inner02 对象,然后调用方法即可
- 如果外部类和局部内部类的成员重名时,默认就近原则,如果想访问外部类的成员,使用 外部类名.this.成员 去访问
package com.bbedu.innerclass;
/**
* 演示局部内部类的使用
*/
public class LocalInnerClass {
public static void main(String[] args) {
Outer02 outer02 = new Outer02();
outer02.m1();
System.out.println("Outer02 hashcode=" + outer02);
}
}
class Outer02 {
private int n1 = 100;
private void m2() {
System.out.println("Outer02 m2()");
}
public void m1() {
// 1.局部内部类是定义在外部类的局部位置,通常在方法
// 3.不能添加访问修饰符,但是可以使用 final 修饰
// 4.作用域:仅仅在定义它的方法或代码块中
class Inner02 { // 本质还是一个类
private int n1 = 999;
public void f1() {
// 2.可以直接访问外部类的所有成员,包含私有的
// 5.局部内部类可以直接访问外部类的成员,如下面的 n1 和 m2()
System.out.println("n1=" + n1 + " 外部类的n1=" + Outer02.this.n1);
System.out.println("Outer02.this hashcode=" + Outer02.this);
m2();
}
}
class Inner03 extends Inner02 {
}
// 6.外部类在方法中,可以创建 Inner02 对象,如何调用方法即可
Inner02 inner02 = new Inner02();
inner02.f1();
}
}
匿名内部类(非常重要)
- 本质是类
- 内部类
- 该类没有名字
- 同时是一个对象
基本语法
new 类或接口(参数列表){
类体;
}
细节
- 匿名内部类的语法比较奇特。匿名内部类既是一个类的定义,同时它本身也是一个对象,因此从语法上看,它既有定义类的特性,也有创建对象的特征,因此可以调用匿名内部类方法
- 可以直接访问外部类的所有成员,包含私有的
- 不能添加访问修饰符,因为它的地位就是一个局部变量
- 作用域:仅仅在定义它的方法或代码块中
- 外部其他类不能访问匿名内部类
- 外部类和匿名内部类的成员重名时,匿名内部类访问时,默认就近原则,如果想访问外部类的成员,使用 外部类名.this.成员 去访问
package com.bbedu.innerclass;
/**
* 演示匿名内部类的使用
*/
public class AnonymousInnerClassDetail {
public static void main(String[] args) {
Outer05 outer05 = new Outer05();
outer05.f1();
// 外部其他类不能访问匿名内部类
}
}
class Outer05 {
private int n1 = 99;
public void f1(){
// 创建一个基于类的匿名内部类
Person p = new Person(){
private int n1 = 66;
@Override
public void hi() {
System.out.println("匿名内部类重写了 hi()");
System.out.println("n1 = " + n1 +
"\nOuter05 n1 = " + Outer05.this.n1);
}
};
p.hi(); // 真实的运行类型为 Outer$1
// 第二种调用,直接调用匿名对象的 hi() 方法
// new Person(){
// @Override
// public void hi() {
// System.out.println("匿名内部类重写了 hi() 哈哈哈");
// }
//
// @Override
// public void ok(String str) {
// super.ok(str);
// }
// }.ok("Mary");
}
}
class Person {
public void hi() {
System.out.println("Person hi()");
}
public void ok(String str){
System.out.println("Person ok() " + str);
}
}
// 抽象类、接口...
最佳实践
当作实参直接传递,简洁高效
package com.bbedu.innerclass;
public class InnerClassExercise01 {
public static void main(String[] args) {
f1(new AA(){
@Override
public void show() {
System.out.println("这是一幅画");
}
});
// 传统方法
f1(new Pic());
}
// 静态方法
public static void f1(AA aa){
aa.show();
}
}
interface AA {
void show();
}
class Pic implements AA{ // 类实现AA -> 硬编码
@Override
public void show() {
System.out.println("这是一幅画");
}
}
package com.bbedu.innerclass;
public class InnerClassExercise02 {
public static void main(String[] args) {
CellPhone cellPhone = new CellPhone();
// 传递的是实现了 Bell 接口的匿名内部类
cellPhone.alarmClock(new Bell() {
@Override
public void ring() {
System.out.println("懒猪起床了!!");
}
});
// 相当于:
Bell bell = new Bell(){
@Override
public void ring() {
System.out.println("懒猪起床了!!");
}
};
cellPhone.alarmClock(bell);
cellPhone.alarmClock(new Bell() {
@Override
public void ring() {
System.out.println("小伙伴上课了");
}
});
}
}
interface Bell{
void ring();
}
class CellPhone{
public void alarmClock(Bell bell){
bell.ring();
}
}
成员内部类
- 定义在外部类的成员位置
- 可以添加任意访问修饰符,因为它的地位就是一个成员
- 作用域是整个外部类类体
- 可以直接访问外部类成员
- 要使用成员内部类,在外部类创建其实例,然后使用相关的方法
- 外部其他类访问成员内部类:两种方法,详见代码
- 重名遵守就近原则,如果想访问外部类的成员,使用 外部类名.this.成员 去访问
package com.bbedu.innerclass;
public class MemberInnerClass01 {
public static void main(String[] args) {
Outer08 outer08 = new Outer08();
outer08.t1();
// 外部其他类,使用衬衣内部类的两种方式
// 1、相当于把 new Inner08() 当作是 outer08 的成员,不必纠结
Outer08.Inner08 inner08 = outer08.new Inner08();
inner08.say();
// 2.在外部类中编写一个方法,可以返回 Inner08 的一个对象实例
Outer08.Inner08 inner081 = outer08.getInner08Instance();
inner081.say();
// 3.把new 外部类 和 方法1 合起来
}
}
class Outer08{
private int n1 = 100;
public String name = "张三";
class Inner08 { // 成员内部类
private int sal = 999;
private int n1 = 66;
public void say(){
// 可以访问外部类的所有成员
System.out.println("n1 = " + n1 + " name = " + name +
" Outer08 n1 = " + Outer08.this.n1);
}
}
public Inner08 getInner08Instance(){
return new Inner08();
}
public void t1(){
Inner08 inner08 = new Inner08();
inner08.say();
System.out.println(inner08.sal);
}
}
静态内部类
在成员内部类的基础上,加上 static 修饰
- 可以直接访问外部类的所有静态成员,包括私有的
- 可以添加任意修饰符
- 作用域:同其他的成员,为整个类体
- 外部其他类访问静态内部类,见代码
- 重名遵守就近原则,如果想访问外部类的成员,使用 外部类名.成员 去访问
package com.bbedu.innerclass;
public class StaticInnerClass01 {
public static void main(String[] args) {
Outer10 outer10 = new Outer10();
outer10.m10();
// 外部其他类访问静态内部类
// 方式1,直接通过类名访问
Outer10.Inner10 inner10 = new Outer10.Inner10();
inner10.say();
// 方式2,编写一个方法,返回静态内部类的对象实例
Outer10.Inner10 inner101 = outer10.getInner10();
inner101.say();
Outer10.Inner10 inner102 = Outer10.getInner10_();
inner102.say();
}
}
class Outer10 {
public static String name = "张三";
private static void cry(){
System.out.println("Outer10 cry()");
}
static class Inner10 { // 成员内部类
private String name = "Mary";
public void say() {
// 可以访问外部类的所有静态成员
System.out.println(name);
System.out.println(Outer10.name);
cry();
}
}
public void m10(){
Inner10 inner10 = new Inner10();
inner10.say();
}
public Inner10 getInner10(){
return new Inner10();
}
public static Inner10 getInner10_(){
return new Inner10();
}
}
练习
5
5
总结
面向对象(基础、中级、高级)到此结束~?
我亦无他,惟手熟尔
本章练习
结果:注意 color 是静态变量
9.0 red
100.0 red
package homework;
public class Homework02 {
public static void main(String[] args) {
Frock frock = new Frock();
System.out.println(frock.getNextNum());
System.out.println(frock.getNextNum());
Frock frock1 = new Frock();
Frock frock2 = new Frock();
Frock frock3 = new Frock();
System.out.println(frock1.getSerialNumber());
System.out.println(frock2.getSerialNumber());
System.out.println(frock3.getSerialNumber());
}
}
class Frock {
private static int currentNum = 100000;
private int serialNumber;
public int getNextNum(){
currentNum += 100;
return currentNum;
}
public Frock() {
serialNumber = getNextNum();
}
public int getSerialNumber() {
return serialNumber;
}
}
package homework;
public class Homework03 {
public static void main(String[] args) {
Animal cat = new Cat();
cat.shout();
Animal dog = new Dog();
dog.shout();
}
}
abstract class Animal {
abstract void shout();
}
class Cat extends Animal {
@Override
void shout() {
System.out.println("猫会喵喵叫~");
}
}
class Dog extends Animal {
@Override
void shout() {
System.out.println("狗会汪汪叫~");
}
}
package homework;
public class Homework04 {
public static void main(String[] args) {
Cellphone cellphone = new Cellphone();
cellphone.testWork(new Cal() {
@Override
public double work(double n1, double n2) {
return n1 + n2;
}
}, 10, 20);
}
}
interface Cal {
double work(double n1, double n2);
}
class Cellphone{
public void testWork(Cal iCal, double n1, double n2){
double work = iCal.work(n1, n2);
System.out.println("result = " + work);
}
}
package homework;
public class Homework05 {
public static void main(String[] args) {
new A().test();
}
}
class A {
private String name = "Bob";
public void test(){
class B{
private String name = "Tom";
public void show(){
System.out.println("Inner B class name=" + name
+ "\nA class name=" + A.this.name);
}
}
B b = new B();
b.show();
}
}
领略程序结构:
package homework;
import javax.print.attribute.standard.JobHoldUntil;
public class Homework06 {
public static void main(String[] args) {
Person monk = new Person("唐僧", new Boat());
monk.common();
monk.passRiver();
monk.passRiver();
monk.common();
monk.passFireHill();
}
}
interface Vehicles {
void work();
}
class Horse implements Vehicles {
@Override
public void work() {
System.out.println("骑马中...");
}
}
class Boat implements Vehicles {
@Override
public void work() {
System.out.println("开船中...");
}
}
class Plane implements Vehicles {
@Override
public void work() {
System.out.println("开飞机...");
}
}
class TransportFactory {
private static Horse horse = new Horse();
public static Boat getBoat() {
Boat boat = new Boat();
return boat;
}
// 确保马始终是同一匹,饿汉式
public static Horse getHorse() {
return horse;
}
public static Plane getPlane() {
Plane plane = new Plane();
return plane;
}
}
class Person {
private String name;
private Vehicles vehicles;
public Person(String name, Vehicles vehicles) {
this.name = name;
this.vehicles = vehicles;
}
// 把具体的要求封装成方法 => 编程思想
public void passRiver(){
//先得到船
if(!(vehicles instanceof Boat)){
vehicles = TransportFactory.getBoat();
}
vehicles.work();
}
public void common() {
// 判断一下当前的 vehicles 属性是否已经存在
if(!(vehicles instanceof Horse)){
vehicles = TransportFactory.getHorse(); // 向上转型
}
vehicles.work();
}
public void passFireHill(){
if(!(vehicles instanceof Plane)){
vehicles = TransportFactory.getPlane();
}
vehicles.work();
}
}
package homework;
public class Homework07 {
public static void main(String[] args) {
Car car = new Car(50.4);
car.new Air().flow();
Car car1 = new Car(-10.0);
car1.new Air().flow();
Car car2 = new Car(24);
car2.new Air().flow();
}
}
class Car {
private double temperature;
public Car(double temperature) {
this.temperature = temperature;
}
class Air {
public void flow() {
if (temperature > 40) {
System.out.println("吹冷气");
} else if (temperature < 0) {
System.out.println("吹暖气");
} else {
System.out.println("关闭空调");
}
}
}
}
package homework;
public class Homework08 {
public static void main(String[] args) {
// 演示 switch 使用
Color green = Color.GREEN;
green.show();
switch (green) {
case GREEN:
System.out.println("匹配到绿色");
break;
case RED:
System.out.println("匹配到红色");
break;
case BLUE:
System.out.println("匹配到蓝色");
break;
default:
System.out.println("没有匹配到");
}
}
}
interface IColor {
void show();
}
enum Color implements IColor{
RED(255,0,0),
BLUE(0, 0,255),
BLACK(0,0,0),
YELLOW(255,255,0),
GREEN(0,255,0);
private int redValue;
private int greenValue;
private int blueValue;
Color(int redValue, int greenValue, int blueValue) {
this.redValue = redValue;
this.greenValue = greenValue;
this.blueValue = blueValue;
}
@Override
public void show() {
System.out.println("属性值为" + redValue + " "
+ greenValue + " " + blueValue);
}
}
评论