1. finalキーワードの基本的な使用法
Javaでは、finalキーワードを使用して、クラス、メソッド、および変数(メンバー変数およびローカル変数を含む)を修飾できます。これら3つの側面からfinalキーワードの基本的な使用法を見てみましょう。
1.変更されたクラス
クラスがfinalで変更されている場合、このクラスは継承できないことを示しています。つまり、クラスを継承させない場合は、finalを使用してクラスを変更できます。最終クラスのメンバー変数は必要に応じてfinalに設定できますが、最終クラスのすべてのメンバーメソッドは暗黙的に最終メソッドとして指定されることに注意してください。
finalを使用してクラスを変更するときは、慎重に選択する必要があります。このクラスが将来の継承または安全上の理由で使用されない限り、クラスをfinalクラスとして設計しないでください。
2.修正方法
次の文章は、「Javaプログラミング思想」の第4版の143ページからの抜粋です。
「最後のメソッドを使用する理由は2つあります。最初の理由は、継承されたクラスがその意味を変更した場合にメソッドをロックすることです。2番目の理由は効率です。以前のバージョンのJava実装では、最後のメソッドはインライン呼び出し。ただし、メソッドが大きすぎると、インライン呼び出しによるパフォーマンスの向上が見られない場合があります。Javaの最近のバージョンでは、これらの最適化のためにfinalメソッドを使用する必要はありません。 "(一般的な噂);
よくある誤解は、メソッドを宣言 final
すると、コンパイラーがメソッドを呼び出す場所に直接メソッドを挿入できるため、効率が向上するということです(インライン展開を参照 )。メソッドは実行時に読み込まれるため 、コンパイラはこれを実行できません。ランタイム環境とJIT コンパイラーだけが、 どのクラスがロードされたかを正確に認識しているため、メソッドがfinalかどうかにかかわらず、いつインライン化するかを決定することができます。
したがって、サブクラスでメソッドがオーバーライドされるのを明示的に禁止する場合にのみ、メソッドをfinalに設定してください。
注:クラスのプライベートメソッドは、暗黙的に最終メソッドとして指定されます。
3.変更された変数
変数の装飾はfinalが最もよく使用される場所であり、この記事の焦点でもあります。最初に、最終的な変数の基本的な構文を理解します。
最終的な変数の場合、それが基本データ型の変数の場合、一度初期化されるとその値は変更できません。参照型の変数の場合、初期化後に別のオブジェクトを指すことはできません。
たとえば、次のとおりです。
上記のコードでは、変数iとobjの再割り当ては間違っています。
2.最終的なキーワードの詳細な理解
最終的なキーワードの基本的な使用法を理解した後、このセクションでは、最終的なキーワードが混同されやすい場所を見ていきます。
1.クラスの最終変数と通常の変数の違いは何ですか?
finalを使用してクラスのメンバー変数を操作する場合、メンバー変数(クラスのメンバー変数であることに注意してください。ローカル変数は、使用前に初期化および割り当てが保証されていれば十分です)は、定義またはコンストラクターで初期化および割り当てを行う必要があり、最終変数いったん初期化されて割り当てられると、それを割り当てることはできなくなります。
では、最終変数と通常の変数の違いは何ですか?次に例を示します。
public class Test { public static void main(String [] args){ String a = "hello2"; final String b = "hello"; 文字列d = "hello"; 文字列c = b + 2; 文字列e = d + 2; System.out.println((a == c)); System.out.println((a == e)); } }
真 偽
この質問の出力を最初に考えることができます。最初の比較結果が真であり、2番目の比較結果が簡単なのはなぜですか。これが最終変数と通常の変数の違いです。最終変数が基本データ型と文字列型の場合、コンパイル時に正確な値がわかっていれば、コンパイラはそれをコンパイル時定数として使用します。つまり、最後の変数が使用される場合、これは直接アクセスされる定数と同等であり、実行時に決定する必要はありません。これはCのマクロ置換に似ています。したがって、上記のコードでは、変数bは最終的に変更されるため、コンパイラー定数として使用され、bが使用される場合、変数bはその値に直接置き換えられます。ただし、変数dへのアクセスは、実行時にリンクを介して実行する必要があります。おそらく、その違いは誰でも理解できるはずですが、コンパイラは、コンパイル中に最終的な変数値が正確にわかる場合にのみ、このような最適化を実行することに注意してください。たとえば、次のコードは最適化されません。
public class Test { public static void main(String [] args){ String a = "hello2"; final String b = getHello(); 文字列c = b + 2; System.out.println((a == c)); } public static String getHello(){ return "hello"; } }
このコードの出力はfalseです。
2.参照変数が指すオブジェクトの内容は、最終変数によって変更されていますか?
上記のように、finalによって変更された参照変数が他のオブジェクトを指すことができなくなると、初期化されて割り当てられた後、参照変数が指すオブジェクトの内容は変化しますか?この例を見てください:
public class Test { public static void main(String [] args){ final MyClass myClass = new MyClass(); System.out.println(++ myClass.i); } } クラスMyClass { public int i = 0; }
このコードは正常にコンパイルでき、出力結果があります。出力結果は1です。これは、参照変数が最終的に変更された後、他のオブジェクトを指すことはできなくても、参照するオブジェクトのコンテンツが変数であることを示しています。
3.finalおよび静的
多くの場合、staticキーワードとfinalキーワードを混同するのは簡単です。staticはメンバー変数に作用して、1つのコピーのみが保存されることを示し、finalの役割は変数が不変であることを確認することです。この例を見てください:
パブリッククラステスト{ public static void main(String [] args){ MyClass myClass1 = new MyClass(); MyClass myClass2 = new MyClass(); System.out.println(myClass1.i); System.out.println(myClass1.j); System.out.println(myClass2.i); System.out.println(myClass2.j); } } クラスMyClass { public final double i = Math.random(); public static double j = Math.random(); }
このコードを実行すると、毎回出力される2つのj値は同じですが、iの値は異なります。ここから、最終変数と静的変数の違いを知ることができます。
4.外部ローカル変数が匿名の内部クラスで使用されるのはなぜ最終的な変数だけなのですか?
この問題に関する前回のブログ投稿の「Java内部クラスの詳細な説明」の説明を参照してください。ここではそれらを繰り返しません。
5.最終的なパラメータに関する質問
インターネット上で流通しているものに関して、メソッドのパラメーターとしてオブジェクト変数を変更する必要がない場合は、finalを明示的に使用して宣言します。これにより、呼び出しメソッドの外部の変数を意図せずに変更して影響を与えることがなくなります。私はこのステートメントを個人的に理解しています。不適切です。
パラメータが基本データ型の変数であるか参照型の変数であるかに関係なく、最後のステートメントでは上記の効果が得られません。
それを明確にするためにこの例を見てください:
上記のコードは、最後の変更後、メソッドで変数iの値を変更できないことを人々に感じさせるようです。誰もが知っているように、Javaパラメータの転送では値の転送が使用されるため、changeValueメソッドとmainメソッドの変数iは変数ではありません。基本型の変数の場合、変数を直接コピーするのと同じです。したがって、最終的な変更がない場合でも、メソッド内の変数iの値を変更しても、メソッド外のiには影響しません。
次のコードを見てください。
パブリッククラステスト{ public static void main(String [] args){ MyClass myClass = new MyClass(); StringBuffer buffer = new StringBuffer( "hello"); myClass.changeValue(buffer); System.out.println(buffer.toString()); } } クラスMyClass { void changeValue(final StringBuffer buffer){ buffer.append( "world"); } }
このコードを実行すると、出力がhelloworldであることがわかります。明らかに、finalで装飾しても、changeValueのバッファが指すオブジェクトの内容を変更できます。一部の人々は、ファイナルが削除された場合、バッファーがchangeValueの他のオブジェクトを指す場合はどうなると言います。この種の考え方を持つ友人は、自分でコードを書いて、結果を試してみることができます。ファイナルが削除され、バッファがchangeValueの他のオブジェクトを指す場合、メインメソッドのバッファには影響しません。値の受け渡しが使用されます。参照変数の場合、参照値が渡されます。つまり、実パラメーターと仮パラメーターが同時に同じオブジェクトを指すようにすると、仮パラメーターが別のオブジェクトを指すようにしても、実パラメーターには影響がありません。
したがって、私はインターネット上で流通する最終的なパラメータに関する声明に個人的に同意しません。