文章目录
(一)Interface Note
1、概念
-
接口反映某一类事物具有什么能力
- 关注该类事物可以完成什么操作
- 但是不关注操作的具体实现细节
-
接口的目的是指明相关或者不相关类的多个对象的共同行为
-
接口体现了程序设计的多态和高内聚低耦合的设计思想
-
为接口命名时建议采用 可以什么的、提供什么支持的、有什么能力的
-
尽可能在一个接口里提供少的方法,接口更多时候不是提供一种方法,而是一种标记 - 什么可以产生什么。(如Cloneable接口,此接口内部没有方法,只是作为标记接口,标记某一个对象能否被克隆。)
2、基本语法
声明接口
[ 修饰符 ] interface 接口名称 { }
继承接口
[ 修饰符 ] interface 子接口名称 extends 父接口名 [ , 父接口名 , ... ] { }
实现接口
[ 修饰符 ] class 类名 implements 接口名 [ , 接口名 , ... ] { }
3、特点
1、接口不是类,没有构造方法,不能实例化
2、接口之间的继承也使用 extends 实现,但接口可以继承多个父接口
3、接口中所有的 “ 没有方法体 ” 方法 都是公开的抽象方法(默认被 public abstract 修饰)
- “没有方法体”的方法 有:抽象方法,本地方法(本地方法不可以在接口中声明)
4、从 Java 8 开始允许在接口中定义static修饰的方法
- 访问修饰符可以是 private 或 public
- 访问修饰符不可以是 protected 或 默认
5、从 Java 8 开始允许在接口中定义 default修饰的、非抽象的、公开的实例方法
6、在接口中只能声明常量(所有的成员变量默认都是 public static final 修饰的)
4、抽象类与接口的区别
共同点
1、抽象类和接口都是抽象的,都不能被实例化
- 抽象类有构造但不允许实例化,构造是供子类的构造调用
- 接口没有构造,因此绝对不可能被实例化
2、抽象类和接口都可以包含常量
3、抽象类和接口都可以包含抽象方法,用于实现他们的子类都必须覆写抽象方法
4、抽象类和接口(从Java8开始)中都可以包含static方法,比如main方法
5、抽象类和接口(从Java9开始)都可以包含私有方法
6、抽象类和接口(从Java8开始)都可以包含由default修饰的、非抽象的、公开的实例方法
7、抽象类和其他类之间、接口和接口之间的继承都使用extends关键字实现
8、修饰抽象方法时,abstract关键字都不能跟static或者final联用
9、都位于继承的顶端,用于被其他实现或继承
区别
1、抽象类是类,接口不是类
2、抽象类只能单继承(一个类最多继承extends一个其他类);接口可以多继承(一个类可以继承implements多个接口)
3、抽象类中可以有构造方法;接口中没有构造方法
4、抽象类中可以有static代码块和实例代码块;接口中没有代码块
5、抽象类中可以有常量;接口中只能有常量
6、抽象类中可以有普通字段(类字段和实例字段);接口中所有的字段默认都是public static final修饰的
7、所有的抽象类的继承顶端是Object类;接口的继承顶端没有限制,可以是父接口也可以没有父接口
8、抽象类可以有非抽象方法(从Object类继承的都是非抽象的);接口从jdk1.8开始可以有static或default修饰的非抽象方法(引入了类方法和默认方法)
9、抽象类中的普通方法书写时没有限制;接口中的default修饰的方法一定是非抽象的、公开的,static方法只能由public或private修饰
10、抽象类可以定义本地(native)方法;接口不能定义本地方法
11、子类用extends关键字继承抽象类;子类用implements关键字实现接口
12、抽象类用abstract关键字声明;接口用interface关键字声明
12、一个类(如抽象类)可以实现多个接口,但不能继承接口;接口可以继承多个接口,但不能实现接口
13、一个类在继承类的同时,也可以继承接口
14、抽象的内容不同
16、抽象类中的方法可以是任意访问修饰符;接口中的方法默认是public修饰符,访问修饰符可以是 private
选择
在接口和抽象类的选择上,遵守:
- 行为模型应该总是通过接口而不是抽象类定义,所以通常是优先选用接口,尽量少用抽象类。
- 选择抽象类时通常是:需要定义子类的行为,又要为子类提供通用的功能。
- 抽象类可以定义一些子类的公共方法,子类只需要增加新的功能,不需要重复写已经存在的方法;而接口中只是对方法的声明和常量的定义。
5、克隆
- 被克隆对象对应的类需要实现Cloneable接口
- 被克隆对象对应的类需要重写从Object类中继承的clone方法
- 在重写后的clone方法中首先调用Object的clone方法完成对对象的 浅克隆
- 克隆一个对象时,将对象所关联的其它对象也克隆,称作 深克隆
附:抽象类 PK 接口
抽象类 | 接口 | |
---|---|---|
构造 | 有构造(供子类构造调用) | 没有构造 |
代码块 | 可以有代码块(静态代码块和普通代码块) | 不能有 |
具体方法 | 可以有非抽象方法 (从Object类继承的方法都是非抽象的) | JDK 1.8 开始,这个可以有 ( static 修饰或 default 修饰) |
抽象方法 | 可以有抽象方法,也可以没有抽象方法 可使用除了private以外的任意权限修饰符 | 可以有抽象方法,也可以没有抽象方法 只能是 public 修饰的 |
常量(Field) | 可以有 | 只能有常量 |
普通字段 | 可以有 可以是各种权限修饰符 | 不能有 ( 因所有字段默认都是 public static final 修饰的) |
继承 | 一个子类只能继承一个父类 | 一个接口可以继承多个父接口 |
实现 | 一个类可以实现多个接口 | 接口只能继承接口,不能实现接口 |
抽象类 可以包含 native
方法,而 接口 不允许包含 native
方法
共同点: 都不能被实例化
- 抽象类有构造但不允许实例化,构造供子类构造调用
- 接口没有构造,因此绝对不可能被实例化
修饰方法时:
abstract
不能跟static
连用abstract
不能跟final
连用
(二)Interface Code
1、体验声明接口和用类来实现接口
Animate.java
package cn.edu.ecut;
// 有生命的
public interface Animate { //所有接口默认都是抽象的,无论写不写abstract
public abstract void breathe() ; // 呼吸
//可写为void breathe();接口中所有没有方法体的方法默认是抽象方法,由public abstract修饰。(本地方法除外)
}
Removeable.java
package cn.edu.ecut;
// 可移动的
public interface Removable {
public abstract void move();
//public native void hello(); //接口中不可定义native方法
}
Animal.java
package cn.edu.ecut;
//定义一个抽象类实现多个接口(如果此类中无法实现抽象方法,则类继续定义为抽象类,此时接口中的抽象方法可不必声明)
public abstract class Animal implements Animate , Removable {
protected String name ;
public native void hello();//抽象类中可以定义native方法
}
Fish.java
package cn.edu.ecut;
//定义一个具体类继承抽象类,间接实现两个接口
public class Fish extends Animal {
@Override //可限定重写,告知编译器可以提供相应错误而写的一个注解(java.lang.Override)
public void breathe() {
System.out.println( "鱼类是用鳃呼吸的" );
}
@Override
public void move() {//具体类中,一定要实现抽象方法
System.out.println( "鱼在水中游动" );
}
}
Tiger.java
package cn.edu.ecut;
public class Tiger extends Animal {
@Override
public void breathe() {
System.out.println( "虎类是用肺呼吸的" );
}
@Override
public void move() {
System.out.println( "虎类可以在陆地上奔跑" );
}
}
1.1、从Java8开始,接口中允许定义static方法和default方法
Tiger.java
package cn.edu.ecut;
public class Tiger extends Animal {
@Override
public void breathe() {
System.out.println( "虎类是用肺呼吸的" );
}
@Override
public void move() {
System.out.println( "虎类可以在陆地上奔跑" );
}
@Override
public void welcome() { // 实现类可以重写 由接口提供的 default 方法
System.out.println( "Tiger#welcome()" );
}
}
Removeable.java
package cn.edu.ecut;
// 可移动的
public interface Removable {
// 在 接口 中只能声明 常量 ,不能声明 普通成员变量
char UNIT = '米' ; // 等同于 public static final char UNIT = '米' ;
// 在 接口 中所有的 没有方法体 的方法默认都是 public abstract 修饰的
void move(); // 等同于 public abstract void move() ;
// 接口不可以有构造方法
//public Removable() { } // Interfaces cannot have constructors
// 从 Java 8 开始 允许在 接口中声明 default 修饰的 、非抽象的 公开的 实例方法
public default void welcome() { // 实现类可以重写 由接口提供的 default 方法
System.out.println( this ); // 实例方法中可以使用 this 关键字获得实例
// Removeable没有父类,因此无法用super关键字来调用方法或字段,super关键字不能单独使用
System.out.println( "Removable#welcome()" );
}
// 从 Java 8 开始 允许在接口中声明 static 修饰的方法 ( 被 static 修饰的方法绝对不可能是抽象的 )
public static void hello() {
System.out.println( "Removable.hello()" );
}
// 因为 从 Java 8 开始 可以在接口中声明 static 修饰的方法,所以定义一个 main 方法也不足为奇
public static void main(String[] args) {
Removable.hello(); // 通过 接口名 来调用 接口中声明的 static 方法
Removable r = null ;
// r.hello(); // 对于接口来说,不允许使用 接口类型的引用变量 来调用 其内部的 static 方法
// r.welcome(); //出现NPE异常
r = new Tiger(); // 实例方法由实例确定,指向谁就调用谁的方法
r.welcome(); // 调用在 接口中声明的 default 修饰的 实例方法
}
}
输出:
Removable.hello()
Tiger#welcome()
若把Tiger类里的welcome方法注释,则输出:
Removable.hello()
cn.edu.ecut.Tiger@5e265ba4
Removable#welcome()
Test.java
Class类中的getInterfaces()方法
package cn.edu.ecut;
public class Test {
public static void main(String[] args) {
Removable r = null ;
// 接口不是类,没有构造方法,不能实例化
// r = new Removable() ; // Cannot instantiate the type Removable
// 接口类型的引用变量 指向了 其实现类类型的实例
r = new Tiger();
r.move(); // 移动
Class<?> c = r.getClass() ; // 获得 运行时类型
// r指向Tiger的对象,运行时类型就是Tiger
System.out.println( c.getName() );//输出cn.edu.ecut.Tiger
// Class 类的 getSuperclass() 方法用于获得 某个类的直接父类
Class<?> p = c.getSuperclass(); // 获得 c 所表示的类的父类,即Tiger的父类
System.out.println( p.getName() );// 输出cn.edu.ecut.Animal
System.out.println( "~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~" );
// 任意一个类型都可以通过 .class 来获取该类型对应的 Class 对象
Class<?> rc = Removable.class ; // rc是Removable接口对应的Class类型的对象
System.out.println( rc.getName() ); // 输出cn.edu.ecut.Removable
Class<?> rp = rc.getSuperclass(); // 获得 rc 所表示的类的父类
System.out.println( rp == null ? "没有父类" : rp.getName() ); // 输出 没有父类
// Class 类中的 getInterfaces() 方法 用于 获得 某个接口继承的所有父接口对应的数组 或 获得某个类直接实现的所有接口组成的数组
Class<?>[] interfaces = rc.getInterfaces();
System.out.println( interfaces ); // 输出 [Ljava.lang.Class;@123a439b
// 每个接口都是一个类型,这些类型组成数组,一个Class类型的数组。(凡是说类型,都用Class)
System.out.println( interfaces.length ); // 输出 0 ,说明没有实现任何接口,也没有继承任何接口
}
}
2、接口可以继承接口,类可以实现接口
Chargeable.java
package org.malajava;
/**
* 可充电的
*/
public interface Chargeable {
void charge() ; // 充电
}
Transfer.java
package org.malajava;
/**
* 可以传输数据的
*/
public abstract interface Transfer { // 所有的接口都是抽象的 ( 接口默认有abstract )
void transmission() ; // 接口中所有的"没有方法体的"方法都是 public abstract 修饰的
}
USB.java
package org.malajava;
/**
* 1、在 Java 语言中一个接口 可以同时继承多个 父接口
* 2、当一个接口继承多个父接口时,父接口名称 之间使用 逗号 隔开
*/
public interface USB extends Chargeable , Transfer { // USB : Universal Serial Bus ( 通用串行总线 )
// USB 接口继承了 Chargeable , Transfer 接口中的抽象方法
}
UGreen.java
package org.malajava;
public class UGreen implements USB { //具体类实现接口
@Override
public void charge() { //实现抽象方法,注意此处一定要显式书写public
System.out.println( "UGreen : charge" );
}
@Override
public void transmission() {
System.out.println( "UGreen : transmission" );
}
}
HuaWei.java
package org.malajava;
public class HuaWei implements USB {
@Override
public void charge() {
System.out.println( "HuaWei : charge" );
}
@Override
public void transmission() {
System.out.println( "HuaWei : transmission" );
}
}
Main.java
package org.malajava;
public class Main {
public static void main(String[] args) {
// USB u = new USB(); // Cannot instantiate the type USB
USB usb = null ; // USB 是 usb 的 编译时类型
// 接口类型的引用变量 指向了 其实现类类型的实例
usb = new UGreen() ;
usb.charge(); // 充电
usb.transmission(); // 传输数据
usb = new HuaWei();
usb.charge(); // 充电
usb.transmission(); // 传输数据
}
}
输出:
UGreen:charge
UGreen:transmission
HuaWei:charge
HuaWei:transmission
TransferTest.java
Class类中的getSuperclass()方法
package org.malajava;
public class TransferTest {
public static void main(String[] args) {
Transfer t = null ; // 变量 t 的编译时类型是 Transfer 类型
t = new UGreen();
// 通过 变量 t 只能调用直接在 接口Transfer 中声明的方法 和 Object 类中声明的方法
t.transmission();
String s = t.toString() ; // 调用 Object 类中声明的方法
System.out.println( s );
Class<?> c = t.getClass(); // 获得 运行时类型
System.out.println( c.getName() );
Class<?> p = c.getSuperclass();// 获得c所表示的类的父类
System.out.println(p.getName());
}
}
输出:
UGreen:tranmission
org.malajava.UGreen@6a6824be
org.malajava.UGreen
java.lang.Object
说明
- 所有的接口类型的引用变量除了可以调用接口中的方法外,还可以调用Object类中的方法;
super_class : java.lang.object ;this_class : 实现类(如:TransferTest)
-
接口的实现类的父类是Object,任意一个接口类型的引用变量指向了一个具体的类,或通过接口的实现类造了一个对象,则此对象一定继承了Object类;
-
我们说接口有父类Object,其实是指接口的实现类有父类Object。
3、实现Cloneable接口完成克隆操作
-
实现Cloneable接口:
public interface Cloneable{}
-
是一个标记接口,用于标记某一个对象能否被克隆;此接口里没有方法。
-
若类实现了这个接口,则说明可以克隆,若没有实现这个接口,则不可克隆。
- 注意:接口是反映某种事物可以干什么,更多时候不是提供一种方法,而是一种标记,什么可以干什么
- 尽可能在一个接口里提供少的方法
-
-
重写克隆方法:
protected native Object clone() throws CloneNotSupportedException;
- clone()是本地方法,调用JVM内部的C++代码实现
- protected的访问权限与当时声明的方法的类所在的包有关,这个类的子类也可用。(本包及子类)
- 子类中直接访问 或者 子类中通过子类对象访问 父类的protected方法可以,但 子类中调用父类的对象 或 子类中通过其他子类的对象 或 另一个包中同包下其他类通过子类的对象 访问父类的protected方法是不行的。
- 子类可以访问和可以在子类中访问是不同概念
- 如果clone()是Object类中声明的方法,在java.lang包下可用,如果想在其他包中使用,需要重写clone方法
Master.java
package cn.oracle;
//类一定要实现Cloneable接口,才可被克隆
public class Master extends Object implements Cloneable {
protected String name ;
public Master(String name) {
super();
this.name = name;
}
@Override
protected Object clone() throws CloneNotSupportedException {
// 一定要重写 从 Object 继承的 clone 方法,才能保证本包中的其它类能够调用本类实例的 clone 方法
// protected 修饰的方法 的访问权限 与 声明该方法的类所在的包 有关
return super.clone(); // 子类可以调用父类中的 protected 方法
}
}
Monkey.java
package cn.oracle;
/**
* 1、如果期望克隆某个类的对象,则该类必须实现 java.lang.Cloneable 接口
* 否则会抛出 java.lang.CloneNotSupportedException
*
* 2、java.lang.Cloneable 接口 中没有定义任何方法,可以理解为某个类实现该接口就是做了一个标记
*
* 3、浅克隆 : 仅仅克隆当前对象本身,而当前对象关联的其它对象是不克隆的
*/
public class Monkey extends Object implements Cloneable {
private String name ;
private int age ;
private Master master ;
public Monkey(String name , int age , Master master ) {
super();
this.name = name;
this.age = age ;
this.master = master ;
}
// Object 类中的 clone 方法: protected native Object clone() throws CloneNotSupportedException ;
@Override
public Object clone() throws CloneNotSupportedException {
// 子类重写后的方法 的 访问修饰符 的范围 不能比父类相应方法的 权限小
// 子类重写后的方法 的 异常抛出 的范围 不能比父类相应方法的 范围大
return super.clone() ; // 仍然是调用由 Object 提供的 clone 支持 ( 一定要调用 )
}
public static void main(String[] args) throws CloneNotSupportedException {
Master a = new Master( "唐三藏" );
Monkey m = new Monkey( "孙悟空" , 1000 , a );
Object o = m.clone();
System.out.println( m == o ); // false,说明克隆后重新new了一个对象,字段值一起复制
Monkey n = (Monkey) o ;
System.out.println( m == n ); // false
System.out.println( "~ ~ ~ ~ ~ ~ ~" );
// == 比较两个变量所存储的值
System.out.println( m.name == n.name ); // true
System.out.println( m.age == n.age ); // true : 比较基本数据类型的值
System.out.println( m.master == n.master ); // true : 说明内部关联的对象没有被克隆
}
}
Pig.java
package cn.oracle;
/**
* 深克隆 : 不仅克隆当前对象本身,连当前对象关联的其它对象也克隆
*/
public class Pig extends Object implements Cloneable {
private String name ; // 引用类型
private int age ; // 基本类型
private Master master ; // 引用类型
public Pig(String name , int age , Master master ) {
super();
this.name = name;
this.age = age ;
this.master = master ;
}
@Override
public Object clone() throws CloneNotSupportedException {
Object o = super.clone() ; // 仍然是调用由 Object 提供的 clone 支持 ( 一定要调用 )
if( o != null ) {
// 将 变量 o 中存储的 地址 赋值给 变量 p 并转换类型
Pig p = (Pig) o ;
// name 是 java.lang.String 类型,而 String 从 Object 继承的 clone 方法 修饰符是 protected
// String 类没有子类,因此 不可能通过 String 子类来调用其 clone 方法 ( Pig 也不是 String 的子类 )
// Pig 与 String 类不在同一个包中,因此不能够调用 Stirng 类的 clone 方法
// p.name = (String)this.name.clone();
p.master = (Master)this.master.clone(); // 把 master 也克隆
}
return o ; // 要理解 变量 o 和 变量 p 指向的 是同一个对象
}
public static void main(String[] args) throws CloneNotSupportedException {
Master a = new Master( "唐三藏" );
Pig m = new Pig( "猪悟能" , 1200 , a );
Object o = m.clone();
System.out.println( m == o ); // false
Pig n = (Pig) o ;
System.out.println( m == n ); // false
System.out.println( "~ ~ ~ ~ ~ ~ ~" );
// == 比较两个变量所存储的值
System.out.println( m.name == n.name ); // true
System.out.println( m.age == n.age ); // true : 比较基本数据类型的值
System.out.println( m.master == n.master ); // false : 说明内部关联的对象被克隆
}
}