Java繼承和多態

1,繼承

(1),

關鍵字 extends

public class Employee {
}
public class Assistant extends Employee {

}

(2),在父子的繼承的關系當中,若果重命則創建子類對象的時候,有兩種方式

(a),直接通過子類對象訪問成員變量, 等號左邊是誰優先用誰
(b),間接通過成員方法訪問成員變量

public static void main(String[] args) {
        Fu fu = new Fu();
        System.out.println(fu.numFu);

        Zi zi = new Zi();
        System.out.println(zi.numFu);
        System.out.println(zi.numZi);
        System.out.println("===============");

        System.out.println(zi.num); //優先子類 200
//        System.out.println(zi.abc); //到處都沒有
        System.out.println("===============");

        //這個方法是子類,優先用子類的。沒有在向上找
        zi.methodZi();//200
        //這個方法是父類的
        zi.methodFu();//100

(3),變量的使用

局部變量; 直接寫
本類的成員變量: this.成員變量
父類的成員變量: super.成員變量

public class Fu {
    int num = 10;
}
public class Zi extends Fu{
    int num = 20;
    public void method(){
        int num = 30;
        System.out.println(num); //30
        System.out.println(this.num); //20
        System.out.println(super.num); //10
    }
}
public class Demo01ExtendsFiled {
    public static void main(String[] args) {
        Zi zi = new Zi();

        zi.method();
    }
}

(4),在父子類的繼承關系當中,創建子類對象訪問成員方法的規則

注意事項:
無論是成員方法還是成員變量,如果沒有都是向上找父類,絕不會向下找子類

重寫 (OverWrite):方法名稱一樣,參數列表也一樣
重載 (Overload):方法名稱一樣,參數列表不一樣

public class Fu {
    public void methodFu(){
        System.out.println("父類方法執行!");
    }

    public void method(){
        System.out.println("父類重名方法執行!");
    }
}
public class Zi extends Fu {
    public void methodZi(){
        System.out.println("子類執行!");
    }
    public void method(){
        System.out.println("子類重名方法執行!");
    }
}
public class Demo01extendsMethod {
    public static void main(String[] args) {
        Zi zi = new Zi();

        zi.methodZi();
        zi.methodFu();

        //創建的是new子類對象,所以優先用子類
        zi.method();

    }
}

(5),方法覆蓋重寫的注意事項

1,必須保證父子類之間的方法名稱相同,參數列表也相同,@Override 寫在方法前面,用來檢測是不是有效的重寫,這個注解不寫也是對的。
2,子類方法的返回值,必須[小于等于]父類方法得返回值得范圍,Object類是所有類的公共最高父類(祖宗類) java.lang.String 是object得子類
3,子類方法得權限必須大于等于父類權限修飾符,public > Protect > (default) > private,default 是什么都不寫,空著

public class Demo01OverWrite {
}

class Fu{
    public Object method(){
        return null;
    }
}

class Zi extends Fu{
    @Override
    public Object method(){
        return null;
    }
}

(6),父子類構造方法得訪問特點

1,子類構造方法中有一個默認得"super()"調用,什么都沒寫,默認贈送。
2,可以通過super關鍵字來子類構造調用父類重載構造
3,super的父類構造調用,必須是子類構造方法得第一個語句。不能一個子類構造,調用多個父類構造
總結:
子類必須調用父類構造方法,不寫則贈送super,寫了則調用指定的super調用,super只能有一個,還必須是第一個

public class Demo01Constructor {
    public static void main(String[] args) {
        Zi zi = new Zi();

    }
}

class Fu{
    public Fu(){

    }
    public Fu(int num){
        System.out.println("父類構造方法");
    }
}

class Zi extends Fu{
//      super();隱藏的
    public Zi(){
        super(10);
        //super(16);
        System.out.println("子類構造方法");
    }
    public void method(){
//        super(10);  寫法錯誤!只有子類構造方法,才能調用父類構造方法。
    }
}

(7),super 關鍵字的用法三種:

1,在子類的成員方法中,訪問父類的成員變量。
2,在子類的成員方法中,調用父類的成員方法。
3,在子類的構造方法中,訪問父類的構造方法。

(8),super 關鍵字用來訪問父類內容,this關鍵字用來訪問本類內容,用法三種

1,在本類的成員方法中,訪問本類成員變量
2,在本類的成員方法中,訪問本類的另一個成員方法
3,在本類的構造方法中,訪問苯類的另一個構造方法
this方法也必須是構造方法的第一個語句。
super 和 this 兩種構造不能同時使用。

public class Fu {
    int num = 30;
}
public class Zi extends Fu {
    int num = 20;
    public Zi(){
        this(123);//本類的無參構造,調用本類的有參構造。
    }
    public Zi(int num){

    }
    public Zi(int n, int m){

    }
    public void showNum(){
        int num = 10;
        System.out.println(num);//局部變量
        System.out.println(this.num);//本類變量
        System.out.println(super.num );//父類中的成員變量

    }

    public void methodA(){
        System.out.println("AAA");
    }
    public void methodB(){
        this.methodA();
        System.out.println("BBB");
    }
}

2,抽象類

(1),

1,不能直接創建抽象類對象。
2,必須用一個子類繼承父類。
3,子類必須覆蓋重寫抽象父類當中的所有抽象方法。覆蓋重寫(實現),子類去掉抽象方法的abstract關鍵字,然后補上方法大括號。
4,常見子類對象。

public abstract class Animal {
    public abstract void eat();

    public void method(){

    }
}
public class Dog extends Animal {
    @Override
    public void eat() {
        System.out.println("吃骨頭");
    }
}
public class DemoMain {
    public static void main(String[] args) {
        Dog dog = new Dog();
        dog.eat();
    }
}

(2),

一個抽象方法不一定非得有抽象類,只要保證抽象方法在抽象類里就行.

public abstract class MyAbstract {
}

(3),抽象類的應用

//最高的抽像父類 
public abstract class Animal {

    public abstract void sleep();
    public abstract void eat();
}

abstract class Dog extends Animal{
    @Override
    public void eat(){
        System.out.println("狗吃骨頭");
    }
    public abstract void sleep();
}

class DogGolden extends Dog {
    @Override
    public void sleep(){
        System.out.println("呼呼呼。。。。");
    }
}

class Dog2H extends Dog{
    @Override
    public void sleep(){
        System.out.println("嘿嘿嘿。。。");
    }
}

class DemoMain{
    public static void main(String[] args) {
        DogGolden dogGolden = new DogGolden();
        dogGolden.eat();
        dogGolden.sleep();
    }
}

(4),發紅包案例

import java.util.ArrayList;
import java.util.Random;

public class Member extends User {
    public Member() {
    }

    public Member(String name, int money) {
        super(name, money);
    }

    public void receive(ArrayList<Integer> list) {
        //從多個紅包里抽取一個
        //隨機獲取一個索引編號
        int index = new Random().nextInt(list.size());
        //從集合當中刪除,并得到被刪除的紅包給我自己
        Integer date = list.remove(index);
        //當前成員有多少錢
        int money = super.getMoney();
        //加上,并重新設置回去
        super.setMoney(money + date);
    }
}
import java.util.ArrayList;
import java.util.Random;

public class Member extends User {
    public Member() {
    }

    public Member(String name, int money) {
        super(name, money);
    }

    public void receive(ArrayList<Integer> list) {
        //從多個紅包里抽取一個
        //隨機獲取一個索引編號
        int index = new Random().nextInt(list.size());
        //從集合當中刪除,并得到被刪除的紅包給我自己
        Integer date = list.remove(index);
        //當前成員有多少錢
        int money = super.getMoney();
        //加上,并重新設置回去
        super.setMoney(money + date);
    }
}
import java.util.ArrayList;

public class Manager extends User {
    public Manager(){

    }

    public Manager(String name, int money) {
        super(name, money);
    }

    public ArrayList<Integer> send(int totalMoney, int count ) {
        //需要一個集合,用來儲存若干個紅包的金額
        ArrayList<Integer> redList = new ArrayList<>();
        //看一下群主有多少錢
        int leftMoney = super.getMoney();
        if(totalMoney > leftMoney) {
            System.out.println("余額不足!");
            return redList;
        }
        //扣錢
        super.setMoney(leftMoney - totalMoney);
        //發紅包,分成多少分
        int avg = totalMoney / count;
        int mod = totalMoney % count;//余額,也就是甩下的零頭
        //剩下的零頭,擺在最后一個紅包里
        //把紅包放到集合里
        for (int i = 0; i < count - 1; i++) {
            redList.add(avg);
        }
        //最后一個紅包
        int last = avg + mod;
        redList.add(last);

        return redList;
    }
}
import java.util.ArrayList;

public class MainRedPacket {
    public static void main(String[] args) {
        Manager manager = new Manager("群主",100);
        Member  one = new Member("成員A",0);
        Member  two = new Member("成員B",0);
        Member  three = new Member("成員C",0);
        manager.show();
        one.show();
        two.show();
        three.show();
        System.out.println("==================");

        ArrayList<Integer> redList = manager.send(20,3);

        one.receive(redList);
        two.receive(redList);
        three.receive(redList);
        manager.show();
        one.show();
        two.show();
        three.show();
    }
}

3,接口

(1),

接口就是多個類的公共規范。
接口就是一種引用類型,最重要的內容,就是抽象方法
定義一個接口的方法
public interface 接口名稱 {
//接口的內容
}

備注:換成了關鍵字 interface之后,編譯器生成的字節碼文件仍然是:.java --> .class

java7 接口內的內容包括:
1,常量
2,抽象方法

如果是java8 還可以額外包含有
3,默認方法
4,靜態方法
java9
5,私有方法

接口使用步驟
1,接口不能直接使用,必須有一個實現類實現接口。
格式:
public class 實現類名稱 implements 接口名稱 {
//實現..........
}
2,接口的實現類必須覆蓋重寫接口所有的抽象類
3,創建實現類的對象,進行使用。

注意事項:
如果實現類并沒有覆蓋重寫接口中所有的抽象方法,那么這個實現類自己就必須是一個抽象類。

/*
抽象方法
public abstract 返回值類型 方法名稱(參數列表);

注意事項:
1,接口當中的抽象方法,修飾符必須是兩個固定的關鍵字, Public abstract
2,這兩個關鍵字的修飾符,可以選則性的省略。(今天新學不推薦)
 */
public interface MyInterFaceAbstract {
    //抽象方法
    public abstract void methodAbstract1();
    void methodAbstract2();  //這也是抽象類

}
public class MyInterfaceAbstractImpl implements MyInterFaceAbstract {
    @Override
    public void methodAbstract1() {
        System.out.println("這是第一個方法。");
    }
    @Override
    public void methodAbstract2() {
        System.out.println("這是第二個方法。");
    }

}
public class Demo01InterFace {
    public static void main(String[] args) {
        //創建實現類使用
        MyInterfaceAbstractImpl impl = new MyInterfaceAbstractImpl();
        impl.methodAbstract1();
        impl.methodAbstract2();
    }

}

(2),默認方法

1,接口的默認方法,可以被接口的實現類對象,直接調用
2,接口的默認方法,可以被接口的實現類進行覆蓋重寫

package cn.itcast.day10.demo01;
/*
從Java8 開始,接口允許定義默認方法
格式:
public default 返回值類型 方法名稱 {
       方法體
}
備注:
接口中的默認方法,可以解決接口升級的問題
 */
public interface MyInterfaceDefault {
    //抽象方法
    public abstract void methodAbs ();
    //新添加了一個抽象方法
//    public abstract void methodAbs2 ();
    public default void methodDefault () {

        System.out.println("這是新添加的默認方法");
    }
}
package cn.itcast.day10.demo01;

public class MyInterfaceDefaultA implements MyInterfaceDefault{
    @Override
    public void methodAbs() {
        System.out.println("實現了方法AAA");
    }
}

package cn.itcast.day10.demo01;

public class MyInterfaceDefaultB implements MyInterfaceDefault{
    @Override
    public void methodAbs() {
        System.out.println("實現了方法BBB");
    }

    @Override
    public void methodDefault() {
        System.out.println("實現了類B的覆蓋重寫");
    }
}
public class Demo02IInterfaceDefault {
    public static void main(String[] args) {
        MyInterfaceDefaultA a = new MyInterfaceDefaultA();
        a.methodAbs();//調用抽象方法,實施運行的是右側實現類
        //調用默認方法,如果實現類中沒有,會向上找接口
        a.methodDefault();


        MyInterfaceDefaultB b = new MyInterfaceDefaultB();
        b.methodAbs();
        b.methodDefault();
    }
}

(3),靜態方法

不能通過接口實現類的對象來調用接口中的靜態問題。

正確用法:通過接口名稱直接調用其中的靜態方法。

/*
Java8 開始,接口中準許定義靜態方法
格式:
public static 返回值類型 方法名稱(參數列表) {
    方法體;
}
 */
public interface MyInterfaceStatic {
    public static void methodStatic(){
        System.out.println("這是接口的靜態方法!");
    }
}
package cn.itcast.day10.demo01;

public class MyInterfaceStaticImpl implements MyInterfaceStatic{

}
public class Demo03InterfaceStatic {
    public static void main(String[] args) {
//        MyInterfaceStaticImpl impl = new MyInterfaceStaticImpl();
        //錯誤寫法!
//        impl.methodStatic;
        //調用靜態方法
        MyInterfaceStatic.methodStatic();
    }
}

(4), 私有方法

問題描述:
我們需要抽取出來一個公共方法,用來解決默認方法之間的重復代碼問題
但是這個公共方法不應該上實現類使用,私有化

定義私有方法:
1,普通私有方法。解決默認方法之間的代碼重復問題。
2,靜態私有方法。解決靜態方法之間的代碼重復問題。

public interface MyInterfacePrivateA {
    public default void methodPrivate1() {
        System.out.println("默認方法1!");
        methodCommon();
    }
    public default void methodPrivate2() {
        System.out.println("默認方法2!");
        methodCommon();
    }
    private void methodCommon() {
        System.out.println("AAA");
        System.out.println("BBB");
        System.out.println("CCC");
    }
}
public class MyInterfacePrivateAImpl implements MyInterfacePrivateA {
    public void methodAnother() {
        System.out.println("實現類方法!");
    }

}
public class Demo04InterfacePrivate {
    public static void main(String[] args) {
        MyInterfacePrivateAImpl impl = new MyInterfacePrivateAImpl();
        impl.methodPrivate1();
        impl.methodPrivate2();
        impl.methodAnother();

    }
}

(6),常量

final 關鍵子的作用
如果引用為基本數據類型,則該引用為常量,該值無法修改;
如果引用為引用數據類型,比如對象、數組,則該對象、數組本身可以修改,但指向該對象或數組的地址的引用不能修改。
如果引用時類的成員變量,則必須當場賦值,否則編譯會報錯。

接口中可以定義“成員變量”,這個成員變量必須使用public static final 三個關鍵字來修飾。
從效果上看就是接口的【常量】。

public static final 數據類型 常量量名稱 = 數據值;
注意事項:
1,接口中的常量可以省略 public static final ,注意,不寫照樣是這一樣
2,接口譜中的的常量必須賦值,不能不賦值。
3,接口中的常量的名稱全用大寫,并且用下劃線分割。

public interface MyInterfaceConst {
    //這就是一個常量,一旦賦值,不可修改
    public static final int NUM_Of_CLASS = 10;
}
public class Demo05InterfaceConst {
    public static void main(String[] args) {
        //訪問接口中的常量
        System.out.println(MyInterfaceConst.NUM_Of_CLASS);
    }
}

(7),接口的使用

使用接口的時候需要注意
1,接口沒有靜態代碼塊,或者構造方法的。
2,一個類的直接父類只有一個,但是一個類可以同時實現多個接口
格式;
public class MyInterfaceImpl implements MyInterfaceA, MyInterfaceB{
//覆蓋重寫所用抽象方法

}
3,如果實現類所實現的多個接口當中,存在重復的抽象方法,那麼只需要覆蓋重寫一次即可
4,如果實現類沒有完全覆蓋重寫所有接口的的抽象方法,那么這個類必須是抽象類。
5,如果實現類所實現的多個接口當中,存在重復的默認方法,那么實現類必須對沖突的默認方法覆蓋重寫。
6.一類若果直接父類當中的方法,和接口當中的默認方法產生了沖突,優先用父類當中的方法

package cn.itcast.day10.demo02;

public interface MyInterface {

    public default void method() {
        System.out.println("接口的默認方法!");
    }
}
public class Fu {

    public void method(){
        System.out.println("父類方法!");
    }
}
package cn.itcast.day10.demo02;

public class Zi extends Fu implements MyInterface {

}
public interface MyInterfaceA {
    //錯誤寫法  接口不能有靜態代碼塊
//    static {

//    }
    //錯誤寫法,接口不能有構造方法
//    public MyInterface(){

//    }

    public abstract void methodA();

    public abstract void methodAbs();

    public default void methodDefault(){
        System.out.println("默認方法AAAAA");
    }
}
public interface MyInterfaceB {
    //錯誤寫法  接口不能有靜態代碼塊
//    static {

//    }
    //錯誤寫法,接口不能有構造方法
//    public MyInterface(){

//    }

    public abstract void methodB();

    public abstract void methodAbs();

    public default void methodDefault(){
        System.out.println("默認方法BBBBB");
    }
}
public class MyInterfaceImpl /*extends Object*/ implements MyInterfaceA, MyInterfaceB {

    @Override
    public void methodA() {
        System.out.println("覆蓋重寫了A!");
    }

    @Override
    public void methodB() {
        System.out.println("覆蓋重寫了B!");
    }
    @Override
    public void methodAbs() {
        System.out.println("覆蓋重寫了AB接口都有的抽象方法!");
    }

    @Override
    public void methodDefault() {
        System.out.println("對多個接口沖突的默認方法驚醒覆蓋重寫");
    }
}
public abstract class MyInterfaceAbstract implements MyInterfaceA, MyInterfaceB{

    @Override
    public void methodA() {

    }

    @Override
    public void methodAbs() {

    }

    @Override
    public void methodDefault(){
        System.out.println();
    }
}
public class Demo01Interface {
    public static void main(String[] args) {
        MyInterfaceImpl impl = new MyInterfaceImpl();
        impl.methodDefault();

        Zi zi = new Zi();
        zi.method();
    }
}

(8),

類與類之間是單繼承的,直接父類只能有一個。
類與接口之間是對實現的。一個類可以實現多個接口。
接口與接口之間是多繼承的,

public interface MyInterfaceA {

   public abstract void methodA();
   public abstract void methodCommon();
}
public interface MyInterfaceB {

   public abstract void methodB();
   public abstract void methodCommon();
}
/*
這個子接口中有幾個方法?
4個 methodA 來源于接口A methodB 來源于接口B methodCommon 來源于接口A和B

*/
public interface MyInterface extends MyInterfaceA, MyInterfaceB {

   public abstract void method();
}
public class Demo01Relations {
   public static void main(String[] args) {

   }
}

4,多態

(1),定義

代碼當中體現多態性,其實就是一句話:父類引用指向子類對象。

格式:
父類名稱 對象名 = new 子類名稱();
或者:
接口名稱 對象名 = new 實現類名稱();

public class Demo01Multi {
    public static void main(String[] args) {
        // 使用多態的寫法
        // 左側父類的引用,指向了右側子類的對象
        Fu obj = new Zi();

        obj.method();
        obj.methodFu();
    }
}

public class Fu {
    public void method() {
        System.out.println("父類方法");
    }

    public void methodFu() {
        System.out.println("父類特有方法");
    }
}

public class Zi extends Fu{
    @Override
    public void method() {
        System.out.println("子類方法");
    }
}

(2),多態的使用

訪問成員變量的兩種方式:

  1. 直接通過對象名稱訪問成員變量:看等號左邊是誰,優先用誰,沒有則向上找。
  2. 間接通過成員方法訪問成員變量:看該方法屬于誰,優先用誰,沒有則向上找。
public class DEmo01MultiField {
    public static void main(String[] args) {
        // 使用多態的寫法,父類引用指向子類對象
        Fu obj = new Zi();
        System.out.println(obj.num); // 父:10
//        System.out.println(obj.age); // 錯誤寫法!
        System.out.println("=============");

        // 子類沒有覆蓋重寫,就是父:10
        // 子類如果覆蓋重寫,就是子:20
        obj.showNum();
    }

}
public class Fu {
    int num = 10;

    public void showNum() {
        System.out.println(num);
    }

    public void method() {
        System.out.println("父類方法");
    }

    public void methodFu() {
        System.out.println("父類特有方法");
    }
}
public class Zi extends Fu {
    int num = 20;

    int age = 16;

    @Override
    public void showNum() {
        System.out.println(num);
    }

    @Override
    public void method() {
        System.out.println("子類方法");
    }

    public void methodZi() {
        System.out.println("子類特有方法");
    }
}

在多態的代碼當中,成員方法的訪問規則是:
看new的是誰,就優先用誰,沒有則向上找。

口訣:編譯看左邊,運行看右邊。

對比一下:
成員變量:編譯看左邊,運行還看左邊。
成員方法:編譯看左邊,運行看右邊。

public class Demo02MultiMethod {
    public static void main(String[] args) {
        Fu obj = new Zi(); // 多態

        obj.method(); // 父子都有,優先用子
        obj.methodFu(); // 子類沒有,父類有,向上找到父類

        // 編譯看左邊,左邊是Fu,Fu當中沒有methodZi方法,所以編譯報錯。
//        obj.methodZi(); // 錯誤寫法!
    }

}

(3),多態的使用

向上轉型一定是安全的,沒有問題的,正確的。但是也有一個弊端:
對象一旦向上轉型為父類,那么就無法調用子類原本特有的內容。

解決方案:用對象的向下轉型【還原】。

如何才能知道一個父類引用的對象,本來是什么子類?
格式:
對象 instanceof 類名稱
這將會得到一個boolean值結果,也就是判斷前面的對象能不能當做后面類型的實例。

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("狗吃SHIT");
    }

    public void watchHouse() {
        System.out.println("狗看家");
    }
}
public class Demo01MAin {
    public static void main(String[] args) {
        // 對象的向上轉型,就是:父類引用指向之類對象。
        Animal animal = new Cat(); // 本來創建的時候是一只貓
        animal.eat(); // 貓吃魚

//        animal.catchMouse(); // 錯誤寫法!

        // 向下轉型,進行“還原”動作
        Cat cat = (Cat) animal;
        cat.catchMouse(); // 貓抓老鼠

        // 下面是錯誤的向下轉型
        // 本來new的時候是一只貓,現在非要當做狗
        // 錯誤寫法!編譯不會報錯,但是運行會出現異常:
        // java.lang.ClassCastException,類轉換異常
        Dog dog = (Dog) animal;
    }
}
public class Demo02Instanceof {
    public static void main(String[] args) {
        Animal animal = new Dog(); // 本來是一只狗
        animal.eat(); // 狗吃SHIT

        // 如果希望掉用子類特有方法,需要向下轉型
        // 判斷一下父類引用animal本來是不是Dog
        if (animal instanceof Dog) {
            Dog dog = (Dog) animal;
            dog.watchHouse();
        }
        // 判斷一下animal本來是不是Cat
        if (animal instanceof Cat) {
            Cat cat = (Cat) animal;
            cat.catchMouse();
        }

        giveMeAPet(new Dog());
    }

    public static void giveMeAPet(Animal animal) {
        if (animal instanceof Dog) {
            Dog dog = (Dog) animal;
            dog.watchHouse();
        }
        if (animal instanceof Cat) {
            Cat cat = (Cat) animal;
            cat.catchMouse();
        }
    }
}

(4),多態的練習

1,進行描述筆記本類,實現筆記本使用USB鼠標、USB鍵盤
2,USB接口,包含開啟功能、關閉功能 筆記本類,包含運行功能、關機功能、使用3,USB設備功能 鼠標類,要實現USB接口,并具備點擊的方法 鍵盤類,要實現4USB接口,具備敲擊的方法

public class Computer {
    public void powerOn() {
        System.out.println("筆記本電腦開機");
    }

    public void powerOff() {
        System.out.println("筆記本電腦關機");
    }

    // 使用USB設備的方法,使用接口作為方法的參數
    public void useDevice(USB usb) {
        usb.open(); // 打開設備
        if (usb instanceof Mouse) { // 一定要先判斷
            Mouse mouse = (Mouse) usb; // 向下轉型
            mouse.click();
        } else if (usb instanceof Keyboard) { // 先判斷
            Keyboard keyboard = (Keyboard) usb; // 向下轉型
            keyboard.type();
        }
        usb.close(); // 關閉設備
    }
}

public interface USB {
    public abstract void open(); // 打開設備

    public abstract void close(); // 關閉設備

}

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("鍵盤輸入");
    }
}

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 DemoMain {
    public static void main(String[] args) {
        // 首先創建一個筆記本電腦
        Computer computer = new Computer();
        computer.powerOn();

        // 準備一個鼠標,供電腦使用
//        Mouse mouse = new Mouse();
        // 首先進行向上轉型
        USB usbMouse = new Mouse(); // 多態寫法
        // 參數是USB類型,我正好傳遞進去的就是USB鼠標
        computer.useDevice(usbMouse);

        // 創建一個USB鍵盤
        Keyboard keyboard = new Keyboard(); // 沒有使用多態寫法
        // 方法參數是USB類型,傳遞進去的是實現類對象
        computer.useDevice(keyboard); // 正確寫法!也發生了向上轉型
        // 使用子類對象,匿名對象,也可以
//        computer.useDevice(new Keyboard()); // 也是正確寫法

        computer.powerOff();
        System.out.println("==================");

        method(10.0); // 正確寫法,double --> double
        method(20); // 正確寫法,int --> double
        int a = 30;
        method(a); // 正確寫法,int --> double
    }

    public static void method(double num) {
        System.out.println(num);
    }
}

5,final關鍵字

(1),final關鍵字代表最終、不可改變的。

常見四種用法:

  1. 可以用來修飾一個類
  2. 可以用來修飾一個方法
  3. 還可以用來修飾一個局部變量
  4. 還可以用來修飾一個成員變量

final修飾一個類:
當final關鍵字用來修飾一個類的時候,格式:
public final class 類名稱 {
// ...
}

含義:當前這個類不能有任何的子類。(太監類)
注意:一個類如果是final的,那么其中所有的成員方法都無法進行覆蓋重寫(因為沒兒子。)

public final class MyClass /*extends Object*/ {

    public void method() {
        System.out.println("方法執行!");
    }
}

// 不能使用一個final類來作為父類
public class MySubClass /*extends MyClass*/ {
}

final修飾一個方法:
當final關鍵字用來修飾一個方法的時候,這個方法就是最終方法,也就是不能被覆蓋重寫。
格式:
修飾符 final 返回值類型 方法名稱(參數列表) {
// 方法體
}

注意事項:
對于類、方法來說,abstract關鍵字和final關鍵字不能同時使用,因為矛盾。

public abstract class Fu {

    public final void method() {
        System.out.println("父類方法執行!");
    }

    public abstract /*final*/ void methodAbs() ;

}

public class Zi extends Fu {
    @Override
    public void methodAbs() {

    }

    // 錯誤寫法!不能覆蓋重寫父類當中final的方法
//    @Override
//    public void method() {
//        System.out.println("子類覆蓋重寫父類的方法!");
//    }
}

final修飾局部變量和成員變量
對于成員變量來說,如果使用final關鍵字修飾,那么這個變量也照樣是不可變。

  1. 由于成員變量具有默認值,所以用了final之后必須手動賦值,不會再給默認值了。
  2. 對于final的成員變量,要么使用直接賦值,要么通過構造方法賦值。二者選其一。
  3. 必須保證類當中所有重載的構造方法,都最終會對final的成員變量進行賦值。
public class Person {

    private final String name/* = "鹿晗"*/;

    public Person() {
        name = "關曉彤";
    }

    public Person(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

//    public void setName(String name) {
//        this.name = name;
//    }
}

public class Student {

    private String name;

    public Student() {
    }

    public Student(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

6,權限修飾符 day11-code demo02

Java中有四種權限修飾符:
public > protected > (default) > private
同一個類(我自己) YES YES YES YES
同一個包(我鄰居) YES YES YES NO
不同包子類(我兒子) YES YES NO NO
不同包非子類(陌生人) YES NO NO NO

注意事項:(default)并不是關鍵字“default”,而是根本不寫。

7,內部類

(1),內部類的定義。

如果一個事物的內部包含另一個事物,那么這就是一個類內部包含另一個類。
例如:身體和心臟的關系。又如:汽車和發動機的關系。

分類:

  1. 成員內部類
  2. 局部內部類(包含匿名內部類)

成員內部類的定義格式:
修飾符 class 外部類名稱 {
修飾符 class 內部類名稱 {
// ...
}
// ...
}

注意:內用外,隨意訪問;外用內,需要內部類對象。

==========================
如何使用成員內部類?有兩種方式:

  1. 間接方式:在外部類的方法當中,使用內部類;然后main只是調用外部類的方法。
  2. 直接方式,公式:
    類名稱 對象名 = new 類名稱();
    【外部類名稱.內部類名稱 對象名 = new 外部類名稱().new 內部類名稱();】

類的定義:

public class Body { // 外部類

    public class Heart { // 成員內部類

        // 內部類的方法
        public void beat() {
            System.out.println("心臟跳動:蹦蹦蹦!");
            System.out.println("我叫:" + name); // 正確寫法!
        }

    }

    // 外部類的成員變量
    private String name;

    // 外部類的方法
    public void methodBody() {
        System.out.println("外部類的方法");
        new Heart().beat();
    }

測試類

public class Demo01InnerClass {

    public static void main(String[] args) {
        Body body = new Body(); // 外部類的對象
        // 通過外部類的對象,調用外部類的方法,里面間接在使用內部類Heart
        body.methodBody();
        System.out.println("=====================");

        // 按照公式寫:
        Body.Heart heart = new Body().new Heart();
        heart.beat();
    }

}

重名時

public class Outer {

    int num = 10; // 外部類的成員變量

    public class Inner /*extends Object*/ {

        int num = 20; // 內部類的成員變量

        public void methodInner() {
            int num = 30; // 內部類方法的局部變量
            System.out.println(num); // 局部變量,就近原則
            System.out.println(this.num); // 內部類的成員變量
            System.out.println(Outer.this.num); // 外部類的成員變量
        }

    }

}
public class Demo02InnerClass {

    public static void main(String[] args) {
        // 外部類名稱.內部類名稱 對象名 = new 外部類名稱().new 內部類名稱();
        Outer.Inner obj = new Outer().new Inner();
        obj.methodInner();
    }

}

(2),局部內部類

如果一個類是定義在一個方法內部的,那么這就是一個局部內部類。
“局部”:只有當前所屬的方法才能使用它,出了這個方法外面就不能用了。

定義格式:
修飾符 class 外部類名稱 {
修飾符 返回值類型 外部類方法名稱(參數列表) {
class 局部內部類名稱 {
// ...
}
}
}

小節一下類的權限修飾符:
public > protected > (default) > private
定義一個類的時候,權限修飾符規則:
1. 外部類:public / (default)
2. 成員內部類:public / protected / (default) / private
3. 局部內部類:什么都不能寫

class Outer {

    public void methodOuter() {
        class Inner { // 局部內部類
            int num = 10;
            public void methodInner() {
                System.out.println(num); // 10
            }
        }

        Inner inner = new Inner();
        inner.methodInner();
    }

}

局部內部類,如果希望訪問所在方法的局部變量,那么這個局部變量必須是【有效final的】

備注:從Java 8+開始,只要局部變量事實不變,那么final關鍵字可以省略。

原因:

  1. new出來的對象在堆內存當中。
  2. 局部變量是跟著方法走的,在棧內存當中。
  3. 方法運行結束之后,立刻出棧,局部變量就會立刻消失。
  4. 但是new出來的對象會在堆當中持續存在,直到垃圾回收消失。
public class MyOuter {

    public void methodOuter() {
        /**final*/ int num = 10; // 所在方法的局部變量

        class MyInner {
            public void methodInner() {
                System.out.println(num);
            }
        }
    }
}

(3),匿名內部類

如果接口的實現類(或者是父類的子類)只需要使用唯一的一次,
那么這種情況下就可以省略掉該類的定義,而改為使用【匿名內部類】。

匿名內部類的定義格式:
接口名稱 對象名 = new 接口名稱() {
// 覆蓋重寫所有抽象方法
};

對格式“new 接口名稱() {...}”進行解析:

  1. new代表創建對象的動作
  2. 接口名稱就是匿名內部類需要實現哪個接口
  3. {...}這才是匿名內部類的內容

另外還要注意幾點問題:

  1. 匿名內部類,在【創建對象】的時候,只能使用唯一一次。
    如果希望多次創建對象,而且類的內容一樣的話,那么就需要使用單獨定義的實現類了。
  2. 匿名對象,在【調用方法】的時候,只能調用唯一一次。
    如果希望同一個對象,調用多次方法,那么必須給對象起個名字。
  3. 匿名內部類是省略了【實現類/子類名稱】,但是匿名對象是省略了【對象名稱】
    強調:匿名內部類和匿名對象不是一回事!!!
public interface MyInterface {

    public abstract void method1(); // 抽象方法

    void method2();

}

public class MyInterfaceImpl implements MyInterface {
    @Override
    public void method1() {
        System.out.println("實現類覆蓋重寫了方法!111");
    }

    @Override
    public void method2() {
        System.out.println("實現類覆蓋重寫了方法!222");
    }
}

測試類:

public class DemoMain {

    public static void main(String[] args) {
//        MyInterface obj = new MyInterfaceImpl();
//        obj.method();

//        MyInterface some = new MyInterface(); // 錯誤寫法!

        // 使用匿名內部類,但不是匿名對象,對象名稱就叫objA
        MyInterface objA = new MyInterface() {
            @Override
            public void method1() {
                System.out.println("匿名內部類實現了方法!111-A");
            }

            @Override
            public void method2() {
                System.out.println("匿名內部類實現了方法!222-A");
            }
        };
        objA.method1();
        objA.method2();
        System.out.println("=================");

        // 使用了匿名內部類,而且省略了對象名稱,也是匿名對象
        new MyInterface() {
            @Override
            public void method1() {
                System.out.println("匿名內部類實現了方法!111-B");
            }

            @Override
            public void method2() {
                System.out.println("匿名內部類實現了方法!222-B");
            }
        }.method1();
        // 因為匿名對象無法調用第二次方法,所以需要再創建一個匿名內部類的匿名對象
        new MyInterface() {
            @Override
            public void method1() {
                System.out.println("匿名內部類實現了方法!111-B");
            }

            @Override
            public void method2() {
                System.out.println("匿名內部類實現了方法!222-B");
            }
        }.method2();
    }

}

(4),類作為成員變量類型

public class Weapon {

    private String code; // 武器的代號

    public Weapon() {
    }

    public Weapon(String code) {

        this.code = code;
    }

    public String getCode() {

        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }
}

// 游戲當中的英雄角色類
public class Hero {

    private String name; // 英雄的名字
    private int age; // 英雄的年齡
    private Weapon weapon; // 英雄的武器

    public Hero() {
    }

    public Hero(String name, int age, Weapon weapon) {
        this.name = name;
        this.age = age;
        this.weapon = weapon;
    }

    public void attack() {
        System.out.println("年齡為" + age + "的" + name + "用" + weapon.getCode() + "攻擊敵方。");
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public Weapon getWeapon() {
        return weapon;
    }

    public void setWeapon(Weapon weapon) {
        this.weapon = weapon;
    }
}
public class DemoMain {

    public static void main(String[] args) {
        // 創建一個英雄角色
        Hero hero = new Hero();
        // 為英雄起一個名字,并且設置年齡
        hero.setName("蓋倫");
        hero.setAge(20);

        // 創建一個武器對象
        Weapon weapon = new Weapon("AK-47");
        // 為英雄配備武器
        hero.setWeapon(weapon);

        // 年齡為20的蓋倫用多蘭劍攻擊敵方。
        hero.attack();
    }

}

(5),接口作為成員變量的類型

定義一個接口:

public interface Skill {

    public abstract void use(); // 釋放技能的抽象方法
    //public abstract 可省略  一定是個抽象方法

}

接口的實現類:

public class SkillImpl implements Skill {
    @Override
    public void use() {
        System.out.println("Biu~biu~biu~");
    }
}

人物類:

public class Hero {

    private String name; // 英雄的名稱
    private Skill skill; // 英雄的技能

    public Hero() {
    }

    public Hero(String name, Skill skill) {
        this.name = name;
        this.skill = skill;
    }

    public void attack() {
        System.out.println("我叫" + name + ",開始施放技能:");
        skill.use(); // 調用接口中的抽象方法
        System.out.println("施放技能完成。");
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Skill getSkill() {
        return skill;
    }

    public void setSkill(Skill skill) {
        this.skill = skill;
    }
}

測試類:

public class DemoGame {

    public static void main(String[] args) {
        Hero hero = new Hero();
        hero.setName("艾希"); // 設置英雄的名稱

        // 設置英雄技能
//        hero.setSkill(new SkillImpl()); // 使用單獨定義的實現類

        // 還可以改成使用匿名內部類
//        Skill skill = new Skill() {
//            @Override
//            public void use() {
//                System.out.println("Pia~pia~pia~");
//            }
//        };
//        hero.setSkill(skill);

        // 進一步簡化,同時使用匿名內部類和匿名對象
        hero.setSkill(new Skill() {
            @Override
            public void use() {
                System.out.println("Biu~Pia~Biu~Pia~");
            }
        });

        hero.attack();
    }

}

(6),接口作為參數和返回值類型

import java.util.ArrayList;
import java.util.List;

/*
java.util.List正是ArrayList所實現的接口。
 */
public class DemoInterface {

    public static void main(String[] args) {
        // 左邊是接口名稱,右邊是實現類名稱,這就是多態寫法
        List<String> list = new ArrayList<>();

        List<String> result = addNames(list);
        for (int i = 0; i < result.size(); i++) {
            System.out.println(result.get(i));
        }
    }

    public static List<String> addNames(List<String> list) {
        list.add("迪麗熱巴");
        list.add("古力娜扎");
        list.add("瑪爾扎哈");
        list.add("沙揚娜拉");
        return list;
    }
}
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,117評論 6 537
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,860評論 3 423
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,128評論 0 381
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,291評論 1 315
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,025評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,421評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,477評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,642評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,177評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,970評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,157評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,717評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,410評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,821評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,053評論 1 289
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,896評論 3 395
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,157評論 2 375

推薦閱讀更多精彩內容

  • Java繼承和多態 繼承 定義 繼承就是利用現有類創建新類的過程,現有的類稱為 父類(基類),新類稱為 子類(派生...
    Golden30閱讀 486評論 0 1
  • 整理來自互聯網 1,JDK:Java Development Kit,java的開發和運行環境,java的開發工具...
    Ncompass閱讀 1,549評論 0 6
  • 一:java概述: 1,JDK:Java Development Kit,java的開發和運行環境,java的開發...
    慕容小偉閱讀 1,817評論 0 10
  • 一:java概述:1,JDK:Java Development Kit,java的開發和運行環境,java的開發工...
    ZaneInTheSun閱讀 2,680評論 0 11
  • 面向對象筆記 一、 對象在內存中的存放方法以及被調用過程 class文件首先被加載到方法區中的class文件內容區...
    VictorBXv閱讀 481評論 0 2