Java入门-面向对象(进阶)

今日语录:佛说:生死疲劳,从贪欲起;少欲无为,身心自在。

一、类变量和类方法

1、类变量(静态变量)

  • 类变量的内存布局

    1、static变量是对象共享不管static变量在哪里

    2、static变量是同一个类所有对象共享

    3、static类变量,在类加载的时候就生成了

    4、类变量是随着类的加载而创建,所以即使没有创建对象实例也是可以访问的

  • 什么是类变量

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

  • 如何定义类变量

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

    类名.类变量名
    or
    对象名.类变量名【静态变量的访问修饰符的访问权限和范围 和 普通属性是一样的】
    推荐使用:类名.类变量名
    • 类变量的使用注意事项和细节

      1、什么时候需要类变量

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

      2、类变量与示例变量(普通属性)的区别

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

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

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

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

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

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

2、类方法(静态方法)

  • 简介

    类方法也叫静态方法

    形式如下:

    访问修饰符 static 数据返回类型 方法名(){ } 【推荐写法】
    static 访问修饰符 s数据返回类型 方法名(){ }
  • 类方法的调用:

    使用方式:类名.类方法名 或者 对象名.类方法名【前提是 满足访问修饰符的访问权限和范围】

  • 类方法经典的使用场景

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

    比如:工具类中的方法 utils

    Math类,Arrays类,Collections类

    2、小结

    程序员实际开发中,往往会将一些通用的方法,设计成静态方法,这样我们不需要创建对象时就可以调用,比如打印一维数组,冒泡排序,完成某个计算任务

  • 类方法使用注意事项和细节

    1、类方法和普通方法都是随着类的加载而加载,将结构信息存储在方法区:

    类方法中无this的参数

    普通方法中隐含无this的参数

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

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

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

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

    6、普通成员方法,可以访问 普通变量(方法),也可以访问静态变量(方法)

    小结

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

二、理解main方法语法

  • 深入理解main方法

    解释main方法的形式:public static void main(String[] args){}

    1、java虚拟机需要调用类的main(),所以该方法的访问权限必须是public

    2、java虚拟机在执行main()方法时不必创建对象,所以该方法必须是static

    3、该方法接收String类型的数组参数,该数组中保存执行java命令时传递给所运行的类的参数

    4、java执行的程序

  • 特别提示

    1、在main()方法中,我们可以直接调用main方法所在类的静态方法访问静态属性

    2、不能直接访问该类中的非静态成员,必须创建该类的一个实例对象后,才能通过这个对象去访问类中的非静态成员

三、代码块

  • 简介

    代码块又称为初始化块,属于类中的成员【即 是类的一部分】,类似于方法,将逻辑语句封装在方法体中,通过{}包围起来。

    但和方法不同,没有方法名,没有返回,没有参数,只有方法体,而且不用通过对象或类显式调用,而是加载类时,或创建对象时隐式调用

  • 基本语法

    [修饰符]{

    代码

    };

  • 注意

    1、修饰符可选,要写的话,也只能写static

    2、代码块分为两类,使用static修饰的叫静态代码块,没有static修饰的,叫普通代码块

    3、逻辑语句可以为任何逻辑语句(输入、输出、方法调用、循环、判断等)

    4、;号可以协商,也可省略

    • 代码块使用注意事项和细节

      1、static代码块也叫进芒果台代码块,作用就是对类进行初始化,而且它随着类的加载而执行,并且只会执行一次,如果是普通代码块,每创建一个对象,就执行

      2、类什么时候加载

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

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

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

      3、普通的代码块,在创建对象实例时,会被隐式的调用

      被创建一次,就会调用一次

      如果只会使用类的静态成员时,普通代码块并不会执行

      小结:

      1、static代码块是类加载时,执行,只会执行一次

      2、普通代码块是在创建对象时调用

      4、创建一个对象时,在一个类调用顺序是:

      1、调用静态代码块和静态属性初始化(注意:静态代码块和静态属性初始化调用的优先级一样,如果有多个静态代码块和多个静态变量初始化,则按他们定义的顺序调用)

      2、调用普通代码块和普通属性的初始化(注意:普通代码块和普通属性初始化调用的优先级一样,如果有多个普通态代码块和多个普通属性初始化,则按他们定义的顺序调用)

      5、构造方法(构造器)的最前面其实隐含了super()和调用普通代码块,静态相关的代码块,属性初始化,在类加载时,就执行完毕,因此优先于构造器和普通代码块执行的

      6、创建一个子类时(继承关系),他们的静态代码块,静态属性初始化,普通代码块,普通属性初始化,构造方法的调用顺序如下:

      1、父类的静态代码块和静态属性(优先级一样,按定义顺序执行)

      2、子类的静态代码块和静态属性(优先级一样,按定义顺序执行)

      3、父类的普通代码块和普通属性初始化(优先级一样,按定义顺序执行)

      4、父类的构造方法

      5、子类的普通代码块和普通属性初始化(优先级一样,按定义顺序执行)

      6、子类的构造方法

      7、静态代码块只能直接调用静态成员(静态属性和静态方法),普通代码块可以调用任意成员。


四、单例设计模式

  • 什么是设计模式

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

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

  • 什么是单例模式

    单例(单个实例)

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

    2、单例模式有两种方法:

    【饿汉式】
    1 将构造器私有化=>防止在类的外面直接创建
    2 在类的内部直接创建对象(该对象是static)
    3 提供一个公共的static方法,返回该对象

    package com.hspedu.single_;

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

    【懒汉式】
    1 构造器私有化
    2 定义一个static静态属性对象
    3 提供一个Public的static方法,可以返回一个对象
    package com.hspedu.single_;

    /**
     * 演示懶漢式的單例模式
     */

    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 + ''' +
                    '}';
        }
    }
  • 饿汉式和懒汉式区别

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

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

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

    4、在我们java SE标准类中,java.lang.Runtime就是经典的单例模式

五、final关键字

  • 基本介绍

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

    在以下情况,,需要用到final

    1、当不希望类被继承时

    2、当不希望父类的某个方法被子类覆盖或重写时

    3、当不希望类的某个属性的值被修改

    4、当不希望某个局部变量被修改

  • 使用注意事项和细节

    1、final修饰的属性又叫常量,一般用xx_xx_xx命名

    2、final修饰的属性在定义时,必须赋初值,并且以后不能再修改,赋值可以在如下位置之一

    1、定义时:如public final double TAX_TATE = 0.08;

    2、在构造器中

    3、在代码块中

    3、如果final修饰的属性是静态的,则初始化的位置只能是

    1、定义时

    2、在静态代码块  不能再构造器中赋值

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

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

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

    7、final不能修饰构造方法(构造器)

    8、final和static往往搭配使用,效果更高,底层编译器做了优化处理

    class Demo{
     public static final int i = 16;
     static{
      System.out.println("韩顺平教育~");
     }
    }

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

六、抽象类

  • 小结

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

  • 抽象类的介绍

    1、用abstract关键字来修饰一个类时,这个类就是抽象类

    2、用abstract关键字来修饰一个方法,这个方法就是抽象方法

    访问修饰符  abstract  返回类型 方法名(参数列表)

    3、抽象类的价值更多作用是在于设计,是设计者设计好之后,让子类继承并实现抽象类

    4、抽象类,是考官比较爱问的知识点,在框架和设计模式使用较多

  • 抽象类使用的注意事项和使用细节

    1、抽象类不能被实例化

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

    方法

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

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

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

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

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

Java入门-面向对象(进阶)
image-20211122233105336

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

  • 最佳实践

    设计一个抽象类(Template)能完成以下功能:

    1、编写方法calculateTime(),可以计算某段代码的耗用时间

    2、编写抽象方法code();

    3、编写一个子类Sub,继承抽象类Template,并实现code方法

    4、编写一个测试类TestTemplate,看看是否好用

    package com.hspedu.abstract_;

    abstract public class Template //抽象类-模板设计模式

     public abstract void job();//抽象方法

     public void calculateTime() {//实现方法,调用job方法
         //得到开始的时间
         long start = System.currentTimeMillis();
         job(); //动态绑定机制
         //得的结束的时间
         long end = System.currentTimeMillis();
         System.out.println("任务执行时间 " + (end - start));
     }
    }

    package com.hspedu.abstract_;

    public class AA extends Template {

     //计算任务
     //1+....+ 800000
     @Override
     public void job() //实现Template的抽象方法job

         long num = 0;
         for (long i = 1; i <= 800000; i++) {
             num += i;
         }
     }

    //    public void job2() {
    //        //得到开始的时间
    //        long start = System.currentTimeMillis();
    //        long num = 0;
    //        for (long i = 1; i <= 200000; i++) {
    //            num += i;
    //        }
    //        //得的结束的时间
    //        long end = System.currentTimeMillis();
    //        System.out.println("AA 执行时间 " + (end - start));
    //    }
    }

    package com.hspedu.abstract_;

    public class BB extends Template{

     public void job() {//这里也去,重写了Template的job方法

         long num = 0;
         for (long i = 1; i <= 80000; i++) {
             num *= i;
         }

     }
    }

    package com.hspedu.abstract_;

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

         AA aa = new AA();
         aa.calculateTime(); //这里还是需要有良好的OOP基础,对多态

         BB bb = new BB();
         bb.calculateTime();
     }
    }

八、接口

  • 基本介绍

    接口就是给出一些没有实现的方法,封装到一起,到某个类要使用的时候,再根据具体情况把这些方法实现出来。

    语法:

    interface 接口名{
     // 属性
     // 方法(1、抽象方法 2、默认实现方法3、静态方法)
    }

    class 类名 implements 接口{
     自己属性;
     自己方法;
     必须实现的接口的抽象方法
    }
    小结:
    1、在jdk7.0以前,接口里的所有方法都没有方法体,即都是抽象方法
    2、jdk8.0后接口可以有静态方法,默认方法,也就是说接口中可以有方法的具体实现
  • 注意事项和使用细节

    1、接口不能被实例化

    2、接口中所有方法是public方法,接口中抽象方法,可以不用abstract关键字修饰

    void aaa();
    ==> 实际上是:abstract void aaa();

    3、一个普通类实现接口,就必须将该接口的所有方法都实现(IDEA可以使用快捷键alt+enter来解决)

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

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

    6、接口中的属性,只能是final的,而且是public static final修饰符,比如:

    int a = 1; 实际上是==>public static final int a = 1;(必须初始化)

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

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

    interface A extends B,C{}

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

Java入门-面向对象(进阶)
image-20211123231239028

1、接口 VS继承

package com.hspedu.interface_;

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() + " 通过学习,可以像鸟儿一样飞翔...");
    }
}

  • 实现接口vs继承类

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

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

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

    2、接口比继承更加灵活

    接口比继承更加灵活,继承是满足is – a 的关系,而接口只需要满足like – a的关系

    3、接口在一定程度上实现代码的解耦[即:接口规范性 + 动态绑定机制]

2、接口多态特性

1、多态参数

Java入门-面向对象(进阶)

2、多态数组

Java入门-面向对象(进阶)

3、接口存在多态传递现象

Java入门-面向对象(进阶)

九、内部类

  • 基本介绍

    一个类的内部又完整嵌套了另一个类。被嵌套的类称为内部类(inner class),嵌套其它类的类称为外部类(outer class)。是我们类的第五大成员,内部类最大的特点是可以直接访问私有属性,并且可以体现类与类之间的包含关系。注意:内部类是学习的难点。同时也是重点,后面看底层源码时,也有大量的内部类。

    PS:类的五大成员

    1、属性

    2、方法

    3、构造器/构造方法

    4、代码块

    5、内部类

  • 基本语法

    class Outer//外部类
     class Inner// 内部类
     
     }
    }
    class other// 外部其他类

    }
    • 内部类的分类

      1、定义在外部类局部位置上(比如方法内):

      A、局部内部类(有类名)

      B、匿名内部类(没有类名,重点)

      2、定义在外部类的成员位置上:A、成员内部类(没有static修饰)

      B、静态内部类(有static修饰)

1、局部内部类

  • 说明

  • 局部内部类是定义在外部类的局部位置,比如方法中,并且有类名。

    1、可以直接访问外部类的所有成员,包括私有的

    2、不能添加访问修饰符,因为它的地位就是一个局部变量。局部变量是不能使用修饰符的。但是可以使用final修饰,因为局部变量也可以使用final

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

    4、局部内部类—访问—>外部类的成员【访问方式:直接访问】

    5、外部类—访问—->局部类变量【访问方式:创建对象,再访问(注意:必须在作用域内)】

    6、外部其它类—不能访问—->局部内部类(因为 局部内部类 地位就是一个局部变量)

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

    package com.hspedu.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.作用域 : 仅仅在定义它的方法或代码块中
            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();
        }

    }

2、匿名内部类

  • 说明

    匿名内部类是定义在外部类的局部位置,比如方法中,并且没有类名

  • 基本语法

    new 类或接口(参数列表){
    类体
    };
    package com.hspedu.innerclass;


    /**
     * 演示匿名内部类的使用
     */

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

    • 匿名内部类的使用和注意事项

      1、匿名内部类的语法比较奇特,因为匿名内部类既是一个类的定义,同时本身也是一个对象,因此从语法上看,它既有类的特征,也有创建对象的特征,因此可以调用匿名内部类方法。

      代码片段示例一:
      new A(){
       @Override
       public void cry(){
        System.out.println("Hi");
       }
      }.cry();

      代码片段示例二:
      A a = new A(){
       @Override
       public void cry(){
        System.out.println("Hi");
       }
      };
      a.cry();

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

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

      4、作用域:仅仅在定义他的方法或代码块中

      5、匿名内部类—-访问—–>外部类成员【访问方式:直接访问】

      6、外部其他类—-不能访问——>匿名内部类(因为匿名内部类地位就是一个局部变量)

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

  • 匿名内部类最佳实践

    1、当作实参直接传递,简洁高效

    /**
     * @author chenx
     * @date 2021年11月27日
     */

    public class Inner {
     public static void main(String[] args) {
      fun(new IE(){
       @Override
       public void show(){
        System.out.println("匿名内部类");
       }
        });
     }

     public static void fun(IE ie){
      ie.show();
     }

    }

    interface IE{
     void show();
    }

    package com.hspedu.innerclass;

    public class InnerClassExercise02 {
        public static void main(String[] args) {
            /*
            1.有一个铃声接口Bell,里面有个ring方法。(右图)
            2.有一个手机类Cellphone,具有闹钟功能alarmClock,参数是Bell类型(右图)
            3.测试手机类的闹钟功能,通过匿名内部类(对象)作为参数,打印:懒猪起床了
            4.再传入另一个匿名内部类(对象),打印:小伙伴上课了
             */

            CellPhone cellPhone = new CellPhone();
            //老韩解读
            //1. 传递的是实现了 Bell接口的匿名内部类 InnerClassExercise02$1
            //2. 重写了 ring
            //3. Bell bell = new Bell() {
            //            @Override
            //            public void ring() {
            //                System.out.println("懒猪起床了");
            //            }
            //        }
            cellPhone.alarmClock(new Bell() {
                @Override
                public void ring() {
                    System.out.println("懒猪起床了");
                }
            });

            cellPhone.alarmClock(new Bell() {
                @Override
                public void ring() {
                    System.out.println("小伙伴上课了");
                }
            });
        }
    }
    interface Bell//接口
        void ring();//方法
    }
    class CellPhone{//类
        public void alarmClock(Bell bell){//形参是Bell接口类型
            System.out.println(bell.getClass());
            bell.ring();//动态绑定
        }
    }

3、成员内部类

1、成员内部类是定义在外部类的成员位置,并且没有static修饰

2、可以直接访问外部类的所有成员,包括私有的

3、可以添加任意访问修饰符(public protected  默认 private),因为它的地位就是一个成员

4、作用域和外部类的其他成员一样,为整个类体。在外部类的成员方法中创建成员内部类对象,再调用方法

5、成员内部类—-访问—->外部类(比如:属性)【访问方式:直接访问】

6、外部类—-访问—–内部类【访问方式:创建对象,再访问】

7、外部其他类—访问—>成员内部类

8、如果外部类和内部类的成员重名时,内部类访问的话,默认遵循就近原则,如果想访问外部类的成员,可以通过:外部类名.this.成员,去访问

package com.hspedu.innerclass;

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


4、静态内部类

1、说明:静态内部类时定义在外部类的成员位置,并且有static修饰

2、可以直接访问外部类的所有静态成员,包含私有的,但是不能直接访问非静态成员

3、可以添加任意访问修饰符(public  protected  默认  private),因为它的地位就是一个成员

4、作用域:同其他的成员,为整个类体

5、静态内部类—访问—->外部类成员【访问方式:直接访问外部类成员】

6、外部类—–访问—->静态内部类【访问方式:创建对象,再访问】

7、外部其他类—–访问—–>静态内部类【有两种方式】

方式一:Outer10.Inner10 inner10 = new Outer10.Inner10();

方式二:编写一个方法,可以返回静态内部类的对象实例.

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

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

Outer10.Inner10 inner101 = outer10.getInner10();
System.out.println("============");
inner101.say();
package com.hspedu.innerclass;

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


5、小结

1、内部类有四种:局部内部类,匿名内部类、成员内部类、静态内部类

2、匿名内部类是重点

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

//…..

};

3、成员内部类,静态内部类是放在外部类的成员位置,本质就是一个成员,可以添加任意修饰符


原文始发于微信公众号(Whisper Coding):Java入门-面向对象(进阶)

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/255455.html

(0)
李, 若俞的头像李, 若俞

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!