Verbessert die Verwendung des endgültigen Schlüsselworts in Java die Leistung?

346

In Java sehen wir viele Stellen, an denen das finalSchlüsselwort verwendet werden kann, aber seine Verwendung ist ungewöhnlich.

Zum Beispiel:

String str = "abc";
System.out.println(str);

Im obigen Fall strkann sein, finalaber dies wird üblicherweise weggelassen.

Wenn eine Methode niemals überschrieben wird, können wir das Schlüsselwort final verwenden. Ähnliches gilt für eine Klasse, die nicht vererbt wird.

Verbessert die Verwendung des endgültigen Schlüsselworts in einem oder allen dieser Fälle die Leistung wirklich? Wenn ja, wie dann? Bitte erkläre. finalWelche Gewohnheiten sollte ein Java-Programmierer entwickeln, um das Schlüsselwort optimal zu nutzen, wenn die ordnungsgemäße Verwendung wirklich für die Leistung von Bedeutung ist?

Abhishek Jain
quelle
Ich glaube nicht, Kumpel, Methodenversand (Call Site Caching und ...) ist ein Problem in dynamischen Sprachen, nicht in statischen Sprachen
Jahan
Wenn ich mein PMD-Tool (Plugin für Eclipse) ausführe, das zu Überprüfungszwecken verwendet wird, wird empfohlen, Änderungen für Variablen vorzunehmen, falls dies wie oben gezeigt ist. Aber ich habe sein Konzept nicht verstanden. Wirklich trifft die Leistung so sehr?
Abhishek Jain
4
Ich dachte, dies sei eine typische Prüfungsfrage. Ich erinnere mich , dass die endgültige tut Einfluss auf die Leistung haben, IIRC finaler Klassen können durch die JRE in irgendeiner Weise optimiert werden , da sie nicht unterklassiert werden können.
Kawu
Ich habe das tatsächlich testen lassen. Auf allen JVMs testete ich die Verwendung der endgültigen auf lokale Variablen haben die Leistung verbessern (leicht, aber dennoch ein Faktor bei der Utility - Methoden sein kann). Der Quellcode ist in meiner Antwort unten.
Rustyx
Verwenden Sie bei Leistungsprüfungen am besten ein Tool wie Caliper für Mikrobenchmarks.
Archimedes Trajano

Antworten:

285

Normalerweise nicht. Bei virtuellen Methoden verfolgt HotSpot, ob die Methode tatsächlich überschrieben wurde, und kann Optimierungen wie Inlining unter der Annahme durchführen, dass eine Methode nicht überschrieben wurde - bis zu diesem Zeitpunkt eine Klasse geladen wird, die die Methode überschreibt Diese Optimierungen können rückgängig gemacht (oder teilweise rückgängig gemacht) werden.

(Dies setzt natürlich voraus, dass Sie HotSpot verwenden - aber es ist bei weitem die häufigste JVM, also ...)

Meiner Meinung nach sollten Sie es finaleher aus Gründen des klaren Designs und der Lesbarkeit als aus Leistungsgründen verwenden. Wenn Sie aus Leistungsgründen Änderungen vornehmen möchten, sollten Sie geeignete Messungen durchführen, bevor Sie den klarsten Code aus der Form bringen. Auf diese Weise können Sie entscheiden, ob eine zusätzliche Leistung die schlechtere Lesbarkeit / das schlechtere Design wert ist. (Nach meiner Erfahrung ist es das fast nie wert; YMMV.)

EDIT: Da die letzten Felder erwähnt wurden, ist es erwähnenswert, dass sie in Bezug auf ein klares Design oft eine gute Idee sind. Sie ändern auch das garantierte Verhalten in Bezug auf die Cross-Thread-Sichtbarkeit: Nach Abschluss eines Konstruktors sind alle endgültigen Felder garantiert sofort in anderen Threads sichtbar. Dies ist wahrscheinlich die häufigste Verwendung von finalmeiner Erfahrung nach , wenn auch als Fan von Josh Blochs „Design für die Vererbung oder verbietet es“ Faustregel ich wahrscheinlich verwenden sollte finalöfter für die Klassen ...

Jon Skeet
quelle
1
@ Abhishek: Worüber im Besonderen? Der wichtigste Punkt ist der letzte - dass Sie sich darüber mit ziemlicher Sicherheit keine Sorgen machen sollten.
Jon Skeet
9
@Abishek: finalwird im Allgemeinen empfohlen, da es das Verständnis von Code erleichtert und das Auffinden von Fehlern erleichtert (weil dadurch die Absicht des Programmierers explizit wird). PMD empfiehlt wahrscheinlich die Verwendung finalaufgrund dieser Stilprobleme, nicht aus Leistungsgründen.
Sleske
3
@Abhishek: Vieles davon ist wahrscheinlich JVM-spezifisch und kann sich auf sehr subtile Aspekte des Kontexts stützen. Ich glaube beispielsweise, dass die HotVpot- Server- JVM weiterhin das Inlining virtueller Methoden ermöglicht, wenn sie in einer Klasse überschrieben werden, gegebenenfalls mit einer schnellen Typprüfung. Die Details sind jedoch schwer zu bestimmen und können sich zwischen den Veröffentlichungen ändern.
Jon Skeet
5
Hier würde ich Effective Java, 2nd Edition, Punkt 15, Minimize Mutability zitieren : Immutable classes are easier to design, implement, and use than mutable classes. They are less prone to error and are more secure.. Des Weiteren An immutable object can be in exactly one state, the state in which it was created.vs Mutable objects, on the other hand, can have arbitrarily complex state spaces.. Aus meiner persönlichen Erfahrung sollte die Verwendung des Schlüsselworts finaldie Absicht des Entwicklers hervorheben, sich der Unveränderlichkeit zuzuwenden und den Code nicht zu "optimieren". Ich ermutige Sie, dieses Kapitel zu lesen, faszinierend!
Louis F.
2
Andere Antworten zeigen, dass die Verwendung des finalSchlüsselworts für Variablen die Menge an Bytecode verringern kann, was sich auf die Leistung auswirken kann.
Julien Kronegg
86

Kurze Antwort: Mach dir keine Sorgen!

Lange Antwort:

Wenn Sie über endgültige lokale Variablen sprechen , denken Sie daran, dass die Verwendung des Schlüsselworts finaldem Compiler hilft, den Code statisch zu optimieren , was letztendlich zu schnellerem Code führen kann. Beispielsweise werden die letzten Zeichenfolgen a + bim folgenden Beispiel statisch (zur Kompilierungszeit) verkettet.

public class FinalTest {

    public static final int N_ITERATIONS = 1000000;

    public static String testFinal() {
        final String a = "a";
        final String b = "b";
        return a + b;
    }

    public static String testNonFinal() {
        String a = "a";
        String b = "b";
        return a + b;
    }

    public static void main(String[] args) {
        long tStart, tElapsed;

        tStart = System.currentTimeMillis();
        for (int i = 0; i < N_ITERATIONS; i++)
            testFinal();
        tElapsed = System.currentTimeMillis() - tStart;
        System.out.println("Method with finals took " + tElapsed + " ms");

        tStart = System.currentTimeMillis();
        for (int i = 0; i < N_ITERATIONS; i++)
            testNonFinal();
        tElapsed = System.currentTimeMillis() - tStart;
        System.out.println("Method without finals took " + tElapsed + " ms");

    }

}

Das Ergebnis?

Method with finals took 5 ms
Method without finals took 273 ms

Getestet auf Java Hotspot VM 1.7.0_45-b18.

Wie hoch ist die tatsächliche Leistungsverbesserung? Ich wage es nicht zu sagen. In den meisten Fällen wahrscheinlich marginal (~ 270 ns in diesem synthetischen Test , weil die String - Verkettung ganz vermieden wird - ein seltener Fall), aber in hoch optimierten Code Dienstprogramm es könnte ein Faktor sein. In jedem Fall lautet die Antwort auf die ursprüngliche Frage Ja, dies könnte die Leistung verbessern, aber bestenfalls geringfügig .

Abgesehen von den Vorteilen bei der Kompilierung konnte ich keine Beweise dafür finden, dass die Verwendung des Schlüsselworts finalmessbare Auswirkungen auf die Leistung hat.

Rustyx
quelle
2
Ich habe Ihren Code ein wenig umgeschrieben, um beide Fälle 100 Mal zu testen. Schließlich betrug die durchschnittliche Zeit des Finales 0 ms und 9 ms für das Nicht-Finale. Durch Erhöhen der Iterationszahl auf 10 MB wird der Durchschnitt auf 0 ms und 75 ms eingestellt. Der beste Lauf für das Nicht-Finale war jedoch 0 ms. Vielleicht sind es die VM-Erkennungsergebnisse, die einfach weggeworfen werden oder so? Ich weiß es nicht, aber unabhängig davon macht die Verwendung von final einen signifikanten Unterschied.
Casper Færgemand
5
Fehlerhafter Test. Frühere Tests erwärmen die JVM und kommen den späteren Testaufrufen zugute. Ordnen Sie Ihre Tests neu und sehen Sie, was passiert. Sie müssen jeden Test in einer eigenen JVM-Instanz ausführen.
Steve Kuo
16
Nein, der Test ist nicht fehlerhaft, das Aufwärmen wurde berücksichtigt. Der zweite Test ist langsamer, nicht schneller. Ohne Aufwärmen wäre der zweite Test NOCH LANGSAM.
Rustyx
6
In testFinal () hat alle Zeit dasselbe Objekt aus dem Zeichenfolgenpool zurückgegeben, da die erneute Verschlüsselung der endgültigen Verkettung von Zeichenfolgen und Zeichenfolgenliteralen zur Kompilierungszeit ausgewertet wird. testNonFinal () jedes Mal, wenn ein neues Objekt zurückgegeben wird, erklärt dies den Geschwindigkeitsunterschied.
Anber
4
Warum halten Sie das Szenario für unrealistisch? StringVerkettung ist viel teurer als das Hinzufügen Integers. Statisch (wenn möglich) zu arbeiten ist effizienter, das zeigt der Test.
Rustyx
62

Ja, kann es. Hier ist ein Fall, in dem final die Leistung steigern kann:

Die bedingte Kompilierung ist eine Technik, bei der Codezeilen nicht basierend auf einer bestimmten Bedingung in die Klassendatei kompiliert werden. Dies kann verwendet werden, um Tonnen von Debugging-Code in einem Produktionsbuild zu entfernen.

Folgendes berücksichtigen:

public class ConditionalCompile {

  private final static boolean doSomething= false;

    if (doSomething) {
       // do first part. 
    }

    if (doSomething) {
     // do second part. 
    }

    if (doSomething) {     
      // do third part. 
    }

    if (doSomething) {
    // do finalization part. 
    }
}

Durch Konvertieren des doSomething-Attributs in ein endgültiges Attribut haben Sie dem Compiler mitgeteilt, dass er doSomething immer dann, wenn er es sieht, gemäß den Ersetzungsregeln zur Kompilierungszeit durch false ersetzen sollte. Der erste Durchlauf des Compilers ändert den Code etwas wie folgt aus :

public class ConditionalCompile {

  private final static boolean doSomething= false;

    if (false){
       // do first part. 
    }

    if (false){
     // do second part. 
    }

    if (false){
      // do third part. 
    }

    if (false){
    // do finalization part. 

    }
}

Sobald dies erledigt ist, wirft der Compiler einen weiteren Blick darauf und stellt fest, dass der Code nicht erreichbare Anweisungen enthält. Da Sie mit einem Compiler von höchster Qualität arbeiten, werden all diese nicht erreichbaren Bytecodes nicht gemocht. Also werden sie entfernt und Sie erhalten Folgendes:

public class ConditionalCompile {


  private final static boolean doSomething= false;

  public static void someMethodBetter( ) {

    // do first part. 

    // do second part. 

    // do third part. 

    // do finalization part. 

  }
}

Dadurch werden übermäßige Codes oder unnötige bedingte Überprüfungen reduziert.

Bearbeiten: Nehmen wir als Beispiel den folgenden Code:

public class Test {
    public static final void main(String[] args) {
        boolean x = false;
        if (x) {
            System.out.println("x");
        }
        final boolean y = false;
        if (y) {
            System.out.println("y");
        }
        if (false) {
            System.out.println("z");
        }
    }
}

Beim Kompilieren dieses Codes mit Java 8 und Dekompilieren mit erhalten javap -c Test.classwir:

public class Test {
  public Test();
    Code:
       0: aload_0
       1: invokespecial #8                  // Method java/lang/Object."<init>":()V
       4: return

  public static final void main(java.lang.String[]);
    Code:
       0: iconst_0
       1: istore_1
       2: iload_1
       3: ifeq          14
       6: getstatic     #16                 // Field java/lang/System.out:Ljava/io/PrintStream;
       9: ldc           #22                 // String x
      11: invokevirtual #24                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      14: iconst_0
      15: istore_2
      16: return
}

Wir können feststellen, dass kompilierter Code nur die nicht endgültige Variable enthält x. Dies beweist, dass endgültige Variablen zumindest für diesen einfachen Fall Auswirkungen auf die Leistung haben.

mel3kings
quelle
1
@ ŁukaszLech Ich habe dies aus einem Oreilly-Buch gelernt: Hardcore Java, in ihrem Kapitel über das endgültige Schlüsselwort.
Mel3kings
15
Hier geht es um die Optimierung zur Kompilierungszeit. Dies bedeutet, dass der Entwickler den WERT der endgültigen booleschen Variablen zur Kompilierungszeit kennt. Was ist der springende Punkt beim Schreiben, wenn Blöcke an erster Stelle stehen, dann für dieses Szenario, in dem die IF-CONDITIONS nicht erforderlich sind und nicht erstellt werden irgendein Sinn? Meiner Meinung nach ist dies, selbst wenn dies die Leistung steigert, in erster Linie falscher Code und kann vom Entwickler selbst optimiert werden, anstatt die Verantwortung an den Compiler zu übergeben, und die Frage beabsichtigt hauptsächlich, nach den Leistungsverbesserungen in üblichen Codes zu fragen, in denen final verwendet wird macht programmatisch Sinn.
Bhavesh Agarwal
7
Der Punkt dabei wäre, Debugging-Anweisungen hinzuzufügen, wie von mel3kings angegeben. Sie können die Variable vor einem Produktionsbuild umdrehen (oder in Ihren Build-Skripten konfigurieren) und den gesamten Code automatisch entfernen, wenn die Distribution erstellt wird.
Adam
16

Ich bin erstaunt, dass niemand einen echten Code gepostet hat, der dekompiliert wurde, um zu beweisen, dass es zumindest einen kleinen Unterschied gibt.

Als Referenz wurde dies gegen javacVersion getestet 8, 9und 10.

Angenommen, diese Methode:

public static int test() {
    /* final */ Object left = new Object();
    Object right = new Object();

    return left.hashCode() + right.hashCode();
}

Wenn Sie diesen Code so kompilieren, wie er ist, wird genau derselbe Bytecode erzeugt, als wenn finaler vorhanden gewesen wäre ( final Object left = new Object();).

Aber dieser:

public static int test() {
    /* final */ int left = 11;
    int right = 12;
    return left + right;
}

Produziert:

   0: bipush        11
   2: istore_0
   3: bipush        12
   5: istore_1
   6: iload_0
   7: iload_1
   8: iadd
   9: ireturn

Weggehen finalGegenwart erzeugt werden:

   0: bipush        12
   2: istore_1
   3: bipush        11
   5: iload_1
   6: iadd
   7: ireturn

Der Code ist so gut wie selbsterklärend. Wenn es eine Kompilierungszeitkonstante gibt, wird er direkt auf den Operandenstapel geladen (er wird nicht wie im vorherigen Beispiel über in einem Array mit lokalen Variablen gespeichert bipush 12; istore_0; iload_0) - was sinnvoll ist da kann es niemand ändern.

Auf der anderen Seite istore_0 ... iload_0ist es mir ein Rätsel, warum der Compiler im zweiten Fall nicht produziert . Es ist nicht so, dass dieser Slot 0in irgendeiner Weise verwendet wird (es könnte das Variablenarray auf diese Weise verkleinern, aber möglicherweise fehlen mir einige interne Details, kann nicht sicher erzählen)

Ich war überrascht, eine solche Optimierung zu sehen, wenn man bedenkt, wie wenig es javactut. Wie sollen wir immer verwenden final? Ich werde nicht einmal einen JMHTest schreiben (den ich anfangs wollte), ich bin mir sicher, dass der Unterschied in der Reihenfolge liegt ns(wenn möglich überhaupt erfasst werden). Der einzige Ort, an dem dies ein Problem sein könnte, ist, wenn eine Methode aufgrund ihrer Größe nicht inline gesetzt werden konnte (und das Deklarieren finaldiese Größe um einige Bytes verkleinern würde).

Es gibt zwei weitere final, die angegangen werden müssen. Erstens, wenn eine Methode final(aus einer JITPerspektive) ist, ist eine solche Methode monomorph - und dies sind die beliebtesten von der JVM.

Dann gibt es finalInstanzvariablen (die in jedem Konstruktor festgelegt werden müssen); Diese sind wichtig, da sie eine korrekt veröffentlichte Referenz garantieren, wie hier ein wenig berührt und auch genau von der JLS.

Eugene
quelle
Ich habe den Code mit Java 8 (JDK 1.8.0_162) mit den Debug-Optionen ( javac -g FinalTest.java) kompiliert, den Code mit dekompiliert javap -c FinalTest.classund nicht die gleichen Ergebnisse erzielt (mit final int left=12, die ich erhalten habe bipush 11; istore_0; bipush 12; istore_1; bipush 11; iload_1; iadd; ireturn). Ja, der generierte Bytecode hängt von vielen Faktoren ab, und es ist schwer zu sagen, ob sich dies finalauf die Leistung auswirkt oder nicht. Da der Bytecode jedoch unterschiedlich ist, kann es zu Leistungsunterschieden kommen.
Julien Kronegg
13

Sie fragen wirklich nach zwei (mindestens) verschiedenen Fällen:

  1. final für lokale Variablen
  2. final für Methoden / Klassen

Jon Skeet hat bereits geantwortet 2). Über 1):

Ich denke nicht, dass es einen Unterschied macht; Bei lokalen Variablen kann der Compiler ableiten, ob die Variable endgültig ist oder nicht (indem er einfach überprüft, ob sie mehr als einmal zugewiesen wurde). Wenn der Compiler also Variablen optimieren wollte, die nur einmal zugewiesen wurden, kann er dies tun, unabhängig davon, ob die Variable tatsächlich deklariert ist finaloder nicht.

final könnte für geschützte / öffentliche Klassenfelder einen Unterschied machen; Dort ist es für den Compiler sehr schwierig herauszufinden, ob das Feld mehr als einmal festgelegt wird, da dies von einer anderen Klasse stammen kann (die möglicherweise noch nicht einmal geladen wurde). Aber selbst dann könnte die JVM die von Jon beschriebene Technik verwenden (optimistisch optimieren, zurücksetzen, wenn eine Klasse geladen wird, die das Feld ändert).

Zusammenfassend sehe ich keinen Grund, warum es die Leistung verbessern sollte. Daher ist es unwahrscheinlich, dass diese Art der Mikrooptimierung hilft. Sie könnten versuchen, es zu vergleichen, um sicherzugehen, aber ich bezweifle, dass es einen Unterschied machen wird.

Bearbeiten:

Laut der Antwort von Timo Westkämper final kann dies in einigen Fällen die Leistung für Klassenfelder verbessern. Ich stehe korrigiert.

sleske
quelle
Ich glaube nicht, dass der Compiler korrekt überprüfen kann, wie oft eine lokale Variable zugewiesen wurde: Was ist mit Wenn-Dann-Sonst-Strukturen mit zahlreichen Zuweisungen?
Gyorgyabraham
1
@gyabraham: Der Compiler überprüft diese Fälle bereits, wenn Sie eine lokale Variable als deklarieren final, um sicherzustellen, dass Sie sie nicht zweimal zuweisen. Soweit ich sehen kann, kann (und wird) dieselbe Überprüfungslogik verwendet werden, um zu überprüfen, ob eine Variable sein könnte final.
Sleske
Die Endgültigkeit einer lokalen Variablen wird nicht im Bytecode ausgedrückt, daher weiß die JVM nicht einmal, dass sie endgültig war
Steve Kuo
1
@SteveKuo: Auch wenn es nicht im Bytecode ausgedrückt wird, kann es helfen javac, besser zu optimieren. Dies ist jedoch nur Spekulation.
Sleske
1
Der Compiler könnte herausfinden, ob eine lokale Variable genau einmal zugewiesen wurde, in der Praxis jedoch nicht (über die Fehlerprüfung hinaus). Wenn andererseits eine finalVariable von einem primitiven Typ oder Typ ist Stringund sofort mit einer Kompilierungszeitkonstante wie im Beispiel der Frage belegt wird, muss der Compiler diese einbinden, da die Variable eine Kompilierungszeitkonstante pro Spezifikation ist. In den meisten Anwendungsfällen sieht der Code möglicherweise anders aus, es spielt jedoch keine Rolle, ob die Konstante in Bezug auf die Leistung inline ist oder von einer lokalen Variablen gelesen wird.
Holger
6

Hinweis: Kein Java-Experte

Wenn ich mich richtig an mein Java erinnere, gibt es kaum eine Möglichkeit, die Leistung mit dem Schlüsselwort final zu verbessern. Ich habe immer gewusst, dass es für "guten Code" existiert - Design und Lesbarkeit.

Neowizard
quelle
1

Ich bin kein Experte, aber ich nehme an, Sie sollten finalder Klasse oder Methode ein Schlüsselwort hinzufügen , wenn es nicht überschrieben wird, und Variablen in Ruhe lassen. Wenn es eine Möglichkeit gibt, solche Dinge zu optimieren, wird der Compiler dies für Sie tun.

Crozin
quelle
1

Beim Testen von OpenGL-Code stellte ich fest, dass die Verwendung des endgültigen Modifikators in einem privaten Feld die Leistung beeinträchtigen kann . Hier ist der Beginn der Klasse, die ich getestet habe:

public class ShaderInput {

    private /* final */ float[] input;
    private /* final */ int[] strides;


    public ShaderInput()
    {
        this.input = new float[10];
        this.strides = new int[] { 0, 4, 8 };
    }


    public ShaderInput x(int stride, float val)
    {
        input[strides[stride] + 0] = val;
        return this;
    }

    // more stuff ...

Mit dieser Methode habe ich die Leistung verschiedener Alternativen getestet, darunter die ShaderInput-Klasse:

public static void test4()
{
    int arraySize = 10;
    float[] fb = new float[arraySize];
    for (int i = 0; i < arraySize; i++) {
        fb[i] = random.nextFloat();
    }
    int times = 1000000000;
    for (int i = 0; i < 10; ++i) {
        floatVectorTest(times, fb);
        arrayCopyTest(times, fb);
        shaderInputTest(times, fb);
        directFloatArrayTest(times, fb);
        System.out.println();
        System.gc();
    }
}

Nach der dritten Iteration, bei der die VM aufgewärmt war, erhielt ich diese Zahlen ohne das letzte Schlüsselwort:

Simple array copy took   : 02.64
System.arrayCopy took    : 03.20
ShaderInput took         : 00.77
Unsafe float array took  : 05.47

Mit dem letzten Schlüsselwort:

Simple array copy took   : 02.66
System.arrayCopy took    : 03.20
ShaderInput took         : 02.59
Unsafe float array took  : 06.24

Beachten Sie die Zahlen für den ShaderInput-Test.

Es war egal, ob ich die Felder öffentlich oder privat machte.

Übrigens gibt es noch ein paar verwirrende Dinge. Die ShaderInput-Klasse übertrifft alle anderen Varianten, auch mit dem endgültigen Schlüsselwort. Dies ist bemerkenswert, da es sich im Grunde genommen um eine Klasse handelt, die ein Float-Array umschließt, während die anderen direkt testen das Array manipulieren. Muss das herausfinden. Kann etwas mit der fließenden Benutzeroberfläche von ShaderInput zu tun haben.

Außerdem ist System.arrayCopy für kleine Arrays anscheinend etwas langsamer als das einfache Kopieren von Elementen von einem Array in das andere in einer for-Schleife. Und die Verwendung von sun.misc.Unsafe (sowie von direktem java.nio.FloatBuffer, der hier nicht gezeigt wird) ist miserabel.

user3663845
quelle
1
Sie haben vergessen, die Parameter endgültig zu machen. <pre> <code> public ShaderInput x (endgültiger int-Schritt, endgültiger float-Wert) {input [strides [Schritt] + 0] = val; gib das zurück; } </ code> </ pre> Nach meinen Erfahrungen kann das Ausführen einer Variablen oder eines Feldfinales tatsächlich die Leistung steigern.
Anticro
1
Oh, und machen Sie auch die anderen endgültig: <pre> <code> final int arraySize = 10; final float [] fb = new float [arraySize]; für (int i = 0; i <arraySize; i ++) {fb [i] = random.nextFloat (); } final int times = 1000000000; für (int i = 0; i <10; ++ i) {floatVectorTest (times, fb); arrayCopyTest (times, fb); shaderInputTest (times, fb); directFloatArrayTest (times, fb); System.out.println (); System.gc (); } </ code> </ pre>
Anticro
1

Final (zumindest für Mitgliedsvariablen und -parameter) ist mehr für den Menschen als für die Maschine.

Es wird empfohlen, Variablen nach Möglichkeit endgültig zu machen. Ich wünschte, Java hätte "Variablen" standardmäßig endgültig gemacht und ein "veränderbares" Schlüsselwort, um Änderungen zuzulassen. Unveränderliche Klassen führen zu viel besserem Thread-Code, und ein Blick auf eine Klasse mit "final" vor jedem Mitglied zeigt schnell, dass sie unveränderlich ist.

Ein anderer Fall: Ich habe viel Code konvertiert, um @ NonNull / @ Nullable-Annotationen zu verwenden. (Sie können sagen, dass ein Methodenparameter nicht null sein darf, dann kann die IDE Sie an jeder Stelle warnen, an der Sie eine Variable übergeben, die nicht mit @ gekennzeichnet ist NonNull - das Ganze verbreitet sich in einem lächerlichen Ausmaß). Es ist viel einfacher zu beweisen, dass eine Mitgliedsvariable oder ein Elementparameter nicht null sein kann, wenn sie als endgültig markiert ist, da Sie wissen, dass sie nirgendwo anders neu zugewiesen werden.

Mein Vorschlag ist, sich daran zu gewöhnen, standardmäßig final für Mitglieder und Parameter anzuwenden. Es sind nur ein paar Zeichen, aber Sie werden dazu angeregt, Ihren Codierungsstil zu verbessern, wenn nichts anderes.

Final für Methoden oder Klassen ist ein weiteres Konzept, da es eine sehr gültige Form der Wiederverwendung nicht zulässt und dem Leser nicht wirklich viel sagt. Die beste Verwendung ist wahrscheinlich die Art und Weise, wie sie String und die anderen intrinsischen Typen endgültig gemacht haben, sodass Sie sich überall auf konsistentes Verhalten verlassen können - das hat viele Fehler verhindert (obwohl es Zeiten gab, in denen ich es geliebt hätte, String zu erweitern ... oh, das Möglichkeiten)

Bill K.
quelle
-3

Mit final deklarierte Mitglieder sind während des gesamten Programms verfügbar, da diese Mitglieder im Gegensatz zu nicht endgültigen Mitgliedern, wenn sie nicht im Programm verwendet wurden, von Garbage Collector nicht behandelt werden und daher aufgrund einer schlechten Speicherverwaltung Leistungsprobleme verursachen können.

Namen
quelle
2
Was für ein Unsinn ist das? Haben Sie eine Quelle, warum Sie glauben, dass dies der Fall ist?
J. Doe
-4

final Das Schlüsselwort kann in Java auf fünf Arten verwendet werden.

  1. Eine Klasse ist endgültig
  2. Eine Referenzvariable ist endgültig
  3. Eine lokale Variable ist endgültig
  4. Eine Methode ist endgültig

Eine Klasse ist endgültig: Eine Klasse ist endgültig bedeutet, dass wir nicht erweitert werden können, oder Vererbung bedeutet, dass Vererbung nicht möglich ist.

Ähnlich - Ein Objekt ist endgültig: Manchmal ändern wir den internen Status des Objekts nicht, sodass wir in diesem Fall angeben können, dass das Objekt das endgültige Objekt ist. Objekt endgültig bedeutet nicht variabel, auch endgültig.

Sobald die Referenzvariable endgültig ist, kann sie keinem anderen Objekt zugewiesen werden. Kann aber den Inhalt des Objekts ändern, solange seine Felder nicht endgültig sind

adityaraj
quelle
2
Mit "Objekt ist endgültig" meinen Sie "Objekt ist unveränderlich".
Gyorgyabraham
6
Sie haben Recht, aber Sie beantworten die Frage nicht. OP fragte nicht, was finalbedeutet, sondern ob die Verwendung finalEinfluss auf die Leistung hat.
Honza Zidek