跳至主要內容

6、面向对象

T4makojava基础语法java大约 27 分钟

面向机器语言:机器语言、汇编语言
面向过程语言:C 语言,FORTRAN 语言

面向过程:面向过程是一种以过程、事件为中心的编程思想。面向过程语言将代码组织成称为过程或函数的块。每个函数或块的目标是完成某个任务。

Java 面向对象内容:

  • Java 类及类成员:属性,方法,构造器,代码块,内部类
  • 面向对象的三大特征:封装,继承,多态
  • 其他关键字:this,super,static,final,abstract,interface,package,import

1、Java 类和类的成员

面向对象的两个要素:

  • 类:对一类事物的描述,是抽象的、概念上的定义
  • 对象:是实际存在的该类事物的每个个体,因而也称为实例(instance)

设计类就是设计类的成员,类包含「变量的声明」和「方法的定义」

  • 属性 = 成员变量 = 域、字段 = field
  • 方法 = 成员方法 = 函数 = method
  • 创建类的对象 = 类的实例化 = 实例化类

类和对象的使用

  • 创建类、设计类成员
  • 创建类的对象
  • 调用类的属性或方法

对象的内存解析

image-20220628195208326

  • 栈(stack):存储局部变量
  • 堆(heap):存放对象实例

image-20220628201310761

1.1、 属性(成员变量)与 局部变量

成员变量又分为 「实例变量」和 「类变量(static)」

「成员变量」与「局部变量」的异同

相同点:

  • 定义变量的格式:数据类型 变量名 = 变量值
  • 先声明后使用
  • 变量都有其对应作用域

不同点:

  • 类中声明的位置不同。
    • 成员变量:直接定义在
    • 局部变量:声明在 方法 内,方法形参,代码块内,构造器形参,构造器内部的变量
  • 权限修饰符的不同
    常用权限修饰符:public、private、protected、缺省 (封装性)
    • 成员变量:可以 在声明属性时使用权限修饰符
    • 局部变量:不可以 使用权限修饰符
  • 默认初始化值的情况
    • 成员变量:有默认初始化值
      • 整形 byte,int,short,long ,默认值:0
      • 浮点型 float double ,默认值:0.0
      • 字符型 char ,默认值:0或'\u0000'
      • 布尔型 boolean ,默认值:false
      • 引用数据类型类、数组、接口 ,默认值:null
    • 局部变量:没有 默认初始化值,在调用局部变量之前 一定要显式赋值
  • 在内存中加载的位置:
    • 属性:(非static)加载到堆空间中
    • 局部变量:加载到栈空间中
class User{
    //属性(或成员变量)
    String name;
    
    public void talk(String language){//形参,局部变量
        String food = "饼";//局部变量
        System.out.println("我们使用"+language+"交流");
    }

1.2、 方法声明与使用

方法:描述类应该具有的功能
一般方法的声明:权限修饰符 返回值类型 方法名(形参列表){ 方法体 }

  • 权限修饰符:private、public、缺省、protected
  • 返回值类型:
    • 有返回值
    • 无返回值
  • 方法名:遵循规范,见名知意
  • 形参列表:可以有0个,1个或多个形参

相关信息

方法可以嵌套调用,不能嵌套定义

对象调用方法时,方法中的「局部变量」被分配内存空间。方法执行完毕,局部变量立即释放内存

1.3、方法的重载

Java 中存在两种多态: 重载(Overload)和 重写(Override)

重载的概念:

  • 同一个类 中,允许存在一个以上的 同名方法
  • 只要它们的 参数个数参数类型不同 即可
  • 方法的 返回类型参数的名字 不参与比较

1.4、构造器

构造器是一种特殊的方法,它的名字与它所在的类完全相同,且没有类型

构造器的作用:

  1. 创建对象
  2. 初始化对象的属性
//创建类的对象:new + 构造器
Person p = new Person()

相关信息

  • 构造器 无返回值void A() 不是构造器)
  • 子类「不能继承」父类的构造器,构造方法只能被显式或隐式的调用
  • 若没有显示定义类的构造器,系统默认提供一个 「空参的构造器」
  • 定义构造器的格式:权限修饰符 类名(形参列表){}
  • 一个类中定义的多个构造器构成 「重载」
  • 显示的定义了类的构造器后,系统就 「不提供」 默认的空参构造器
  • 一个类中,至少有一个构造器

1.5、return 关键字

作使用范围:方法体中用:

  • 结束方法
  • 返回所要的数据
  • return关键字后不可以声明执行语句(同break;)

1.6、对象数组

内存解析的说明:

相关信息

引用类型的变量,只可能存储两类值:null 和 地址值(含变量类型)

image-20220630142507804

1.7、匿名对象,抽象类的匿名子类对象

  1. 匿名对象的意义:一般只使用一次
public class StudentTest{
    public static void main(String[] args){
        //匿名对象
        new Phone.sendEmail();
        new Phone.sendEmail();//两个不同的匿名对象
        PhoneMall mall = new PhoneMall();
        //匿名对象的使用 
        mall.show(new Phone());
    }
  1. 抽象类的匿名子类对象

    抽象类的匿名字类对象:可以少写一个类,直接重写方法

//一个匿名子类的对象:P
Person p = new Person(){
    @oiverride.
    public void eat(){
        System.out.println("eat");
    }
};

1.8、可变个数的形参

jdk5.0 新增内容
可变个数形参的格式:数据类型...变量名(相当于数组)

相关信息

可变个数的形参的方法与本类中方法名同名,形参类型也相同的数组之间 不构成重载
可变个数形参在方法的形参中必须声明在 末尾,且最多只能有一个

public void show(String ... strs){
    //方法体
}
/*public void show(String[] strs){
    //方法体
}与上述方法相同*/
//不构成重载,冲突

1.9、方法参数的值传递机制

关于变量的赋值:
如果变量是基本数据类型,此时赋值的是变量所保存的数据值
如果变量是引用数据类型,此时赋值的是变量所保存的数据的地址值

方法的形参的传递机制:值传递机制

1.10、属性赋值的先后顺序

1、默认初始化
2、显示初始化 5、代码块中赋值
3、构造器赋值
4、通过“对象.方法/属性”赋值

顺序:1→2,5→3→4(由父及子,静态先行

1.11、代码块

  • 代码块用来初始化类、对象
  • 代码块如果有修饰的话,只能用static修饰
  • 分类:静态代码块、非静态代码块

静态代码块

  • 内部可以有输出语句
  • 随着类的加载而执行且 只执行一次
  • 用于初始化类的信息(静态的属性)
  • 一个类中定义的多个静态代码块按声明的先后顺序执行
  • 静态代码块的执行优先于非静态代码块
  • 静态代码块内只能调用静态的属性,静态的方法,不能调用非静态的结构

非静态代码块

  • 内部可以有输出语句
  • 随着对象的创建而执行,每创建一个对象,就执行一次非静态代码块
  • 用于对对象的属性等进行初始化
  • 如果一个类中定义了多个非静态代码块,则按照声明的先后顺序执行
  • 非静态代码块内能调用静态,非静态的属性,方法都能调用

1.12、内部类

java 中允许将一个类 A 声明在另一个类 B 中,则类 A 就是内部类,类 B 称为外部类(外嵌类)

内部类的分类:

  • 成员内部类
  • 局部内部类(方法、代码块、构造器内)

成员内部类:

  • 作为外部类的成员
    • 调用外部类的结构
    • 可以被 static 修饰
    • 可以被四种不同权限修饰
  • 作为一个类:
    • 可以定义属性、构造器、方法等
    • 可以被 final 修饰,表示此类不能被继承。不使用 final 就可以被继承
    • 可以被 abstract 修饰

内部类对应的字节码文件的命名格式是「外嵌类名$内部类名」

关注下列三个问题

  • 如何实例化成员内部类的对象
  • 如何在成员内部类中区分调用外部类的结构
  • 局部内部类的使用
public class InnerClassTest{
    public static void main(String[] args){
        //创建 Dog 实例(静态的成员内部类):
        Person.Dog dog = new Person.Dog();
        //创建 Bird 实例(非静态的成员内部类):
        Person p = new Person();
        Person.Bird bird = p.new Bird();
    }
}
class Person{
    String name;
    int age;
    public void eat(){
    }
    //静态成员内部类
    static class Dog{
        String name;
        int age;
        public void show(){
            System.out.println("");
        }
    }
    //非静态成员内部类
    class Bird{
        public void sing(){
            System.out.println("");
            Person.this.eat(); //调用外部类的结构,Person.this可省略
        }
        public void display(String name){
            System.out.println(name);//方法形参
            System.out.println(this.name);//内部类的属性
            System.out.println(Person.this.name);//外部类的属性
        }
    }
    public void method(){
        //局部内部类
        class AA{
        }
	}
    {
        //局部内部类
        class BB{
            
        }
    }
    public Person(){
        //局部内部类
        class cc{ 
        }
    }
}
// 开发中局部内部类的使用
public class InnerClassTest1{
    // 开发中很少见
    public void method(){
        //局部内部类
        class AA{
        }
	}
    //返回了一个实现了Comparable接口的类的对象
    public Comparable getComparable(){
        //创建一个实现了Comparable接口的类:局部内部类
        //方式一
        class MyComparable implements Comparable{
            @override
            public int compareTo(Object o){
                return 0;
            }
        }
        return new MyCompare();
        //方式二:
        return new Comparable(){
            @Override
            public int compareTo(Object o){
                return 0;
            }
        };
    }
}

局部内部类使用的注意点:

在局部内部类的方法中(show),如果调用局部内部类所在方法(method)中的局部变量的话,要求此局部变量(num)声明为final的
jdk 7之前的版本要求显示声明final的
jdk 8之后的版本,可以省略final

public void method(){
	int num = 10;
    class AA{
        public boid show(){
            //num = 20;
            System.out,println(num);
		}
    }
}

1.13、匿名类

Java 允许直接使用一个类的子类的类体创建一个子类对象,例如:

class People{

}

new People(){
    // 匿名类的类体
}
  • 匿名类一定是内部类
  • 匿名类没有类名
  • 匿名类是一个子类
  • 匿名类不可以创建 static 成员变量和 static 方法

案例:

public class Father {
    Animal test(){
         return new Animal(){  // 本质是 Animal 子类的一个对象
            // static int x = 2; 报错,实例不能含有 static 的变量与方法
            public int a = 2; // 无法访问
             @Override
             void hello() {
                 System.out.println(123);
             }
         };
    }

    public static void main(String[] args) {
        Father father = new Father();
        Animal test = father.test();
        System.out.println(test.a); // 1
        test.hello(); // 123
    }
}

class Animal{
    public int a;

    void hello(){
    }
}

1.14、Lambda 表达式

Java 中 Lambda 表达式的目的是使用单接口(只含有一个方法的接口)匿名类时,使代码更简洁

Lambda 表达式就是一个匿名方法(函数)

(参数列表) -> {
    方法体
}

interface C{
    double getC(double x);
}

C c = (double x) -> {
    return x*x;
}

2、封装、继承、多态

1、封装

封装:实现了信息的隐藏

Java 提供了 4 中权限修饰符来修饰类及类的内部结构
Java 规定的四种权限(从低到高):private、缺省(default,友好)、protected、public

image-20220701162418303

相关信息

权限修饰符可以修饰类及类的内部结构:属性、方法、构造器、内部类

修饰类只能使用:「缺省」、「public」

2、继承

继承的好处:

  • 减少代码冗余
  • 便于功能扩展
  • 为多态性的使用提供前提

继承性的格式:

  • class A extends B{}

A:子类、派生类、subclass
B:父类、超类、基类、superclass

  • 一旦「子类 A」继承「父类 B」以后,「子类 A」 中就获取了「父类 B」中声明的属性、方法。
  • 子类继承父类以后,还可以声明自己特有的属性或方法、实现功能拓展。

相关信息

父类的 private 属性分配了内存空间,但不作为子类对象的变量
子类不能访问父类的私有变量

java 中关于继承的规定(类与父类的关系)

  • 一个类可以被多个子类继承
  • 一个类只能有一个父类(单继承)
  • 子类直接继承的类是直接父类,间接继承的父类为间接父类
  • 如果没有显示的声明一个类的父类的话,则默认此类继承与 java.lang.Object
  • 所有的 java 类(除 java.lang.Object 类之外)都直接或间接继承与 java.lang.Object
  • 所有的 java 类具有 java.lang.Object 类声明的功能

2.1、成员变量的隐藏

在子类中定义以个与父类「同名」的变量即实现了对父类成员变量的隐藏
被隐藏的成员变量任可通过继承父类的方法操作

2.2、方法的重写(override/overwrite)

  • 重写:子类继承父类后,可以对父类中 同名同参数 的方法,进行覆盖操作
  • 重写后,创建子类对象,通过子类对象调用父类中的同名参数的方法时,实际执行 子类重写父类 的方法
  • 重写的规定:
    • 方法的声明:权限修饰符 返回值类型 方法名(形参列表) throws 异常的类型{方法体}
    • 子类中的叫重写的方法,父类中的叫被重写的方法
    • 子类重写方法的方法名和形参列表和父类中被重写的方法的 方法名和形参列表相同
    • 子类重写方法的权限 修饰符不小于父类 被重写的方法的权限修饰符
    • 子类不能重写父类中声明为 private 权限的方法
    • 返回值类型:
      • 父类被重写方法的返回值是 void,则子类重写的方法的返回值类型只能是 void
      • 父类被重写的方法的返回值类型是 A类型,则子类重写的方法的返回值类型可以是 A类或A类的子类
      • 父类被重写的方法的返回值类型是 基本数据类型 则子类重写的方法的返回值类型必须是 相同的基本数据类型
    • 子类重写的方法抛出的异常类型 不大于父类 被重写的方法抛出的 异常类型

相关信息

static 的方法没有重写,static 的变量和方法随着类的加载而加载

public class Father {
    static int x = 10;
    static void fun(){
        System.out.println(10);
    }

    public static void main(String[] args) {
        Father father = new Son();
        father.fun(); // 10
        System.out.println(father.x); // 10
        Son son = new Son();
        son.fun(); // 20
        System.out.println(son.x); // 20
    }
}

class Son extends Father{
    static int x = 20;
    static void fun(){
        System.out.println(20);
    }
}

3、多态性

多态性:可以理解为一个事物的多种形态
对象的多态性(或称上转型对象):父类的引用指向子类的对象(子类的对象赋给父类的引用)

1、多态的使用:虚拟方法调用

上转型对象对「方法」的调用:

  • 可以调用子类继承的方法或子类重写的方法
  • 编译期 ,只能调用 父类中声明的方法
  • 运行期 ,我们实际执行的是 子类重写父类的方法
  • 如果子类重写了父类的静态方法,上转型对象只能调用父类的静态方法
    总结:编译看左边,运行看右边

上转型对象对「属性」的调用:

  • 不能操作子类 新增 的成员变量与方法
  • 可以访问子类 继承隐藏 的成员变量(原本父类就有的)

2、多态性的使用前提:

  • 类的继承关系
  • 方法的重写
//对象的多态性:父类的引用指向子类的对象  
Person p2 = new Man();
//多态的使用:当调用子父类同名同参数的方法时,实际执行的是子类重写父类的方法---虚拟方法调用
p2.eat()
//p2.earnMoney();    子类中特有的方法,编译错误

相关信息

对象的多态性只适用于方法,不适用于属性

虚拟方法调用的理解

image-20220705204204460

3、其他关键字

3.1、this 关键字

this 关键字的使用:

  • this 可以用来修饰、调用:属性、方法、构造器
  • this 修饰属性和方法:this 理解为当前对象
  • 在类的构造器中可以使用 this(形参列表) 方式调用本类中的其他构造器
  • 构造器中不能通过 this(形参列表) 调用自己
  • this(形参列表) 必须声明在构造器首行
  • 构造器内部最多只能声明一个 this(形参列表) 用来调用其他的构造器

在类的方法或构造器中,我们可以使用 this.属性this.方法 的方式,调用当前对象属性或方法。但是通常情况下我们都省略this.
特殊情况下,如果方法或构造器的 形参类的属性重名时,我们必须显示的使用 this.变量 的方式表明此变量是属性,而非形参。

3.2、package 关键字

  • 使用 package 声明类或者接口所属的包,声明在源文件的首行
  • 包属于标识符,遵循标识符的命名规则(全部小写、见名知意)
  • . 一次,就代表一层文件目录
  • 同一包下,不可以命名同名的接口或类。不同的包下可以命名同名的接口、类。
  • 如果源程序中省略了 package 语句,则该源文件定义的类或接口被认为是无包名的,只要这些类的字节码被存放在相同的目录中,那么它们属于同一个包但没有包名。

包名的目的是有效区分名字相同的类,那么就涉及怎样区分包名,建议使用自己所在公司的Internet域名倒置包作为包名
Java 语言不允许用户程序使用 java 作为包名的第一部分,如 java.bird 是非法的包名(发生运行遗异常)

image-20220702205908691

3.3、import 关键字

如果一个类要使用不同包的类,则需要 import 语句

import:导入

  • 在 「package 语句」和「类定义之间」 使用 import 导入指定包下的 「类、接口」
  • 可以使用 xxx. 的方式,表示可以导入 xxx 包下的结构
  • 如果使用的类或接口是 java.lang 包定义的,则可以省略 import 结构
  • 如果使用的类或接口是「本包」下定义的,则可以省略 import 结构
  • 如果在源文件中,使用了「不同包」下的「同名」的类,则必须至少有一个类需要以全类名的方式显示(创建对象时,也是用全类名)
  • 如果使用「xxx.*」的方式表明可以调用 xxx 包下的所有结构。但是如果使用的是 xxx 子包下的结构,则仍需要显式导入。
  • import static:导入指定类或接口中的静态结构:属性,方法。

3.4、super 关键字

  • super 理解为父类的,可以用来调用:属性、方法、构造器
  • super 的使用
    • 在子类的方法或构造器中,通过使用 super.属性super.方法 显式的调用父类中声明的属性或方法。但通常情况下都习惯省略 super 关键字。
    • 特殊情况,当子类和父类定义了同名的属性,必须显式的使用 super.属性的方式,表明调用的是父类中声明的属性
    • 当子类重写了父类中的方法后,想在子类方法中调用父类被重写的方法,必须显式的使用 super.方法 的方式,表明调用的是父类中声明的方法。
    • super 调用构造器:
      • 可以在子类的构造器中显式的使用 super(形参列表) 的方式,调用父类中声明的指定的构造器
      • super(形参列表) 的使用也必须声明在子类构造器的 首行
      • 在类的构造器中,针对 this(形参列表)super(形参列表) 只能二选一,不能同时出现
      • 在构造器的首行没有显示的声明 this(形参列表)super(形参列表) ,则 默认调用 的是父类中空参的构造器,super()
      • 在类的多个构造器中,至少有一个类的构造器中使用了 super(形参列表),调用父类中的构造器

相关信息

子类对象实例化全过程
结果上:
子类获取父类中声明的属性和方法
创建子类的对象,在堆空间中,就会加载所有父类中声明的属性

过程上:
通过子类构造器直接或间接的调用父类的构造器,直到调用到 java.lang.Object 类中空参的构造器。
加载过所有父类的结构,所以才能看到内存中有父类的结构,子类对象可以进行调用。

3.5、instanceof 关键字

如何调用子类特有的属性和方法

强制类型转换

Person p1 = new Man();
//使用强制类型转换符(向下转型)
Man m1 = (Man)p1;

image-20220706104023004

使用强转时,可能会出现ClassCastException的异常

instanceof 的使用

a instanceof A 判断a是否时A的实例,如果是,返回true,若不是,返回false

如果a instanceof A返回true,则a instanceof B也返回true,那么类B是类A的父类。

if(p2 instanceof Woman){
    Woman w1 = (Woman)p2;
    wi.goShopping();
}
if(p2 instanceof Man){
   Man m1 = (Man)p2;
    m1.earnMoney();
}
//常见问题
//编译通过,运行不通过

/*同级之间不可转换
Person p3 = new Woman();
Man m3 = (Man)p3;*/

/*new出来的对象时Person,无法拥有子类的属性,方法
Person p4 = new Person();
Man m4 = (Man)p4;*/

//编译通过,运行也通过
Object obj = new Woman();
Person p = (Person)obj;

Man m = new Man();
Person p = m;
System.out.println(p == m);//true 地址相等

3.6、static 关键字

static 可以修饰:属性,方法,代码块,内部类(无构造器)

1、使用 static 修饰属性:静态变量(类变量)

属性:按是否使用 static 修饰分为:
「静态属性(类变量)」和 「非静态属性(实例变量)」

  • 类变量:创建了多个类的对象,多个对象共享一个静态变量。当通过某一个对象修改静态对象时,会 导致其他对象调用此静态变量时,是修改过了的

  • 实例变量:创建了多个类的对象,每个类都拥有一套类中的非静态属性。当修改其中一个对象中的非静态属性时,不会导致其他对象中的同样的属性值修改。

public class StaticTest{
    public static void main(String[] args){
        
        Chinese.nation = "China";
        Chinese.show();
        
        Chinese c1 = new Chinese();
        c1.name = "A";
        c2.nation = "CHN";//c2的nation也变为CHN
        Chinese c2 = new Chinese();
        c2.name = "B";
    }
}
class Chinese{
    String name;
    static String nation;
    public static void show(){
    } 
}

static 修饰属性的其他说明:

  • 类变量 随着类的加载而加载。可以通过 类.静态变量 的方式进行调用
  • 静态变量的加载要 早于对象的创建
  • 由于类只会加载一次,则静态变量在内存中也会存在一份:存在方法区的静态域
是否可调用类变量实例变量
yesno
对象yesyes

2、类变量和实例变量的内存解析

image-20220707194043724

3、使用 static 修饰方法:静态方法

  • 使用 static 修饰的方法:类方法、静态方法

  • 不使用 static 修饰的方法:实例方法

  • 随着类的加载而加载,可以通过“类.静态方法”的方式进行调用

是否可调用静态方法非静态方法
yesno
对象yesyes
  • 静态方法中,只能调用静态的方法或属性
    非静态方法中,既可以调用非静态的方法或属性,也可以调用静态方法或属性
  • static注意点:在静态的方法内,不能使用this关键字、super关键字(this,super是相对于对象而言的,调用非静态方法,隐含this.),静态方法不能被重写

4、单例设计模式

优点:减少性能的开销

image-20220708113553895

饿汉式:坏处:对象加载时间过长 好处:是线程安全的
懒汉式:好处:延迟对象的创建 目前写法的坏处:线程不安全

3.7、main() 方法的使用说明

  • main() 方法作为程序的入口
  • main() 方法也是一个普通的静态方法
  • main() 也可以作为我们与控制台交互的方式。(形参,Scanner)

3.8、final 关键字

  • final 可以用来修饰的结构:类、方法、变量
  • final 用来修饰一个「类」:此类不能被其他类所继承
    如:String 类,System 类,StringBuffer 类
  • final 用来修饰「方法」:表明此方法不可以被重写
    如:Object 类中的 getClass()
  • final 用来修饰「变量(成员变量和实例变量)」:此时的变量就称为一个「常量」,常量不允许改变,常量在声明时没有默认值,所以声明时必须指定值

① final 修饰「属性」,可以写的位置:显示初始化、代码块中初始化、构造器中初始化(没有方法)
② final 修饰「局部变量」:尤其视同 final 修饰形参时,表明此形参是一个常量。当调用此方法时,给常量形参赋一个实参。一旦赋值以后,就只能在方法体内使用此形参,不能再重新赋值

相关信息

static final 用来修饰属性:全局常量

3.9、abstract 抽象类和抽象方法

  • abstract 可以用来修饰:类、方法
  • abstrasct 类,抽象类
    • 此类不能实例化
    • 抽象类可以声明对象,该对象可以作为子类的上转型对象
    • 抽象类中一定有构造器,便于子类实例化时调用
    • 都会提供抽象类的子类,让子类对象实例化,完成相关操作
public abstract class Employee {
    private String name;
}
  • abstract修饰方法:抽象方法
    • 抽象方法只有方法的声明,没有方法体
    • 包含抽象方法的类一定是一个抽象类,反之抽象类中可以没有抽象方法
    • 子类重写了父类中的所有的抽象方法后,此子类方可实例化,若子类没有重写父类中的所有抽象方法子类也是一个抽象类,需使用abstract修饰
public abstract void eat();

abstract不能来修饰:属性、构造器等结构(只能修饰类和方法)
abstract不能用来修饰私有方法、静态方法、final的方法、final的类

模板方法设计模式

image-20220709165841679

abstract class Template{
    public void spendTime(){
        System.out.println("123");//父类的操作
        this.code() //不确定的部分、易变的部分(子类重写的方法)
    }
    public abstract void code();
}
class SubTempLate extends Template{
    public void code(){
        //方法体
	}
}
public class TemplateTest{
    public static void main(String[] args){
        SubTemplate t = new SubTemplate(){
            t.spendTime();//调用父类的方法
        }
    }
}

3.10、interface 接口

image-20220709220816216

  • 接口使用 interface 来定义

  • Java 中,接口和类是并列的两个结构

  • 如何定义接口:定义接口中的成员

    • JDK8 以前:

      • 只能定义「全局常量」和「抽象方法」
        • 全局常量:public static final 的,书写时可以省略
        • 抽象方法:public abstract 的,书写时可以省略
    • JDK8 及以后:

      • 除了定义「全局变量」和「抽象方法」之外还可以定义 「静态方法」、「默认方法」
      • 静态方法:public static,有方法体
      • 默认方法:public default,有方法体,权限一定是 public 或 省略
    • 不可以用 static 和 abstract 修饰一个方法

      相关信息

      类实现某接口后,该类继承了接口中的的常量和 default 方法,也可重写 default 方法。
      但该类不拥有接口的 static 方法

    abstract 类和 接口 的比较

    abstract 类接口
    可以有抽象方法可以有抽象方法
    既可以常量也可以有变量还能有常量,不能有变量
    可以有非抽象方法,不能有 default 方法不可以有非抽象方法,可以有 default 方法
public interface CompareA{
    //静态方法
    public static void method1(){
        System.out.println("");
    }
    //默认方法
    public default void method2(){
        System.out.println("");
	}
    //省略了 public 的默认方法
    default void method3(){
         System.out.println("");
	}
}

class SubClass implements CompareA{
    // 重写默认方法
    public void method2(){
        System.out.println("");
	}
}

public class SubClassTest {
    public static void main(String[] args){
        SubClass s = new SubClass();

        //1、接口中定义的静态方法,只能通过接口来调用
        CompareA.method1();

        //2、通过实现类的对象,可以调用接口中的默认方法或朝重写过的默认方法。
        s.method2();

        //3、如果子类(实现类)继承的「父类」和「实现的接口」中声明了同名同参数的方法,那么子类在没有重写此方法的情况下,默认调用父类中的同名同参的方法。(类优先原则)

        //4、如果实现类实现了多个接口,且这多个接口定义了同名同参数的默认方法,那么在实现类没有重写此方法的情况下,报错(接口冲突)
	}
}


相关信息

  • 接口中定义的静态方法,只能通过接口来调用
  • 通过实现类的对象,可以调用接口中的默认方法。如果实现类重写了接口中的默认方法,调用时,调用的是重写后的方法
  • 接口中不能定义构造器,意味着接口不能实例化
  • Java 中,接口通过类 「实现 implements」的方式来使用

    • 如果 实现类覆盖了 接口中的 所有抽象方法,则此 实现类就可以实例化
    • 如果 实现类没有覆盖 接口中 所有抽象方法,则此实现类 仍为一个抽象类
  • Java 类可以实现多个接口,弥补了 Java 单继承性的局限性
    格式:class A extends B implements C,D,E

  • 接口与接口之间可以继承,而且可以多继承

public class InterfaceTest{
    public static void main(String[] args){
        System.out.println(Flyable.MAX_SPEED);
        System.out.println(Flyable.MIN_SPEED);
    }
}
interface Flyable{
    //全局常量
    public static final int MAX_SPEED = 7900;//第一宇宙速度
    int MIN_SPEED = 1;//同样时常量,省略了public static final
    //抽象方法
    public abstract void fly();
    void stop();//省略了public abstract
}
interface Attackable{
    void attack();
}
class Plan implements Flyable{
    @override
    public void fly(){
        //方法体
	}
    @override
    public void stop(){
	}
}
class bullet extends Object implements Flyable,Attackable{
    @override
    public void fly(){
        //方法体
	}
    @override
    public void stop(){
	}
    @override
    public void attack(){
	}
}

//接口的继承
interface AA{
    void method1();
}
interface BB{
     void method2();
}
interface CC extends AA,BB{
    
}

接口的使用、回调、多态性

  • 接口使用上也满足多态性
  • 接口回调指实现某一接口的对象赋值给接口声明的变量,那么该变量可以调用类重写的接口方法和接口中的 default 方法
  • 接口实际上就是定义了一种规范
//接口多态性的体现
public class USBTest{
    public static void main(){
        Computer com = new Computer();
        //1、创建了接口的非匿名实现类的非匿名对象
        Flash falsh = new Flash;
        com.transforData(flash);
        
        //2、创建了接口的非匿名实现类的匿名对象
        com.transforData(new Flash);
        
        //3、创建了接口的匿名实现类的非匿名对象
        USB phone = new USB(){
            @override
             public void start(){
        	//方法体
             }
            @override
    		public void stop(){
        	//方法体
        	}
        };
        com.transforData(phone);
            
		//4、创建了接口的匿名实现类的匿名对象
        com.transforData(new USB(){
            @override
             public void start(){
        	//方法体
             }
            @override
    		public void stop(){
        	//方法体
        	}
        });
	}
}
class Computer{
    public void transforData(USB usb){
        usb.start();
        usb.stop();
	}
}
interface USB{
    void start();
    void end();
}
class flash implements USB{
    public void start(){
        //方法体
	}
    public void stop(){
        //方法体
	}
}

接口的应用:代理模式

image-20220710153043102

public class Network{
    public static void main(String[] args){
        Server sever = new Server();
        ProxyServer proxyServer= new ProxySever(sever);
        proxyServer.browse();
    }
}
interface Network{
    public void browse();
}
//被代理类
class Server implements Network{
    @override
    public void browse(){
        System.out.println("真实的服务器访问");
	}
}
//代理类
class ProxySever implements Network{
    private Network work;
    public ProxySever(Network work){
        this.work = work;
    }
    public void check(){
        System.out.println("联网前的一些检查工作");
    }
     @override
    public void browse(){
        check();
        work.browse;
	}
}

image-20220710154540213

接口的应用:工厂模式
接口,父类同名属性的讨论:

interface A{
    int x = 0;
}
class B{
    int x = 1;
}
class C extends B implements A{
    public void s{
        //System.out.println(x);//编译不通过,x是不明确的
        System.out.println(super.x);//1
        System.out.println(A.x);//0
    }
}
评论
  • 按正序
  • 按倒序
  • 按热度
Powered by Waline v2.15.5