こんにちは!「修飾語」という言葉はすでによく知られています。少なくとも、アクセス修飾子 (パブリック、プライベート) と静的修飾子については説明しました。今日は特別なfinal修飾子について話します。これは、プログラムの中で、継続的、明確、不変の動作が必要な領域を「固める」と言えます。これはプログラムの 3 つの領域 (クラス、メソッド、変数) で使用できます。 一つずつ見ていきましょう。クラス宣言にFinal修飾子が含まれている場合、このクラスから継承できないことを意味します。前の講義では、継承の簡単な例を見てきました。親クラス
Animal
と 2 つの子クラスがありましたCat
。Dog
public class Animal {
}
public class Cat extends Animal {
//..поля и методы класса Cat
}
public class Dog extends Animal {
//..поля и методы класса Dog
}
Animal
ただし、クラスに修飾子を 指定するとfinal
、クラスはそれを継承できなくなりCat
ますDog
。
public final class Animal {
}
public class Cat extends Animal {
//ошибка! Cannot inherit from final Animal
}
コンパイラはすぐにエラーを生成します。Java にはすでに実装されているクラスが多数ありますfinal
。あなたが常に使用しているものの中で最も有名なものは ですString
。さらに、クラスが として宣言されている場合final
、そのすべてのメソッドも になりますfinal
。それはどういう意味ですか?修飾子がメソッドに指定されている場合final
、このメソッドをオーバーライドすることはできません。たとえば、Animal
メソッドを定義するクラスがありますvoice()
。しかし、犬と猫では明らかに「話し方」が異なります。したがって、各クラスでメソッドを作成しますCat
が、その実装方法は異なります。 Dog
voice()
public class Animal {
public void voice() {
System.out.println("Voice!");
}
}
public class Cat extends Animal {
@Override
public void voice() {
System.out.println("Meow!");
}
}
public class Dog extends Animal {
@Override
public void voice() {
System.out.println("Woof!");
}
}
クラスでは、Cat
親Dog
クラスのメソッドをオーバーライドしました。これで、動物はどのクラス オブジェクトであるかに応じて声を出します。
public class Main {
public static void main(String[] args) {
Cat cat = new Cat();
Dog dog = new Dog();
cat.voice();
dog.voice();
}
}
結論: ニャー!横糸! ただし、クラス内のAnimal
メソッドをvoice()
として宣言するとfinal
、他のクラスでそれを再定義することはできなくなります。
public class Animal {
public final void voice() {
System.out.println("Voice!");
}
}
public class Cat extends Animal {
@Override
public void voice() {//ошибка! final-метод не может быть переопределен!
System.out.println("Meow!");
}
}
次に、オブジェクトはvoice()
親クラスで定義されているメソッドを強制的に使用するようになります。
public static void main(String[] args) {
Cat cat = new Cat();
Dog dog = new Dog();
cat.voice();
dog.voice();
}
結論: 声を出して!声! 次に、final
- 変数について説明します。それ以外の場合は、定数と呼ばれます。まず (そして最も重要なことですが)、定数に割り当てられた最初の値は変更できません。それは一度限り、永遠に割り当てられます。
public class Main {
private static final int CONSTANT_EXAMPLE = 333;
public static void main(String[] args) {
CONSTANT_EXAMPLE = 999;//ошибка! Нельзя присвоить новое meaning final-переменной!
}
}
定数をすぐに初期化する必要はありません。これは後で行うことができます。ただし、最初に割り当てられた値は永久に残ります。
public static void main(String[] args) {
final int CONSTANT_EXAMPLE;
CONSTANT_EXAMPLE = 999;//так делать можно
}
次に、変数の名前に注意してください。Java 定数には異なる命名規則があります。これは私たちが慣れ親しんでいるキャメルケースではありません。通常の変数の場合は、constantExample と呼びますが、定数の名前は大文字で書かれ、単語の間に (単語が複数ある場合) アンダースコア「CONSTANT_EXAMPLE」が入ります。なぜ定数が必要なのでしょうか? たとえば、プログラム内で定数値を常に使用する場合に便利です。あなたが歴史に名を残すゲーム「The Witcher 4」を一人で書くことに決めたとしましょう。ゲームでは明らかに、主人公の名前「リヴィアのゲラルト」が常に使用されます。この行と他のヒーローの名前を定数として分離することをお勧めします。必要な値は 1 か所に保存されるため、100 万回入力しても間違いを犯すことはありません。
public class TheWitcher4 {
private static final String GERALT_NAME = "Геральт из Ривии";
private static final String YENNEFER_NAME = "Йеннифэр из Венгерберга";
private static final String TRISS_NAME = "Трисс Меригольд";
public static void main(String[] args) {
System.out.println("Ведьмак 4");
System.out.println("Это уже четвертая часть Ведьмака, а " + GERALT_NAME + " ниHow не определится кто ему" +
" нравится больше: " + YENNEFER_NAME + " or " + TRISS_NAME);
System.out.println("Но если вы никогда не играли в Ведьмака - начнем сначала.");
System.out.println("Главного героя зовут " + GERALT_NAME);
System.out.println(GERALT_NAME + " - ведьмак, охотник на чудовищ");
}
}
結論:
Ведьмак 4
Это уже четвертая часть Ведьмака, а Геральт из Ривии ниHow не определится, кто ему нравится больше: Йеннифэр из Венгерберга or Трисс Меригольд.
Но если вы никогда не играли в Ведьмака — начнем сначала.
Главного героя зовут Геральт из Ривии
Геральт из Ривии — ведьмак, охотник на чудовищ
キャラクターの名前を定数に分けたので、スペルミスは絶対になくなり、毎回手書きする必要もなくなりました。もう 1 つの利点: 最終的にプログラム全体で変数の値を変更する必要がある場合、コード全体を手動でやり直すのではなく、1 か所で変更するだけで十分です:)
不変型
Java で作業している間、おそらくプログラマがすべてのオブジェクトの状態をほぼ完全に制御しているという事実にすでに慣れているでしょう。募集 - オブジェクトを作成しましたCat
。気が向いたら名前を変更しました。彼が望めば、年齢などを変えることもできた。しかし、Java には特別な状態を持つデータ型がいくつかあります。それらはimmutable、またはImmutableです。これは、クラスが不変の場合、そのオブジェクトの状態は変更できないことを意味します。例は?驚かれるかもしれませんが、 Immutable クラスの最も有名な例は ですString
。文字列の値は変更できないように思えますか? やってみよう:
public static void main(String[] args) {
String str1 = "I love Java";
String str2 = str1;//обе переменные-ссылки указывают на одну строку.
System.out.println(str2);
str1 = "I love Python";//но поведение str1 ниHow не влияет на str2
System.out.println(str2);//str2 продолжает указывать на строку "I love Java", хотя str1 уже указывает на другой an 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;//обе переменные-ссылки указывают на одну строку.
System.out.println(str2);
str1.replace("Java", "Python");//попробуем изменить состояние str1, заменив слово "Java" на “Python”
System.out.println(str2);
}
結論: Java が大好きです。Java が大好きです。 またうまくいきませんでした。おそらく曲線法は機能しないでしょうか?別のものを試してみましょう。例えば、substring()
。送信された文字の数に基づいて文字列をトリミングします。最初の 10 文字をトリミングしましょう。
public static void main(String[] args) {
String str1 = "I love Java";
String str2 = str1;//обе переменные-ссылки указывают на одну строку.
System.out.println(str2);
str1.substring(10);//обрезаем исходную строку
System.out.println(str2);
}
結論: Java が大好きです。 Java が大好きです。 何も変わりません。そして、そうあるべきではありませんでした。すでに述べたように、オブジェクトはString
不変です。では、これらすべてのクラスメソッドとは何でしょうかString
? 行をトリミングしたり、その中の文字を変更したりできます。何も起こらないのに、なぜそれらが必要なのでしょうか? できるのです!ただし、毎回新しい文字列オブジェクトが返されます。次のように書いても無駄です。
str1.replace("Java", "Python");
- 元のオブジェクトは変更しません。しかし、メソッドの結果を新しい参照変数に書き込むと、違いがすぐにわかります。
public static void main(String[] args) {
String str1 = "I love Java";
String str2 = str1;//обе переменные-ссылки указывают на одну строку.
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」オブジェクトを使用したいくつかの操作の結果。」 他にどのような型が不変ですか? ここで絶対に覚えておく必要があることから、プリミティブ型のすべてのラッパー クラスは不変です。 Integer
、、、、、、、、、-これらすべてのクラスはImmutableオブジェクトを作成Byte
します。これには、大きな数値を作成するために使用されるクラス (および) も含まれます。最近、例外を検討し、 について触れました。したがって、java.lang.StackTraceElementクラスのオブジェクトも不変です。これは論理的です。誰かがスタック上のデータを変更できた場合、そのデータに対するすべての作業が無効になる可能性があります。誰かがStackTraceに入り、OutOfMemoryError をFileNotFoundExceptionに変更したと想像してください。そして、このスタックを操作してエラーの原因を探す必要があります。そして、プログラムはファイルをまったく使用しません :) したがって、安全を期すために、これらのオブジェクトは不変にされました。まあ、StackTraceElementを使えば、それは多かれ少なかれ明らかです。なぜ文字列を不変にしたいと思うのでしょうか? 価値観を変えることができたら何が問題になるでしょうか。おそらくさらに便利になるでしょう :/ これにはいくつかの理由があります。まずはメモリの節約です。不変の文字列を配置することができ、新しい文字列を作成する代わりに毎回同じ文字列を使用できます。第二に、安全性です。たとえば、プログラム内のほとんどのログインとパスワードは文字列です。これらを変更すると、認証に関して問題が発生する可能性があります。他にも理由はありますが、Java の学習ではまだその理由に到達していません。後ほど説明します。 Character
Short
Boolean
Long
Double
Float
BigInteger
BigDecimal
StackTrace
String Pool
GO TO FULL VERSION