ความแตกต่างระหว่างสุดท้ายและสุดท้ายได้อย่างมีประสิทธิภาพ


351

ฉันเล่นกับ lambdas ในชวา 8 local variables referenced from a lambda expression must be final or effectively finalและฉันมาข้ามคำเตือน ฉันรู้ว่าเมื่อฉันใช้ตัวแปรภายในคลาสนิรนามพวกเขาจะต้องเป็นคนสุดท้ายในชั้นนอก แต่ยัง - อะไรคือความแตกต่างระหว่างขั้นสุดท้ายและสุดท้ายอย่างมีประสิทธิภาพ ?


2
มีคำตอบมากมาย แต่ก็มีจำนวนทั้งหมดที่ "ไม่ต่างกัน" แต่นั่นเป็นเรื่องจริงหรือ น่าเสียดายที่ฉันไม่สามารถหาข้อกำหนดภาษาสำหรับ Java 8
Aleksandr Dubinsky

3
@AleksandrDubinsky docs.oracle.com/javase/specs
EIS

@AleksandrDubinsky ไม่ "จริง" จริง ฉันพบข้อยกเว้นหนึ่งข้อสำหรับกฎนี้ ตัวแปรโลคัลที่เริ่มต้นด้วยค่าคงที่ไม่ใช่นิพจน์คงที่ของคอมไพเลอร์ คุณไม่สามารถใช้ตัวแปรดังกล่าวสำหรับกรณีในสวิตช์ / กรณีจนกว่าคุณจะเพิ่มคำหลักสุดท้ายอย่างชัดเจน เช่น "int k = 1; switch (someInt) {case k: ... "
Henno Vermeulen

คำตอบ:


234

... เริ่มต้นใน Java SE 8 คลาสโลคัลสามารถเข้าถึงตัวแปรโลคัลและพารามิเตอร์ของบล็อกที่ล้อมรอบซึ่งสุดท้ายหรือสุดท้ายได้อย่างมีประสิทธิภาพ ตัวแปรหรือพารามิเตอร์ที่ไม่เคยมีการเปลี่ยนแปลงค่าใด ๆ

ตัวอย่างเช่นสมมติว่าตัวแปรnumberLengthไม่ได้ถูกประกาศครั้งสุดท้ายและคุณเพิ่มคำสั่งการกำหนดที่ทำเครื่องหมายไว้ในตัวPhoneNumberสร้าง:

public class OutterClass {  

  int numberLength; // <== not *final*

  class PhoneNumber {

    PhoneNumber(String phoneNumber) {
        numberLength = 7;   // <== assignment to numberLength
        String currentNumber = phoneNumber.replaceAll(
            regularExpression, "");
        if (currentNumber.length() == numberLength)
            formattedPhoneNumber = currentNumber;
        else
            formattedPhoneNumber = null;
     }

  ...

  }

...

}

เนื่องจากคำสั่งการมอบหมายนี้ตัวแปร numberLength จึงไม่เป็นที่สิ้นสุดอีกต่อไป เป็นผลให้คอมไพเลอร์ Java สร้างข้อความแสดงข้อผิดพลาดคล้ายกับ "ตัวแปรท้องถิ่นอ้างอิงจากชั้นในจะต้องเป็นขั้นสุดท้ายหรือมีประสิทธิภาพขั้นสุดท้าย"ที่ชั้นใน PhoneNumber พยายามเข้าถึงตัวแปร numberLength:

http://codeinventions.blogspot.in/2014/07/difference-between-final-and.html

http://docs.oracle.com/javase/tutorial/java/javaOO/localclasses.html


68
+1 หมายเหตุ: หากไม่มีการเปลี่ยนแปลงการอ้างอิงจะเป็นผลสุดท้ายแม้ว่าวัตถุที่อ้างอิงจะเปลี่ยนไป
Peter Lawrey

1
@stanleyerror นี่อาจเป็นความช่วยเหลือบางอย่าง: stackoverflow.com/questions/4732544/…

1
ผมคิดว่ามีประโยชน์มากกว่าตัวอย่างของการไม่ได้สุดท้ายได้อย่างมีประสิทธิภาพเป็นตัวอย่างของบางสิ่งบางอย่างเมื่อเป็นครั้งสุดท้ายได้อย่างมีประสิทธิภาพ แม้ว่าคำอธิบายจะทำให้ชัดเจน ไม่จำเป็นต้องประกาศค่าสุดท้ายหากไม่มีการเปลี่ยนแปลงรหัสเป็นค่า
Skychan

1
ตัวอย่างไม่ถูกต้อง รหัสนี้รวบรวมได้อย่างสมบูรณ์แบบ (ไม่มีจุดแน่นอน) ในการรับข้อผิดพลาดคอมไพเลอร์รหัสนี้ควรอยู่ในวิธีการบางอย่างเพื่อที่numberLengthจะกลายเป็นตัวแปรท้องถิ่นของวิธีนี้
mykola

1
มีเหตุผลทำไมตัวอย่างนี้ซับซ้อนหรือไม่ ทำไมรหัสส่วนใหญ่ถึงเกี่ยวข้องกับการดำเนินการ regex ที่ไม่เกี่ยวข้องทั้งหมด? และอย่างที่ @mykola พูดไว้แล้วมันไม่มีเครื่องหมายที่เกี่ยวข้องอย่างสมบูรณ์เกี่ยวกับคุณสมบัติสุดท้ายที่มีประสิทธิภาพเนื่องจากเกี่ยวข้องกับตัวแปรท้องถิ่นเท่านั้นและไม่มีตัวแปรท้องถิ่นในตัวอย่างนี้
Holger

131

ฉันพบวิธีที่ง่ายที่สุดในการอธิบาย "ขั้นสุดท้ายอย่างมีประสิทธิภาพ" คือการจินตนาการการเพิ่มfinalตัวแก้ไขลงในการประกาศตัวแปร หากมีการเปลี่ยนแปลงนี้โปรแกรมจะยังคงทำงานในลักษณะเดียวกันทั้งในเวลาคอมไพล์และเวลารันไทม์ดังนั้นตัวแปรนั้นจึงถือเป็นที่สุด


4
สิ่งนี้เป็นจริงตราบใดที่ความเข้าใจใน "final" ของ java 8 นั้นเป็นที่เข้าใจกันดี มิฉะนั้นฉันจะดูตัวแปรที่ไม่ได้ประกาศครั้งสุดท้ายว่าคุณทำการมอบหมายให้ในภายหลังและคิดว่าไม่ผิด คุณอาจพูดว่า "แน่นอน" ... แต่ไม่ใช่ทุกคนที่ให้ความสนใจกับการเปลี่ยนแปลงเวอร์ชันภาษาล่าสุดเท่าที่ควร
fool4jesus

8
ข้อยกเว้นหนึ่งสำหรับกฎนี้คือตัวแปรท้องถิ่นที่เริ่มต้นด้วยค่าคงที่ไม่ใช่นิพจน์คงที่ของคอมไพเลอร์ คุณไม่สามารถใช้ตัวแปรดังกล่าวสำหรับกรณีในสวิตช์ / กรณีจนกว่าคุณจะเพิ่มคำหลักสุดท้ายอย่างชัดเจน เช่น "int k = 1; switch (someInt) {case k: ... "
Henno Vermeulen

2
@HennoVermeulen case-switch ไม่ใช่ข้อยกเว้นสำหรับกฎในคำตอบนี้ ภาษาระบุว่าcase kต้องการนิพจน์คงที่ซึ่งอาจเป็นตัวแปรคงที่ ("ตัวแปรคงที่เป็นตัวแปรสุดท้ายของชนิดดั้งเดิมหรือชนิดสตริงที่เริ่มต้นด้วยนิพจน์คงที่" JLS 4.12.4 ) ซึ่งเป็นกรณีพิเศษของสุดท้าย ตัวแปร.
Colin D Bennett

3
ในตัวอย่างของฉันคอมไพเลอร์บ่นว่า k ไม่ใช่นิพจน์คงที่ดังนั้นจึงไม่สามารถใช้กับสวิตช์ได้ เมื่อเพิ่มครั้งสุดท้ายพฤติกรรมการรวบรวมจะเปลี่ยนไปเพราะตอนนี้มันเป็นตัวแปรคงที่และสามารถใช้ในสวิตช์ได้ ดังนั้นคุณถูก: กฎยังคงถูกต้อง มันใช้ไม่ได้กับตัวอย่างนี้และไม่ได้บอกว่า k เป็นผลสุดท้ายหรือไม่
Henno Vermeulen

36

ตามเอกสาร :

ตัวแปรหรือพารามิเตอร์ที่ไม่เคยมีการเปลี่ยนแปลงค่าใด ๆ

โดยทั่วไปถ้าคอมไพเลอร์พบว่าตัวแปรที่ไม่ปรากฏในการมอบหมายงานด้านนอกของการเริ่มต้นของมันแล้วตัวแปรถือว่าเป็นครั้งสุดท้ายได้อย่างมีประสิทธิภาพ

ตัวอย่างเช่นพิจารณาบางคลาส:

public class Foo {

    public void baz(int bar) {
        // While the next line is commented, bar is effectively final
        // and while it is uncommented, the assignment means it is not
        // effectively final.

        // bar = 2;
    }
}

เอกสารพูดคุยเกี่ยวกับตัวแปรท้องถิ่น barในตัวอย่างของคุณไม่ใช่ variaable ในท้องที่ แต่เป็นเขตข้อมูล "มีประสิทธิภาพขั้นสุดท้าย" ในข้อความแสดงข้อผิดพลาดตามที่กล่าวไว้ข้างต้นไม่สามารถใช้กับฟิลด์ได้เลย
Antti Haapala

6
@AnttiHaapala barเป็นพารามิเตอร์ที่นี่ไม่ใช่ฟิลด์
peter.petrov

30

'Effectively final' เป็นตัวแปรที่จะไม่ให้ข้อผิดพลาดของคอมไพเลอร์หากถูกต่อท้ายด้วย 'final'

จากบทความโดย 'Brian Goetz'

ตัวแปรโลคัลเป็นตัวแปรสุดท้ายที่มีประสิทธิภาพหากค่าเริ่มต้นไม่เคยเปลี่ยนแปลง - กล่าวอีกนัยหนึ่งการประกาศตัวแปรสุดท้ายจะไม่ทำให้เกิดการรวบรวมล้มเหลว

lambda-state-final- Brian Goetz


2
คำตอบนี้จะแสดงเป็นคำพูด แต่ไม่มีข้อความที่แน่นอนเช่นในบทความของไบรอันเพื่อตรวจสอบว่าไม่คำต่อท้าย นี่เป็นเครื่องหมายคำพูดแทน: อย่างไม่เป็นทางการตัวแปรท้องถิ่นจะเป็นผลสุดท้ายหากค่าเริ่มต้นไม่เคยเปลี่ยนแปลง - กล่าวอีกนัยหนึ่งการประกาศว่าสุดท้ายจะไม่ทำให้เกิดการรวบรวมล้มเหลว
lcfd

จากการคัดลอกคำต่อคำบทความ: อย่างไม่เป็นทางการตัวแปรท้องถิ่นจะสิ้นสุดอย่างมีประสิทธิภาพหากค่าเริ่มต้นไม่เคยเปลี่ยนแปลง - ในคำอื่น ๆ ประกาศว่าสุดท้ายจะไม่ก่อให้เกิดความล้มเหลวในการรวบรวม
Ajeet Ganga

26

ตัวแปรด้านล่างนี้ถือเป็นที่สุดดังนั้นเราไม่สามารถเปลี่ยนค่าของมันได้เมื่อเริ่มต้นแล้ว หากเราพยายามเราจะได้รับข้อผิดพลาดในการรวบรวม ...

final int variable = 123;

แต่ถ้าเราสร้างตัวแปรแบบนี้เราสามารถเปลี่ยนค่าของมัน ...

int variable = 123;
variable = 456;

แต่ในJava 8ตัวแปรทั้งหมดถือเป็นที่สุดตามค่าเริ่มต้น แต่การดำรงอยู่ของสายที่ 2 ในรหัสที่ทำให้มันไม่ใช่ครั้งสุดท้าย ดังนั้นหากเราลบบรรทัดที่ 2 ออกจากโค้ดด้านบนตัวแปรของเราจะเป็น"ขั้นสุดท้ายที่มีประสิทธิภาพ" ...

int variable = 123;

ดังนั้น .. ตัวแปรใด ๆ ที่ได้รับมอบหมายทันทีและเพียงครั้งเดียวคือ "สุดท้ายได้อย่างมีประสิทธิภาพ"


ง่ายเหมือนคำตอบที่ควรจะเป็น
superigno

@Eurig การอ้างอิงที่จำเป็นสำหรับ "ตัวแปรทั้งหมดถือเป็นที่สิ้นสุดโดยค่าเริ่มต้น"
Pacerier

10

ตัวแปรถือเป็นตัวแปรสุดท้ายหรือมีผลบังคับใช้สุดท้ายเมื่อตัวแปรถูกเริ่มต้นครั้งเดียวและจะไม่กลายพันธุ์ในระดับเจ้าของ และเราไม่สามารถเริ่มต้นในลูปหรือการเรียนภายใน

สุดท้าย :

final int number;
number = 23;

สุดท้ายได้อย่างมีประสิทธิภาพ :

int number;
number = 34;

หมายเหตุ : รอบชิงชนะเลิศและมีผลบังคับใช้รอบชิงชนะเลิศที่มีความคล้ายคลึง (ค่าของพวกเขาไม่ได้เปลี่ยนไปหลังจากที่ได้รับมอบหมาย) finalแต่เพียงว่าตัวแปรรอบชิงชนะเลิศที่มีประสิทธิภาพไม่ได้ประกาศด้วยคำสำคัญ


7

เมื่อนิพจน์แลมบ์ดาใช้ตัวแปรโลคัลที่กำหนดจากพื้นที่ปิดล้อมจะมีข้อ จำกัด ที่สำคัญ การแสดงออกแลมบ์ดาอาจใช้ตัวแปรเฉพาะที่มีค่าไม่เปลี่ยนแปลง ข้อ จำกัด นั้นเรียกว่า "การดักจับตัวแปร " ซึ่งอธิบายไว้ว่า แลมบ์ดาค่าการจับภาพการแสดงออกไม่ตัวแปร
ตัวแปรโลคัลที่นิพจน์แลมบ์ดาอาจใช้เรียกว่า " มีประสิทธิภาพขั้นสุดท้าย "
ตัวแปรสุดท้ายที่มีประสิทธิภาพคือสิ่งที่ค่าไม่เปลี่ยนแปลงหลังจากถูกกำหนดครั้งแรก ไม่จำเป็นต้องประกาศตัวแปรดังกล่าวเป็นที่สิ้นสุดอย่างชัดเจนแม้ว่าการทำเช่นนั้นจะไม่เกิดข้อผิดพลาด
ลองดูด้วยตัวอย่างเรามีตัวแปรโลคอล i ซึ่งเริ่มต้นด้วยค่า 7 โดยในนิพจน์แลมบ์ดาเรากำลังพยายามเปลี่ยนค่านั้นโดยการกำหนดค่าใหม่ให้กับ i ซึ่งจะส่งผลในการรวบรวมข้อผิดพลาด - " ตัวแปรท้องถิ่นฉันที่กำหนดไว้ในขอบเขตที่จะต้องปิดล้อมสุดท้ายหรือรอบสุดท้ายได้อย่างมีประสิทธิภาพ "

@FunctionalInterface
interface IFuncInt {
    int func(int num1, int num2);
    public String toString();
}

public class LambdaVarDemo {

    public static void main(String[] args){             
        int i = 7;
        IFuncInt funcInt = (num1, num2) -> {
            i = num1 + num2;
            return i;
        };
    }   
}

2

หัวข้อสุดท้ายที่มีประสิทธิภาพอธิบายไว้ในJLS 4.12.4และย่อหน้าสุดท้ายประกอบด้วยคำอธิบายที่ชัดเจน:

หากตัวแปรนั้นมีประสิทธิภาพเป็นขั้นสุดท้ายการเพิ่มตัวแก้ไขสุดท้ายในการประกาศจะไม่ทำให้เกิดข้อผิดพลาดในการคอมไพล์ ในทางกลับกันตัวแปรท้องถิ่นหรือพารามิเตอร์ที่ประกาศครั้งสุดท้ายในโปรแกรมที่ถูกต้องจะกลายเป็นขั้นสุดท้ายได้อย่างมีประสิทธิภาพหากการปรับเปลี่ยนขั้นสุดท้ายจะถูกลบออก


2

finalเป็นตัวแปรที่ประกาศด้วยคำสำคัญfinalตัวอย่าง:

final double pi = 3.14 ;

มันยังคงอยู่finalตลอดโปรแกรม

ขั้นสุดท้ายอย่างมีประสิทธิภาพ : ตัวแปรหรือพารามิเตอร์ท้องถิ่นใด ๆ ที่กำหนดค่าเพียงครั้งเดียวในขณะนี้ (หรืออัปเดตเพียงครั้งเดียว) มันอาจไม่เหลือจบอย่างมีประสิทธิภาพตลอดโปรแกรม ดังนั้นหมายความว่าตัวแปรสุดท้ายอย่างมีประสิทธิภาพอาจสูญเสียคุณสมบัติสุดท้ายได้อย่างมีประสิทธิภาพหลังจากทันทีที่ได้รับมอบหมาย / อัปเดตอย่างน้อยหนึ่งการมอบหมายเพิ่มเติม ตัวอย่าง:

class EffectivelyFinal {

    public static void main(String[] args) {
        calculate(124,53);
    }

    public static void calculate( int operand1, int operand2){   
     int rem = 0;  //   operand1, operand2 and rem are effectively final here
     rem = operand1%2  // rem lost its effectively final property here because it gets its second assignment 
                       // operand1, operand2 are still effectively final here 
        class operators{

            void setNum(){
                operand1 =   operand2%2;  // operand1 lost its effectively final property here because it gets its second assignment
            }

            int add(){
                return rem + operand2;  // does not compile because rem is not effectively final
            }
            int multiply(){
                return rem * operand1;  // does not compile because both rem and operand1 are not effectively final
            }
        }   
   }    
}

สิ่งนี้ไม่ถูกต้องตามข้อกำหนดภาษา Java: " เมื่อใดก็ตามที่เกิดขึ้นเป็นด้านซ้ายในนิพจน์การกำหนดค่าจะไม่ได้กำหนดและแน่นอนไม่ได้กำหนดไว้ก่อนการมอบหมาย" ตัวแปร / พารามิเตอร์อาจเป็นครั้งสุดท้ายหรือไม่สิ้นสุดอย่างมีประสิทธิภาพ หากคุณไม่สามารถเพิ่มfinalคำหลักลงในการประกาศโดยที่ไม่มีข้อผิดพลาดในการรวบรวมข้อผิดพลาดจะไม่เป็นผลอย่างสมบูรณ์ มันเป็นสิ่งที่ขัดแย้งกับคำสั่งนี้: "ถ้าตัวแปรนั้นมีประสิทธิภาพขั้นสุดท้ายการเพิ่มโมดิฟายเออร์สุดท้ายในการประกาศจะไม่ทำให้เกิดข้อผิดพลาดในการคอมไพล์เวลา"
AndrewF

ความคิดเห็นในรหัสตัวอย่างไม่ถูกต้องด้วยเหตุผลทั้งหมดที่อธิบายไว้ในความคิดเห็นของฉัน "ขั้นสุดท้ายอย่างมีประสิทธิภาพ" ไม่ใช่รัฐที่สามารถเปลี่ยนแปลงได้ตลอดเวลา
AndrewF

@AndrewF หากไม่เปลี่ยนแปลงตลอดเวลาคุณคิดว่าบรรทัดสุดท้ายไม่ได้รวบรวมอะไร rem เป็นบรรทัดสุดท้ายในบรรทัดที่ 1 ในวิธีการคำนวณอย่างมีประสิทธิภาพ อย่างไรก็ตามในบรรทัดสุดท้ายคอมไพเลอร์บ่นว่า rem ไม่ได้ผลสุดท้าย
The Scientific Method

คุณถูกต้องที่จะต้องลบรหัสบางส่วนออกจากบล็อครหัสของคุณเพื่อรวบรวม แต่นั่นไม่ได้สะท้อนพฤติกรรมการใช้งานจริง ณ เวลารวบรวมคุณสามารถตัดสินใจว่าตัวแปรนั้นมีประสิทธิภาพหรือไม่ - ขึ้นอยู่กับข้อมูลจำเพาะว่าเป็นตัวแปรสุดท้ายที่มีประสิทธิภาพเสมอไปหรือไม่สิ้นสุดอย่างมีประสิทธิภาพ คอมไพเลอร์สามารถบอกได้โดยดูที่วิธีการใช้ตัวแปรตลอดขอบเขต ไม่สามารถรับหรือสูญเสียคุณสมบัติได้เมื่อโปรแกรมทำงาน คำนี้มีการกำหนดอย่างชัดเจนโดยสเป็ค - ตรวจสอบคำตอบอื่น ๆ ซึ่งอธิบายได้ค่อนข้างดี
AndrewF

1
public class LambdaScopeTest {
    public int x = 0;        
    class FirstLevel {
        public int x = 1;    
        void methodInFirstLevel(int x) {

            // The following statement causes the compiler to generate
            // the error "local variables referenced from a lambda expression
            // must be final or effectively final" in statement A:
            //
            // x = 99; 

        }
    }    
}

ดังที่คนอื่น ๆ ได้กล่าวไว้ตัวแปรหรือพารามิเตอร์ที่ไม่เคยมีการเปลี่ยนแปลงค่าใด ๆ ในโค้ดข้างต้นหากคุณเปลี่ยนค่าของxในชั้นในFirstLevelแล้วคอมไพเลอร์จะให้ข้อความผิดพลาด:

ตัวแปรท้องถิ่นที่อ้างอิงจากนิพจน์แลมบ์ดาจะต้องเป็นที่สิ้นสุดหรือมีผลบังคับใช้ขั้นสุดท้าย


1

หากคุณสามารถเพิ่มโมดิfinalฟายเออร์ให้กับตัวแปรโล คอล

นิพจน์แลมบ์ดาสามารถเข้าถึงได้

  • ตัวแปรคงที่

  • ตัวแปรอินสแตนซ์

  • พารามิเตอร์วิธีสุดท้ายอย่างมีประสิทธิภาพและ

  • ตัวแปรท้องถิ่นขั้นสุดท้ายได้อย่างมีประสิทธิภาพ

แหล่งที่มา: OCP: คู่มือการศึกษา Oracle Certified Professional Java SE 8 Programmer II, Jeanne Boyarsky, Scott Selikoff

นอกจากนี้

effectively finalตัวแปรเป็นตัวแปรที่มีค่าไม่เคยเปลี่ยน แต่มันก็ไม่ได้ประกาศกับfinalคำหลัก

ที่มา: เริ่มต้นด้วย Java: จากโครงสร้างการควบคุมถึงออบเจกต์ (รุ่นที่ 6), Tony Gaddis

นอกจากนี้อย่าลืมความหมายของการfinalเริ่มต้นอย่างแน่นอนหนึ่งครั้งก่อนที่มันจะถูกใช้เป็นครั้งแรก


0

การประกาศตัวแปรfinalหรือไม่ประกาศfinalแต่ทำให้มันมีประสิทธิภาพนั้นอาจส่งผล (ขึ้นอยู่กับคอมไพเลอร์) ในโค้ดไบต์ที่แตกต่างกัน

ลองมาดูตัวอย่างเล็ก ๆ :

    public static void main(String[] args) {
        final boolean i = true;   // 6  // final by declaration
        boolean j = true;         // 7  // effectively final

        if (i) {                  // 9
            System.out.println(i);// 10
        }
        if (!i) {                 // 12
            System.out.println(i);// 13
        }
        if (j) {                  // 15
            System.out.println(j);// 16
        }
        if (!j) {                 // 18
            System.out.println(j);// 19
        }
    }

ไบต์ที่สอดคล้องกันของmainวิธีการ (Java 8u161 บน Windows 64 บิต):

  public static void main(java.lang.String[]);
    Code:
       0: iconst_1
       1: istore_1
       2: iconst_1
       3: istore_2
       4: getstatic     #16                 // Field java/lang/System.out:Ljava/io/PrintStream;
       7: iconst_1
       8: invokevirtual #22                 // Method java/io/PrintStream.println:(Z)V
      11: iload_2
      12: ifeq          22
      15: getstatic     #16                 // Field java/lang/System.out:Ljava/io/PrintStream;
      18: iload_2
      19: invokevirtual #22                 // Method java/io/PrintStream.println:(Z)V
      22: iload_2
      23: ifne          33
      26: getstatic     #16                 // Field java/lang/System.out:Ljava/io/PrintStream;
      29: iload_2
      30: invokevirtual #22                 // Method java/io/PrintStream.println:(Z)V
      33: return

ตารางหมายเลขบรรทัดที่สอดคล้องกัน:

 LineNumberTable:
   line 6: 0
   line 7: 2
   line 10: 4
   line 15: 11
   line 16: 15
   line 18: 22
   line 19: 26
   line 21: 33

ที่เราเห็นรหัสที่มาที่เส้น12, 13, 14ไม่ปรากฏในรหัสไบต์ นั่นเป็นเพราะiเป็นtrueและจะไม่เปลี่ยนของรัฐ ดังนั้นรหัสนี้ไม่สามารถเข้าถึงได้ (เพิ่มเติมในคำตอบนี้) ด้วยเหตุผลเดียวกันนี้โค้ดที่บรรทัด9ก็ขาดหายไปเช่นกัน รัฐiไม่จำเป็นต้องได้รับการประเมินเพราะมันเป็นtrueเพื่อตรวจสอบว่า

ในทางกลับกันแม้ว่าตัวแปรjจะมีประสิทธิภาพสุดท้ายก็ไม่ได้ดำเนินการในลักษณะเดียวกัน ไม่มีการใช้การเพิ่มประสิทธิภาพดังกล่าว สถานะของjถูกประเมินสองครั้ง bytecode เหมือนกันโดยไม่คำนึงถึงjการเป็นขั้นสุดท้ายได้อย่างมีประสิทธิภาพ


ฉันจะพิจารณาว่านี่เป็นคอมไพเลอร์ที่ไร้ประสิทธิภาพและไม่จำเป็นต้องเป็นคอมไพเลอร์ที่ใหม่กว่า ในการคอมไพล์ที่สมบูรณ์แบบหากตัวแปรนั้นมีประสิทธิภาพสุดท้ายแล้วมันจะสร้างการปรับให้เหมาะสมเหมือนกันทั้งหมดกับที่หนึ่งที่ประกาศสุดท้าย ดังนั้นอย่าพึ่งพาความคิดที่ว่าการปิดท้ายอย่างมีประสิทธิภาพนั้นช้ากว่าการประกาศสิ่งสุดท้าย
AndrewF

@AndrewF โดยทั่วไปคุณถูกพฤติกรรมอาจเปลี่ยนแปลงได้ นั่นเป็นเหตุผลที่ฉันเขียนว่า " อาจส่งผลให้ (ขึ้นอยู่กับคอมไพเลอร์) ในไบต์ที่ต่างกัน " เนื่องจากการปรับให้เหมาะสมที่ขาดหายไปเท่านั้น (รหัสไบต์ที่แตกต่างกัน) ฉันจะไม่ถือว่าการดำเนินการช้าลง แต่ก็ยังคงมีความแตกต่างในกรณีที่แสดง
LuCio

0

ตัวแปรสุดท้ายอย่างมีประสิทธิภาพเป็นตัวแปรท้องถิ่นที่:

  1. ไม่ได้นิยามว่า final
  2. มอบหมายให้เพียงครั้งเดียวเท่านั้น

ในขณะที่ตัวแปรสุดท้ายคือตัวแปรที่:

  1. ประกาศด้วยfinalคำหลัก

-6

อย่างไรก็ตามการเริ่มต้นใน Java SE 8 คลาสโลคัลสามารถเข้าถึงตัวแปรโลคัลและพารามิเตอร์ของบล็อกที่ล้อมรอบซึ่งสุดท้ายหรือมีผลอย่างสุดท้าย

สิ่งนี้ไม่ได้เริ่มต้นบน Java 8 ฉันใช้มันมานานแล้ว รหัสนี้ใช้ (ก่อน java 8) เพื่อให้ถูกกฎหมาย:

String str = ""; //<-- not accesible from anonymous classes implementation
final String strFin = ""; //<-- accesible 
button.addActionListener(new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent e) {
         String ann = str; // <---- error, must be final (IDE's gives the hint);
         String ann = strFin; // <---- legal;
         String str = "legal statement on java 7,"
                +"Java 8 doesn't allow this, it thinks that I'm trying to use the str declared before the anonymous impl."; 
         //we are forced to use another name than str
    }
);

2
คำสั่งหมายถึงความจริงที่ว่าใน <Java 8 เพียง finalตัวแปรที่สามารถเข้าถึงได้ แต่ใน Java 8 ยังผู้ที่มีอย่างมีประสิทธิภาพในขั้นสุดท้าย
Antti Haapala

ฉันเห็นเฉพาะโค้ดที่ใช้งานไม่ได้ไม่ว่าคุณจะใช้งาน Java 7 หรือ Java 8 หรือไม่ก็ตาม
Holger
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.