529

Javaではfinal、変数とともにキーワードを使用して、値を変更しないように指定します。しかし、クラスのコンストラクター/メソッドで値を変更できることがわかりました。繰り返しますが、変数がの場合、staticそれはコンパイルエラーです。

コードは次のとおりです。

import java.util.ArrayList;
import java.util.List;

class Test {
  private final List foo;

  public Test()
  {
      foo = new ArrayList();
      foo.add("foo"); // Modification-1
  }
  public static void main(String[] args) 
  {
      Test t = new Test();
      t.foo.add("bar"); // Modification-2
      System.out.println("print - " + t.foo);
  }
}

上記のコードは正常に機能し、エラーはありません。

次に、変数を次のように変更しますstatic

private static final List foo;

これでコンパイルエラーになります。finalこれは実際にどのように機能しますか?

4

19 に答える 19

618

これはお気に入りのインタビューの質問です。この質問を使用して、インタビュアーは、コンストラクター、メソッド、クラス変数(静的変数)、およびインスタンス変数に関するオブジェクトの動作をどの程度理解しているかを調べようとします。

import java.util.ArrayList;
import java.util.List;

class Test {
    private final List foo;

    public Test() {
        foo = new ArrayList();
        foo.add("foo"); // Modification-1
    }

    public void setFoo(List foo) {
       //this.foo = foo; Results in compile time error.
    }
}

上記の場合、「Test」のコンストラクターを定義し、「setFoo」メソッドを指定しました。

コンストラクターについて:コンストラクターは、キーワードを使用して、オブジェクトの作成ごとに1回 だけ呼び出すことができます。newコンストラクターはそうするように設計されていないため、コンストラクターを複数回呼び出すことはできません。

メソッドについて:メソッドは何度でも呼び出すことができ(決して呼び出さない場合でも)、コンパイラーはそれを認識します。

シナリオ1

private final List foo;  // 1

fooインスタンス変数です。クラスオブジェクトを作成するとTest、インスタンス変数fooがクラスのオブジェクト内にコピーされますTest。コンストラクター内で割り当てる場合foo、コンパイラーはコンストラクターが1回だけ呼び出されることを認識しているため、コンストラクター内での割り当てに問題はありません。

メソッド内で割り当てる場合foo、コンパイラーは、メソッドが複数回呼び出される可能性があることを認識しています。つまり、値を複数回変更する必要があります。これは、final変数では許可されていません。したがって、コンパイラーはコンストラクターが適切であると判断します。最終変数に値を割り当てることができるのは1回だけです。

シナリオ2

private static final List foo = new ArrayList();

foo静的変数になりました。Testクラスのインスタンスを作成すると、は静的でfooあるため、オブジェクトにコピーされません。fooNowfooは、各オブジェクトの独立したプロパティではありません。これはTestクラスのプロパティです。ただし、複数のオブジェクトで確認でき、キーワードをfoo使用して作成されたすべてのオブジェクトが、複数のオブジェクトの作成時に値を変更するコンストラクターを最終的に呼び出す場合(すべてのオブジェクトでコピーされるわけではなく、複数のオブジェクト間で共有されることを忘れないでください) 。)newTeststatic foo

シナリオ3

t.foo.add("bar"); // Modification-2

上記Modification-2はあなたの質問からです。上記の場合、最初に参照されるオブジェクトを変更するのではなく、foo許可されているコンテンツを内部に追加します。参照変数new ArrayList()にを割り当てようとすると、コンパイラは文句を言います。ルール変数を初期化した場合、別のオブジェクトを参照するように変数を変更することはできません。(この場合)foo
finalArrayList

最終クラスをサブクラス化することはできません。
最終メソッドをオーバーライドすることはできません。(このメソッドはスーパークラスにあります)
finalメソッドはオーバーライドできます。(これを文法的に読んでください。このメソッドはサブクラスにあります)

于 2013-03-27T10:08:12.330 に答える
573

変数はいつでも初期化できます。finalコンパイラーは、それを1回だけ実行できることを確認します。

変数に格納されているオブジェクトのメソッドの呼び出しfinalは、のセマンティクスとは関係がないことに注意してくださいfinal。言い換えると、:finalは参照自体に関するものであり、参照されるオブジェクトの内容に関するものではありません。

Javaにはオブジェクトの不変性の概念がありません。これは、オブジェクトを注意深く設計することによって実現され、些細なことではありません。

于 2013-03-27T09:03:50.473 に答える
233

finalキーワードには、さまざまな使用方法があります。

  • 最終クラスをサブクラス化することはできません。
  • 最終的なメソッドをサブクラスでオーバーライドすることはできません
  • 最終変数は一度だけ初期化できます

その他の使用法:

  • 匿名の内部クラスがメソッドの本体内で定義されている場合、そのメソッドのスコープでfinalと宣言されたすべての変数は、内部クラス内からアクセスできます。

静的クラス変数はJVMの最初から存在し、クラスで初期化する必要があります。これを行うと、エラーメッセージは表示されません。

于 2013-03-27T09:09:57.367 に答える
66

キーワードは、final使用目的に応じて2つの異なる方法で解釈できます。

値のタイプ:int s、 sなどの場合double、値が変更されないようにします。

参照タイプ:オブジェクトへの参照の場合final参照が変更されないようにします。つまり、常に同じオブジェクトを参照します。参照されているオブジェクト内の値が同じであるという保証はありません。

そのため、final List<Whatever> foo;foo常に同じリストを参照しますが、そのリストの内容は時間の経過とともに変更される可能性があります。

于 2013-03-27T09:12:38.107 に答える
24

foo静的にする場合は、次の例のように、クラスコンストラクター(または定義する場所でインライン)で初期化する必要があります。

クラスコンストラクター(インスタンスではない):

private static final List foo;

static
{
   foo = new ArrayList();
}

列をなして:

private static final List foo = new ArrayList();

ここでの問題は、修飾子がどのように機能するかではなく、final修飾子がどのように機能するかstaticです。

修飾子はfinal、コンストラクターの呼び出しが完了するまでに参照の初期化を強制します(つまり、コンストラクターで初期化する必要があります)。

属性をインラインで初期化すると、コンストラクターに定義したコードが実行される前に属性が初期化されるため、次の結果が得られます。

  • の場合、クラスに定義したコンストラクターfooが実行される前に実行されますstaticfoo = new ArrayList()static{}
  • そうfooでない場合はstaticfoo = new ArrayList()コンストラクターが実行される前に実行されます

属性をインラインで初期化しない場合、final修飾子は、属性を初期化すること、およびコンストラクターで初期化する必要があることを強制します。修飾子もある場合static、属性を初期化する必要があるコンストラクターは、クラスの初期化ブロックです:static{}

static{}コードで発生するエラーは、クラスがロードされたときに、そのクラスのオブジェクトをインスタンス化する前に実行されるという事実に起因します。fooしたがって、クラスの作成時に初期化されていません。

static{}ブロックは、タイプのオブジェクトのコンストラクターと考えてくださいClass。ここで、クラス属性の初期化を行う必要がありstatic finalます(インラインで行わない場合)。

サイドノート:

修飾子はfinal、プリミティブ型と参照に対してのみconst-nessを保証します。

finalオブジェクトを宣言すると、そのオブジェクトへのfinal 参照が得られますが、オブジェクト自体は一定ではありません。

属性を宣言するときに実際に達成しているfinalことは、特定の目的(宣言したfinal Listものなど)でオブジェクトを宣言すると、そのオブジェクトのみがその目的で使用されるということですList foo。別のものですが、アイテムを追加/削除することでList変更できます(使用しているものは同じですが、内容が変更されているだけです)。ListList

于 2013-03-27T09:06:28.713 に答える
12

これは非常に良い面接の質問です。最終的なオブジェクトと不変のオブジェクトの違いは何かと尋ねられることもあります。

1)誰かが最終オブジェクトに言及する場合、それは参照を変更できないことを意味しますが、その状態(インスタンス変数)は変更できます。

2)不変オブジェクトとは、状態を変更できないが、参照を変更できるオブジェクトです。元:

    String x = new String("abc"); 
    x = "BCG";

ref変数xは、別の文字列を指すように変更できますが、「abc」の値は変更できません。

3)インスタンス変数(非静的フィールド)は、コンストラクターが呼び出されたときに初期化されます。したがって、コンストラクター内で変数の値を初期化できます。

4)「しかし、クラスのコンストラクター/メソッドで値を変更できることがわかりました」。--メソッド内で変更することはできません。

5)静的変数はクラスのロード中に初期化されます。したがって、コンストラクター内で初期化することはできません。コンストラクターの前でも初期化する必要があります。したがって、宣言自体の間に静的変数に値を割り当てる必要があります。

于 2014-09-24T20:34:16.993 に答える
11

javaのfinalキーワードは、ユーザーを制限するために使用されます。javafinalキーワードは、多くのコンテキストで使用できます。ファイナルは次のようになります。

  1. 変数
  2. 方法
  3. クラス

finalキーワードは変数とともに適用できます。値のない変数finalは、空白final変数または初期化されていないfinal変数と呼ばれます。コンストラクターでのみ初期化できます。空白のfinal変数は、ブロックでのみstatic初期化される変数にすることもできます。static

Javaの最終変数:

変数をとして作成した場合、変数の値を変更することはできませんfinal(定数になります)。final

final変数の例

最終変数の速度制限があります。この変数の値を変更しますが、値が割り当てられた最終変数は変更できないため、変更できません。

class Bike9{  
    final int speedlimit=90;//final variable  
    void run(){  
        speedlimit=400;  // this will make error
    }  

    public static void main(String args[]){  
    Bike9 obj=new  Bike9();  
    obj.run();  
    }  
}//end of class  

Java最終クラス:

としてクラスを作成した場合final、それを拡張することはできません

最終クラスの例

final class Bike{}  

class Honda1 extends Bike{    //cannot inherit from final Bike,this will make error
  void run(){
      System.out.println("running safely with 100kmph");
   }  

  public static void main(String args[]){  
      Honda1 honda= new Honda();  
      honda.run();  
      }  
  }  

Javaのfinalメソッド:

いずれかのメソッドをfinalとして作成した場合、それをオーバーライドすることはできません

メソッドの例final(Hondaのrun()はBikeのrun()をオーバーライドできません)

class Bike{  
  final void run(){System.out.println("running");}  
}  

class Honda extends Bike{  
   void run(){System.out.println("running safely with 100kmph");}  

   public static void main(String args[]){  
   Honda honda= new Honda();  
   honda.run();  
   }  
}  

共有元: http ://www.javatpoint.com/final-keyword

于 2017-01-03T04:45:18.923 に答える
10

いくつかの簡単な定義に言及する価値があります:

クラス/メソッド

finalメソッドをサブクラスでオーバーライドできないことを示すために、クラスメソッドの一部またはすべてをとして宣言できます。

変数

final変数が初期化されると、常に同じ値が含まれます。

final基本的に、場合によっては、何か(サブクラス、変数「再割り当て」)による上書き/上書きを避けます。

于 2016-06-29T16:05:11.723 に答える
7

"A final variable can only be assigned once"

*Reflection*- 「すごい待って、ビールを持って」


フィールドのフリーズは、次のfinal2つのシナリオで発生します。

  • コンストラクターの終わり。
  • リフレクションがフィールドの値を設定するとき。(何度でも

法を破ろう

public class HoldMyBeer 
{
    final int notSoFinal;
    
    public HoldMyBeer()
    {
       notSoFinal = 1;
    }

    static void holdIt(HoldMyBeer beer, int yetAnotherFinalValue) throws Exception
    {
       Class<HoldMyBeer> cl = HoldMyBeer.class;
       Field field = cl.getDeclaredField("notSoFinal");
       field.setAccessible(true);
       field.set(beer, yetAnotherFinalValue);
    }

    public static void main(String[] args) throws Exception 
    {
       HoldMyBeer beer = new HoldMyBeer();
       System.out.println(beer.notSoFinal);
       holdIt(beer, 50);
       System.out.println(beer.notSoFinal);
       holdIt(beer, 100);
       System.out.println(beer.notSoFinal);
       holdIt(beer, 666);
       System.out.println(beer.notSoFinal);
       holdIt(beer, 8888);
       System.out.println(beer.notSoFinal);
    }    
}

出力:

1
50
100
666
8888

final」フィールドには、5つの異なる 「final」値が割り当てられています(引用符に注意してください)。そして、それは何度も何度も異なる値を割り当てられ続ける可能性があります...

なんで?リフレクションはチャックノリスに似ているため、初期化された最終フィールドの値を変更したい場合は変更します。彼自身が新しい値をスタックにプッシュする人であると言う人もいます:

Code:
   7: astore_1
  11: aload_1
  12: getfield                
  18: aload_1
  19: bipush        50        //wait what
  27: aload_1
  28: getfield                
  34: aload_1
  35: bipush        100       //come on...
  43: aload_1
  44: getfield                
  50: aload_1
  51: sipush        666      //...you were supposed to be final...
  60: aload_1
  61: getfield                
  67: aload_1
  68: sipush        8888     //ok i'm out whatever dude
  77: aload_1
  78: getfield                
于 2021-01-30T20:04:02.960 に答える
4

finalは、ユーザーを制限するためにJavaで予約されているキーワードであり、メンバー変数、メソッド、クラス、およびローカル変数に適用できます。最終変数はstatic、Javaでキーワードを使用して宣言されることが多く、定数として扱われます。例えば:

public static final String hello = "Hello";

変数宣言でキーワードを使用する場合、finalその変数内に格納されている値は後で変更できません。

例えば:

public class ClassDemo {
  private final int var1 = 3;
  public ClassDemo() {
    ...
  }
}

:finalとして宣言されたクラスは、拡張または継承できません(つまり、スーパークラスのサブクラスは存在できません)。また、finalとして宣言されたメソッドは、サブクラスによってオーバーライドできないことに注意してください。

このスレッドでは、finalキーワードを使用する利点について説明します。

于 2014-02-17T02:05:35.797 に答える
2

赤と白の2つの貯金箱があるとします。これらの貯金箱には2人の子供のみを割り当て、ボックスを交換することはできません。つまり、赤または白の貯金箱(最終)があり、箱を変更することはできませんが、箱にお金を入れることはできます。誰も気にしません(変更-2)。

于 2016-01-27T20:04:40.823 に答える
2

すべての答えを読んでください。

finalキーワードをメソッド引数で使用できる別のユーザーケースがあります。

public void showCaseFinalArgumentVariable(final int someFinalInt){

   someFinalInt = 9; // won't compile as the argument is final

}

変更してはならない変数に使用できます。

于 2017-06-27T23:48:06.357 に答える
1
  1. 最終変数は非静的であるため、コンストラクターで初期化できます。ただし、静的にすると、コンストラクターで初期化できなくなります(コンストラクターは静的ではないため)。
  2. リストへの追加は、リストを最終にすることによって停止することは期待されていません。final参照を特定のオブジェクトにバインドするだけです。そのオブジェクトの「状態」は自由に変更できますが、オブジェクト自体は変更できません。
于 2013-03-27T09:03:55.613 に答える
1

静的ファイナルにする場合は、静的初期化ブロックで初期化する必要があります

    private static final List foo;

    static {
        foo = new ArrayList();
    }

    public Test()
    {
//      foo = new ArrayList();
        foo.add("foo"); // Modification-1
    }
于 2013-03-27T09:08:24.223 に答える
1

このfinalキーワードは、変数を初期化できるのは1回だけであることを示しています。コードでは、finalの初期化を1回だけ実行しているため、条件が満たされます。このステートメントは、の単独の初期化を実行しますfoo。!=不変であることに注意してくださいfinal。これは、参照が変更できないことを意味するだけです。

foo = new ArrayList();

クラスのロード時に変数を初期化する必要があり、インスタンス化(コンストラクターの呼び出し)に依存して初期化することはできないため、静的フィールドはクラスのインスタンスなしで使用可能である必要があるため、宣言する場合foo。静的フィールドを使用する前にコンストラクターが呼び出されるという保証はありません。static finalfoo

static finalシナリオでメソッドを実行すると、この時点でTestインスタンス化する前にクラスがロードされ、初期化されていないことを意味するインスタンス化がないため、すべてのオブジェクトのデフォルトである。に設定されます。この時点で、リストにアイテムを追加しようとすると、コードがaをスローすると思います。tfoofoonullNullPointerException

于 2013-03-27T09:09:30.237 に答える
1

まず、fooを初期化する(つまり、初めて割り当てる)コード内の場所は次のとおりです。

foo = new ArrayList();

fooはオブジェクト(List型)であるため、型(intなど)ではなく参照型です。そのため、リスト要素が保存されているメモリ位置(0xA7D2A834など)への参照を保持します。このような行

foo.add("foo"); // Modification-1

fooの値を変更しないでください(これも、メモリ位置への単なる参照です)。代わりに、参照されるメモリ位置に要素を追加するだけです。最後のキーワードに違反するには、次のようにfooを再割り当てする必要があります。

foo = new ArrayList();

それはあなたにコンパイルエラーを与えるでしょう。


さて、それが邪魔にならないように、 staticキーワードを追加するとどうなるかを考えてみましょう。

staticキーワードがない場合、クラスをインスタンス化する各オブジェクトには、独自のfooのコピーがあります。したがって、コンストラクターはfoo変数の空白の新しいコピーに値を割り当てます。これは完全に問題ありません。

ただし、staticキーワードがある場合、クラスに関連付けられているfooはメモリに1つだけ存在します。2つ以上のオブジェクトを作成する場合、コンストラクターはその1つのfooを毎回再割り当てしようとし、finalキーワードに違反します。

于 2014-07-22T19:02:21.247 に答える
1

以下は、finalが使用されるさまざまなコンテキストです。

最終変数最終変数は一度だけ割り当てることができます。変数が参照である場合、これは、変数を再バインドして別のオブジェクトを参照できないことを意味します。

class Main {
   public static void main(String args[]){
      final int i = 20;
      i = 30; //Compiler Error:cannot assign a value to final variable i twice
   }
}

最終変数には、後で値を割り当てることができます(宣言時に値を割り当てる必要はありません)が、1回だけです。

最終クラス最終クラスを拡張(継承)することはできません

final class Base { }
class Derived extends Base { } //Compiler Error:cannot inherit from final Base

public class Main {
   public static void main(String args[]) {
   }
}

Finalメソッドfinalメソッドは、サブクラスによってオーバーライドできません。

//Error in following program as we are trying to override a final method.
class Base {
  public final void show() {
       System.out.println("Base::show() called");
    }
}     
class Derived extends Base {
    public void show() {  //Compiler Error: show() in Derived cannot override
       System.out.println("Derived::show() called");
    }
}     
public class Main {
    public static void main(String[] args) {
        Base b = new Derived();;
        b.show();
    }
}
于 2016-10-07T06:26:44.330 に答える
1

私はここに更新された詳細な答えを書くことを考えました。

finalキーワードはいくつかの場所で使用できます。

  1. クラス

Afinal classは、他のクラスがその最終クラスを拡張できないことを意味します。Javaランタイム(JRE)は、オブジェクト参照が最終クラス(たとえば、F)のタイプであることを知っている場合、その参照の値はFのタイプのみであることができることを知っています。

元:

F myF;
myF = new F();    //ok
myF = someOther;  //someOther cannot be in type of a child class of F.
                  //because F cannot be extended.

したがって、そのオブジェクトのメソッドを実行する場合、実行時に仮想テーブルを使用してそのメソッドを解決する必要はありません。つまり、実行時のポリモーフィズムは適用できません。したがって、実行時間はそれについて気にしません。つまり、処理時間が節約され、パフォーマンスが向上します。

  1. メソッド

final method任意のクラスのAは、そのクラスを拡張する子クラスがその最終メソッドをオーバーライドできないことを意味します。したがって、このシナリオでの実行時の動作も、クラスについて説明した前の動作とまったく同じです。

  1. フィールド、ローカル変数、メソッドパラメータ

上記のいずれかをとして指定finalした場合は、値が既に確定されているため、値を変更できないことを意味します。

元:

フィールドの場合、ローカルパラメータ

final FinalClass fc = someFC; //need to assign straight away. otherwise compile error.
final FinalClass fc; //compile error, need assignment (initialization inside a constructor Ok, constructor can be called only once)
final FinalClass fc = new FinalClass(); //ok
fc = someOtherFC; //compile error
fc.someMethod(); //no problem
someOtherFC.someMethod(); //no problem

メソッドパラメータの場合

void someMethod(final String s){
    s = someOtherString; //compile error
}

これは単に、final基準値の値を変更できないことを意味します。つまり、初期化は1つだけ許可されます。このシナリオでは、実行時に、JREは値を変更できないことを認識しているため、これらすべての最終化された値(最終参照の)をL1キャッシュにロードします。メインメモリから何度もロードバックする必要がないためです。それ以外の場合は、L2キャッシュにロードされ、メインメモリから時々ロードされます。したがって、これはパフォーマンスの向上でもあります。

したがって、上記の3つのシナリオすべてで、final使用できる場所でキーワードを指定しなかった場合、心配する必要はありません。コンパイラーの最適化によってそれが行われます。コンパイラの最適化が私たちのために行うことは他にもたくさんあります。:)

于 2018-01-14T03:49:16.070 に答える
0

とりわけ正しい。さらに、他の人にクラスからサブクラスを作成させたくない場合は、クラスをfinalとして宣言します。次に、クラスツリー階層のリーフレベルになり、それ以上拡張することはできません。クラスの巨大な階層を避けることは良い習慣です。

于 2013-12-23T02:10:04.280 に答える