Java面向对象编程(高级部分)

目录

一、类变量和类方法

1、快速了解入门类变量

2、类变量内存布局

3、什么是类变量

4、如何定义类变量

5、类变量使用注意事项

6、类方法基本介绍(类比类变量简单易懂)

7、类方法使用注意事项

二、类中的代码块

三、单例设计模式

1、什么是设计模式

2、什么是单例模式

3、饿汉式和懒汉式单例模式的实现

4、饿汉式 VS 懒汉式

四、final 关键字

1、基本介绍

2、final 使用注意事项

五、抽象类

1、代码快速入门

2、抽象类的介绍

3、抽象类使用的注意事项

六、接口

1、为什么有接口

2、接口快速入门

3、基本介绍

4、举例讨论

 5、注意事项和细节

6、实现接口 vs 继承类

7、接口的多态特性

七、内部类

1、基本介绍

2、基本语法

3、快速人门代码

4、内部类的分类

5、局部内部类的使用

6、匿名内部类的使用(重要!)

7、匿名内部类的最佳实践

8、成员内部类的使用

9、静态内部类的使用


一、类变量和类方法

1、快速了解入门类变量

问题:有一群小孩在玩堆雪人,不时有新的小孩加入,请问如何知道现在共有多少人在玩?编写程序解决。

思路:

如果,设计一个 int count 表示总人数,我们在创建一个小孩时,就把 count 加 1,并且 count 是所有对象共享的就 ok 了! 我们使用类变量来解决 ChildGame.java 改进。

public class ChildGame {
    public static void main(String[] args) {

        //定义一个变量 count, 统计有多少小孩加入了游戏
        int count = 0;

        Child child1 = new Child("白骨精");
        child1.join();
        //count++;
        child1.count++;

        Child child2 = new Child("狐狸精");
        child2.join();
        //count++
        child2.count++;

        Child child3 = new Child("老鼠精");
        child3.join();
        //count++;
        child3.count++;

        //===========
        //类变量,可以通过类名来访问
        System.out.println("共有" + Child.count + " 小孩加入了游戏...");

        //下面的代码输出什么?
        System.out.println("child1.count=" + child1.count); //3
        System.out.println("child2.count=" + child2.count); //3
        System.out.println("child3.count=" + child3.count); //3
    }
}

class Child { //类
    private String name;
    //定义一个变量 count ,是一个类变量(静态变量) static 静态
    //该变量最大的特点就是会被 Child 类的所有的对象实例共享
    public static int count = 0;

    public Child(String name) {
        this.name = name;
    }

    public void join() {
        System.out.println(name + " 加入了游戏..");
    }
}

2、类变量内存布局

记住一点,static变量是对象共享,不管static变量在哪里,共识:

1)static变量是同一个类所有对象共享;

2)static类变量,在类加载的时候就生成了。

内存布局需要了解时自己搜一下(略)

3、什么是类变量

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

4、如何定义类变量

语法:

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

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

5、类变量使用注意事项

1)什么时候需要用类变量?当我们需要让某个类的所有对象都共享一个变量时,就可以考虑使用类变量(静态变量)。比如:定义学生类,统计所有学生共交多少钱。

 2)类变量与实例变量(普通属性)区别?类变量是该类的所有对象共享的,而实例变量是每个对象独享的

3)加上static称为类变量或静态变量,否则称为实例变量/普通变量/非静态变量

4)类变量可以通过 类名.类变量名  或者  对象名.类变量名 来访问,但Java设计者推荐我们使用  类名.类变量名方式访问.【前提是满足访问修饰符的访问权限和范围】

5)实例变量不能通过 类名.类变量名 方式访问

6)类变量是在类加载时就初始化了,也就是说,即使你没有创建对象,只要类加载了,就可以使用类变量了

7)类变量的生命周期是随类的加载开始,随着类消亡的销毁。

6、类方法基本介绍(类比类变量简单易懂)

类方法也叫静态方法。

形式如下:

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

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

public class StaticMethod {
    public static void main(String[] args) {
        //创建 2 个学生对象,叫学费
        Stu tom = new Stu("tom");
        //tom.payFee(100);
        Stu.payFee(100);//对不对?对

        Stu mary = new Stu("mary");
        //mary.payFee(200);
        Stu.payFee(200);//对

        //输出当前收到的总学费
        Stu.showFee();//300

        //如果我们希望不创建实例,也可以调用某个方法(即当做工具来使用)
        //这时,把方法做成静态方法时非常合适
        System.out.println("9 开平方的结果是=" + Math.sqrt(9));
        System.out.println(MyTools.calSum(10, 30));
    }
}

//开发自己的工具类时,可以将方法做成静态的,方便调用
class MyTools {
    //求出两个数的和
    public static double calSum(double n1, double n2) {
        return n1 + n2;
    }
    //可以写出很多这样的工具方法...
}

class Stu {
    private String name;//普通成员
    //定义一个静态变量,来累积学生的学费
    private static double fee = 0;

    public Stu(String name) {
        this.name = name;
    }
    //说明
    //1. 当方法使用了 static 修饰后,该方法就是静态方法
    //2. 静态方法就可以访问静态属性/变量
    public static void payFee(double fee) {
        Stu.fee += fee;//累积到
    }

    public static void showFee() {
        System.out.println("总学费有:" + Stu.fee);
    }
}

7、类方法使用注意事项

1)类方法和普通方法都是随着类的加载而加载,将结构信息存储在方法区:类方法中无this的参数,普通方法中隐含着this的参数。

2)类方法可以通过类名调用,也可以通过对象名调用。

3)普通方法和对象有关,需要通过对象名调用,比如 "对象名.方法名" ,不能通过类名调用。

4)类方法中不允许使用和对象有关的关键字,比如this和super。普通方法(成员方法)可以。

5)类方法(静态方法)中只能访问 静态变量 或 静态方法。

6)普通成员方法,既可以访问  非静态成员  ,也可以访问 静态成员 。

小结:静态方法,只能访问静态的成员,非静态的方法,可以访问静态成员和非静态成员,(必须遵守访问权限)

public class StaticMethodDetail {
    public static void main(String[] args) {
        D.hi();//ok
        //非静态方法,不能通过类名调用
        //D.say();, 错误,需要先创建对象,再调用
        new D().say();//可以

    }
}

class D {
    private int n1 = 100;
    private static int n2 = 200;

    public void say() {//非静态方法,普通方法

    }

    public static void hi() {//静态方法,类方法
        //类方法中不允许使用和对象有关的关键字,
        //比如 this 和 super。普通方法(成员方法)可以。
        //System.out.println(this.n1);
    }

    //类方法(静态方法)中 只能访问 静态变量 或静态方法
    //口诀:静态方法只能访问静态成员
    public static void hello() {
        System.out.println(n2);
        System.out.println(D.n2);
        //System.out.println(this.n2);不能使用
        hi();//OK
        //say();//错误
    }

    //普通成员方法,既可以访问 非静态成员,也可以访问静态成员
    //小结: 非静态方法可以访问 静态成员和非静态成员
    public void ok() {
        //非静态成员
        System.out.println(n1);
        say();
        //静态成员
        System.out.println(n2);
        hello();
    }
}

二、类中的代码块

有静态代码块和普通代码块。

没啥好记的,遇到的时候搜一下便知。

三、单例设计模式

1、什么是设计模式

1)静态方法和属性的经典使用。

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

2、什么是单例模式

单例(单个的实例)

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

2)单例模式有两种方式:   a.饿汉式              b.懒汉式

3、饿汉式和懒汉式单例模式的实现

步骤如下:

1)构造器私有化 → 防止直接new

2)类的内部创建对象

3)向外暴露一个静态的公共方法

4)代码实现

饿汉式单例模式代码:

public class SingleTon01 {
    public static void main(String[] args) {
        //GirlFriend xh = new GirlFriend("小红");
        //GirlFriend xb = new GirlFriend("小白");

        //通过方法可以获取对象
        GirlFriend instance = GirlFriend.getInstance();
        System.out.println(instance);

        GirlFriend instance2 = GirlFriend.getInstance();
        System.out.println(instance2);

        System.out.println(instance == instance2);//T
        //System.out.println(GirlFriend.n1);


    }
}

//有一个类, GirlFriend
//只能有一个女朋友
class GirlFriend {

    private String name;
    //public static int n1 = 100;
    //为了能够在静态方法中,返回 gf 对象,需要将其修饰为 static
    //對象,通常是重量級的對象, 餓漢式可能造成創建了對象,但是沒有使用
    private static GirlFriend gf = new GirlFriend("小红红");

    //如何保障我们只能创建一个 GirlFriend 对象
    //步骤[单例模式-饿汉式]
    // 1. 将构造器私有化
    // 2. 在类的内部直接创建对象(该对象是 static)
    // 3. 提供一个公共的 static 方法,返回 gf 对象
    private GirlFriend(String name) {
        System.out.println("構造器被調用.");
        this.name = name;
    }

    public static GirlFriend getInstance() {
        return gf;
    }

    @Override
    public String toString() {
        return "GirlFriend{" + "name='" + name + '\'' + '}';
    }

}

懒汉式单例模式代码:

public class SingleTon02 {
    public static void main(String[] args) {
        //new Cat("大黃");
        //System.out.println(Cat.n1);
        Cat instance = Cat.getInstance();
        System.out.println(instance);

        //再次調用 getInstance
        Cat instance2 = Cat.getInstance();
        System.out.println(instance2);
        System.out.println(instance == instance2);//T
    }
}

//希望在程序運行過程中,只能創建一個 Cat 對象
//使用單例模式
class Cat {
    private String name;
    public static int n1 = 999;
    private static Cat cat; //默認是 null

    //步驟
    //1.仍然構造器私有化
    //2.定義一個 static 靜態屬性對象
    //3.提供一個 public 的 static 方法,可以返回一個 Cat 對象
    //4.懶漢式,只有當用戶使用 getInstance 時,才返回 cat 對象, 後面再次調用時,會返回上次創建的 cat 對象
    // 從而保證了單例
    private Cat(String name) {
        System.out.println("構造器調用...");
        this.name = name;
    }

    public static Cat getInstance() {
        if (cat == null) {//如果還沒有創建 cat 對象
            cat = new Cat("小可愛");
        }
        return cat;
    }

    @Override
    public String toString() {
        return "Cat{" + "name='" + name + '\'' + '}';
    }
}

4、饿汉式 VS 懒汉式

1)两者最主要的区别在于创建对象的时机不同:饿汉式是在类加载就创建了实例对象,而懒汉式是在使用时才创建。

2)饿汉式不存在线程安全问题,懒汉式存在线程安全问题

3)饿汉式存在浪费资源的可能,因为如果程序员一个对象实例都没有使用,那么饿汉式创建的对象就浪费了,懒汉式是使用时才创建,就不存在这个问题

4)在JavaSE标准类中,java.lang.Runtime就是经典的单例模式。

四、final 关键字

1、基本介绍

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

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

1)当不希望类被继承时,可以用final修饰

2)当不希望父类的某个方法被子类覆盖或重写时,可以用final关键字修饰

3)当不希望类的某个属性的值被修改,可以用final修饰

4)当不希望某个局部变量被修改,可以使用final修饰

public class Final01 {
    public static void main(String[] args) {
        E e = new E();
        //e.TAX_RATE = 0.09;
    }
}

//如果我们要求 A 类不能被其他类继承
//可以使用 final 修饰 A 类
final class A {
    
}

//class B extends A {}  (这是错误的)

class C {
    //如果我们要求 hi 不能被子类重写
    //可以使用 final 修饰 hi 方法
    public final void hi() {
    }
}

class D extends C {
    // @Override    (这个重写是错误的)
    // public void hi() {
    //      System.out.println("重写了 C 类的 hi 方法..");
    // }
}

//当不希望类的的某个属性的值被修改,可以用 final 修饰
class E {
    public final double TAX_RATE = 0.08;//常量
}

//当不希望某个局部变量被修改,可以使用 final 修饰
class F {
    public void cry() {
        //这时,NUM 也称为 局部常量
        final double NUM = 0.01;
        //NUM = 0.9; 
        System.out.println("NUM=" + NUM);
    }
}

2、final 使用注意事项

1)final 修饰的属性又叫常量,一般用 XX_XX_XX 来命名

2)final 修饰的属性在定义时,必须赋初值,并且以后不能再修改,赋值可以在如下位置之一【选择一个位置赋初值即可】:①定义时如public final double TAX_RATE=0.08;②在构造器中;③在代码块中。

3)如果final修饰的属性是静态的,则初始化的位置只能是:①定义时;②在静态代码块不能在构造器中赋值;

4)final类不能继承,但是可以实例化对象;

5)如果类不是final类,但是含有final方法,则该方法虽然不能重写,但是可以被继承。

public class FinalDetail01 {
    public static void main(String[] args) {
        CC cc = new CC();
        new EE().cal();
    }
}

class AA {
    /*1. 定义时:如 public final double TAX_RATE=0.08;
      2. 在构造器中
      3. 在代码块中 */
    public final double TAX_RATE = 0.08;   //1.定义时赋值
    public final double TAX_RATE2;
    public final double TAX_RATE3;

    public AA() {//构造器中赋值
        TAX_RATE2 = 1.1;
    }

    {//在代码块赋值
        TAX_RATE3 = 8.8;
    }
}

class BB {
    /*如果 final 修饰的属性是静态的,则初始化的位置只能是
        1 定义时
        2 在静态代码块 不能在构造器中赋值。 */
    public static final double TAX_RATE = 99.9;
    public static final double TAX_RATE2;

    static {
        TAX_RATE2 = 3.3;
    }
}

//final 类不能继承,但是可以实例化对象
final class CC {
}

//如果类不是 final 类,但是含有 final 方法,则该方法虽然不能重写,但是可以被继承
//即,仍然遵守继承的机制.
class DD {
    public final void cal() {
        System.out.println("cal()方法");
    }
}
class EE extends DD { }

5)一般来说,如果一个类已经是final类了,就没有必要再将方法修饰成final方法。

6)final 不能修饰构造方法(即构造器)

7)final 和 static 往往搭配使用,效率更高,不会导致类加载。底层编译器做了优化处理。

8)包装类 (Integer,Double,Float,Boolean等都是final),  String也是final类。

public class FinalDetail02 {
    public static void main(String[] args) {
        System.out.println(BBB.num);
        //包装类,String 是 final 类,不能被继承
    }
}

//final 和 static 往往搭配使用,效率更高,不会导致类加载.底层编译器做了优化处理
class BBB {
    public final static int num = 10000;

    static {
        System.out.println("BBB 静态代码块被执行");
    }
}

final class AAA{
    //一般来说,如果一个类已经是 final 类了,就没有必要再将方法修饰成 final 方法
    //public final void cry() {}
}

五、抽象类

1、代码快速入门

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

abstract class Animal {
    private String name;

    public Animal(String name) {
        this.name = name;
    }

    //思考:这里 eat 这里你实现了,其实没有什么意义
    //即: 父类方法不确定性的问题
    //===> 考虑将该方法设计为抽象(abstract)方法
    //===> 所谓抽象方法就是没有实现的方法
    //===> 所谓没有实现就是指,没有方法体
    //===> 当一个类中存在抽象方法时,需要将该类声明为 abstract 类
    //===> 一般来说,抽象类会被继承,有其子类来实现抽象方法.

    // public void eat() {
    //      System.out.println("这是一个动物,但是不知道吃什么..");
    // }
    public abstract void eat() ; 
}

2、抽象类的介绍

3、抽象类使用的注意事项

1)抽象类不能被实例化

2)抽象类不一定要包含abstract方法。也就是说,抽象类可以没有abstract方法

3)一旦类包含了abstract方法,则这个类必须声明为abstract

4)abstract只能修饰类和方法,不能修饰属性和其他的。

public class AbstractDetail01 {
    public static void main(String[] args) { //抽象类,不能被实例化
        //new A();
    }
}

//抽象类不一定要包含 abstract 方法。也就是说,抽象类可以没有 abstract 方法
//,还可以有实现的方法。
abstract class A {
    public void hi() {
        System.out.println("hi");
    }
}

//一旦类包含了 abstract 方法,则这个类必须声明为 abstract
abstract class B {
    public abstract void hi();
}

//abstract 只能修饰类和方法,不能修饰属性和其它的
class C { // public abstract
    int n1 = 1;
}

5)抽象类可以有任意成员【抽象类本质还是类】,比如:非抽象方法、构造器、静态属性等等;

6)抽象方法不能有主体,即不能实现

7) 如果一个类继承了抽象类,则它必须实现抽象类的所有抽象方法,除非它自己也声明为abstract类。

8)抽象方法不能使用private、final 和 static 来修饰,因为这些关键字都是和重写相违背的。

public class AbstractDetail02 {
    public static void main(String[] args) {
        System.out.println("hello");
    }
}

//抽象方法不能使用 private、final 和 static 来修饰,因为这些关键字都是和重写相违背的
abstract class H {
    public abstract void hi();//抽象方法
}

//如果一个类继承了抽象类,则它必须实现抽象类的所有抽象方法,除非它自己也声明为 abstract 类
abstract class E {
    public abstract void hi();
}

abstract class F extends E {
}

class G extends E {
    @Override
    public void hi() {  //这里相等于 G 子类实现了父类 E 的抽象方法,所谓实现方法,就是有方法体
    }
}

//抽象类的本质还是类,所以可以有类的各种成员
abstract class D {
    public int n1 = 10;
    public static String name = "韩顺平教育";

    public void hi() {
        System.out.println("hi");
    }

    public abstract void hello();

    public static void ok() {
        System.out.println("ok");
    }
}

六、接口

1、为什么有接口

因为方便,能帮助少写些代码。

2、接口快速入门

public interface UsbInterface {         //接口
    //规定接口的相关方法,老师规定的.即规范...
    public void start();

    public void stop();
}

public class Camera implements UsbInterface {    //实现接口,就是把接口方法实现
    @Override
    public void start() {
        System.out.println("相机开始工作...");
    }

    @Override
    public void stop() {
        System.out.println("相机停止工作....");
    }
}

//Phone 类 实现 UsbInterface
//解读 1. 即 Phone 类需要实现 UsbInterface 接口 规定/声明的方法
public class Phone implements UsbInterface {
    @Override
    public void start() {
        System.out.println("手机开始工作...");
    }

    @Override
    public void stop() {
        System.out.println("手机停止工作.....");
    }
}

public class Interface01 {
    public static void main(String[] args) {
        //创建手机,相机对象
        //Camera 实现了 UsbInterface
        Camera camera = new Camera();
        //Phone 实现了 UsbInterface
        Phone phone = new Phone();
        //创建计算机
        Computer computer = new Computer();
        computer.work(phone);
        System.out.println("===============");
        computer.work(camera);//把相机接入到计算机//把手机接入到计算机
    }
}

3、基本介绍

4、举例讨论

public interface DBInterface { //项目经理

    public void connect();//连接方法

    public void close();//关闭连接
}

//A 程序
public class MysqlDB implements DBInterface {
    @Override
    public void connect() {
        System.out.println("连接 mysql");
    }

    @Override
    public void close() {
        System.out.println("关闭 mysql");
    }
}

//B 程序员连接 Oracle
public class OracleDB implements DBInterface {
    @Override
    public void connect() {
        System.out.println("连接 oracle");
    }

    @Override
    public void close() {
        System.out.println("关闭 oracle");
    }
}

public class Interface03 {
    public static void main(String[] args) {
        MysqlDB mysqlDB = new MysqlDB();
        t(mysqlDB);
        OracleDB oracleDB = new OracleDB();
        t(oracleDB);
    }

    public static void t(DBInterface db) {
        db.connect();
        db.close();
    }
}

 5、注意事项和细节

1)接口不能被实例化

2)接口中的所有方法是 public 方法,接口中抽象方法,可以不用abstract修饰,图示:

3)一个普通类实现接口,就必须将该接口的所有方法都实现

4)抽象类实现接口,可以不用实现接口的方法 

public class InterfaceDetail01 {
    public static void main(String[] args) {
        //new IA();
    }
}

// 1.接口不能被实例化
// 2.接口中所有的方法是 public 方法, 接口中抽象方法,可以不用 abstract 修饰
// 3.一个普通类实现接口,就必须将该接口的所有方法都实现,可以使用 alt+enter 来解决
// 4.抽象类去实现接口时,可以不实现接口的抽象方法
interface IA {
    void say();//修饰符 public protected 默认private

    void hi();
}

class Cat implements IA {
    @Override
    public void say() {
    }

    @Override
    public void hi() {
    }
}

abstract class Tiger implements IA {
}

5)一个类同时可以实现多个接口

6)接口中的属性,只能是 final 的,而且是 public static final 修饰符。比如int a=1;实际上是public static final int a=1;(必须初始化)

7)接口中属性的访问形式:接口名.属性名

8)接口不能继承其他的类,但是可以继承多个别的接口

9)接口的修饰符只能是public和默认这点和类的修饰符是一样的

public class InterfaceDetail02 {
    public static void main(String[] args) { //已证明 接口中的属性,是 public static final
        System.out.println(IB.n1);//说明 n1 就是 static
        //IB.n1 = 30; 说明 n1 是 final
    }
}

interface IB {
    //接口中的属性,只能是 final 的,而且是 public static final 修饰符
    int n1 = 10; //等价 public static final int n1 = 10;

    void hi();
}

interface IC {
    void say();
}

//接口不能继承其它的类,但是可以继承多个别的接口
interface ID extends IB, IC {
}

//接口的修饰符 只能是 public 和默认,这点和类的修饰符是一样的
interface IE {
}

//一个类同时可以实现多个接口
class Pig implements IB, IC {
    @Override
    public void hi() {
    }

    @Override
    public void say() {
    }
}

6、实现接口 vs 继承类

代码解释得十分清楚

public class ExtendsVsInterface {
    public static void main(String[] args) {
        LittleMonkey wuKong = new LittleMonkey("悟空");
        wuKong.climbing();
        wuKong.swimming();
        wuKong.flying();
    }
}

//猴子
class Monkey {
    private String name;

    public Monkey(String name) {
        this.name = name;
    }

    public void climbing() {
        System.out.println(name + " 会爬树...");
    }

    public String getName() {
        return name;
    }
}

//接口
interface Fishable {
    void swimming();
}

interface Birdable {
    void flying();
}

//继承
//小结: 当子类继承了父类,就自动的拥有父类的功能
//如果子类需要扩展功能,可以通过实现接口的方式扩展.
//可以理解 实现接口 是 对 java 单继承机制的一种补充.

class LittleMonkey extends Monkey implements Fishable, Birdable {
    public LittleMonkey(String name) {
        super(name);
    }

    @Override
    public void swimming() {
        System.out.println(getName() + " 通过学习,可以像鱼儿一样游泳...");
    }

    @Override
    public void flying() {
        System.out.println(getName() + " 通过学习,可以像鸟儿一样飞翔...");
    }
}

7、接口的多态特性

public class InterfacePolyParameter {
    public static void main(String[] args) {
        //接口的多态体现
        //接口类型的变量 if01 可以指向 实现了 IF 接口类的对象实例
        IF if01 = new Monster();
        if01 = new Car();

        //继承体现的多态
        //父类类型的变量 a 可以指向 继承 AAA 的子类的对象实例
        AAA a = new BBB();
        a = new CCC();
    }
}

interface IF {
}

class Monster implements IF {
}

class Car implements IF {
}

class AAA {
}

class BBB extends AAA {
}

class CCC extends AAA {
}

//--------------------------------------------------------
public class InterfacePolyArr {
    public static void main(String[] args) {
        //多态数组 -> 接口类型数组
        Usb[] usbs = new Usb[2];
        usbs[0] = new Phone_();
        usbs[1] = new Camera_();

        /* 给 Usb 数组中,存放 Phone 和 相机对象,Phone 类还有一个特有的方法 call(),
           请遍历 Usb 数组,如果是 Phone 对象,除了调用 Usb 接口定义的方法外,
           还需要调用 Phone 特有方法 call
        */

        for (int i = 0; i < usbs.length; i++) {
            usbs[i].work();//动态绑定
            //和前面一样,我们仍然需要进行类型的向下转型
            if (usbs[i] instanceof Phone_) {//判断他的运行类型是 Phone_
                ((Phone_) usbs[i]).call();
            }
        }
    }
}

interface Usb {
    void work();
}

class Phone_ implements Usb {
    public void call() {
        System.out.println("手机可以打电话...");
    }

    @Override
    public void work() {
        System.out.println("手机工作中...");
    }
}

class Camera_ implements Usb {
    @Override
    public void work() {
        System.out.println("相机工作中...");
    }
}

/*** 演示多态传递现象 */
public class InterfacePolyPass {
    public static void main(String[] args) {
        //接口类型的变量可以指向,实现了该接口的类的对象实例
        IG ig = new Teacher();
        //如果 IG 继承了 IH 接口,而 Teacher 类实现了 IG 接口
        //那么,实际上就相当于 Teacher 类也实现了 IH 接口.
        //这就是所谓的 接口多态传递现象
        IH ih = new Teacher();
    }
}

interface IH {
    void hi();
}

interface IG extends IH {
}

class Teacher implements IG {
    @Override
    public void hi() {
    }
}

七、内部类

如果定义类在局部位置(方法中/代码块) :   (1) 局部内部类         (2) 匿名内部类

定义在成员位置: (1) 成员内部类          (2) 静态内部类

1、基本介绍

2、基本语法

3、快速人门代码

public class InnerClass01 { //外部其他类
    public static void main(String[] args) {

    }
}

class Outer { //外部类
    private int n1 = 100;//属性

    public Outer(int n1) {//构造器
        this.n1 = n1;
    }

    public void m1() {//方法
        System.out.println("m1()");
    }

    {//代码块
        System.out.println("代码块...");
    }

    class Inner { //内部类, 在 Outer 类的内部
    }
}

4、内部类的分类

5、局部内部类的使用

/*** 演示局部内部类的使用 */
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.作用域 : 仅仅在定义它的方法或代码块中
        final class Inner02 {//局部内部类(本质仍然是一个类)
            //2.可以直接访问外部类的所有成员,包含私有的
            private int n1 = 800;

            public void f1() {
                //5. 局部内部类可以直接访问外部类的成员,比如下面 外部类 n1 和 m2() //7. 如果外部类和局部内部类的成员重名时,默认遵循就近原则,如果想访问外部类的成员, // 使用 外部类名.this.成员)去访问
                //解读 Outer02.this 本质就是外部类的对象, 即哪个对象调用了 m1, Outer02.this 就是哪个对象
                System.out.println("n1=" + n1 + " 外部类的 n1=" + Outer02.this.n1);
                System.out.println("Outer02.this hashcode=" + Outer02.this);
                m2();
            }
        }
        //6. 外部类在方法中,可以创建 Inner02 对象,然后调用方法即可
        Inner02 inner02 = new Inner02();
        inner02.f1();
    }
}

6、匿名内部类的使用(重要!)

/*** 演示匿名内部类的使用 */
public class AnonymousInnerClass {
    public static void main(String[] args) {
        Outer04 outer04 = new Outer04();
        outer04.method();
    }
}

class Outer04 { //外部类
    private int n1 = 10;//属性

    public void method() {//方法
        //基于接口的匿名内部类
        //解读
        //1.需求: 想使用 IA 接口,并创建对象
        //2.传统方式,是写一个类,实现该接口,并创建对象
        //3.需求是 Tiger/Dog 类只是使用一次,后面再不使用
        //4. 可以使用匿名内部类来简化开发
        //5. tiger 的编译类型 ? IA
        //6. tiger 的运行类型 ? 就是匿名内部类 Outer04$1
        /*
            我们看底层 会分配 类名 Outer04$1
            class Outer04$1 implements IA {
                @Override
                public void cry() {
                    System.out.println("老虎叫唤...");
                }
            }
        */
        //7. jdk 底层在创建匿名内部类 Outer04$1,立即马上就创建了 Outer04$1 实例,并且把地址
        // 返回给 tiger

        // 8. 匿名内部类使用一次,就不能再使用
        IA tiger = new IA() {
            @Override
            public void cry() {
                System.out.println("老虎叫唤...");
            }
        };
        System.out.println("tiger 的运行类型=" + tiger.getClass());
        tiger.cry();
        tiger.cry();
        tiger.cry();


        // IA tiger = new Tiger();
        // tiger.cry();

        //演示基于类的匿名内部类
        //分析

        // 1. father 编译类型 Father
        // 2. father 运行类型 Outer04$2
        // 3. 底层会创建匿名内部类
        /*
        class Outer04$2 extends Father {
            @Override
            public void test() {
                System.out.println("匿名内部类重写了 test 方法");
            }
        }
        */
        //4. 同时也直接返回了 匿名内部类 Outer04$2 的对象
        //5. 注意("jack") 参数列表会传递给 构造器
        Father father = new Father("jack") {
            @Override
            public void test() {
                System.out.println("匿名内部类重写了 test 方法");
            }
        };
        System.out.println("father 对象的运行类型=" + father.getClass());//Outer04$2
        father.test();

        //基于抽象类的匿名内部类
        Animal animal = new Animal() {
            @Override
            void eat() {
                System.out.println("小狗吃骨头...");
            }
        };
        animal.eat();
    }
}

interface IA {//接口

    public void cry();
}

//class Tiger implements IA {
//    @Override
//    public void cry() {
//        System.out.println("老虎叫唤...");
//    }
//}
//
//class Dog implements IA {
//    @Override
//    public void cry() {
//        System.out.println("小狗汪汪...");
//    }
//}

class Father {//类

    public Father(String name) {//构造器
        System.out.println("接收到 name=" + name);
    }

    public void test() {//方法
    }
}

abstract class Animal { //抽象类
    abstract void eat();
}

2)匿名内部类的语法比较奇特,请注意,因为匿名内部类既是一个类的定义,同时它本身也是一个对象,因此从语法上看,它既有定义类的特征,也有创建对象的特征,对前面代码分析可以看出这个特点,因此可以调用匿名内部类方法

3)可以直接访问外部类的所有成员,包含私有的

4)不能添加访问修饰符,因为它的地位就是一个局部变量

5)作用域:仅仅在定义它的方法或代码块中

6)匿名内部类 → 访问 → 外部类成员【访问方式:直接访问】

7)外部其他类 → 不能访问 → 匿名内部类(因为匿名内部类地位是一个局部变量)

8)如果外部类和匿名内部类的成员重名时,匿名内部类访问的话,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.this.成员)去访问

public class AnonymousInnerClassDetail {
    public static void main(String[] args) {
        Outer05 outer05 = new Outer05();
        outer05.f1();
        //外部其他类---不能访问----->匿名内部类
        System.out.println("main outer05 hashcode=" + outer05);
    }
}

class Outer05 {
    private int n1 = 99;

    public void f1() {
        //创建一个基于类的匿名内部类
        //不能添加访问修饰符,因为它的地位就是一个局部变量
        //作用域 : 仅仅在定义它的方法或代码块中
        Person p = new Person() {
            private int n1 = 88;

            @Override
            public void hi() {
                //可以直接访问外部类的所有成员,包含私有的
                //如果外部类和匿名内部类的成员重名时,匿名内部类访问的话,
                //默认遵循就近原则,如果想访问外部类的成员,则可以使用 (外部类名.this.成员)去访问
                System.out.println("匿名内部类重写了 hi 方法 n1=" + n1 + " 外部内的 n1=" + Outer05.this.n1);
                //Outer05.this 就是调用 f1 的 对象
                System.out.println("Outer05.this hashcode=" + Outer05.this);

            }
        };
        p.hi();//动态绑定, 运行类型是 Outer05$1

        //也可以直接调用, 匿名内部类本身也是返回对象
        // class 匿名内部类 extends Person {}
        // new Person(){
        // @Override
        // public void hi() {
        // System.out.println("匿名内部类重写了 hi 方法,哈哈...");
        // }
        // @Override
        // public void ok(String str) {
        // super.ok(str);
        // }
        // }.ok("jack");
    }
}

class Person {//类

    public void hi() {
        System.out.println("Person hi()");
    }

    public void ok(String str) {
        System.out.println("Person ok() " + str);
    }
}

7、匿名内部类的最佳实践

当做实参直接传递,简洁高效。


public class InnerClassExercise01 {
    public static void main(String[] args) {

        //当做实参直接传递,简洁高效
        f1(new IL() {
            @Override
            public void show() {
                System.out.println("这是一副名画~~...");
            }
        });

        //传统方法
        f1(new Picture());

    }

    //静态方法,形参是接口类型
    public static void f1(IL il) {
        il.show();
    }
}

//接口
interface IL {
    void show();
}

//类->实现 IL => 编程领域 (硬编码)
class Picture implements IL {
    @Override
    public void show() {
        System.out.println("这是一副名画 XX...");
    }
}

8、成员内部类的使用

public class MemberInnerClass01 {
    public static void main(String[] args) {
        Outer08 outer08 = new Outer08();
        outer08.t1();
        //外部其他类,使用成员内部类的三种方式
        //解读
        //第一种方式
        //outer08.new Inner08(); 相当于把 new Inner08()当做是 outer08 成员
        //这就是一个语法,不要特别的纠结

        Outer08.Inner08 inner08 = outer08.new Inner08();
        inner08.say();
        // 第二方式 在外部类中,编写一个方法,可以返回 Inner08 对象
        Outer08.Inner08 inner08Instance = outer08.getInner08Instance();
        inner08Instance.say();
    }
}

class Outer08 { //外部类
    private int n1 = 10;
    public String name = "张三";

    private void hi() {
        System.out.println("hi()方法...");
    }

    //1.注意: 成员内部类,是定义在外部内的成员位置上
    //2.可以添加任意访问修饰符(public、protected 、默认、private),因为它的地位就是一个成员
    public class Inner08 {//成员内部类
        private double sal = 99.8;
        private int n1 = 66;

        public void say() {
            //可以直接访问外部类的所有成员,包含私有的
            //如果成员内部类的成员和外部类的成员重名,会遵守就近原则.
            //,可以通过 外部类名.this.属性 来访问外部类的成员
            System.out.println("n1 = " + n1 + " name = " + name + " 外部类的 n1=" + Outer08.this.n1);
            hi();
        }
    }

    //方法,返回一个 Inner08 实例
    public Inner08 getInner08Instance() {
        return new Inner08();
    }

    //写方法
    public void t1() {
        //使用成员内部类
        //创建成员内部类的对象,然后使用相关的方法
        Inner08 inner08 = new Inner08();
        inner08.say();
        System.out.println(inner08.sal);
    }
}

9、静态内部类的使用

 代码:

public class StaticInnerClass01 {
    public static void main(String[] args) {
        Outer10 outer10 = new Outer10();
        outer10.m1();

        //外部其他类 使用静态内部类
        //方式 1
        //因为静态内部类,是可以通过类名直接访问(前提是满足访问权限)

        Outer10.Inner10 inner10 = new Outer10.Inner10();
        inner10.say();

        //方式 2
        //编写一个方法,可以返回静态内部类的对象实例.
        Outer10.Inner10 inner101 = outer10.getInner10();
        System.out.println("============");
        inner101.say();
        Outer10.Inner10 inner10_ = Outer10.getInner10_();
        System.out.println("************");
        inner10_.say();
    }
}

class Outer10 { //外部类
    private int n1 = 10;
    private static String name = "张三";

    private static void cry() {
    }

    //Inner10 就是静态内部类 //1. 放在外部类的成员位置
    //2. 使用 static 修饰
    //3. 可以直接访问外部类的所有静态成员,包含私有的,但不能直接访问非静态成员
    //4. 可以添加任意访问修饰符(public、protected 、默认、private),因为它的地位就是一个成员
    //5. 作用域 :同其他的成员,为整个类体
    static class Inner10 {
        private static String name = "韩顺平教育";

        public void say() {
            //如果外部类和静态内部类的成员重名时,静态内部类访问的时,
            //默认遵循就近原则,如果想访问外部类的成员,则可以使用 (外部类名.成员)
            System.out.println(name + " 外部类 name= " + Outer10.name);
            cry();
        }
    }

    public void m1() { //外部类---访问------>静态内部类 访问方式:创建对象,再访问
        Inner10 inner10 = new Inner10();
        inner10.say();
    }

    public Inner10 getInner10() {
        return new Inner10();
    }

    public static Inner10 getInner10_() {
        return new Inner10();
    }
}

我是吕同学,祝自己也祝您变强了~

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

吕飞雨的头发不能秃

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值