接口
一、初识接口
使用接口的原因
- 要求实现防盗门功能
1、门要有”开“和”关“的功能,锁要有“上锁”和“开锁”的功能。
2、将门和锁分别定义为抽象类。
由于防盗门无法即继承门又继承锁,所以需要使用接口。
3、将门定义为抽象类,锁定义为接口。
4、防盗门继承门,实现锁的接口。
概念
认识接口:
public interface MyInterface {
public void foo();
//其他方法
}
//当中所有的方法都是:public abstract
用法:
1、使用interface来修饰。
2、接口中可以包含多个方法,且方法跟抽象类中的抽象方法一致,可以不写实现,子类在实现的时候必须要实现代码逻辑。
3、子类实现接口使用implements关键字。
特征:
1、接口中所有方法都是抽象方法,不能包含方法的实现。
2、接口中所有方法的访问修饰权限都是public,不写并不是默认访问权限,而是public。
3、接口不能被实例化。
4、接口的子类必须实现接口中的所有方法,跟抽象类有所不同,抽象类中的抽象方法必须被子类实现。
5、子类可以拥有实现多个接口。
6、接口中的变量都是静态常量,如果变量没有使用static关键字修饰,它也表示静态常量,不用final关键字修饰,也是常量。
7、接口中的方法和常量无论是否添加public修饰,默认的权限有且仅有一个,就是public。
二、接口的使用
可以用程序描述USB接口
- USB接口本身没有实现任何功能
- USB接口规定了数据传输的要求
- USB接口可以被多种USB设备实现
可以使用Java接口来实现
编写USB接口——》根据需求设计方法
实现USB接口——》实现所有方法
使用USB接口——》用多态的方式使用
编码实现
//编写接口
public interface UsbInterface{
//USB接口提供服务。
void service();
}
//实现接口
//多个接口使用","分隔
public class UDisk implements UsbInterface{
public void service() {
System.out.println("连接USB口,开始传输数据。");
}
}
//使用接口
UsbInterface uDisk = new UDisk();
//用接口实现多态
uDisk.service();
三、面向接口编程
接口表示一种能力
- “做这项工作需要一个钳工(木匠/程序员)”
这里的钳工是一种”能力“,不关心具体是谁。 - 接口代表一种能力:接口中可以定义N多个方法,子类进行实现的时候,必须要实现这些方法,将这些方法进行实现,就意味着具备了方法的能力,关心实现类有何能力,而不关心实现细节。
- 面向接口编程
防盗门的实现
实现防盗门的功能
轻轻拉门,门关上了。
插入钥匙,向左旋转钥匙三圈,锁上了,拔出钥匙。
插入钥匙,向右旋转钥匙三圈,锁打开了,拔出钥匙。
用力推,门打开了。
分析
实现过程
一个人可以具有多项能力,所以一个类也可以实现多个接口。
- 扩展防盗门门铃功能,主要是实现拍照存档。
轻轻拉门,门关上了。
插入钥匙,向左旋转钥匙三圈,锁上了,拔出钥匙。
铃。。。。。咔嚓。。。。照片已存储。
插入钥匙,向右旋转钥匙三圈,锁打开了,拔出钥匙。
用力推,门打开了。
编码实现
- 门的抽象类
public abstract class Door {
public abstract void openDoor();
public abstract void closeDoor();
}
- 锁的接口
public interface Lock {
void openLock();
void closeLock();
}
- 门铃的接口
public interface DoorBell {
void photo();
}
- 继承门的类实现锁接口和门铃接口的类
public class LockDoor extends Door implements Lock,DoorBell{
@Override
public void openDoor() {
System.out.println("开门");
}
@Override
public void closeDoor() {
System.out.println("关门");
}
@Override
public void openLock() {
System.out.println("开锁");
}
@Override
public void closeLock() {
System.out.println("关锁");
}
@Override
public void photo() {
System.out.println("拍照存档");
}
}
- 测试类
public class TestLockDoor {
public static void main(String[] args) {
LockDoor lockDoor = new LockDoor();
lockDoor.openDoor();
lockDoor.openLock();
lockDoor.photo();
lockDoor.closeDoor();
lockDoor.closeLock();
}
}
接口是一种约定
- 生活中,我们使用的两相电源插座,规定了:
1、两个接头间的额定电压
2、两个接头间的距离
3、接头的形状 - 接口是一种约定
- 面向接口编程
程序设计时面向接口的约定而不考虑具体实现。
开发打印机
- 墨盒:彩色、黑白
- 纸张类型:A4、A5
- 墨盒和纸张都不是打印机厂商提供的
- 打印机厂商要兼容市场上的墨盒、纸张
分析
墨盒和纸张的规格就是一种约定,而打印机需要遵循这些约定。
- 用面向接口编程的方式开发
1、制定墨盒、纸张的约定或标准。
2、打印机厂商使用墨盒、纸张的标准开发打印机。
3、其他厂商按照墨盒、纸张的标准生产墨盒和纸张。
编码实现
- 墨盒接口InkBox
public interface InkBox {
public void getWriteAndBlock();
public void getColor();
}
- 纸张接口Paper
public interface Paper {
public void getA4();
public void getB5();
}
- 打印机类
public class CopyMechine implements InkBox,Paper{
@Override
public void getWriteAndBlock() {
System.out.print("使用黑白墨盒");
}
@Override
public void getColor() {
System.out.print("使用彩色墨盒");
}
@Override
public void getA4() {
System.out.println("在A4纸张上打印");
}
@Override
public void getB5() {
System.out.println("在B5纸张上打印");
}
}
- 测试类
public class Test {
public static void main(String[] args) {
CopyMechine copyMechine = new CopyMechine();
copyMechine.getWriteAndBlock();
copyMechine.getA4();
}
}
四、理解接口 interface
需要接口的原因
- 接口和抽象类的区别
1、接口就是比“抽象类”还“抽象”的“抽象类”,可以更加规范的对子类进行约束。全面地专业地实现了:规范和具体实现的分离。
2、接口就是规范,定义的是一组规则,体现了现实世界中“如果是你。。。则必须能。。。”的思想。
3、接口的本质是契约,就像我们的法律一样。制定好后大家都要遵守。
4、项目的具体需求是多变的,我们必须以不变应万变才能从容开发,此处的“不变”就是“规范”。因此,我们开发项目往往都是面向接口编程!
抽象类和接口的区别
- 抽象类中的方法可以有抽象方法,也可以有普通方法,但是接口中只能包含抽象方法。
- 抽象类需要使用abstract关键字来修饰,而接口使用interface关键字来修饰。
- 子类使用extends关键字来继承抽象类,使用implements来实现接口。
- 子类继承抽象类的时候必须要实现所有的抽象方法,普通方法可以不重写,而接口中所有的方法必须实现。
- 抽象类中可以定义成员变量,而接口中只能定义静态常量。
- 抽象类在子类实现的时候是单继承,而接口是多继承。
- 抽象类和接口都不能实例化,但是抽象类中可以有构造方法,而接口中不能有构造方法。
- 抽象类中可以实现接口,并且不实现接口中的方法,而接口只能继承接口,不能实现接口。
定义接口
格式:
[访问修饰符] interface 接口名 [extends 父接口1, 父接口2...] {
常量定义 //总是public static final
方法定义 //总是public abstract
}
如何实现接口:
1、子类通过implements来实现接口中的规范。
2、接口不能创建实例,但是可用于声明引用变量类型。
3、一个类实现了接口,必须实现接口中所有的方法,并且这些方法只能是public的。
4、Java的类只支持单继承,接口支持多继承。
与C++的继承对比
- C++支持多重继承,Java支持单重继承。
- C++多重继承的危险性在于一个类可能继承了同一个方法的不同实现,会导致系统崩溃。
- Java中,一个类只能继承一个类,但同时可以实现多个接口,既可以实现多重继承的效果和功能,也避免了多重继承的危险性。
注意:
1、extends 必须位于implements之前。
2、在实际的项目开发过程中,如果可以使用接口,尽量使用接口,将单继承的父类留到最关键的地方。
五、内部类
概念:把一个类定义在另一个类的内部称为内部类。
分类
成员内部类
示例代码:
package com.liyunfei;
class Outer {
private String info = "hello World"; // 声明私有属性
class Inner// 声明类
{
public void print() {// 打印输出的方法
System.out.println(info);
}
}
public void fun() {// 声明方法
new Inner().print();// 通过内部类调用方法
// 内部类轻松访问外部类的私有属性
}
}
public class Test {// 测试类
public static void main(String[] args) {
new Outer().fun();// 调用方法
}
}
注意事项:
1、外部类不能直接使用内部类的成员和方法。
2、如果外部类和内部类具有相同的成员变量或方法,内部类默认访问自己的成员变量或方法,如果要访问外部类的成员变量,需要使用this关键字。
在外部访问内部类
使用规则:
外部类 外部类对象 = new 外部类();
外部类.内部类 内部类对象 = 外部类对象.new 内部类();
示例代码:
public static void main(String[] args) {
Outer outer = new Outer();// 创建外部类的对象
Outer.Inner inner = outer.new Inner();// 创建内部类的对象
inner.print();// 访问内部类的方法
}
注意:
如果主方法在外部类内部,则可以省略Outer。
Inner inner = out.new Inner();
静态内部类
静态内部类:在内部类中可以定义静态内部类,使用static关键字进行修饰。
使用规则:
new 外部类名.内部类().方法名
外部类.内部类 类的引用名称 = new 外部类.内部类();
示例代码:
class Outer {
private static String info = "hello World"; // 声明私有属性
static class Inner// 声明静态内部类类
{
public void print() {// 打印输出的方法
System.out.println(info);
}
}
}
public class Test {// 测试类
public static void main(String[] args) {
new Outer.Inner().print();// 调用方法
}
}
注意:
使用static声明的内部类不能访问非static的外部方法。
匿名内部类
匿名内部类:当定义了一个类,实现了某个接口的时候,在使用过程中只需要使用一次,没有其他用途。其实考虑到代码编写的简洁,可以考虑不创建具体的类,而采用new interface(){添加未实现的方法}就叫做匿名内部类。
示例代码:
public class NoNameInnerDemo {
public static void main(String[] args) {
System.out.println("有一万行代码");
new Thread(new Runner());
System.out.println("有一万行代码");
}
}
class Runner implements Runnable{
@Override
public void run() {
}
}
由于上述接口只是用了一次但是我们创建了一个单独的类为了代码逻辑的简洁于是就可以使用匿名内部类来解决。
public class NoNameInnerDemo {
public static void main(String[] args) {
System.out.println("有一万行代码");
new Thread(new Runnable() {
@Override
public void run() {
}
});
System.out.println("有一万行代码");
}
}
方法内部类
方法内部类:在外部类的方法中也可以定义类,此时叫做方法内部类(了解即可)
注意事项:
1、只能在方法中创建对象,因为此class的作用域就是当前方法。
2、方法内部类不能使用访问控制符和static修饰符。
示例代码:
public class MethodInnerClass {
public void show(int number){
System.out.println("show");
class InnerClass{
private String name;
public void test(int a){
System.out.println("test");
System.out.println(a);
System.out.println(number);
}
}
new InnerClass().test(12);
}
public static void main(String[] args) {
MethodInnerClass methodInnerClass = new MethodInnerClass();
methodInnerClass.show(1234);
}
}
总结
- 将一个类定义置入另一个类定义中叫作“内部类”。
- 类中定义的内部类特点:
特点:
1、内部类可以方便的访问外部类的私有属性。
2、外部类不能访问内部类的私有属性,但是如果创建了内部类的对象,此时可以在外部类中访问私有属性。
3、内部类中不能定义静态属性。
4、当内部类和外部类具有相同的私有属性的时候,在内部类中访问的时候,可以直接访问内部类的属性,如果需要访问外部类的属性,那么需要添加外部类类名.this.属性。 - 匿名内部类Anonymous
-
- 可以实现一个接口,或者继承一个父类。
-
- 只能实现一个接口。
-
- 适合创建那种只需要一次使用的类,不能重复使用。比较常见的是在图形界面编程GUI里用得到。
-
- 匿名内部类要使用外部类的局部变量,必须使用final修饰该局部变量。
六、垃圾回收机制
对象空间的分配:使用new关键字创建对象即可。
对象空间的释放:
- 传统的C/C++语言,需要程序员负责回收已分配内存。显式回收垃圾的缺点:
-
- 程序忘记及时回收,从而导致内存泄露,降低系统性能。
-
- 程序错误回收程序核心类库的内存,导致系统崩溃。
- Java语言不需要程序员直接控制内存回收,是由JRE在后台自动回收不再使用的内存,称之为垃圾回收机制(Garbage Collection)。
-
- 可以提高编程效率。
-
- 保护程序的完整性。
-
- 其开销影响性能。Java虚拟机必须跟踪程序中有用的对象,确定哪些是无用的。
关键点
- 垃圾回收机制只回收JVM堆内存里的对象空间。
- 对其他物理连接,比如数据库连接、输入输出流、Socket连接无能为力。
- 现在的JVM有多种垃圾回收实现算法,表现各异。
- 垃圾回收发生具有不可预知性,程序无法精确控制垃圾回机制执行。
- 可以将对象的引用变量设置为null,暗示垃圾回收机制可以回收该对象。
- 程序员可以通过System.gc()或者Runtime.getRuntime().gc()来通知系统进行垃圾回收,会有一些效果,但是系统是否进行垃圾回收依然不确定。
- 垃圾回收机制回收任何对象之前,总会先调用它的finalize方法(如果覆盖该方法,让一个新的引用变量重新引用该对象,则会重新激活对象)。
- 永远不要主动调用某个对象的finalize方法,因该交给垃圾回收机制调用。