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.1.3 访问变量(前提是满足修饰符的访问权限和范围)
【推荐使用】: 类名.类变量名
   或者:   对象名.类变量名

在这里插入图片描述

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.3 访问方法(前提是满足修饰符的访问权限和范围)
【推荐使用】: 类名.类方法名
   或者:   对象名.类方法名
1.2.4 静态方法什么时候使用

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

1.3 main方法

public static void main(String[] args){}
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. 修饰符可选,只能选择:不写 / 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");
    	}
    }	
    

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

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

    举例说明

    在这里插入图片描述

    执行过程:新建一个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. 静态属性和方法的继承

    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.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
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

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;
          }
      
  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. ❌抽象类不能被实例化(就是不能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;
        }
    }
}
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.9 接口

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

1.9.1 接口语法
interface 接口名{
	//属性
	//可以有3种方法(1.抽象方法(不需要abstract关键字) 2.默认实现方法(default) 3.静态方法(static))
}


class 类名 implements 接口{
	自己属性;
	自己方法;
	必须实现的使用的接口的抽象方法;
}
  • 在接口中,抽象方法可以省略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.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");
        }
    }
    
  5. 如果用抽象类实现接口,那么就可以不用实现接口方法

    public interface myinterface {
        void look();
        void eat();
    }
    // 如果用抽象类实现接口,那么就可以不用实现接口方法
    abstract class Tiger implements myinterface{
    
    }
    
  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");
        }
    }
    
  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.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{} // 继承父类

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("相机工作中");
    }
}

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.9.7 接口的练习题
//☆访问接口的 x 就使用 A.x
//☆访问父类的 x 就使用 super.x
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();
    }
}
  • 46
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值