你好!您已經熟悉“修飾符”一詞。至少,您遇到過訪問修飾符(public、private)和 static 修飾符。今天我們將討論一個名為final的特殊修飾符。您可以說 final 修飾符“鞏固”了我們程序中需要恆定、明確、不變行為的部分。您可以在程序中的三個地方使用它:類、方法和變量。
讓我們按順序瀏覽它們。如果類聲明中使用了final修飾符,則意味著該類不能被繼承。在之前的課程中,我們使用了一個簡單的繼承示例:我們有一個
沒有任何改變。它不應該有。正如我們之前所說,字符串是不可變的。那麼,類中的所有方法是什麼

Animal
父類和兩個子類:Cat
和Dog
public class Animal {
}
public class Cat extends Animal {
// Fields and methods of the Cat class
}
public class Dog extends Animal {
// Fields and methods of the Dog class
}
但是,如果我們在類上使用finalAnimal
修飾符,則類Cat
和Dog
類不能繼承它。
public final class Animal {
}
public class Cat extends Animal {
// Error! Cannot inherit from final Animal
}
編譯器立即生成錯誤。在 Java 中,許多最終類已經實現。在您經常使用的那些中,String
是最著名的。此外,如果一個類被聲明為final,則該類的所有方法也將變為final。這意味著什麼?如果使用final修飾符聲明方法,則不能重寫該方法。例如,這裡我們有一個Animal
聲明speak()
方法的類。但是,狗和貓肯定以不同的方式“說話”。因此,我們將在Cat
和Dog
類中聲明 speak() 方法,但我們將以不同方式實現它們。
public class Animal {
public void speak() {
System.out.println("Hello!");
}
}
public class Cat extends Animal {
@Override
public void speak() {
System.out.println("Meow!");
}
}
public class Dog extends Animal {
@Override
public void speak() {
System.out.println("Woof!");
}
}
我們使Cat
和Dog
類覆蓋了在父類中聲明的方法。現在,動物會說不同的話,這取決於它是什麼類型的物體:
public class Main {
public static void main(String[] args) {
Cat cat = new Cat();
Dog dog = new Dog();
cat.speak();
dog.speak();
}
}
輸出: 喵!緯! 但是,如果我們將Animal
類的speak()
方法聲明為final,那麼我們就不能在其他類中重寫它:
public class Animal {
public final void speak() {
System.out.println("Hello!");
}
}
public class Cat extends Animal {
@Override
public void speak() {// Error! A final method can't be overridden!
System.out.println("Meow!");
}
}
我們的對象將被迫使用speak()
父類中定義的方法:
public static void main(String[] args) {
Cat cat = new Cat();
Dog dog = new Dog();
cat.speak();
dog.speak();
}
輸出: 你好!你好! 現在,關於最終變量。它們也稱為常量。首先(也是最重要的),不能更改分配給常量值的初始值。它是一次性分配的。
public class Main {
private static final int CONSTANT_EXAMPLE = 333;
public static void main(String[] args) {
CONSTANT_EXAMPLE = 999;// Error! You can't assign a new value to a final variable!
}
}
常量不需要立即初始化。那可以稍後再做。但是,最初分配給它的值將永遠保持不變。
public static void main(String[] args) {
final int CONSTANT_EXAMPLE;
CONSTANT_EXAMPLE = 999;// This is allowed
}
其次,記下我們變量的名稱。Java 對常量有不同的命名約定。它不是通常的駝峰命名法。如果它是一個普通變量,我們會稱它為constantExample. 但是,常量的名稱全部大寫,單詞之間有下劃線(如果有多個單詞),例如“CONSTANT_EXAMPLE”。為什麼我們需要常量?它們非常有用,例如,如果您經常在程序中使用固定值。例如,您決定創造歷史並親手編寫遊戲“巫師 4”。遊戲顯然會經常使用主角的名字:“利維亞的傑洛特”。這個字符串(以及其他英雄的名字)最好聲明為常量:它的值將存儲在一個地方,你輸入一百萬次絕對不會錯字。
public class TheWitcher4 {
private static final String GERALT_NAME = "Geralt of Rivia";
private static final String YENNEFER_NAME = "Yennefer of Wengerberg";
private static final String TRISS_NAME = "Triss Merigold";
public static void main(String[] args) {
System.out.println("The Witcher 4");
System.out.println("It's already the fourth Witcher game, but " + GERALT_NAME + " still can't decide who" +
" he likes more: " + YENNEFER_NAME + " or " + TRISS_NAME);
System.out.println("But, if you've never played The Witcher before, we'll start from the beginning.");
System.out.println("The protagonist's name is " + GERALT_NAME);
System.out.println(GERALT_NAME + " is a witcher, a monster hunter");
}
}
輸出: The Witcher 4 已經是巫師的第四場比賽了,但是 Rivia 的 Geralt 仍然無法決定他更喜歡誰:Wengerberg 的 Yennefer 還是 Triss Merigold 但是,如果你以前從未玩過 The Witcher,我們將從開始。主角的名字是利維亞的傑洛特 利維亞的傑洛特是獵魔人,怪物獵人 我們已經將英雄的名字聲明為常量。現在肯定不會打錯了,也不用每次都手寫了。另一個優點:如果我們需要在整個程序中更改變量的值,您可以在一個地方完成,而不是在整個代碼庫中手動修改它。:)
不可變類型
當您使用過 Java 時,您可能已經習慣了這樣的想法,即程序員幾乎可以完全控制所有對象的狀態。如果你想創建一個Cat
對象,你可以。如果你想重命名它,你可以。如果你想改變它的年齡或其他東西,你可以。但是 Java 有幾種具有特殊屬性的數據類型。它們是不可變的。如果一個類是不可變的,那麼它的對象的狀態就不能改變。想要一些例子嗎?您可能會感到驚訝,但最著名的不可變類是 String!那麼,我們真的不能改變一個字符串的值嗎?好吧,讓我們試試看:
public static void main(String[] args) {
String str1 = "I love Java";
String str2 = str1;// Both reference variables point to the same string.
System.out.println(str2);
str1 = "I love Python";// but changing str1 has no impact on str2
System.out.println(str2);// str2 continues to point to the "I love Java" string, but str1 now points to a different object
}
輸出: 我愛 Java 我愛 Java 我們寫完之後
str1 = "I love Python";
字符串"I love Java"
對像沒有改變或去任何地方。它仍然愉快地存在並且具有與以前完全相同的文本。代碼
str1 = "I love Python";
只是創建了另一個對象,str1現在指向它。但是,我們似乎無法對“I love Java”字符串對象產生任何影響。好吧,讓我們試試別的吧!這個String
類充滿了方法,其中一些似乎可以改變對象的狀態!例如,有一個replace()
方法。讓我們將字符串中的單詞“Java”更改為“Python”!
public static void main(String[] args) {
String str1 = "I love Java";
String str2 = str1;// Both reference variables point to the same string.
System.out.println(str2);
str1.replace("Java", "Python");// We try to change the state of str1 by swapping the word "Java" with "Python"
System.out.println(str2);
}
輸出: I love Java I love Java 它又沒用了!也許替換方法不起作用? 讓我們試試別的。例如,substring()
。它根據作為參數傳遞的字符索引返回一個子字符串。讓我們切斷字符串的前 10 個字符:
public static void main(String[] args) {
String str1 = "I love Java";
String str2 = str1;// Both reference variables point to the same string.
System.out.println(str2);
str1.substring(10);// Truncate the original String
System.out.println(str2);
}
輸出: 我愛 Java 我愛 Java 
String
?畢竟,它們可以截斷字符串、更改字符等等。如果什麼都沒發生,那又有什麼意義呢?他們居然可以做這些事!但是,他們每次都返回一個新的字符串。寫的沒意義
str1.replace("Java", "Python");
因為您無法更改原始對象。但是,如果將方法的結果寫入新的引用變量,您會立即看到不同之處!
public static void main(String[] args) {
String str1 = "I love Java";
String str2 = str1;// Both reference variables point to the same string.
System.out.println(str2);
String str1AfterReplacement = str1.replace("Java", "Python");
System.out.println(str2);
System.out.println(str1AfterReplacement);
}
所有String
方法都以這種方式工作。無法對該"I love Java"
對象進行任何操作。您可以創建一個新對象並寫入:“<new object> = 操作的結果"I love Java" object "
。還有哪些類型是不可變的?您肯定需要立即記住的一些是原始類型的所有包裝類。 Integer
,Byte
, Character
, Short
, Boolean
, Long
, Double
, Float
:所有這些類都創建immutable
對象(我們將在接下來的課程中討論它們)。這包括用於創建大數字的類,例如BigInteger
和BigDecimal
。我們最近介紹了異常並觸及了堆棧跟踪。好吧,猜猜是什麼,java.lang.StackTraceElement對像也是不可變的。這是有道理的:如果有人可以更改我們堆棧的數據,那麼整個事情就毫無意義了。想像一下有人通過堆棧跟踪並將OutOfMemoryError更改為FileNotFoundException。然後你使用那個堆棧來找出錯誤的原因。但是該程序甚至不使用文件。:) 所以,他們讓這些對像不可變,以防萬一。好的,所以它或多或少對StackTraceElement有意義。但是,為什麼有人需要讓字符串不可變呢?為什麼改變他們的價值觀會成為一個問題?它可能會更方便。:/ 有幾個原因。首先,它節省內存。不可變字符串可以放在字符串池中,允許重複使用字符串而不是創建新字符串。第二,為了安全。例如,幾乎每個程序中的用戶名和密碼都是字符串。使更改它們成為可能可能會導致授權問題。還有其他原因,但我們對 Java 的學習還沒有涉及到它們,所以我們稍後再回過頭來。
更多閱讀: |
---|
GO TO FULL VERSION