文章目录
- 基础大纲
- 1.类的结构
- 类的语法结构
- 对象的方法存储、调用时内存的工作机制
- 1>类中-成员变量和局部变量的区别
- 2>类中-方法
- 方法的重载overlode
- 可变个数的形参
- 3>类中-构造器
- 4>类中-初始化化块
- 5>类中-内部类
- 2.类的初始化顺序
- 3.面向对象三大特征
- 封装
- 继承
- 继承中成员变量的特点:
- 继承中构造方法的特点
- 继承中成员方法的特点
- @重写
- 多态
- 多态的调用
- 内存图
- 优缺点
- 多态中的转型问题
- 多态应用
- 4.面向对象中的关键字
- 1)package,import
- JDK中主要的包介绍:
- 2)4种访问权限修饰符
- 3)this关键字
- 4)super关键字
- 5)instanceof 关键字
- 6)static关键字
- 7)final关键字
- 5.抽象类与抽象方法
- 模板方法设计TemplateMethod
- 6.Object类
- ==与equals
- toString()
- 包装类
- 7.UML类图
- 8.其他知识点
基础大纲
什么是面向对象?
面向对象是一种思想, 更符合我们思考问题的一种习惯:将复杂的问题简单化,将我们从执行者的位置变成了指挥者。
面向对象和面向过程的思想对比?
- 面对过程: 强调的是过程, 自己通过第一步, 第二步, 一步步的去实现对应的需求 (自己将功能实现)
- 面向对象: 强调的是对象, 通过指挥对象中的功能, 来完成对应的需求(指挥对象去实现功能)
OOP: Object Oriented Programming
面向过程:procedure oriented programming
面向对象知识结构:’
1.类的结构
类的语法结构
修饰符 class 类名 {
属性声明;
方法声明;
}
类的结构:
1)成员属性
2)成员方法
3)构造器
构造器Constructor不能被继承,因此不能重写Override,但可以被重载Overload
4)代码块/静态代码块[加载时机不同]
5)内部类
对象的方法存储、调用时内存的工作机制
问题:啥是内存中的垃圾?
Java语言有完善的垃圾回收机制, 会在不定时的时候对内存中的垃圾进行清扫。
当堆内存中的对象地址,没有任何一个引用[变量]记录的时候, 那么此对象就会在内存中变成垃圾对象。
此时会又Java垃圾回收器不定时地对其进行清扫。
1>类中-成员变量和局部变量的区别
- 1.位置不同:
成员变量 : 类中方法外
局部变量 : 方法中, 或者是方法的声明上(形参)
public static void method(int a){} - 2.初始化值不同:
成员变量 : 有默认初始化值
局部变量 : 没有默认初始化值, 使用之前必须赋值才能使用 - 3.在内存中的位置不同
成员变量:堆内存
局部变量:栈内存 - 4.生命周期不同
成员变量:随着对象创建而存在,随着对象消失而消失
局部变量:随着方法调用而存在,随着方法弹栈而消失 - 5.作用域不同
局部变量:仅在它所在大括号内有效
成员变量:整个类当中有效
局部变量也需要先声明赋值,后使用,也可采用默认的初始化值。
注意:方法的形参为特殊,它的初始化为方法调用时传入的值;
局部变量没有权限修饰符,它的权限跟随方法,或者构造器的权限;
局部变量存放在栈空间;
2>类中-方法
语法格式:
修饰符 返回值类型 方法名 (参数列表) {
方法体语句;
}
说明:
- 修饰符:public, private, protected等。
- 返回值类型:
没有返回值:void。
有返回值,声明出变量的类型 - 方法名:“见名知意”
- 方法的参数列表:
可以包含一到多个参数
调用方法时,参数的类型与数量必须完全匹配
Java的实参值如何传入方法呢?
Java里方法的参数传递方式只有一种:值传递。 即将实际参数值的副本(复制品)传入方法内,而参数本身不受影响
方法的重载overlode
同一类中,方法名相同,参数列表不同,与返回值无关。
可变个数的形参
//下面采用数组形参来定义方法
public static void test(int a ,String[] books);
//以可变个数形参来定义方法
public static void test(int a ,String…books);
说明:
- 可变参数:方法参数部分指定类型的参数个数是可变多个
- 声明方式:方法名(参数的类型名…参数名)
- 可变参数方法的使用与方法参数部分使用数组是一致的
- 方法的参数部分有可变形参,需要放在形参声明的最后
3>类中-构造器
构造器的特征:
- 它具有与类相同的名称
- 它不声明返回值类型。(与声明为void不同)
- 不能被static、final、synchronized、abstract、native修饰
- 没有返回值,不能有return语句返回具体的值。
构造器的作用:创建对象,给对象进行初始化
修饰符 类名 (参数列表) {
初始化语句;
}
根据参数不同,构造器可以分为如下两类:
- 隐式无参构造器(系统默认提供)
- 显式定义一个或多个构造器(无参、有参)
注 意:
- Java语言中,每个类都至少有一个构造器
- 默认构造器的修饰符与所属类的修饰符一致
- 一旦显式定义了构造器,则系统不再提供默认构造器
- 一个类可以创建多个重载的构造器
- 父类的构造器不可被子类继承
4>类中-初始化化块
初始化块(代码块)作用:对Java类或对象进行初始化
一个类中初始化块若有修饰符,则只能被static修饰,称为静态代码块(static block),当类被载入时,类属性的声明和静态代码块先后顺序被执行,且只被执行一次。
static块通常用于初始化static (类)属性:
class Person {
public static int total;
static {
total = 100;//为total赋初值
}
…… //其它属性或方法声明
}
非静态代码块:没有static修饰的代码块
- 可以有输出语句。
- 可以对类的属性、类的声明进行初始化操作。
- 可以调用静态的变量或方法。
- 若有多个非静态的代码块,那么按照从上到下的顺序依次执行。
- 每次创建对象的时候,都会执行一次。且先于构造器执行
静态代码块:用static 修饰的代码块
- 可以有输出语句。
- 可以对类的属性、类的声明进行初始化操作。
- 不可以对非静态的属性初始化。即:不可以调用非静态的属性和方法。
- 若有多个静态的代码块,那么按照从上到下的顺序依次执行。
- 静态代码块的执行要先于非静态代码块。
- 静态代码块只执行一次
5>类中-内部类
见Java基础(三)内部类
2.类的初始化顺序
类中属性赋值的位置:
- ①声明成员变量的默认初始化
- ②显式初始化
- ③构造器初始化
- ④通过对象.(属性|方法)修改属性值
- ⑤代码块中初始化
类中属性初始化顺序:
- ①-②/⑤-③-④
- ②和⑤的执行顺序取决于二者的声明的先后顺序:先声明,就先执行
静态代码块:只能访问到在其之前定义的静态变量,定义在之后的静态变量,其可以赋值,不能访问
private static int a;
static {
System.out.println(a);
//System.out.println(b); //Cannot reference a field before it is defined
b = 4;
}
private static int b;
public static void main(String[] args) {
System.out.println(b); //4
}
3.面向对象三大特征
封装
隐藏实现细节,仅暴露公共的访问接口。
其提高了代码的安全性,与复用性
继承
- 继承就是让类与类之间产生关系, 子父类关系, 子类就可以直接使用到父类中非私有的成员.(私有的内容有继承,只是不能直接调用)
- 使用extends关键字
class Subclass extends Superclass {
}
1.优点
- A. 提高了代码的复用性
- B. 提高了代码的维护性
- C. 继承是多态的前提
2.弊端
- 类的耦合性增强了
3.Java当中只支持单继承, 不支持多继承, 但是可以多层继承.
4.关于继承的规则:
- 父类中的成员,无论是公有(public)还是私有(private),均被子类继承。
- 子类不能对继承的私有成员直接进行访问,可通过继承的公有方法来访问。
5.子类对象实例化过程
思考:
1).为什么super(…)和this(…)调用语句不能同时在一个构造器中出现?
2).为什么super(…)或this(…)调用语句只能作为构造器中的第一句出现?
继承中成员变量的特点:
- 如果子父类中出现了相同的成员变量, 那么创建子类对象的时候, 用的是谁的成员变量?
- 继承中成员变量的特点
4.总结:
- 如果子父类中出现了相同的成员变量, 那么创建子类对象调用成员变量的时候, 使用的是子类的成员变量
- 就近原则 : 自己本类有, 就不去父类找了.
继承中构造方法的特点
一定是父类先进行初始化, 子类随后进行初始化
问题: 为什么要先进行父类的初始化呢?
因为子类在创建对象的时候, 可能会用到父类的成员, 如果父类没有完成初始化, 子类将使用不到这些成员.
问题: 子类在创建对象之前, 是怎样完成父类初始化的呢?
在子类的所有构造方法中, 都默认隐藏了一个语句
- super();
问题: 为什么要隐藏一个super()来调用父类的构造方法呢?
因为初始化一个对象就要走该对象的构造方法.
问题: 如果父类没有空参构造子类怎么办?
- 手动通过super访问父类带参构造 (不推荐)
- 推荐, 今后写代码的时候, 空参有参都手动给出
注意:
- super() : 调用父类构造方法
- this() : 调用本类构造方法
- 两条语句必须写在构造方法的第一行有效语句,而且两者都在争夺第一行的位置,
- 所以二者冲突
继承中成员方法的特点
- 子父类中如果出现了相同的方法, 那么调用的时候将会采用???
成员方法访问特点:
- 子父类中如果出现了相同的方法, 那么调用的时候将会采用子类的成员方法.
- 虽然现象上是就近原则的效果, 但是这里有一个名词, 叫做方法的重写(覆盖)
@重写
Override(重写\覆盖\覆写) : 在子父类当中, 出现了方法声明一模一样的方法 (方法名和参数列表必须完全一致)
什么情况下需要使用重写??
当子类需要父类的功能, 但是子类方法的功能主体又有自己特有的实现方式, 这时候就可以对父类的方法进行重写,这样做既沿袭了父类的功能, 又定义了子类特有的内容.
注解: @Override : 用于校验当前的方法是否是一个重写的方法
问题: Overload和Override的区别?
- Overload(重载) : 在同一个类当中, 方法名相同, 参数列表不同, 与返回值无关
参数列表不同: 个数不同, 类型不同, 顺序不同 - Override(重写)覆盖,覆写 : 在子父类关系中, 出现了方法声明一模一样的方法(方法名和参数列表也必须一致)
问题: 子类重写父类方法有哪些注意事项?
- 子类重写方法的访问权限:
- 访问权限需要大于等于父类. (最好就一致):访问权限 private -> 默认的 -> protected -> public
- 固不能重写父类的private私有方法
- 重写方法必须和被重写方法具有相同的方法名称、参数列表和返回值类型。
- 重写父类方法时,参数列表对应参数类型范围不能大于父类参数类型
- 重写和被重写的方法须同时为static的,或同时为非static的
- 子类方法抛出的异常不能大于父类被重写方法的异常
多态
事物存在的多种形态,在Java中有两种体现:
- override和overload
- 对象的多态性:可以直接应用在抽象类和接口上
Cat c = new Cat(); // 事物(对象)是一只猫
Animal a = new Cat(); // 事物(对象)是一只动物
对象多态的前提:
- A. 要有继承关系
- B. 要有父类引用指向子类对象
多态的调用
Java引用变量有两个类型:编译时类型和运行时类型:
- 编译时类型由声明该变量时使用的类型决定
- 运行时类型由实际赋给该变量的对象决定
若编译时类型和运行时类型不一致,就出现多态(Polymorphism)
- 多态调用成员变量
- 多态调用成员方法
- 多态调用静态方法
总结:
1. 编译看左边(父类), 运行看左边(父类)[编译出错,不会有字节码文件,也就不能运行]
// 10 // 20
Animal a = new Cat();
System.out.println(a.num); // 10
原因: 因为是父类的类型, 所以只能看到堆内存中, super的一小块空间
2. 编译看左边(父类), 运行看右边(子类)
在多态创建对象, 调用成员方法的时候
Animal a = new Cat();
a.eat();
编译时会检查父类中是否有此方法的声明
没有: 编译失败
有 : 编译通过, 但是运行的时候会执行子类的方法逻辑——动态绑定机制
3. 编译看左边(父类), 运行看右边(子类)
静态修饰的成员跟类相关, 跟对象没有关系。
即使是通过对象名调用静态成员, 编译的时候也会自动翻译成[类名.调用 ]
*
* 编写的源代码: f.show();
* 字节码文件中: Fu.show();
内存图
- 多态调用成员变量:
- 多态调用成员方法:
优缺点
好处:
1. 提高了代码的复用性
2. 提高了代码的维护性
继承好处
---------------------
3. 提高了代码的扩展性
* 1)面向接口编程:声明接口,但使用的是接口的子类对象
* 2)可以将方法的形参定义为父类类型, 该方法就能接受这个父类的任意子类对象
// Animal a = new Cat();
// Animal a = new Dog();
public static void useAnimal(Animal a){
a.eat();
}
弊端:不能调用子类特有的属性和行为
问题: 非要调用咋办??
1. 直接创建子类对象去
2. 向下转型
建议:多态最好只调用共有的成员方法
多态中的转型问题
Person p = new SuperMan(); // 向上转型
SuperMan sm = (SuperMan)p; // 向下转型
注意:
1)向下转型的强转, 必须发生在子父类的关系当中
Animal a = new Dog();
Cat c = (Cat)a; // 运行错误 : ClassCastException
2)而且必须先转上去, 才能转下来.
Animal a = new Animal();
Dog d = (Dog)a; // 运行错误 : ClassCastException
ClassCastException : 类型转换异常, 原因: 出现了错误的强转.
多态应用
1.多态数组:在引用类型的数组中,使用多态形式存放对象
Person[] ps =new Person[2];
Ps[0] = new Teacher();
Ps[1] = new Student();
2.多态参数:形参类型为父类类型,使用多态形式传递 参数
Public void method(Person p){//Person p = new Teacher();
}
Method(new Teacher());
4.面向对象中的关键字
1)package,import
package语句作为Java源文件的第一条语句,指明该文件中定义的类所在的包。(若缺省该语句,则指定为无名包)。
它的格式为:
package 顶层包名.子包名 ;
举例:pack\Test.java
package p1; //指定类Test属于包p1
public class Test{
public void display(){
System.out.println("in method display()");
}
}
- 包对应于文件系统的目录,package语句中,用 “.” 来指明包(目录)的层次;
- 包通常用小写单词,类名首字母通常大写。
import语句告诉编译器到哪里去寻找类,为使用定义在不同包中的Java类,需用import语句来引入指定包层次下所需要的类或全部类(.*)
语法格式:
import 包名[.子包名…]. <类名 |*>
//应用举例:
import p1.Test; //import p1.*;表示引入p1包中的所有类
public class TestPackage{
public static void main(String args[]){
Test t = new Test(); //Test类在p1包中定义
t.display();
}
}
import 注意:
- 在源文件中使用import显式的导入指定包下的类或接口
- 声明在包的声明和类的声明之间。
- 如果需要导入多个类或接口,那么就并列显式多个import语句即可
- 举例:可以使用java.util.*的方式,一次性导入util包下所有的类或接口。
- 如果导入的类或接口是java.lang包下的,或者是当前包下的,则可以省略此import语句。
- 如果在代码中使用不同包下的同名的类。那么就需要使用类的全类名的方式指明调用的是哪个类。
- import static组合的使用:调用指定类或接口下的静态的属性或方法
- 如果已经导入java.a包下的类。那么如果需要使用a包的子包下的类的话,仍然需要导入。
JDK中主要的包介绍:
- java.lang----包含一些Java语言的核心类,如String、Math、Integer、System和Thread,提供常用功能。
- java.net----包含执行与网络相关的操作的类和接口。
- java.io ----包含能提供多种输入/输出功能的类。
- java.util----包含一些实用工具类,如定义系统特性、接口的集合框架类、使用与日期日历相关的函数。
- java.text----包含了一些java格式化相关的类
- java.sql----包含了java进行JDBC数据库编程的相关类/接口
- java.awt----包含了构成抽象窗口工具集(abstract window toolkits)的多个类,这些类被用来构建和管理应用程序的图形用户界面(GUI)
2)4种访问权限修饰符
Java权限修饰符public、protected、private置于类的成员定义前,用来限定对象对该类成员的访问权限。
对于class的权限修饰只可以用public和default(缺省)
- public类可以在任意地方被访问。
- default类只可以被同一个包内部的类访问(固不同包需要import导入)
3)this关键字
this表示当前对象,可以调用类的属性、方法和构造器
- 它在方法内部使用,即这个方法所属对象的引用(区分局部变量和属性);
- 它在构造器内部使用,表示该构造器正在初始化的对象。
什么时候使用this关键字呢?
当在方法内需要用到调用该方法的对象时,就用this。具体的:
- 1.可以用this来区分局部变量和属性。比如当形参与成员变量重名时:this.name = name;
- 2.在任意方法内,如果使用当前类的成员变量或成员方法可以在其前面添加this,增强程序的阅读性
- 3.this可以作为一个类中,构造器相互调用的特殊格式
class Person{ // 定义Person类
private String name ;
private int age ;
public Person(){ // 无参构造器
System.out.println("新对象实例化") ;
}
public Person(String name,int age){
this(); 调用本类中的无参构造器,且放在首行
this.name = name ; //构造器中使用
this.age = age ; }
public void getInfo(){
System.out.println("姓名:" + name) ;
this.speak();
}
public void speak(){
System.out.println(“年龄:” + this.age); //方法中使用
}
}
注意:
- 使用this()必须放在构造器的首行!
- 使用this调用本类中其它的构造器,至少有一个构造器是不用this的。
为什么static类方法中不能使用this提取成员变量,其只能使用static成员?
静态方法先于对象的存在而存在,this代表当前对象的引用,故而不可以。
4)super关键字
- super代表父类空间的引用(不仅限于直接父类) --> 简单记: super可以调用父类的相关成员
- this : 代表当前对象的引用
super 和 this 的区别:
1)成员变量:
- this.成员变量 : 调用本类的成员变量
- this也可以调用父类成员变量, 但是存在前提(子父类中没有出现重名的成员变量)
原因: 父类中的成员被子类继承下来了, 既然子类继承下来了, 就相当于子类自己有一份, this调用自己的是没有问题的.
- super.成员变量 : 调用父类的成员变量
2)成员方法:
- this.成员方法 : 调用本类的成员方法
- 也可以调用父类成员方法, 前提 : 子父类中没有重名的成员方法
- super.成员方法 : 调用父类的成员方法
3)构造方法:两者都必须在第一行,且需要有对应的构造方法
- this() : 只能调用本类构造方法
- super() : 只能调用父类构造方法
class Dad {
String name = "建霖";
}
class Kid extends Dad {
String name = "四葱";
public void show() {
String name = "五葱";
System.out.println(); //五葱
System.out.println(); //四葱
System.out.println(); //建霖
}
}
思考:
1).为什么super(…)和this(…)调用语句不能同时在一个构造器中出现?
2).为什么super(…)或this(…)调用语句只能作为构造器中的第一句出现?
因为:子类要具备父类中的数据,就要先明确父类是如何对这些数据初始化的。
5)instanceof 关键字
作用:判断左边的引用是否是右边的类型,返回值 --> true, false
x instanceof A:检验x是否为类A的对象,返回值为boolean型。
- 要求x所属的类与类A必须是子类和父类的关系,否则编译错误。
- 如果x属于类A的子类B,x instanceof A值也为true。
6)static关键字
类只有通过new才会产生对象,我们有时候希望无论是否产生了对象或无论产生了多少对象的情况下,某些特定的数据在内存空间里只有一份。
在Java类中,可用static修饰属性、方法、代码块、内部类
修饰后的成员具备以下特点:
- 随着类的加载而加载
- 优先于对象存在
- 修饰的成员,被所有对象所共享
- 访问权限允许时,可不创建对象,直接被类调用
public class Person {
private int id;
public static int total = 0; //类变量 class variable
public static int getTotalPerson() { //类方法 class method
id++;
return total;
}
}
注意:
- 在static方法内部只能访问类的static属性,不能访问类的非static属性
- 因为不需要实例就可以访问static方法,因此static方法内部不能有this、也不能有super
- 重载的方法需要同时为static的或者非static的
7)final关键字
在Java中声明类、变量和方法时,可使用关键字final来修饰,表示“最终”
- final标记的类不能被继承(太监类)——提高安全性,提高程序的可读性。
String类、System类、StringBuffer类 - final标记的方法不能被子类重写
Object类中的getClass()。 - final标记的变量(成员变量或局部变量)即称为常量——名称大写,且只能被赋值一次
final标记的成员变量:必须在构造方法结束之前完成赋值
- 声明的同时,或
- 在每个构造方法中,或
- 代码块中显式赋值,然后才能使用。
class A{
private final String INFO = "ASD"; //声明常量
private final double PI;
private static final String X = "ABC"; //声明全局常量
public A(){
PI = 3.1415926; 可在构造方法中给final变量赋值
}
}
5.抽象类与抽象方法
抽象类是用来模型化那些父类无法确定全部实现,而是由其子类提供具体实现的对象的类。
Java允许类设计者指定:超类声明一个方法但不提供实现,该方法的实现由子类提供。这样的方法称为抽象方法。有一个或更多抽象方法的类称为抽象类。
1.抽象方法定义:
- 去掉方法体的大括号, 然后分号结束, 并在返回值类型前面加入关键字[abstract]
public abstract void eat();
限制: 抽象方法必须存活在抽象类,或者是接口当中
2.抽象类如何定义:
- 在class的前面加入abstract
abstract class Animal{}
3.抽象类和普通父类的区别在于, 抽象类可以定义抽象方法.
4.抽象类的特点
- A:抽象类和抽象方法必须使用abstract关键字修饰
- B:抽象类中不一定有抽象方法,有抽象方法的类一定是抽象类【或者是接口】
- C:抽象类不能实例化
- D:抽象类的子类
1)必须重写父类(抽象类)中所有的抽象方法[推荐方案]
2)要么将自己也变成一个抽象类[不推荐]
- E:不能用abstract修饰属性、私有方法、构造器、静态方法、final的方法
5.抽象类如何实例化呢?
可以使用多态的形式, 父类引用指向子类对象
f.eat(); 编译看左边(父类), 运行看右边(子类)
编译看父类 : 检查父类中是否有此方法的[声明];如果有, 编译通过, 但是运行的时候, 执行子类的方法.
6.抽象类的成员特点
- 成员变量 :
A. 可以是一般变量
B. 也可以是常量 - 成员方法 :
可以是一般方法, 也可以是抽象方法
- 构造方法 :
有, 目的是为了方便子类进行初始化的.
结论: 抽象类中除了可以定义抽象方法以外, 其余的定义, 和普通的类是一致的.
抽象类能否可以一个抽象方法都没有?
可以:其目的是,为了不让其他类创建本类对象——————适配器设计模式
模板方法设计TemplateMethod
抽象类体现的就是一种模板模式的设计,抽象类作为多个子类的通用模板,子类在抽象类的基础上进行扩展、改造,但子类总体上会保留抽象类的行为方式。
//模板抽象类
abstract class Template{
public final void getTime(){
long start = System.currentTimeMillis();
code();
long end = System.currentTimeMillis();
System.out.println("执行时间是:"+(end - start));
}
public abstract void code();
}
//模板类的子类
class SubTemplate extends Template{
public void code(){
for(int i = 0;i<10000;i++){
System.out.println(i);
} } }
6.Object类
Object类是所有Java类的根父类
- 如果在类的声明中未使用extends关键字指明其父类,则默认父类为Object类
==与equals
= =:
- 1)基本类型比较值:只要两个变量的值相等,即为true.
int a=5; if(a==6){…} - 2)引用类型比较引用(是否指向同一个对象):只有指向同一个对象时,==才返回true.
- 3)用“==”进行比较时,符号两边的数据类型必须兼容(可自动转换的基本数据类型除外),否则编译出错;
equals():所有类都继承了Object,也就获得了equals()方法,可以被重写。
- 只能比较引用类型,如果没有被重写,其作用与“==”相同,比较是否指向同一个对象
- 如果被重写,则使用重写后的方法逻辑
toString()
toString()方法在Object类中定义,其返回值是String类型,返回类名和它的引用地址。
- 1)在进行String与其它类型数据的连接操作时,自动调用toString()方法
Date now=new Date();
System.out.println(“now=”+now); 相当于
System.out.println(“now=”+now.toString());
- 2)可以根据需要在用户自定义类型中重写toString()方法
- 3)基本类型数据转换为String类型时,调用了对应包装类的toString()方法
int a=10;
System.out.println(“a=”+a);
包装类
见Java基础—包装类
- 装箱、拆箱
- toString()方法
- intValue()方法
- Integer.parseInt()方法
7.UML类图
- + 表示 public 类型, - 表示 private 类型,#表示protected类型
- 方法的写法: 方法的类型(+、-) 方法名(参数名: 参数类型):返回值类型
8.其他知识点
- Singleton单例设计模式
- 接口interface