文章目錄
接口
接口的定義基本格式
接口就是多個類的公共規範
接口是一種引用數據類型,最重要的內容就是其中的抽象方法
如何定義一個接口的格式:
public interface 接口名稱{
//接口內容
}
備註:換成了關鍵字interface之後,編譯生成的字節碼文件仍然是:.java --> .class
如果是java7,那麼接口中包含的內容有:
- 常量
- 抽象方法
如果是java8,還可以額外包含有:
- 默認方法
- 靜態方法
如果是java9,還可以額外包含有:
- 私有方法
接口的抽象方法定義
在任何版本的java中,接口都能定義抽象方法
接口和抽象類的區別
注意事項:
- 接口當中的抽象方法,修飾符必須是兩個固定的關鍵字:public abstract
- 這兩個關鍵字修飾符,可以選擇性地省略
- 方法的三要素,可以隨意定義
public interface MyInterfaceAbstract {
// 這是一個抽象方法
public abstract void methodAbs1();
// 這也是抽象方法
abstract void methodAbs2();
// 這也是抽象方法
public void methodAbs3();
// 這也是抽象方法
void methodAbs4();
}
接口的抽象方法使用
接口使用步驟:
- 接口不能直接使用,必須有一個“實現類”來“實現”該接口。
格式:
public class 實現類名稱 implements 接口名稱 {
// …
} - 接口的實現類必須覆蓋重寫(實現)接口中所有的抽象方法
去掉abstract關鍵字,加上方法體大括號 - 創建實現類的對象,進行使用
public class MyInterfaceAbstractImpl implements MyInterfaceAbstract {
@Override
public void methodAbs1() {
System.out.println("這是第一個方法");
}
@Override
public void methodAbs2() {
System.out.println("這是第二個方法");
}
@Override
public void methodAbs3() {
System.out.println("這是第三個方法");
}
@Override
public void methodAbs4() {
System.out.println("這是第四個方法");
}
}
注意事項:
如果實現類並沒有覆蓋重寫接口中所有抽象方法,那麼這個實現類自己就必須是抽象類
public class Demo01Interface {
public static void main(String[] args) {
//創建實現類的對象使用
MyInterfaceAbstractImpl impl = new MyInterfaceAbstractImpl();
impl.methodAbs1();
impl.methodAbs2();
}
}
接口的默認方法使用
從java8開始,接口裏允許定義默認方法。
格式:
public default 返回值類型 方法名稱(參數列表){
方法體
}
備註:接口當中的默認方法可以解決接口升級的問題
public interface MyInterfaceDefault {
// 抽象方法
public abstract void methodAbs();
// 新添加了一個抽象方法,會對實現類造成影響
// public abstract void methodAbs2();
// 新添加的方法,改成默認方法
public default void methodDefault(){
System.out.println("這是新添加的默認方法");
}
}
兩個實現類
public class MyInterfaceDefaultA implements MyInterfaceDefault {
@Override
public void methodAbs() {
System.out.println("實現了抽象方法,AAA");
}
}
public class MyInterfaceDefaultB implements MyInterfaceDefault {
@Override
public void methodAbs() {
System.out.println("實現了抽象方法,BBB");
}
@Override
public void methodDefault() {
System.out.println("實現類覆蓋重寫了接口的default方法");
}
}
- 接口的默認方法,可以通過接口的實現類對象直接調用
- 接口的默認方法,也可以被接口實現類進行覆蓋重寫
public class Demo02Interface {
public static void main(String[] args) {
MyInterfaceDefaultA a = new MyInterfaceDefaultA();
a.methodAbs(); // 調用抽象方法,實際運行的是右側實現類
//調用默認方法,如果實現類中沒有,會向上找接口
a.methodDefault();
MyInterfaceDefaultB b = new MyInterfaceDefaultB();
b.methodAbs();
b.methodDefault();//實現類B覆蓋重寫了接口的默認方法
}
}
結果
接口的靜態方法定義
從java8開始,接口當中允許定義靜態方法
格式:
public static 返回值類型 方法名稱(參數列表){
方法體
}
提示:就是將abstract或者default換成static即可,帶上方法體
public interface MyInterfaceStatic {
public static void methodStatic(){
System.out.println("這是接口的靜態方法");
}
}
實現類
public class MyInterfaceStaticImpl implements MyInterfaceStatic {
// 沒有抽象方法,不報錯
}
靜態方法的調用
public class Demo03Interface {
public static void main(String[] args) {
MyInterfaceAbstractImpl impl = new MyInterfaceAbstractImpl();
//錯誤寫法!
// impl.methodStatic();
// 直接通過接口名稱調用靜態方法
MyInterfaceStatic.methodStatic();
}
}
注意:不能通過接口實現類的對象來調用接口當中的靜態方法,靜態跟對象沒關係
正確用法:通過接口名稱,直接調用其中的靜態方法
格式:
接口名稱.靜態方法名(參數);
接口的私有方法定義
問題描述:
我們需要抽取一個共有方法,用來解決兩個默認方法之間重複代碼的問題
但是這個共有方法不應該讓實現類使用,應該是私有化的
解決方案:
從java9開始,接口當中允許定義私有方法
- 普通私有方法:解決多個默認方法之間重複代碼問題
private 返回值類型 方法名稱(參數列表){
方法體
} - 靜態私有方法:解決多個靜態方法之間重複代碼問題
private static 返回值類型 方法名稱(參數列表){
方法體
}
普通私有方法
public interface MyInterfacePrivateA {
public default void methodDefault1(){
System.out.println("默認方法1");
methodCommon();
}
public default void methodDefault2(){
System.out.println("默認方法2");
methodCommon();
}
private void methodCommon(){
System.out.println("AAA");
System.out.println("BBB");
System.out.println("CCC");
}
}
靜態私有方法
public interface MyInterfacePrivateB {
public static void methodStatic1(){
System.out.println("默認方法1");
methodStaticCommon();
}
public static void methodStatic2(){
System.out.println("默認方法2");
methodStaticCommon();
}
private static void methodStaticCommon(){
System.out.println("AAA");
System.out.println("BBB");
System.out.println("CCC");
}
}
接口的常量定義和使用
接口當中也可以定義“成員變量”,但是必須使用public static final三個關鍵字進行修飾
從效果上看,這其實就是接口的【常量】
格式:
public static final 數據類型 常量名稱 = 數據值;
備註:
一旦使用final關鍵字進行修飾,說明不可改變
注意事項:
- 接口當中的常量,可以省略public static final,注意:不寫也照樣是這樣
- 接口當中的常量,必須進行賦值,不能不賦值
- 接口中常量的名稱,使用完全大寫的字母,用下劃線進行分割
定義
public interface MyInterfaceConst {
// 這其實就是一個常量,一旦賦值,不可以修改
public static final int NUM_OF_MY_CLASS = 10;
}
訪問
public class Demo05Interface {
public static void main(String[] args) {
// 訪問接口中的常量
System.out.println(MyInterfaceConst.NUM_OF_MY_CLASS);
}
}
接口的內容小結
- 成員變量其實是常量,【格式】:
public static final 數據類型 常量名稱 = 數據值;
【注意】:常量必須進行賦值,而且一旦賦值不能改變
常量名稱完全大寫,用下劃線進行分隔 - 接口中最重要的就是抽象方法,【格式】:
public abstract 返回值類型 方法名稱(參數列表);
【注意】:實現類必須覆蓋重寫接口的所有抽象方法,除非實現類是抽象類 - 從java8開始,接口裏允許定義默認方法,【格式】:
public default 返回值類型 方法名稱(參數列表){方法體}
【注意】:默認方法也可以被覆蓋重寫 - 從java8開始,接口裏允許定義靜態方法,【格式】:
public static 返回值類型 方法名稱(參數列表){方法體}
【注意】:應該通過接口名稱進行調用,不能通過實現類對象調用接口靜態方法 - 從java9開始,接口裏允許定義私有類方法,【格式】:
普通私有方法:private 返回值類型 方法名稱(參數列表){方法體}
靜態私有方法:private static 返回值類型 方法名稱(參數列表){方法體}
【注意】:private的方法只有接口自己才能調用,不能被實現類或別人使用。
繼承父類並實現多個接口
使用接口時,需注意:
- 接口是沒有靜態代碼塊或構造方法的
- 一個類的直接父類是唯一的,但是一個類可以同時實現多個接口
格式:
public class MyInterfaceImpl implements MyInterfaceA,MyInterfaceB{
//覆蓋重寫所有抽象方法
} - 如果實現類所實現的多個接口當中存在重複的抽象方法,那麼只需要覆蓋重寫一次即可
- 如果實現類沒有覆蓋重寫所有接口當中的所有抽象方法,那麼實現類就必須是一個抽象類
- 如果實現類所實現的多個接口當中,存在重複的默認方法,那麼實現類一定要對沖突的默認方法進行覆蓋重寫
- 一個類如果直接父類當中的方法和接口當中的默認方法產生了衝突,優先用父類當中的方法
接口之間的多繼承
- 類與類之間是單繼承的,直接父類只有一個
- 類與接口之間是多實現的,一個類可以實現多個接口
- 接口與接口之間是多繼承的
多態
extends繼承或者implements實現,是多態性的前提
代碼當中體現多態性,其實就是一句話,父類引用指向子類對象
格式:
父類名稱 對象名 = new 子類名稱();
或者:
接口名稱 對象名 = new 實現類名稱();
多態中成員方法的使用特點
在多態的代碼中,成員方法的訪問規則是:
看new的是誰,就優先用誰,沒有則向上找
public class Fu {
int num = 10;
public void method(){
System.out.println("父類方法");
}
public void methodFu(){
System.out.println("父類特有方法");
}
}
public class Zi extends Fu {
int num = 20;
@Override
public void method() {
System.out.println("子類方法");
}
public void methodZi(){
System.out.println("子類特有方法");
}
}
public class Demo01Multi {
public static void main(String[] args) {
// 使用多態的寫法
//左側父類的引用,指向了右側子類的對象
Fu obj = new Zi();
obj.method(); //父子都有,優先用子
obj.methodFu(); //子類沒有,父類有,向上找到父類
}
}
【注意】編譯看左邊,運行看右邊
多態的好處
對象的向上轉型
其實就是多態寫法:
父類名稱 對象名 = new 子類名稱();
eg: Animal animial = new Cat();
含義:右側創建一個子類對象,把它當作父類來看待使用
注意事項:向上轉型一定是安全的,從小範圍轉向了大範圍
對象的向下轉型
向上轉型有一個弊端:對象一旦向上轉型爲父類,那麼就無法調用子類原本特有的內容
解決方案:用對象的向下轉型【還原】
格式:
子類名稱 對象名 = (子類名稱)父類對象;
含義:將父類對象,【還原】成爲本來的子類對象
類似於: 基本數據類型的強制數據轉換
父類
public abstract class Animal {
public abstract void eat();
}
貓——子類
public class Cat extends Animal{
@Override
public void eat() {
System.out.println("貓喫魚");
}
public void catchMouse(){
System.out.println("貓抓老鼠");
}
}
狗——子類
public class Dog extends Animal {
@Override
public void eat() {
System.out.println("狗喫屎");
}
public void watchHouse(){
System.out.println("狗看家");
}
}
main方法
public class down {
public static void main(String[] args) {
Animal animal = new Cat();//本來是貓,向上轉型爲動物
animal.eat();//貓喫魚
/*
注意事項:
1. 必須保證對象本來創建的時候,就是貓,才能夠向下轉型爲貓。
2. 如果對象創建的時候本來不是貓,現在非要向下轉型爲貓,就會報錯(ClassCastException)。
*/
// 向下轉型,進行【還原】動作
Cat cat = (Cat) animal;//本來是貓,意見被當作動物了,還原回來成爲本來的貓
cat.catchMouse();//貓抓老鼠
//本來是貓,現在非要當成狗c
Dog dog = (Dog) animal;//錯誤寫法!編譯不會報錯,但是會出現異常 java.lang.ClassCastException
}
}
instanceof關鍵字
知道一個父類引用的對象本來是什麼子類
格式:
對象 instanceof 類名稱
這將會得到一個boolean值結果,也就是判斷前面的對象能不能當作後面類型的實例
public class Demo02Instanceof {
public static void main(String[] args) {
Animal animal = new Cat();
animal.eat();
if(animal instanceof Dog){
Dog dog =(Dog) animal;
dog.watchHouse();
}
if(animal instanceof Cat){
Cat cat = (Cat) animal;
cat.catchMouse();
}
}
// instanceof 的作用,不知道傳入參數的類型時使用
public static void giveMeAPat(Animal animal){
if(animal instanceof Dog){
Dog dog =(Dog) animal;
dog.watchHouse();
}
if(animal instanceof Cat){
Cat cat = (Cat) animal;
cat.catchMouse();
}
}
}
案例分析
USB接口
public abstract interface USB {
public abstract void open();//打開設備
public abstract void close();//關閉設備
}
筆記本類
public class Computer {
public void powerOn() {
System.out.println("筆記本電腦開機");
}
public void powerOff() {
System.out.println("筆記本電腦關機");
}
// 使用USB設備的方法
public void useDevice(USB usb) {
usb.open();
usb.close();
if (usb instanceof Keyboard) {
Keyboard keyboard = (Keyboard) usb;
keyboard.type();
} else if (usb instanceof Mouse) {
Mouse mouse = (Mouse) usb;
mouse.click();
}
}
}
鼠標類
public class Mouse implements USB {
@Override
public void open() {
System.out.println("打開鼠標");
}
@Override
public void close() {
System.out.println("關閉鼠標");
}
public void click(){
System.out.println("點擊鼠標");
}
}
鍵盤類
public class Keyboard implements USB {
@Override
public void open() {
System.out.println("打開鍵盤");
}
@Override
public void close() {
System.out.println("關閉鍵盤");
}
public void type(){
System.out.println("鍵盤輸入");
}
}
Main方法
public class DemoMain {
public static void main(String[] args) {
Computer computer = new Computer();
computer.powerOn();
//準備一個鼠標,供電腦使用
//首先進行向上轉型
USB usbMouse = new Mouse();
computer.useDevice(usbMouse);
//創建一個USB鍵盤
Keyboard keyboard = new Keyboard();
//方法參數時USB類型,傳遞進去的是實現類對象
computer.useDevice(keyboard);//正確寫法!也發生了向上轉型
computer.powerOff();
}
}