Chapter10_OOP高级
侧边栏壁纸
  • 累计撰写 53 篇文章
  • 累计收到 5 条评论

Chapter10_OOP高级

bbchen
2023-02-26 / 0 评论 / 40 阅读 / 正在检测是否收录...

面向对象编程(高级)

本章内容

image-20220930154922420

类变量

引入

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 + " 加入了游戏...");
    }
}

分析:

  1. count 是一个独立于对象,很尴尬
  2. 以后我们访问 count 很麻烦, 没有使用到 OOP
  3. 因此,我们引出 类变量/静态变量

快速入门

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++;
    }
}

理解

  1. 静态变量被对象共享,在类加载中就生成了,JDK7以上版本,静态域存储于定义类型的 Class 对象中,Class 对象如同堆中其他对象一样,存在于 GC 堆中。
  2. 因此不影响对静态变量的使用

image-20220930162125363

类变量又叫静态变量/静态属性,是该类的所有对象共享的变量,任何一个该类的独享去访问它,取到的都是相同的值,同样任何一个该类的对象去修改它时,修改的都是同一个变量

定义类变量:

访问修饰符 static 数据类型 变量名; [推荐]

static 访问修饰符 数据类型 变量名;

访问类变量:

类名.类变量 [推荐]

或者对象名.类变量

细节

  1. 什么时候使用类变量

    当我们需要让某个类的所有对象共享一个变量时,就可以考虑使用类变量

  2. 类变量与实例变量(普通属性)的区别

    类变量是该类的所有对象共享的,而实例变量是每个对象独享的

  3. 加上 static 称为类变量或静态变量,否则称为实例变量/普通变量/非静态变量
  4. 类变量可以通过 类名.类变量名 或者 对象名.类变量名 来访问,但 java 设计者推荐使用 类名.类变量名 的方式来访问
  5. 实例变量不能通过 类名.变量名 的方式来访问
  6. 类变量是在类创建时就初始化了,也就是说,即使没有创建对象,只要类加载了,就可以使用类变量了
  7. 类变量的生命周期是随着类的加载开始,随着类的消亡而销毁

类方法

基本介绍

类方法也叫静态方法,形式如下:

访问修饰符 static 返回数据类型 方法名() [推荐]

static 访问修饰符 返回数据类型 方法名()

类方法的调用:

类名.类方法名 或 对象名.类方法名 (需要满足访问修饰符的访问权限和范围)

类方法的使用场景:

当方法中不涉及到任何和对象相关的成员,则可以讲方法设计成静态方法,提高开发效率

如果我们希望不创建对象,也可以调用某个方法(即当作工具来使用),这时,把方法设计成静态方法非常合适

比如,工具类中的方法 utils Math类、Arrays类、Collections集合类

细节

  1. 类方法和普通方法都是随着类的加载而加载,讲结构信息存储在方法区
  2. 类方法可以通过类名调用,也可以通过对象名调用
  3. 普通方法和对象有关,需要通过对象名调用,比如 对象名.方法(参数),不能通过类名调用
  4. 类方法中不允许使用和对象有关的关键字,如 super 和 this,普通方法可以
  5. 类方法中只能访问 静态变量 或 静态方法(静态方法只能访问静态成员)
  6. 普通成员方法,既可以访问 非静态方法/普通变量(方法),也可以访问 静态方法/静态变量(方法)

练习

image-20220930184526219

count=9

count=10

11

image-20220930184618195

id++; 错误

输出:

0

1

image-20220930185204947

4

main 方法

理解 main 方法

public static void main(String[] args){}

  1. main 方法是虚拟机调用
  2. java 虚拟机需要调用类的 main() 方法,所以该方法的访问权限必须是 public
  3. java 虚拟机在执行 main() 方法时,不必创建对象,所以方法必须是 static
  4. 该方法接收 String 类型的数组参数,该数组中保存执行 java 命令时传递给所运行的类的参数
  5. java 执行的程序 参数1 参数2 参数3

特别说明

  1. 在 main() 方法中,我们可以直接调用 main 方法所在类的静态方法或静态属性
  2. 但是,不能直接访问该类中的非静态成员,必须创建该类的有个实例对象后,才能通过这个对象去访问类中的非静态成员
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 中传值

image-20220930224541458

image-20220930224525497

image-20220930224518891

代码块

代码化块又称为初始化块,属于类中的成员[即是类的一部分],类似于方法,将逻辑语句封装在方法体中,通过包围起来。
但和方法不同,没有方法名,没有返回,没有参数,只有方法体,而且不用通过对象或类显式调用,而是加载类时,或创建对象时隐式调用。

基本语法

[修饰符]{

代码

}

  1. 修饰符可选,要写的话,也只能写 static
  2. 代码块分为两类,使用 static 修饰的叫静态代码块,没有 static 修饰的,叫普通代码块
  3. 逻辑语句可以为任意逻辑语句(输入、输出、方法调用、循环、判断等)
  4. ; 号可以写,也可以忽略

细节

  1. static 代码块也叫静态代码块,作用就是对类进行初始化,而且它随着类的加载而执行,并且只会执行一次。如果是普通代码块,每创建一个对象,就执行
  2. 类声明时候被加载

    (1) 创建对象实例时 (new)

​ (2) 创建子类对象实例,父类也会被加载

​ (3) 使用类的静态成员时 (静态属性、静态方法)

  1. 普通的代码块,在创建对象实例是,会被隐式地调用。被创建一次,就会调用一次。如果只是使用类的静态成员,普通代码块并不会执行。可以把普通代码块简单的理解为构造器的补充
  2. 创建一个对象时,在一个类中调用顺序是:

    1. 调用静态代码块和静态属性初始化(若有多个静态代码块和多个静态变量初始化,则按顺序依次调用)
    2. 调用普通代码块和普通属性的初始化(同上,若有多个,则按顺序)
    3. 调用构造器
  3. 构造方法(构造器) 的最前面隐含了 super() 和 调用普通代码块,静态相关的代码块,属性初始化,在类加载时,就执行完毕,因此是优先于构造器和普通代码块执行的
  4. 创建子类对象时(继承关系),同名的静态代码块,静态属性初始化,普通代码块,普通属性初始化,构造方法的调用顺序如下:/面试题

    1. 父类的静态代码块和静态属性(优先级一样,按定义顺序执行)
    2. 子类的静态代码块和静态属性(优先级一样,按定义顺序执行)
    3. 父类的普通代码块和普通属性初始化(优先级一样。按定义顺序执行)
    4. 父类的构造方法
    5. 子类的普通代码块和普通属性初始化(优先级一样,按定义顺序执行)
    6. 子类的构造方法

    总结:父子静,父普构,子普构

  5. 静态代码块只能调用静态成员,普通代码块可以调用所有成员

练习

image-20221003085900270

输出:

in static block!

total = 100

total = 100

image-20221003090355918

输出:

静态成员sam初始化

static块执行

sam1成员初始化

Test默认构造函数被调用

单例设计模式

什么是设计模式

  1. 静态方法和属性的经典使用
  2. 设计模式是在大量的实践中总结和理论化之后优选的代码结构、编程风格以及解决问题的思考方式。设计模式就像是经典的棋谱,不同的棋局,我们用不同的棋谱,免去我们自己在思考和摸索

什么是单例模式

  1. 所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法
  2. 单例模式有两种方式:饿汉式,懒汉式

饿汉式

  1. 构造器私有化 => 防止直接 new
  2. 类的内部创建对象
  3. 向外暴露一个静态的公共方法
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 + '\'' +
                '}';
    }
}

懒汉式

  1. 构造器私有化 => 防止直接 new
  2. 类的内部创建对象
  3. 向外暴露一个静态的公共方法
  4. 只有当用户调用 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 懒汉式

  1. 二者最主要的区别在于创建对象的时机不同:饿汉式是在类加载就创建了对象实例,而懒汉式是在使用时才创建。
  2. 饿汉式不存在线程安全问题,懒汉式存在线程安全问题。(后面学习线程后,会完善)

3. 饿汉式存在浪费资源的可能。因为如果程序员一个对象实例都没有使用,那么饿汉式创建的对象就浪费了,懒汉式是使用时才创建,就不存在这个问题。
4. 在我们javaSE标准类中,java.lang.Runtime就是经典的单例模式。

final 关键字

final 可以修饰类、属性、方法和局部变量

在某些情况下,程序员可能有以下需求,就会用到 final:

  1. 当不希望类被继承时
  2. 当不希望父类的某个方法被子类重写时
  3. 当不希望类的某个属性的值被修改时
  4. 当不希望某个局部变量被修改时

细节

  1. final 修饰的属性又叫常量,一般用 XX_XX_XX来命名
  2. final 修饰的属性在定义时,必须赋初值,并且以后不能在修改,赋值可以在下面位置之一:

    1. 定义时
    2. 构造器中
    3. 代码块中
  3. 如果 final 修饰的属性是静态的,贼初始化的位置只能是:

    1. 定义时
    2. 在静态代码块,不能再构造器中赋值
  4. final 类不能继承,但是可以实例化对象
  5. 如果类不是 final 类,但是含有 final 方法,则该方法虽然不能重写,但是可以被继承,即仍然遵守继承规则
  6. 一般来说,如果一个类已经是 final 类了,就没必要再将方法修饰成 final 方法
  7. final 不能修饰构造方法
  8. final 和 static 往往搭配使用,效率更高,不会导致类的加载,底层编译器做了相关优化
  9. 包装类(Integer,String,Double)是 final 类,不能被继承

注意

final 关键字只是表示存储在变量中的对象不会再指示另一个不同的对象,不过这个对象可以更改,例如:

final char[] a = {'a', 'b', 'c'};
a[0] = 'd';
System.out.println(a);

换句话说,final 修饰基本数据类型的话值是不能进行改变的,修饰引用类型的话就是指地址不能被改变而值可以进行修改,这个需要区分一下

练习

image-20221004142140699

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;
    }
}

image-20221004142159094

有误,x不可自增

抽象类

当父类的某些方法,需要声明,但是又不确定如何实现时,可以将其声明为抽象方法,那么这个类就是抽象类

所谓抽象方法,就是没有实现的方法 => 没有方法体

当一个类中存在抽象方法时,需要将该类声明为 abstract 类

一般来说,抽象类会被继承,由其子类来实现抽象方法

介绍

  1. 用abstract关键字来修饰一个类时,这个类就叫抽象类访问修饰符abstract类名{}
  2. 用abstract关键字来修饰一个方法时,这个方法就是抽象方法
    访问修饰符 abstract 返回类型方法名(参数列表);//没有方法体
  3. 抽象类的价值更多作用是在于设计,是设计者设计好后,让子类继承并实现抽象类()
  4. 抽象类,是考官比较爱问的知识点,在框架和设计模式使用较多

细节

  1. 抽象类不能实例化
  2. 抽象类不一定要包含 abstract 方法
  3. 一旦包含了 abstract 方法,则这个类必须声明为 abstract
  4. abstract 只能修饰类和方法,不能修饰属性和其他
  5. 抽象类可以拥有任意成员(抽象类本质还是类),如:非抽象方法、构造方法、静态属性...
  6. 抽象方法不能有主体,即不能实现
  7. 如果一个类继承了抽象类,则它必须实现抽象类的所有抽象方法,除非它自己也声明为 abstract 类
  8. 抽象方法不能使用 private final static 来修饰,因为这些关键字都是和重写违背的

练习

image-20221004162539985

image-20221004162733508

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();
    }
}

抽象类最佳实践-模板设计模式

需求

  1. 有多个类,完成不同的任务
  2. 能够统计得到各自完成任务的时间
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 关键字修饰;可以有静态方法

细节

  1. 接口不能被实例化
  2. 接口中的所有方法都是 public 方法,接口中的抽象方法,可以不用 abstract 修饰
  3. 一个普通类实现接口,就必须将该接口的所有方法都实现
  4. 抽象类实现接口,可以不用实现接口的方法
  5. 一个类可以同时实现多个接口
  6. 接口中的属性,只能是 final 的,而且是 public static final 修饰符,比如 int a = 1; 实际上等价于 public static final int a = 1;(必须初始化)
  7. 接口中属性的访问形式:接口名.属性名
  8. 接口不能继承其他的类,但是可以继承多个别的接口

练习

image-20221004194634334

都正确,输出:

23

23

23

实现接口 vs 继承类

当子类继承了父类,就自动的拥有了父类的功能,如果子类需要扩展功能,可以通过实现接口的方式来扩展

可以理解:实现接口是对 java 单继承的一种补充

接口和继承解决的问题不同

继承的价值主要在于:解决代码的复用性和可维护性

接口的价值主要在于:设计,设计好各种规范(方法),让其他类去实现这些方法,更加灵活

继承是满足 is-a 的关系,而接口只需满足 like - a的关系

接口在一定程度上实现代码解耦(即 接口规范性+动态绑定)

接口的多态特性

  1. 多态参数,接口类型的变量 可以指向 实现了此接口的对象实例 => 类比继承的向上转型
  2. 多态数组,动态绑定 + 使用 instanceof 判断 + 向下转型
  3. 多态传递,接口间继承,类实现时遵守继承规则

    image-20221004203446275

练习

pX() 中 x 不明确,A.x, super.x,输出为:

0

1

小结

类的五大成员:

  1. 属性
  2. 方法
  3. 构造器
  4. 代码块
  5. *内部类

内部类

一个类的内部又完整地嵌套了另一个类结构,被嵌套的类成为内部类(inner class),嵌套其他类的类成为外部类(outer class)。内部类是java 类的第五大成员,内部类的最大特点是可以直接访问私有属性,并且可以体现类鱼类的包含关系。内部类是学习的重难点,底层源码中有大量的内部类

基本语法

class Outer{ // 外部类

class Inner{ // 内部类

}

}

class Other{ // 外部其他类

}

分类

定义在外部类局部位置上:

  1. 局部内部类(有类名)
  2. 匿名内部类(没有类名,重点)

定义在外部类的成员位置上:

  1. 成员内部类(没用 static 修饰)
  2. 静态内部类(使用 static 修饰)

局部内部类

  1. 局部内部类是定义在外部类的局部位置,通常在方法
  2. 可以直接访问外部类的所有成员,包含私有的
  3. 不能添加访问修饰符,但是可以使用 final 修饰
  4. 作用域:仅仅在定义它的方法或代码块中
  5. 局部内部类可以直接访问外部类的成员
  6. 外部类在方法中,可以创建 Inner02 对象,然后调用方法即可
  7. 如果外部类和局部内部类的成员重名时,默认就近原则,如果想访问外部类的成员,使用 外部类名.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();
    }
}

匿名内部类(非常重要)

  1. 本质是类
  2. 内部类
  3. 该类没有名字
  4. 同时是一个对象

基本语法

new 类或接口(参数列表){

类体;

}

细节

  1. 匿名内部类的语法比较奇特。匿名内部类既是一个类的定义,同时它本身也是一个对象,因此从语法上看,它既有定义类的特性,也有创建对象的特征,因此可以调用匿名内部类方法
  2. 可以直接访问外部类的所有成员,包含私有的
  3. 不能添加访问修饰符,因为它的地位就是一个局部变量
  4. 作用域:仅仅在定义它的方法或代码块中
  5. 外部其他类不能访问匿名内部类
  6. 外部类和匿名内部类的成员重名时,匿名内部类访问时,默认就近原则,如果想访问外部类的成员,使用 外部类名.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("这是一幅画");
    }
}

image-20221005134717439

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();
    }
}

成员内部类

  1. 定义在外部类的成员位置
  2. 可以添加任意访问修饰符,因为它的地位就是一个成员
  3. 作用域是整个外部类类体
  4. 可以直接访问外部类成员
  5. 要使用成员内部类,在外部类创建其实例,然后使用相关的方法
  6. 外部其他类访问成员内部类:两种方法,详见代码
  7. 重名遵守就近原则,如果想访问外部类的成员,使用 外部类名.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 修饰

  1. 可以直接访问外部类的所有静态成员,包括私有的
  2. 可以添加任意修饰符
  3. 作用域:同其他的成员,为整个类体
  4. 外部其他类访问静态内部类,见代码
  5. 重名遵守就近原则,如果想访问外部类的成员,使用 外部类名.成员 去访问
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();
    }
}

练习

image-20221005152549485

5
5

总结

面向对象(基础、中级、高级)到此结束~?

我亦无他,惟手熟尔

本章练习

image-20221006083026926

结果:注意 color 是静态变量

9.0 red

100.0 red

image-20221006083453071

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;
    }
}

image-20221006084333092

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("狗会汪汪叫~");
    }
}

image-20221006084818916

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);
    }

}

image-20221006111336262

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();
    }
}

image-20221006112250070

领略程序结构:

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();
    }
}

image-20221006151237785

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("关闭空调");
            }
        }
    }
}

image-20221006152031624

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);
    }
}
0

评论

博主关闭了所有页面的评论