CodeGym/Java Blog/Toto sisi/Java 中的固定值:final、constants 和 immutable
John Squirrels
等級 41
San Francisco

Java 中的固定值:final、constants 和 immutable

在 Toto sisi 群組發布
個成員
你好!您已經熟悉“修飾符”一詞。至少,您遇到過訪問修飾符(public、private)和 static 修飾符。今天我們將討論一個名為final的特殊修飾符。您可以說 final 修飾符“鞏固”了我們程序中需要恆定、明確、不變行為的部分。您可以在程序中的三個地方使用它:類、方法和變量。 Java 中的固定值:final、constants 和 immutable - 2 讓我們按順序瀏覽它們。如果類聲明中使用了final修飾符,則意味著該類不能被繼承。在之前的課程中,我們使用了一個簡單的繼承示例:我們有一個Animal父類和兩個子類:CatDog
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修飾符,則類CatDog類不能繼承它。
public final class Animal {

}

public class Cat extends Animal {

   // Error! Cannot inherit from final Animal
}
編譯器立即生成錯誤。在 Java 中,許多最終類已經實現。在您經常使用的那些中,String是最著名的。此外,如果一個類被聲明為final,則該類的所有方法也將變為final。這意味著什麼?如果使用final修飾符聲明方法,則不能重寫該方法。例如,這裡我們有一個Animal聲明speak()方法的類。但是,狗和貓肯定以不同的方式“說話”。因此,我們將在CatDog類中聲明 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!");
   }
}
我們使CatDog類覆蓋了在父類中聲明的方法。現在,動物會說不同的話,這取決於它是什麼類型的物體:
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 Java 中的固定值:final、constants 和 immutable - 3 沒有任何改變。它不應該有。正如我們之前所說,字符串是不可變的。那麼,類中的所有方法是什麼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 "。還有哪些類型是不可變的?您肯定需要立即記住的一些是原始類型的所有包裝類。 IntegerByte, Character, Short, Boolean, Long, Double, Float:所有這些類都創建immutable對象(我們將在接下來的課程中討論它們)。這包括用於創建大數字的類,例如BigIntegerBigDecimal。我們最近介紹了異常並觸及了堆棧跟踪。好吧,猜猜是什麼,java.lang.StackTraceElement對像也是不可變的。這是有道理的:如果有人可以更改我們堆棧的數據,那麼整個事情就毫無意義了。想像一下有人通過堆棧跟踪並將OutOfMemoryError更改為FileNotFoundException。然後你使用那個堆棧來找出錯誤的原因。但是該程序甚至不使用文件。:) 所以,他們讓這些對像不可變,以防萬一。好的,所以它或多或少對StackTraceElement有意義。但是,為什麼有人需要讓字符串不可變呢?為什麼改變他們的價值觀會成為一個問題?它可能會更方便。:/ 有幾個原因。首先,它節省內存。不可變字符串可以放在字符串池中,允許重複使用字符串而不是創建新字符串。第二,為了安全。例如,幾乎每個程序中的用戶名和密碼都是字符串。使更改它們成為可能可能會導致授權問題。還有其他原因,但我們對 Java 的學習還沒有涉及到它們,所以我們稍後再回過頭來。
留言
  • 受歡迎
你必須登入才能留言
此頁面尚無留言