天天看點

Java 進階,學習筆記-6 final 關鍵字、權限修飾符以及内部類的使用

final 關鍵字、權限修飾符及内部類

文章目錄

      • final 關鍵字
      • 權限修飾符
      • 内部類
        • 成員内部類
        • 内部類的同名變量使用
        • 局部内部類的定義
        • 局部内部類中的 final
        • 匿名内部類(重點)
      • 類作為成員變量類型

final 關鍵字

final 關鍵字代表最終、不可變的

1、可以用來修飾一個類

格式

public final class 類名稱 {
    // 方法體
}
           

注意:

final 類不能有任何的子類

一個類如果是 final 的,那麼其中的所有成員方法都無法進行覆寫重寫

2、可以用來修飾一個方法

格式:

修飾符 final 傳回值類型 方法名稱(參數清單) {
    // 方法體
}
           

注意:

對于類、方法來說,

abstract

關鍵字和

final

關鍵字不能同時使用

注意:

對于基本類型來說,不可變說的是變量當中的資料不可改變

對于引用類型來說,不可變說的是變量當中的位址值不可變

3、可以用來修飾一個局部變量

public class Test {
    public static void main(String agrs[]) {
        int num1 = 20;
        System.out.println("num1 = " + num1);
        num1 = 50;
        System.out.println("num1 = " + num1);

        final int num2 = 70;
        System.out.println("num2 = " + num2);
//        num2 = 100; // 報錯,說明我們用 final 關鍵字修飾的局部變量不能被更改
    }
}
           

一旦用了

final

關鍵字來修飾局部變量,那麼這個變量就不能再進行更改,即用 final 關鍵字修飾的局部變量隻能被指派一次

4、可以用來修飾一個成員變量

1、由于成員變量具有預設值,是以用了 final 之後必須手動指派,不會再給預設值

2、對于 final 的成員變量,要麼使用直接指派,要麼通過構造方法指派

3、必須保證類當中所有重載的構造方法,都最終會對 final 的成員變量進行指派

權限修飾符

public -> protected -> (default) -> private

注意:這裡的

default

并不是

default

關鍵字,而是不寫。

位置 public protected (default) private
同一個包(我自己) YES YES YES YES
同一個類(我鄰居) YES YES YES NO
不同包子類(我兒子) YES YES No NO
不同包非子類(陌生人) YES NO NO NO

在使用的時候,建議:

成員變量使用

private

,用來隐藏細節

構造方法使用

public

,友善建立對象

成員方法使用

public

,友善調用方法

内部類

一個類中包含另外一個類

如身體和心髒的關系,汽車和發動機的關系

分類:

1、成員内部類

2、局部内部類(包含匿名内部類)

成員内部類

修飾符 class 外部類名稱 {
    修飾符 class 内部類名稱 {
        //内部類
    }
    //。。。
}
           

使用:

1、間接方式。在外部類的方法當中,使用内部類,然後

main

隻是調用外部類的方法

2、直接方式。

外部類名稱.内部類名稱 對象名 = 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 String getName() {
        return name;
    }

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

}

// 定義測試類
public class Test {

    public static void main(String args[]) {
        // 建立對象
        Body body = new Body();
        body.setName("Pluto");
        /*
        通過外部類的對象,調用外部類的方法,
        裡面間接在使用内部類 Heart
        */
        body.methodBody();
    }

}
           

輸出:

外部類的方法!
心髒跳動!
我叫:Pluto
           

内部類的直接使用

public class Test {

    public static void main(String args[]) {
        Body.Heart inner = new Body().new Heart();
        inner.beat();
    }

}
           

輸出:

心髒跳動!
我叫:Pluto
           

内部類的同名變量使用

之前我們在遇到本類中的變量和方法中的局部變量重名的時候,我們用

this.變量名

來表示本類中的那個變量,局部變量的話直接用即可,那麼現在,在外部類和内部類以及内部類中的方法中的局部變量重名的使用,我們用

外部類名稱.this.變量名

來表示外類的那個變量

// 外部類
public class Outer {

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

    // 内部類
    public class Inner {

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

        // 内部類中的成員方法
        public void methodInner() {
            // 局部變量
            int num = 30;
            System.out.println("局部 num 變量:" + num);
            System.out.println("内部類中的 num 變量:" + this.num);
            System.out.println("外部類中的 num 變量:" + Outer.this.num);
        }

    }// 内部類

}

// 定義測試類
public class Test {

    public static void main(String args[]) {
        Outer.Inner inner = new Outer().new Inner();
        inner.methodInner();
    }

}
           

輸出:

局部 num 變量:30
内部類中的 num 變量:20
外部類中的 num 變量:10
           

局部内部類的定義

如果一個類是定義在一個方法内部的,那麼這就是一個局部内部類。即隻有在目前所屬的方法才能使用它,在這個方法以外就不能使用它

格式:

修飾符 class 外部類名稱 {
    修飾符 傳回值類型 外部類方法名稱(參數清單) {
        class 局部内部類名稱 {
            // ···
        }
    }
}
           
// 外部類
public class Outer {

    // 外部類的成員方法
    public void methodOuter() {

        // 局部内部類的定義
        class Inner {

            // 局部内中的成員變量
            int num = 10;

            // 局部内中的成員方昂發
            public void methodInner() {
                System.out.println("局部内部類的方法:" + num);
            }
        }

        // 建立局部内部類的對象并調用裡面的方法
        Inner inner = new Inner();
        inner.methodInner();
    }

}

// 定義測試類
public class Test {

    public static void main(String args[]) {
        Outer obj = new Outer();
        obj.methodOuter();
    }

}
           

輸出:

局部内部類的方法:10
           

定義類時的權限修飾符注意事項:

1、外部類:public / (default)

2、成員内部類:public / protected / (default) / private

3、局部内部類:什麼都不能寫

局部内部類中的 final

局部内部類中,如果希望通路所在方法的局部變量,那麼這個局部變量必須是有效 final 的。

即局部變量通路的所在方法的局部變量必須是不能改變的(隻有一次指派),并且要寫上 final 關鍵字。

從 java 8+ 開始,隻要局部變量事實不變,那麼 final 關鍵字可以省略。

解釋:

因為 new 出來的對象是在堆記憶體當中的。局部變量是在方法内部的,是跟着方法走的,方法在運作之後,立刻出棧,那麼局部變量就會被釋放。但是 new 出來的對象會在堆當中持續存在,直到垃圾回收消失。那麼在局部内部類中還想使用方法中的局部變量,怎麼辦呢?隻有保證那個局部變量不變就可以了。是以局部内部類中通路方法的局部變量,那麼這個局部變量必須是有效 final (不變)的,或者在局部變量前面加上 final 修飾符即可。

// 外部類
public class Outer {

    // 外部類的方法
    public void methodOuter() {
        // 局部變量
        int num1 = 10;  // 寫法 1
        final int num2 = 20;    // 寫法 2
        /*
        * 錯誤寫法
        * int num3 = 10;
        * num3 = 20;
        * */

        // 局部内部類
        class Inner {
            // 局部内部類中的方法
            public void methodInner() {
                System.out.println("num1 = " + num1);
                System.out.println("num2 = " + num2);
            }
        }
    }

}
           

匿名内部類(重點)

如果接口的實作類(或者是父類的子類)隻需要使用唯一的一次。

那麼這種情況下就可以省略掉該類的定義,而改為使用匿名内部類

格式:

接口名稱 對象名 = new 接口名稱() {
    // 覆寫重寫接口中所有抽象方法
}
           

傳統方法

// 定義接口
public interface MyInterface {

    // 定義接口中的抽象方法
    public abstract void method();

}

// 接口 MyInterface 的實作類
public class MyInterfaceImpl implements MyInterface{

    @Override
    public void method() {
        System.out.println("接口 MyInterface 的實作方法");
    }

}

// 定義測試類
public class Test {

    public static void main(String args[]) {
        MyInterfaceImpl impl = new MyInterfaceImpl();
        impl.method();
    }

}
           

對于上面的接口,如果我們隻對接口做一次使用,那麼我們就可以不用再定義一個實作類,可以直接在測試類中定義匿名内部類

// 定義接口
public interface MyInterface {

    // 定義接口中的抽象方法
    public abstract void method();

}

// 定義測試類
public class Test {

    public static void main(String args[]) {
        // 定義匿名内部類
        MyInterface obj = new MyInterface() {
            // 覆寫重寫接口
            @Override
            public void method() {
                System.out.println("定義匿名内部類來實作接口 MyInterface");
            }
        };
        obj.method();

    }

}
           

匿名内部類的注意事項:

1、匿名内部類在建立的時候,隻能使用唯一一次。如果你還是希望多次使用這個實作類,那麼你必須另外建立一個實作類,不能使用匿名内部類

2、匿名對象,在調用方法的時候,隻能調用唯一一次。即如果希望同一個對象,調用多次方法,那麼必須給對象起個名字。

3、匿名内部類是省略了實作類(子類),匿名對象是省略了對象名稱

類作為成員變量類型

所謂的類作為成員變量類型就是我們自己定義的類,作為一個成員變量。就像下面的代碼中,我定義了一個武器了,然後在玩家類中我定義了一個武器類型的成員變量

// 定義玩家類
public class Player {

    // 定義玩家姓名
    private String playerName;
    // 定義武器
    private Weapon weapon;

    // 構造方法
    public Player() {
    }

    public Player(String playerName, Weapon weapon) {
        this.playerName = playerName;
        this.weapon = weapon;
    }

    // 攻擊方法
    public void defeat(Player player) {
        System.out.println(this.playerName + " 用 " + weapon.getWeaponName() + " 擊倒了 " + player.getPlayerName());
    }

    public String getPlayerName() {
        return playerName;
    }

    public void setPlayerName(String playerName) {
        this.playerName = playerName;
    }

    public Weapon getWeapon() {
        return weapon;
    }

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

// 定義武器類
public class Weapon {

    private String weaponName;

    // 構造方法
    public Weapon() {}

    public Weapon(String weaponName) {
        this.weaponName = weaponName;
    }

    public String getWeaponName() {
        return weaponName;
    }

    public void setWeaponName(String weaponName) {
        this.weaponName = weaponName;
    }
}

// 定義測試類
public class Test {

    public static void main(String args[]) {

        // 定義武器
        Weapon weapon_98k = new Weapon("98k");
        Weapon weapon_AK47 = new Weapon("AK47");

        // 定義玩家
        Player player1 = new Player("Alex", weapon_98k);
        Player player2 = new Player("Pluto", weapon_AK47);

        // defeat 方法
        player1.defeat(player2);

    }

}
           

輸出:

Alex 用 98k 擊倒了 Pluto
           

當然我們還有其他的用法

1、接口作為成員變量的類型

2、接口作為方法的參數或傳回值