CodeGym /Java Blog /ランダム /Java の固定値: Final、定数、不変
John Squirrels
レベル 41
San Francisco

Java の固定値: Final、定数、不変

ランダム グループに公開済み
やあ!「修飾子」という言葉はすでにご存知でしょう。少なくとも、アクセス修飾子 (パブリック、プライベート) と静的修飾子は説明済みです。今日は、 finalと呼ばれる特別な修飾子について説明します。最後の修飾子は、一定、明確、不変の動作が必要なプログラムの部分を「固める」と言えるでしょう。プログラム内でそれを使用できる場所は、クラス、メソッド、変数の 3 つです。 Java の固定値: Final、定数​​、不変 - 2 順番に見ていきましょう。Final修飾子がクラス宣言で使用されている場合、そのクラスは継承できないことを意味します。前のレッスンでは、単純な継承の例を使用しました。Animal親クラスと 2 つの子クラスがありました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
}
ただし、クラスでFinal修飾子 を使用するとAnimalCatおよびDogクラスはそれを継承できません。

public final class Animal {

}

public class Cat extends Animal {

   // Error! Cannot inherit from final Animal
}
コンパイラはすぐにエラーを生成します。Java では、多くの最終クラスがすでに実装されています。よく使うものの中では、Stringが最もよく知られています。さらに、クラスがFinalとして宣言されている場合、そのクラスのメソッドもすべてFinalになります。どういう意味ですか?Final修飾子を使用してメソッドが宣言されている場合、そのメソッドをオーバーライドすることはできません。たとえば、ここにはメソッドAnimalを宣言するクラスがありますspeak()。しかし、犬と猫では確かに「話す」方法が異なります。Catしたがって、とクラスの両方で speech() メソッドを宣言しますDogが、それらを別の方法で実装します。

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 には、定数に対して異なる命名規則があります。通常のキャメルケース表記ではありません。それが通常の変数であれば、それを定数と呼ぶでしょう例。ただし、定数の名前はすべて大文字で書かれ、単語の間にアンダースコアが入ります (複数の単語がある場合) (例: "CONSTANT_EXAMPLE")。なぜ定数が必要なのでしょうか? たとえば、プログラム内で定期的に使用する固定値がある場合に非常に便利です。たとえば、あなたは歴史を作り、ゲーム「The Witcher 4」を自分で書くことに決めました。ゲームでは明らかに主人公の名前「リヴィアのゲラルト」が定期的に使用されます。この文字列 (および他のヒーローの名前) は定数として宣言するのが最適です。その値は 1 か所に保存されるため、100 万回入力しても間違いなくタイプミスをすることはありません。

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");
   }
}
出力: ウィッチャー 4 これはすでに 4 番目のウィッチャー ゲームですが、リヴィアのゲラルトはまだ誰がより好きかを決めることができません: ヴェンガーバーグのイェネファーとトリス メリゴールド ただし、これまでにウィッチャーをプレイしたことがない場合は、始まり。主人公の名前はリヴィアのゲラルト リヴィアのゲラルトはウィッチャー、モンスターハンターです 英雄の名前を定数として宣言しました。これで、間違いなくタイプミスをすることはなくなり、毎回手書きで記入する必要もなくなりました。もう 1 つの利点は、プログラム全体で変数の値を変更する必要がある場合、コード ベース全体で手動で変更するのではなく、1 か所で変更できることです。:)

不変型

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);
}
出力: Java が大好きです Java が大好きです また動作しませんでした! もしかしたらreplaceメソッドが機能しないのでしょうか? 別のことを試してみましょう。たとえば、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、定数​​、不変 - 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"。新しいオブジェクトを作成して、次のように記述するだけです。「<新しいオブジェクト> = を操作した結果"I love Java" object "。他に不変の型は何ですか? すぐに覚えておく必要がある型は、プリミティブ型のすべてのラッパー クラスです IntegerByte, Character, Short, Boolean, Long, Double, Float: これらのクラスはすべてimmutableオブジェクトを作成します (今後のレッスンで説明します)。これには、 や など、大きな数を作成するために使用されるクラスが含まれます。最近では例外について説明し、BigIntegerスタックトレースBigDecimalについても触れました。そうですね、java.lang.StackTraceElementオブジェクトも不変です。これは当然のことです。誰かがスタックのデータを変更できたら、すべてが無意味になってしまいます。誰かがスタック トレースを調べてOutOfMemoryError をFileNotFoundExceptionに変更するところを想像してください。そして、そのスタックを使用してエラーの原因を見つけます。ただし、プログラムはファイルさえ使用しません。:) そこで、念のため、これらのオブジェクトを不変にしました。さて、これはStackTraceElementについては多かれ少なかれ意味があります。しかし、なぜ String を不変にする必要があるのでしょうか? 彼らの価値観を変えることがなぜ問題となるのでしょうか? おそらくさらに便利になるでしょう。:/ これにはいくつかの理由があります。まず、メモリが節約されます。不変の文字列を文字列プールに配置できる、新しい文字列を作成する代わりに文字列を再利用できるようになります。2番目に、セキュリティのためです。たとえば、ほとんどすべてのプログラムでは、ユーザー名とパスワードは文字列です。これらを変更できるようにすると、認証の問題が発生する可能性があります。他にも理由はありますが、Java の研究ではまだ取り上げていないため、後ほど説明します。
コメント
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION