• Java大总结之——静态方法和静态变量+代码块+抽象类+接口+单例设计模式+模版设计模式


    小小笔记03 面向对象plus

    1.类变量(静态变量)和类方法(静态方法)

    1.1 静态变量 (Static变量)(类变量)

    1.1.1 静态变量的定义
    1. ⭐️静态变量被同一个类的所有对象共享!!

      1. 任何该类的对象访问的时候,取到的都是相同的值

      2. 任何该类的对象去修改的时候,修改的也都是同一个变量

    2. ⭐️静态变量在类加载的时候就生成了!!

      1. 在加载类的时候也初始化了静态变量,也就是说:即使没有创建类对象,只要类加载了,就可以使用静态变量
      2. 类变量的生命周期是随类的加载开始的,随类的消亡而销毁
    3. 一些小区别:jdk8以前的版本,static变量在方法区里。jdk8之后认为static变量通常在堆里:当一个类加载的时候,会在堆里生成一个对应这个类的class对象(属于一个原型对象),而静态变量就在这个class对象的尾部。

    1.1.2 定义变量
    访问修饰符 static 数据类型 变量名
    public static int num;
    
    • 1
    • 2
    1.1.3 访问变量(前提是满足修饰符的访问权限和范围)
    【推荐使用】: 类名.类变量名
       或者:   对象名.类变量名
    
    • 1
    • 2

    在这里插入图片描述

    1.1.4 静态变量什么时候使用

    需要让某个类的所有对象共享一个变量的时候。
    其与实例变量(普通属性)的区别是:类变量是该类所有的对象共享的,而实例变量是每个对象独有的,实例变量不能通过类名.类变量名访问。

    注意:在Java中,子类不能继承父类的静态属性。静态属性(也称为类属性)是属于类的,而不是属于任何类的实例。因此,它们不会被子类继承。
    但是,子类可以通过类名直接访问父类的静态属性,前提是这些属性是可访问的(例如,它们是公开的或受保护的)。

    1.2 静态方法 (Static方法)(类方法)

    1.2.1 静态变量的定义
    1. 当方法使用了static修饰后,该方法就是静态方法。
    2. 静态方法(类方法)可以通过类名调用,也可以通过对象名调用(需要遵循访问权限);而普通方法只能通过对象名调用。
    3. 静态方法不允许使用和对象有关的关键字,比如this,super
    4. 访问问题:
      1. 【静态方法】 能访问静态方法 或 静态变量, 想要访问非静态的属性时,需要先创建类的对象,再去调用即可。
      2. 【普通方法】 既能访问普通方法 又能访问静态方法,既能访问普通变量 又能访问静态变量。
    1.2.2 定义方法
    访问修饰符 static 数据返回类型 方法名(){}
    private static double look(){}
    
    • 1
    • 2
    1.2.3 访问方法(前提是满足修饰符的访问权限和范围)
    【推荐使用】: 类名.类方法名
       或者:   对象名.类方法名
    
    • 1
    • 2
    1.2.4 静态方法什么时候使用

    如果不希望创建实例,也可以调用某个方法:类名 方法名()。也就是当方法中不涉及任何和对象相关的成员(即当做工具来使用时),就可以把方法设计成静态方法,提高开发效率。
    在这里插入图片描述

    1.3 main方法

    public static void main(String[] args){}
    
    • 1
    1.3.1 main方法说明
    1. main方法是jvm虚拟机调用的,所以该方法的访问权限必须是public
    2. 虚拟机调用main方法的时候,不必创建对象,所以该方法必须是static
    3. main方法接受String类型的数组参数args,该数组中—保存着执行java命令时传递给所运行命令的参数

    1.4 代码块

    1.4.1 代码块的定义

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

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

    相当于是另外一种形式的构造器(相当于是构造器的补充机制),去做初始化的操作,⭐️代码块调用的顺序优先于构造器

    1.4.2 什么时候用代码块?

    如果多个构造器都有重复的语句,可以抽取到初始化块中,提高代码复用性。

    1.4.3 代码块的基本语法
    [修饰符可有可无]{
    	代码
    };
    
    • 1
    • 2
    • 3
    1. 修饰符可选,只能选择:不写 / static
    2. static修饰的叫做静态代码块
    3. 逻辑语句可以为任意语句
    4. 最后的分号可以写也可以省略
    1.4.4 细节说明
    1. 当多个构造器都有相同的语句 ,这样代码看起来比较冗余 , 这时我们可以把相同的语句,放入到一个代码块中。这样当我们不管调用哪个构造器,创建对象,都会先调用代码块的内容代码块调用的顺序优先于构造器。

    2. 💁静态代码块 & 静态属性初始化:随着类的加载而执行,并且只会执行一次

      💁普通代码块 & 普通属性初始化:每创建一个该类的对象,就执行一次

    3. ⭐️⭐️⭐️类什么时候被加载❓【重要!!!!】

      1. 创建对象实例的时候new

      2. 创建子类对象实例父类也会被加载 ,且先加载父类后加载子类;

      3. 使用类的静态成员(静态变量,静态方法)时,该类也会被加载。

    4. 普通的代码块,在创建对象实例的时候,会被隐式的调用,对象被创建一次,就会调用一次
      如果只是使用类的静态成员,普通代码块并不会执行(因为使用类的静态成员并没有创建对象)

    5. 💥构造器的最前面其实是隐藏了super()和 调用本类的普通代码块以及普通属性初始化。

      class A{}
      	public A(){//构造器
      		System.out.println("okok");
      	}
      }	
      
      • 1
      • 2
      • 3
      • 4
      • 5

      上面的构造器在实际运行时,实际上隐藏的一些执行要求是这样的⤵️

      class A{}
      	public A(){//构造器
              
              super(); // 1.默认调用父类的无参构造器
              
              // 2.调用普通代码块 以及 普通属性初始化
      		
              // 3.最后再调用构造器内部的语句
              System.out.println("okok");
              
      	}
      }	
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12

      举例说明

      在这里插入图片描述

      执行过程:新建一个BBB类的对象,首先进入BBB构造器,进入后:
      ​ (1)默认的super( )调用父类AAA的无参构造器
      ​ (2)默认的super( )调用父类Obgect的无参构造器 返回
      ​ (3)调用本类AAA的代码块,输出【这是AAA的普通代码块】
      ​ (4)执行AAA的构造器中的内容,输出【这是AAA的构造器】
      ​ (5)调用本类BBB的代码块,输出【这是BBB的普通代码块】
      ​ (6)执行BBB的构造器中的内容,输出【这是BBB的构造器】

    6. ⭐️⭐️⭐️创建一个对象时:在 一个类中 的调用顺序是:

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

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

      3. 最后调用构造器方法

    7. ⭐️⭐️⭐️创建一个子类对象时:静态代码块、静态属性初始化、普通代码块、普通属性初始化、构造方法的调用顺序是:

      1. 创建对象的时候首先就是要进行类的加载(先加载父类,后加载子类)

        1. 父类的静态代码块 和 静态属性初始化(优先级一样,按定义顺序执行)
        2. 子类的静态代码块 和 静态属性初始化(优先级一样,按定义顺序执行)
      2. 进行对象的创建(先从子类的构造器开始,子类构造器里默认包含super() 调用父类无参构造器 and 调用本类的普通代码块以及普通属性初始化,之后在进行构造器内容)

        1. 父类的普通代码块 和 普通属性初始化(优先级一样,按定义顺序执行)
        2. 父类的构造器方法
        3. 子类的普通代码块 和 普通属性初始化(优先级一样,按定义顺序执行)
        4. 子类的构造器方法
    package com.hspedu.static_;
    
    public class constructor {
       public static void main(String[] args) {
           new BBB();
    // 先加载类 加载父类-加载子类
    // 在执行创建对象的一些操作:子类构造器--super()父类构造器--普通代码块、普通属性初始化--构造器内容
       }
    }
    
    class AAA{ // 父类
    
       public int n = n();
       public static int m = m();
       static {
           System.out.println("这是AAA的静态代码块");
       }
    
       {
           System.out.println("这是AAA的普通代码块");
       }
    
       public AAA(){
           // (1) super() 调用父类Object的构造器
           // (2) 调用本类的普通代码块以及普通属性初始化
           System.out.println("这是AAA的构造器");
       }
    
       public int n(){
           System.out.println("这是AAA的普通属性初始化");
           return 1;
       }
    
       public static int m(){
           System.out.println("这是AAA的静态属性初始化");
           return 0;
       }
    }
    
    class BBB extends AAA{ //子类
       static {
           System.out.println("这是BBB的静态代码块");
       }
    
       {
           System.out.println("这是BBB的普通代码块");
       }
    
       public BBB(){
           // (1) super() 调用父类AAA的构造器
           // (2) 调用本类的普通代码块以及普通属性初始化
           System.out.println("这是BBB的构造器");
       }
    }
    
    
    >>>输出
       这是AAA的静态属性初始化
       这是AAA的静态代码块
       这是BBB的静态代码块
       这是AAA的普通属性初始化
       这是AAA的普通代码块
       这是AAA的构造器
       这是BBB的普通代码块
       这是BBB的构造器
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    1. 静态代码块只能调用静态成员(静态属性和静态方法)
      普通代码块可以调用任意成员

    2. 静态属性和方法的继承

      1. 静态方法不能继承
      2. 如果父类中有静态方法,子类中没有,但是子类可以调用父类的静态方法
      3. 如果父类中有静态方法,子类中也有跟父类相同的静态方法,那么父类不管是 类名调用 还是 实例调用 都是调用的父类的静态方法
      4. 子类不管是 类名调用 还是 实例调用 都是调用子类的静态方法
    1.4.5 例题
    class Person {
    	public static int total;//静态变量
    	static {//静态代码块
    		total = 100;
    		System.out.println("in static block!");//(1) 只在加载类的时候执行一次!!一次!!
    	}
    }
    
    public class Test {
    	public static void main(String[] args) {
    		System.out.println("total = "+ Person.total); //100
    		System.out.println("total = "+ Person.total); //100
    	}
    }
    
    >>>输出
        in static block!
        100
        100
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    ⭐️1.5 单例设计模式

    1.5.1 什么是单例模式

    单例——单个的实例

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

    单例模式有两种方式: 1)饿汉式 2)懒汉式

    1.5.2 实现步骤

    演示饿汉式和懒汉式单例模式的实现步骤如下:

    1. 构造器私有化 -> 防止直接 new
    2. 在类的内部创建对象
    3. 向外暴露一个静态的公共方法
    1.⭐️饿汉式单例模式【只要类被加载,对象就被创建了】

    0391_韩顺平Java_单例模式饿汉式_哔哩哔哩_bilibili

    package com.hspedu.static_.single;
    
    public class SingleTon01 {
        public static void main(String[] args) {
            
     使用类名就可以调用getInstance静态public方法,返回创建的gf对象,
     且由于创建对象时 gf是静态的,所以类加载的时候只执行一次,因此无论后面怎么调用,返回的gf对象都是同一个
            GirlFriend instance1 = GirlFriend.getInstance();
            System.out.println(instance1);
            GirlFriend instance2 = GirlFriend.getInstance();
            System.out.println(instance2);
    
            // instance1 和 instance2是一个对象
            System.out.println(instance1 == instance2);
        }
    }
    
    // 有一个类 GirlFriend 只允许有一个女朋友!!!!
    // 如何保证只能创建一个GirlFriend对象?【饿汉式】
    // 		1.构造器私有化 —— 不能new对象
    // 		2.在类的内部创建对象(该对象是static)
    // 		3.提供一个public的static方法,返回gf对象
    
    class GirlFriend{
        private String name;
    
        // 【在类的内部创建一个对象】 
        // 同时,为了保证静态方法getInstance可以使用 gf对象,所以需要修饰新建的gf对象为static
        private static GirlFriend gf = new GirlFriend("小红");
    
        // 【构造器私有化】 保证不能在外面肆意new对象
        private GirlFriend(String name) {
            this.name = name;
        }
    
        // 【提供一个public的static方法,返回gf对象】
        // static用来保证 不用新建一个GirlFriend对象就可以调用这个方法
        public static GirlFriend getInstance(){
            return gf;
        }
    
        
        @Override
        public String toString() {
            return "GirlFriend{" +
                    "name='" + name + '\'' +
                    '}';
        }
    }
    
    >>> GirlFriend{name='小红'}
    	GirlFriend{name='小红'}
    	true
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    2.⭐️懒汉式单例模式

    0392_韩顺平Java_单例模式懒汉式_哔哩哔哩_bilibili

    package com.hspedu.static_.single;
    
    public class SingleTon02 {
        public static void main(String[] args) {
            Cat mycat1 = Cat.getInstance();
            System.out.println(mycat1);
            Cat mycat2 = Cat.getInstance();
            System.out.println(mycat2);
    
            // mycat1 和 mycat2 是一个对象
            System.out.println(mycat1 == mycat2);
        }
    }
    
    // 希望程序进行中, 只能创建一个Cat对象!!!!!
    // 使用单例模式【懒汉式】
    //  1.仍然构造器私有化
    //  2.定义一个static属性对象,但是不初始化
    //  3.提供一个public的static方法 可以返回一个Cat对象
    //  4.懒汉式:只有当用户使用getInstance方法的时候,才返回Cat对象,后面再次调用时会返回上次创建的cat对象,从而保证了单例
    class Cat{
        private String name;
        
    	// 【定义一个static属性对象 但是不初始化,没有new创建一个对象的话构造器是不会被调用的】
        private static Cat cat; 
    
        // 【构造器私有化】
        private Cat(String name){
            this.name = name;
        }
    
        // 【提供一个public的static方法 可以返回一个Cat对象】
        //  只有当用户使用getInstance方法的时候,才返回Cat对象。如果不是调用getInstance方法,那么虽然可能类被加载,但是没有创建对象
        //  后面再次调用时会返回上次创建的cat对象,从而保证了单例模式
        public static Cat getInstance(){
            if(cat == null){ // 如果没有创建cat对象,那么就创建一个cat对象
                cat = new Cat("小花猫");
            }
            return cat;
        }
    
        
        @Override
        public String toString() {
            return "Cat{" +
                    "name='" + name + '\'' +
                    '}';
        }
    }
    
    
    >>>Cat{name='小花猫'}
       Cat{name='小花猫'}
       true
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54

    3.饿汉式 VS 懒汉式创建单例模式

    在这里插入图片描述

    ​ Runtime是饿汉式的:在类加载就创建对象实例,可能存在资源浪费的问题


    1.6 final关键字(不希望类被继承、方法被重写、属性被修改)

    1.6.1 final用法

    final 可以修饰类、属性、方法和局部变量在某些情况下,程序员可能有以下需求,就会使用到final:

    1. 不希望被继承时,可以用final修饰.【final class】

    2. 不希望父类的某个方法被子类覆盖/重写(override)时,可以用final关键字修饰。【访问修饰符 final 返回类型 方法名】

    3. 不希望类的的某个属性的值被修改,可以用final修饰。【public final double RATE=0.08】

    4. 不希望某个局部变量被修改,可以使用final修饰【final double RATE=0.08】

    1.6.2 final细节
    1. final修饰的属性又叫常量,一般用XX_XX_XX来命名(大写)

    2. final修饰的属性在定义时必须赋初值,并且以后不能再修改!
      赋值可以在如下位置之一 [ 选择一个位置赋初值即可 ]

      1. 在定义时: 如 public final double TAX_RATE=0.08

      2. 在构造器中赋值

      3. 在代码块中赋值

            private final double PI = 3.14; // 赋值位置1 :定义时候就赋值
        
            private final double PI;  // 赋值位置2 : 构造器内
            public Circle(double radius){
                this.radius = radius;
                PI = 3.14;
            }
        
        	private final double PI;  //赋值位置3:代码块中赋值
            {
                PI = 3.14;
            }
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
        • 10
        • 11
        • 12
    3. 如果final修饰的属性是静态的,则初始化的位置只能是:

      1. 在定义时
      2. 在静态代码块
      3. 不能在构造器中赋值❌【静态属性的初始化时需要类加载,类加载的时候构造器没被调用,创建对象的时候才调用构造器】
    4. final类不能继承,但是可以实例化对象。

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

    6. 一般来说如果已经是一个final类了,就没必要修饰final方法了[都不能被继承了 那重写不重写方法没意义]

    7. final不能修饰构造器。

    8. final和static搭配使用,不会导致类的加载,效率更高。

      在这里插入图片描述

    9. 包装类(Integer Double Float Boolean String等)都是final类,都不能被继承

    小题目

    final x,++改变了x的值所以不行,x+1没有改变本身x的值所以可以

    在这里插入图片描述


    1.7 抽象类

    当父类的某些方法需要声明,但是又不确定如何实现的时候
    =》可以将其声明为抽象方法——用abstract关键字来修饰该方法对应的这个类就是抽象类——用abstract修饰该类

    package com.hspedu.abstract_;
    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 abstract void eat() ;
    
    }
    
    class cat extends Animal{
        public void eat(){
            //如果一个类继承了抽象类,则他必须实现抽象类的所有抽象方法,否则这个类也必须声明为抽象类
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    1. ❌抽象类不能被实例化(就是不能new)
    2. 抽象类不一定要包含抽象方法
    3. 一旦包含了abstract方法,那么这个类就必须是abstract类
    4. abstract只能修饰类和方法,不能修饰属性
    5. 抽象类还是类,抽象类里面想有啥都可以,可以有构造器、非抽象方法、静态属性巴拉巴拉的任意成员
    6. ❌抽象方法没有主体 (即没有{ } ← 这部分内容)
    7. 如果一个类继承了抽象类,则他必须实现抽象类的所有抽象方法,否则这个类也必须声明为抽象类
    8. ❌抽象方法不能使用private final static来修饰,因为这些关键字都是和重写相违背的
      1. private的方法不能被重写
      2. final关键字修饰方法:表示方法不可以被重写
      3. **static关键字和方法重写无关!!**static方法是属于类而不是对象的方法,可以直接通过类名调用,而不需要创建对象实例。抽象方法需要被子类实现,而静态方法属于类而不属于对象,因此把抽象方法声明为静态方法没有意义

    1.8 模板设计模式(抽象类的最佳实践)

    需求:有多个类完成不同的任务job,要求统计得到各自完成任务的时间

    模板设计模式能解决的问题:

    1. 当功能内部一部分实现是确定,一部分实现是不确定的。这时可以把不确定的部分暴露出去,让子类去实现。

    2. 编写一个抽象父类,父类提供了多个子类的通用方法,并把一个或多个方法留给其子类实现,就是一种模板模式

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

    • 编写方法calculateTime(),可以计算某段代码的耗时时间
    • 编写抽象方法job
    • 编写一个子类Sub,继承抽象类Template,并实现job方法
    • 编写一个测试类TestTemplate,看看是否好用
    package com.hspedu.static_.abstract_.TestTemplate;
    
    public abstract class Template { // 抽象类-模板设计模式
        public abstract void job(); // 抽象方法
    
        public void calculateTime(){ // 实现方法 调用了job方法
            long start  = System.currentTimeMillis();
            
            job(); // 动态绑定机制,调用对象方法的时候,看运行类型,和对象绑定。
            // sub1.calculateTime(),对象sub1的运行类型是Sub1,调用calculateTime方法,子类Sub1没有,找到父类Template类调用calculateTime方法,运行到调用job方法时,根据动态绑定机制:调用的是sub1运行类型——即Sub1的job方法。
            // sub2.calculateTime()就调用Sub2的job
            
            long end = System.currentTimeMillis();
            System.out.println("耗时间" + (end-start));
        }
    }
    
    class Sub1 extends Template{
        @Override
        public void job() { // 实现Template类的抽象方法job
            int num = 0;
            for (int i = 0; i < 80000; i++) {
                num += i;
            }
        }
    }
    
    class Sub2 extends Template{
        @Override
        public void job() { // 实现Template类的抽象方法job
            int num = 0;
            for (int i = 0; i < 8000000; i++) {
                num += i;
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    package com.hspedu.static_.abstract_.TestTemplate;
    
    public class Test {
        public static void main(String[] args) {
            Sub1 sub1 = new Sub1(); // 新建一个子类对象 编译类型和运行类型都是Sub1
            sub1.calculateTime(); // 调用模板类的方法计算时间
    
            Sub2 sub2 = new Sub2(); // 新建一个子类对象 编译类型和运行类型都是Sub2
            sub2.calculateTime(); // 调用模板类的方法计算时间
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    ⭐️动态绑定机制:当调用对象方法的时候,该方法会和该对象的内存地址(也就是运行类型)绑定


    1.9 接口

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

    1.9.1 接口语法
    interface 接口名{
    	//属性
    	//可以有3种方法(1.抽象方法(不需要abstract关键字) 2.默认实现方法(default) 3.静态方法(static))
    }
    
    
    class 类名 implements 接口{
    	自己属性;
    	自己方法;
    	必须实现的使用的接口的抽象方法;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 在接口中,抽象方法可以省略abstract关键字
    • JDK 7.0之前接口里所有的方法都没有方法体,即都是抽象方法
    • JDK 8.0之后接口里可以有默认实现方法,需要使用default关键字修饰。也可以有静态方法static。
    1.9.2 接口使用场景

    在这里插入图片描述

    // 定义接口
    public interface Manager {
        void connect(); // 接口中所有的方法必须是public方法(默认),以及默认是abstract的;
        void close();
    }
    
    
    // A程序员实现接口:把接口中的方法完成
    public class MySQLDB implements Manager{
        @Override
        public void connect() {
            System.out.println("connect MYSQL");
        }
    
        @Override
        public void close() {
            System.out.println("close MYSQL");
        }
    }
    // B程序员实现接口:把接口中的方法完成...
    
    // 测试
    public class test {
        public static void main(String[] args) {
            // 连接MYSQL
            MySQLDB mySQLDB = new MySQLDB();
            my.mytest(mySQLDB);
        }
    }
    class my{
        public static void mytest(Manager manager){ // 接入接口,通过接口来调用方法
            manager.connect();
            manager.close();
        }
    }
    
    >>>输出:
        connect MYSQL
    	close MYSQL
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    1.9.3 接口的注意事项
    1. 接口不能被实例化(因为接口本身是一个抽象的概念,希望的就是其他类实现它);

    2. 接口中所有的方法必须是public方法;

    3. 接口中抽象方法可以不用abstract修饰;

    4. 一个普通类实现(implements)接口,就必须把该接口的所有方法都实现。

      public interface myinterface {
          void look();
          void eat();
      }
      // 普通类实现接口,就必须把该接口的所有方法都实现
      class Tiger implements myinterface{
      	public void look() {
              System.out.println("looking");
          }
          public void eat() {
              System.out.println("eating");
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
    5. 如果用抽象类实现接口,那么就可以不用实现接口方法

      public interface myinterface {
          void look();
          void eat();
      }
      // 如果用抽象类实现接口,那么就可以不用实现接口方法
      abstract class Tiger implements myinterface{
      
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
    6. 一个类可以同时实现多个接口~~

      interface myinterface1 { // 接口1
          void look();
      }
      interface myinterface2 { // 接口2
          void look();
      }
      
      // 一个类可以同时实现多个接口
      class Tiger implements myinterface1,myinterface2{
      	public void look() {
              System.out.println("looking");
          }
          public void eat() {
              System.out.println("eating");
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
    7. 接口中的属性,只能是final的!且只能是public static final修饰符!!!!!!!!!

      int a = 1; 相当于:public static final int a = 1;

    8. 接口中属性的访问形式接口名.属性名

    9. 接口不能继承其他类,但是接口可以继承(extends)多个别的接口

      接口和接口是:interface A extends B,C;

      类和接口是:class A implement B;

    10. 接口的修饰符只能是 public 或者 默认 ,和类的修饰符一样

      public interface myinterface{}

      interface myinterface{}

    1.9.4 接口练习题

    在这里插入图片描述

    都正确 都输出23。因为A里面的int a = 23 实际上等价于public static final int a = 23;

    因为a变量是public的,所以b是对象实例访问a可以。

    因为接口A中a变量为static ,所以可以用过类名A调用

    在Java中,子类不能继承父类的静态属性。静态属性(也称为类属性)是属于类的,而不是属于任何类的实例。因此,它们不会被子类继承。但是,子类可以通过类名直接访问父类的静态属性,前提是这些属性是可访问的public。而B实现了A,因此B可以使用A里面的属性。

    1.9.5 接口和继承类

    实现接口是对 Java 单继承机制的补充

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

    接口比继承更加灵活,继承满足is-a的关系,接口组织需要满足like-a的关系

    接口在一定程度上实现代码的解耦【即接口规范性 + 动态绑定机制】

    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();
    }
    
    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() + " 通过学习,可以像鸟儿一样飞翔...");
    	}
    }
    
    // 小结: 当子类继承了父类,就自动的拥有父类的功能
    // 如果子类需要扩展功能,可以通过实现接口的方式扩展. 
    // 可以理解 实现接口 是 对 java 单继承机制的一种补充
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    1.9.6 接口的多态
    1.多态参数 + 接口的多态和继承的多态对比
    packagecom.hspedu.interface_;
    public class InterfacePolyParameter{
     	public static void main(String[] args){
        //接口的多态体现
        //接口类型的变量if01 可以指向 实现了IF接口类的对象实例
    	IF if01 = new Monster();
     	if01 = newCar();
            
     	//继承体现的多态
    	//父类类型的变量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{} // 继承父类
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    2.多态数组
    package com.hspedu.interface_.test3;
    
    public class InterfacePolyArr {
        public static void main(String[] args) {
            // 多态数组: 定义一个接口类型数组
            Usb[] usbs = new Usb[2];
            usbs[0] = new Phone(); //Phone实现了接口 所以可以放到数组内
            usbs[1] = new Carema();
    
            /*
            给Usb数组中,存放Phone和Carema对象,
            Phone类还有一个特有的方法call()
            请遍历Usb数组,如果是Phone对象,除了调用Usb接口定义的方法外,还需要调用Phone 特有方法call
            */
            for (int i = 0; i < usbs.length; i++) { // 遍历Usb数组,调用work方法,调用Phone的特有方法
                usbs[i].work(); //动态绑定
                if(usbs[i] instanceof Phone){
                    ((Phone) usbs[i]).call();  //向下转型
                }
            }
        }
    }
    
    interface Usb{
        void work();
    }
    
    class Phone implements Usb{ // Phone实现Usb接口
        @Override
        public void work() {
            System.out.println("手机工作中");
        }
    
        public void call(){ //Phone类的特有方法
            System.out.println("手机可以打电话");
        }
    }
    
    class Carema implements Usb{// Carema实现Usb接口
        @Override
        public void work() {
            System.out.println("相机工作中");
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    3.接口存在多态传递现象

    如果IG继承了IH接口,而Teacher类实现了IG接口 ---------> 那么,实际上就相当于Teacher类也实现了IH接口.

    /**
     *演示多态传递现象
    */
    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{ 
        // 这里要实现hi方法的原因就是,如果IG继承了IH接口,Teacher又实现了IG,那么就相当于Teacher也要实现IH,所以必须这样,不然会报错。
    	@Override
     	public void hi(){}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    1.9.7 接口的练习题
    //☆访问接口的 x 就使用 A.x
    //☆访问父类的 x 就使用 super.x
    
    • 1
    • 2
    interface A{ 
    	int x=0;
    } //想到等价public static final int x=0;
    
    class B{
        int x=1;
    }//普通属性
    
    class C extends B implements A{
        public void pX(){
     	//System.out.println(x);//错误,原因不明确x
    	//可以明确的指定x
        //☆访问接口的 x 就使用 A.x
     	//☆访问父类的 x 就使用 super.x
    	 System.out.println(A.x + " " + super.x);
        }
     	
        public static void main(String[] args) {
     		new C().pX();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
  • 相关阅读:
    网络分析笔记01:pcapng格式的整体解包
    HINet: Half Instance Normalization Network for Image Restoration
    链表(二)——LinkedList(双向链表)的CURD操作的模拟实现
    前端(小程序) echarts图表 tooptip 层级过高遮罩弹层层覆盖其他组件问题
    「uniapp」 H5高德地图定位、周边、路线规划、多点绘制、位置点击监听
    Docker部署EMQX
    java计算机毕业设计高校会议室预约系统MyBatis+系统+LW文档+源码+调试部署
    学习笔记-Secure-Linux
    CNN RNN DNN transformer 区别以及音频信号处理适合方式
    Teams Tab App 分析
  • 原文地址:https://blog.csdn.net/prince0520/article/details/136341404