1. 程式人生 > 實用技巧 >java自學第3期——繼承、多型、介面、抽象類、final關鍵字、許可權修飾符、內部類

java自學第3期——繼承、多型、介面、抽象類、final關鍵字、許可權修飾符、內部類


一.繼承:

關鍵字extends

/*
定義一個父類:人類
定義父類格式:public class 父類名稱{
}
定義子類格式:public class 子類名稱 extends 父類名稱{
}
*/
  • 程式碼示範:
    *要點:
    1.子類中在main方法中建立父類物件可呼叫父類方法;
    2.不加關鍵字直接訪問本方法區域性變數(可重名時區分);
    3.使用this關鍵字訪問本類成員變數(可重名時區分);
    4.使用super關鍵字訪問父類成員變數(可重名時區分);
    5.如果存在方法的重名:父類與子類有重名方法:
    ——物件new的誰則優先呼叫誰的重名方法,沒有則向上尋找;
    前面介紹了成員變數、成員方法繼承的訪問特點,接下來是構造方法:
    ——子類構造方法(父類無參時)中有一個預設贈送的super()方法
    ——父類有參時,子類裡呼叫super的()裡傳參,過載時誰對的上呼叫誰;
    6.如果要更新修改父類方法,本著設計原則儘量不去直接修改正在使用的類,
    則可以進行覆蓋重寫:格式:通過使用super關鍵字繼承父類需要的方法。
@Override
方法外部相同(){
   super.父類方法();
   //這裡新增新的內容
}
/*
小結super
super關鍵字作用:
示例:
   public class demo06Super extends demo01people {

   public void method1(){
       System.out.println(super.num);	//1.在子類成員方法中訪問父類的成員變數
   }
   public void method2(){
       super.methodChongMing();	// 2.在子類成員方法中訪問父類的成員方法
   }
   public demo06Super(){
       super();	//3.在子類的構造方法中訪問父類的構造方法
   }
}
*/

7.this關鍵字的作用小結:

this關鍵字的作用:
1.在本類的成員方法中訪問本類的成員變數
2.在本類成員方法中訪問本類的另一個成員方法
3.在本類的構造方法中訪問本類的另一個構造方法:
(1)this(...)呼叫必須是構造方法的第一個語句,一個this;
(2)super和this兩種構造呼叫不能同時使用
  • 程式碼示範:
String name = "python";
public void method1(){
        String name = "python";
        System.out.println(name);//無關鍵字時直接訪問本方法區域性變數
        System.out.println(this.name);//訪問本類成員變數
        System.out.println(super.num);//訪問父類成員變數
        this.method2();//訪問本類另一個成員方法
    }

8.最後,繼承三大特點:

1.單繼承:一個子類只有一個直接父類
2.多級繼承:父類、子類、子類也可作父類再向下延伸,最上級為java.lang.object類
3.一個父類可以有多個子類

二、多型

1.多型性:父類引用指向子類物件
多型的一個用處:無論右邊new的時候換成哪個子類物件,等號左邊呼叫方法都不會發生變化

格式:
父類名稱 物件名 = new 子類名稱();
或者:
介面名 物件名 = new 實現類名稱()。

2.成員方法:編譯看左,執行看右;
成員變數:編譯看左,執行也看左;例項變數不具備多型性.
程式碼示範:

//前面省略部分Zi類已經繼承Fu類
  public static void main(String[] args) {

        Fu one = new Zi();//多型的寫法
        one.methodcommon();//重名時,成員方法優先使用子類(new誰先呼叫誰)。

        one.fu1();//它的編譯時型別為Fu,可以呼叫Fu中的方法
//      one.zi1(); 該呼叫編譯時會報錯,因為它的編譯時型別為Fu,無法呼叫zi1方法;
        System.out.println(one.num);//優先看左邊Fu類

        Zi two = (Zi) one;//將父類物件還原成子類物件。
        System.out.println(one instanceof Fu);//輸出true,one可以做Fu類的例項物件
        System.out.println(one instanceof Zi);//輸出true,one可以做Zi類的例項物件
    }

3.轉型多型寫法左父右子是正常的向上轉型
4.向下轉型:為了讓物件呼叫子類方法(向上轉型只能呼叫左邊編譯型別的父類方法)
左子類右父類,是一個還原的過程,將父類物件還原成子類物件,且不能向下轉成別的子類。

格式:  子類名 物件名 = (子類名) 父類物件名;     (後者也可以是介面)

Fu fu = new Fu();//向上轉型建立父類物件
Zi two = (Zi) one;//將父類物件還原成子類物件。

5.強制向下轉型時,判斷前面的物件是否是後面型別的例項,是否可以成功轉換,從而保證程式碼更加健壯。
格式: 物件 instanceof 型別
得到一個boolean值結果(true/false),判斷前面的物件能否作為後面型別的例項

Zi two = (Zi) one;//將父類物件還原成子類物件。
        System.out.println(one instanceof Fu);//輸出true,one可以做Fu類的例項物件
        System.out.println(one instanceof Zi);//輸出true,one可以做Zi類的例項物件

三、介面

1.介面定義了某一批類需要遵守的規範。這就意味著接口裡通常是定義一組公用方法。
2.介面是一種引用資料型別(類、介面、陣列),注意其中的抽象方法。
3.介面可以繼承介面,但不能繼承類。
4.Java9 裡可以有常量、抽象方法、預設方法、靜態方法、私有方法。
5.備註:介面編譯後生成的同樣是.class的位元組碼檔案

//格式
public interface 介面名稱(首字母大寫){
	//抽象方法
}
  • 接口裡的成員變數只能是常量,必須用public static final修飾,可省略
 //final即為不可改變
    public static final int MAX_NUMBER = 20;
  • 接口裡的[普通方法]只能是抽象方法,public abstract可以省略
 public abstract void out();
   				 void getDate();
  • 介面中的[預設方法]需要用default修飾
    /*
    當介面新新增方法時,新方法寫為預設方法,則可以不去動其實現類,預設方法自動被其繼承
    預設方法同樣可以被覆寫。
     */
    public default void print() {
        foo();
        System.out.println("預設方法呼叫3");
    }
  • 介面中定義[靜態方法],需要用static修飾
public static void staticTest() {
        System.out.println("靜態方法!");
    }
  • 當倆預設方法中有重複內容時,抽取出來定義私有方法
//定義私有預設方法,給預設方法呼叫,但不應被實現類使用,所以許可權為私有
    private void foo() {
        System.out.println("預設方法呼叫2");
    }
    
//定義私有靜態方法,給靜態方法呼叫,但不應被實現類使用,所以許可權為私有
    private static void bar() {
        System.out.println("bar私有靜態方法");
    }
}
  • 介面不能建立例項,但能用於宣告引用型別變數,且必須引用到實現類的物件;
  • 一個實現類可以同時實現多個介面
//格式:
.public class 實現類名稱 impliments{
 	//必須覆寫介面中所有抽象方法;
},	
  • 在實現類(impliments)中進行介面的實現:
//
public class Demo01InterfaceImpl implements Demo01Interface,Demo02Interface {
    //1,2介面都有out抽象方法,但只需覆寫一次
    @Override
    public void out() {
        System.out.println("抽象方法覆寫!");
    }

    @Override
    public void getDate() {
        System.out.println("抽象方法覆寫!");
    }

    @Override
    public void print(){
        System.out.println("衝突的預設方法也需要覆寫!");//不衝突則不用覆寫
    }
}
  • 然後在main類裡建立實現類的物件,進行呼叫
public class Impliments  {

    public static void main(String[] args) {
  	    //建立實現類的物件
        Demo01InterfaceImpl ImplementationObject1 = new Demo01InterfaceImpl();
        ImplementationObject1.getDate();
        ImplementationObject1.out();
        ImplementationObject1.print();//呼叫實現類裡繼承自介面的預設方法。
        
		//接口裡的靜態方法只能通過介面名稱直接呼叫。
        Demo01Interface.staticTest();

		//通過介面名直接訪問常量。
        System.out.println(Demo01Interface.MAX_NUMBER);
    }
}
  • 介面是多繼承,一個介面可以有多個父介面。
  • 多個父介面中的抽象方法如果存在重名,正常覆寫;但預設方法重名需要帶有default關鍵字覆寫。
  • 正常不衝突抽象方法不需要再覆寫(實現類多個介面時也是如此)。
//介面的多繼承,同樣使用extends關鍵字
public interface Demo03InterfaceExtends extends Demo01Interface,Demo02Interface {
    @Override
    default void print() {
		//覆寫父介面的預設方法不能省略default關鍵字
    }
}

四、抽象類

  • 抽象方法:加上abstract關鍵字,去掉大括號,直接分號結束;
  • 抽象類:抽象方法所在的類必須是抽象類,再class之前寫上abstract即可
  • 不能直接new抽象類物件,必須用一個子類來繼承抽象父類
    (抽象類物件呼叫的方法存在沒有具體內容的方法體,因此沒有意義)
  • 建立子類物件,不可建立抽象父類物件;
public abstract class demo01Abstract {

    public abstract void method1();
    public abstract void method2();
}
  • 子類必須覆寫抽象類中的所有抽象方法(去掉abstrat,補上大括號)。
    (假設不全部覆寫,物件呼叫的方法存在抽象方法,沒有意義)
//使用extends繼承抽象父類
public abstract class demo02Zi extends demo01Abstract {

    //只覆寫了一個抽象方法,沒有將抽象方法全部覆寫完,也就是說本子類還存在著繼承下來的未覆寫的抽象方法
    //所以該類也同樣是抽象類
    
    @Override
    public void method1() {
        System.out.println("已經覆寫第method1方法!");
    }
}
//在這個子子類中已經將所有抽象方法全部覆寫,所以該類不再是抽象類!
public class demo03Sun extends demo02Zi {
    @Override
    public void method2() {
        System.out.println("已經覆寫method2方法!");
    }
}
  • 最後在main類中的main方法裡建立物件進行呼叫即可。

五、final關鍵字

  • final表示它修飾的類、方法、成員變數、區域性變數不可改變。
  • final修飾的類不可被繼承下去。無子類,不可被覆蓋重寫。
public final class 類名(){}
  • final修飾的方法不可被覆蓋重寫。
public final void method(){}
  • 類和方法不能同時使用final和abstract,二者矛盾。abstract表示抽象,是待定的;final表示最終的,是確定的。
  • final修飾區域性變數,若是基本資料型別的數值,則不可改變;若是引用資料型別,則地址值不可改變,但地址指向的物件的內容可以改變。
  • final修飾成員變數,也不可變,但必須要進行手動賦值,成員變數加final後不允許再擁有預設值。
public final class Demo01Final {

    public static void main(String[] args) {
        int NUM = 1;
        System.out.println(NUM);
        NUM = 2;//可以改變
        System.out.println(NUM);
        
        final int NUM1 = 2;
//      NUM1 = 3;  錯誤,NUM是確定的值,不可被改變。
    }
}

六、許可權修飾符

  • 許可權大小:
    四種許可權修飾符,訪問許可權從大到小:
    public > protected > 空 > private
    同一個類: yes yes yes yes
    同一個包: yes yes yes no
    不同包子類: yes yes no no
    不同包非子類:yes no no no

七、內部類

一個外部類包含的一個巢狀的類,叫內部類。
分類:
1.成員內部類。
2.區域性內部類(包含匿名內部類)。

  • 注意:內部類可以無限制地訪問外部類,外部類訪問內部類需要內部類物件。
  • 各個類對應可以使用的許可權修飾符如下:
    外部類:public/(default)
    成員內部類:public.protect.(default).private
    區域性內部類:什麼都沒有,注意並不是(default)
  • 內部類定義示例:
public class main {
    private String name = "成員變數";

    //定義一個成員內部類
    public class innerClass {
        private String innername = "內部成員變數";
    }

		// 定義一個成員內部類內的成員方法
        public void innermethod1() {
            System.out.println("內部類方法");
            System.out.println(name);
        }
        
  }

使用內部類的兩種方式:
1.直接:在main方法中建立內部類物件:格式:
外.內 = new 外().內();

外部類名稱.內部類名稱 物件名 = new 外部類名稱().new 內部類名稱();

 public static void main(String[] args) {
        //直接建立內部類的物件。
        InnerclassUsing.innerClass object = new InnerclassUsing().new innerClass();
        object.innermethod1();
    }

2.間接:在外部類的方法中,只用內部類,main只是呼叫外部類的方法,通過外部類方法訪問內部類。

public class InnerclassUsing {

    private String name = "成員變數";

    //定義一個成員內部類
    public class innerClass {
    
        private String innername = "內部成員變數";

        // 定義一個成員內部類的成員方法
        public void innermethod1() {
            System.out.println("內部類方法");
            System.out.println(name);
        }
    }

    public void outMethod() {
        System.out.println("外部類成員方法");
        
  //    System.out.println(innername);  錯誤。
  
        //通過匿名物件訪問內部類變數和方法
        System.out.println(new innerClass().innername);
        
        //建立匿名物件並呼叫innermethod1方法。
        new innerClass().innermethod1();
    }
    
 }

內部類的重名變數訪問格式:

  • 本方法: 空
  • 本類成員變數:this.
  • 外部類成員變數:外部類的名稱.this.
public class Demo03CommonName {
	//外部類成員變數
    int NAME = 1;

    public class innerClass{
    	//內部類成員變數
        int NAME = 2;
        
        public void method (){
        	//內部類區域性變數
            int NAME = 3;
            
            System.out.println(NAME);//3,區域性變數,就近原則。
            System.out.println(this.NAME);//2,本類成員變數。
            System.out.println(Demo03CommonName.this.NAME);//1,外部類的成員變數;
        }
    }
}

如果一個類定義在方法內部,那麼這個類叫區域性內部類。
區域性內部類只能被當前所屬的方法所使用,外部不可。

格式:

public class Localinnerclass {
    String NAME = "外部成員變數";

    public void method(){
    
    	//定義區域性內部類
        class localLinnerClass{
            String ONE = "區域性內部類成員變數";
            
            public void nmethod2(){
                System.out.println("區域性內部類的成員方法");
                System.out.println(ONE);
            }
        }

    }
}

區域性內部類,如果希望訪問所在方法的區域性變數,該變數應該滿足【有效final】的條件

public class Demo04Final {

    public void method1(){
    
        //這裡即使不寫final的話,不去修改 也算是有效final [從java8開始]
        final String NAME = "bilibili";

        class LocalInnerclass{
        
            public void localInnerMethod(){
            	//區域性內部類內的方法訪問所在類外部的方法的區域性變數
                System.out.println(NAME);
            }
        }

    }
}

當介面是實現類只使用唯一一次時,可以使用匿名內部類

  • 格式
介面名稱 物件名 = new 介面名稱(){
    //這裡進行抽象方法的覆寫。
};    //別忘了結尾的分號

匿名內部類在建立物件的時候,只能使用唯一一次
匿名物件在使用方法時,只能使用唯一一次
匿名內部類是省略了(實現類/子類名稱),而匿名物件則是省略了物件名稱,二者不同。

//當實現類只使用一次時,可以使用匿名內部類的寫法!
        Anonymous obj6 = new Anonymous() {
            //覆寫匿名內部類裡所有抽象方法!
            @Override
            public void method1() {
                System.out.println("bilibili?");
            }
            @Override
            public void method2(){
                System.out.println("覆寫method!");
            }
        };
        obj6.method1();//列印內容
        obj6.method2();

將類作為成員變數的寫法

//定義一個武器類
public class Weapon {
    private String itsname;//武器名稱
    private int attacknum;//武器攻擊力
		
	//無參構造方法
    public Weapon() {
    }
	//全參方法
    public Weapon(String itsname, int attacknum) {
        this.itsname = itsname;
        this.attacknum = attacknum;
    }
	//get名字
    public String getItsname() {
        return itsname;
    }
	//set名字
    public void setItsname(String itsname) {
        this.itsname = itsname;
    }
	//get攻擊力
    public int getAttacknum() {
        return attacknum;
    }
	//set攻擊力
    public void setAttacknum(int attacknum) {
        this.attacknum = attacknum;
    }
}

//定義一個女武神類
public class Valkyrie1 {
    private String name;//女武神之名
    private Weapon weapon;//將武器類變為成員變數 交給女武神類

	//女武神攻擊方法
    public void attack(){
        System.out.println(name + "使用的" + weapon.getItsname() + "具有" + weapon.getAttacknum() +"點攻擊力");
    }

    
	//無參構造
    public Valkyrie1() {};
	//全參
    public Valkyrie1(String name, Weapon weapon) {
        this.name = name;
        this.weapon = weapon;
    }
	//get女武神名字
    public String getName() {
        return name;
    }
	//set女武神名字
    public void setName(String name) {
        this.name = name;
    }
	//get女武神武器
    public Weapon getWeapon() {
        return weapon;
    }
	//set女武神武器
    public void setWeapon(Weapon weapon) {
        this.weapon = weapon;
    }
}
  • 定義了女武神類和武器類後,在main方法中進行呼叫
 		Valkyrie1 valkyrie = new Valkyrie1();//建立一個女武神
        valkyrie.setName("bronya"); //set女武神名字叫bronya
        
        Weapon weapon = new Weapon("真理之鑰",1000);//建立一把武器,並同時賦予名稱和攻擊力
//     也可以分開寫: weapon.setItsname("真理之鑰");//武器名字
//     也可以分開寫: weapon.setAttacknum(1000);//武器攻擊力

        valkyrie.setWeapon(weapon);//將定義好的武器交給女武神
        
        valkyrie.attack();//最後,女武神使用這把武器進行攻擊