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、接口作為方法的參數或傳回值