Làm thế nào để từ khóa cuối cùng của Wikipedia trong Java hoạt động? (Tôi vẫn có thể sửa đổi một đối tượng.)


480

Trong Java, chúng tôi sử dụng finaltừ khóa với các biến để chỉ định các giá trị của nó sẽ không bị thay đổi. Nhưng tôi thấy rằng bạn có thể thay đổi giá trị trong hàm tạo / phương thức của lớp. Một lần nữa, nếu biến là staticthì đó là lỗi biên dịch.

Đây là mã:

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);
  }
}

Mã trên hoạt động tốt và không có lỗi.

Bây giờ thay đổi biến là static:

private static final List foo;

Bây giờ nó là một lỗi biên dịch. Làm thế nào điều này finalthực sự làm việc?


vì foo không hiển thị - làm thế nào nó có thể biên dịch?
Bjorn Hallström

5
@therealprashant không đúng Biến tĩnh riêng là hợp lệ, chúng có thể truy cập được từ các phương thức tĩnh bên trong lớp mà chúng được định nghĩa. Biến tĩnh có nghĩa là biến tồn tại một lần và không bị ràng buộc với một thể hiện của lớp.
mbdavis

3
@mbdavis ơi! cảm ơn bạn. Nhưng tôi vẫn không xóa bình luận để giúp những người đang suy nghĩ giống tôi và sau đó bình luận của bạn sẽ khiến họ suy nghĩ đúng hướng.
therealprashant

@therealprashant ổn không lo!
mbdavis

Câu trả lời:


518

Bạn luôn được phép khởi tạo một finalbiến. Trình biên dịch đảm bảo rằng bạn chỉ có thể làm điều đó một lần.

Lưu ý rằng các phương thức gọi trên một đối tượng được lưu trữ trong một finalbiến không liên quan gì đến ngữ nghĩa của final. Nói cách khác: finalchỉ về bản thân tài liệu tham khảo chứ không phải về nội dung của đối tượng được tham chiếu.

Java không có khái niệm về tính bất biến của đối tượng; điều này đạt được bằng cách thiết kế cẩn thận đối tượng và là một nỗ lực không hề nhỏ.


12
cố gắng làm t.foo = new ArrayList (); trong phương thức chính và bạn sẽ gặp lỗi biên dịch ... foo tham chiếu được liên kết với chỉ một đối tượng cuối cùng của ArrayList ... nó không thể trỏ đến bất kỳ ArrayList nào khác
Code2Interface 27/03/13

50
hừm Tất cả về tham chiếu của nó không có giá trị. Cảm ơn!
GS

2
Tôi có một câu hỏi. Một người mà tôi biết đã tuyên bố "cuối cùng" cũng làm cho biến được lưu trữ trên ngăn xếp. Điều này có đúng không? Tôi đã tìm kiếm khắp nơi và không thể tìm thấy bất kỳ tài liệu tham khảo nào có thể phê duyệt hoặc từ chối yêu cầu này. Tôi đã tìm kiếm trên cả tài liệu Java và Android. Cũng đã tìm kiếm "mô hình bộ nhớ Java". Có thể nó hoạt động theo cách này trên C / C ++, nhưng tôi không nghĩ nó hoạt động theo cách này trên Java. Tôi có đúng không?
nhà phát triển Android

4
@androiddeveloper Không có gì trong Java có thể kiểm soát rõ ràng vị trí stack / heap. Cụ thể hơn, vị trí ngăn xếp theo quyết định của trình biên dịch JIT HotSpot có thể phân tích thoát , điều này liên quan nhiều hơn so với việc kiểm tra xem một biến có final. Các đối tượng có thể thay đổi cũng có thể được phân bổ ngăn xếp. finalTuy nhiên, các trường có thể giúp phân tích thoát, nhưng đó là một tuyến khá gián tiếp. Cũng lưu ý rằng các biến cuối cùng có hiệu quả có cách xử lý giống hệt như các biến được đánh dấu finaltrong mã nguồn.
Marko Topolnik

5
finalcó mặt trong tệp lớp và có các hậu quả ngữ nghĩa quan trọng đối với thời gian chạy tối ưu hóa. Nó cũng có thể phải chịu chi phí vì JLS có một sự đảm bảo mạnh mẽ về tính nhất quán của finalcác trường của một đối tượng. Ví dụ, bộ xử lý ARM phải sử dụng một lệnh rào cản bộ nhớ rõ ràng ở cuối mỗi hàm tạo của một lớp có finalcác trường. Trên các bộ xử lý khác, điều này là không cần thiết, tuy nhiên.
Marko Topolnik

574

Đây là một câu hỏi phỏng vấn yêu thích . Với câu hỏi này, người phỏng vấn cố gắng tìm hiểu mức độ bạn hiểu hành vi của các đối tượng đối với các hàm tạo, phương thức, biến lớp (biến tĩnh) và biến thể hiện.

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.
    }
}

Trong trường hợp trên, chúng tôi đã xác định hàm tạo cho 'Kiểm tra' và cung cấp cho nó phương thức 'setFoo'.

Giới thiệu về hàm tạo: Trình xây dựng chỉ có thể được gọi một lần cho mỗi lần tạo đối tượng bằng cách sử dụng newtừ khóa. Bạn không thể gọi hàm tạo nhiều lần, vì hàm tạo không được thiết kế để làm như vậy.

Về phương thức: Một phương thức có thể được gọi bao nhiêu lần bạn muốn (Thậm chí không bao giờ) và trình biên dịch biết nó.

cảnh 1

private final List foo;  // 1

foolà một ví dụ khác nhau. Khi chúng ta tạo Testđối tượng lớp thì biến thể hiện foo, sẽ được sao chép bên trong đối tượng của Testlớp. Nếu chúng ta gán foobên trong hàm tạo, thì trình biên dịch sẽ biết rằng hàm tạo sẽ chỉ được gọi một lần, do đó không có vấn đề gì khi gán nó bên trong hàm tạo.

Nếu chúng ta gán foobên trong một phương thức, trình biên dịch sẽ biết rằng một phương thức có thể được gọi nhiều lần, điều đó có nghĩa là giá trị sẽ phải được thay đổi nhiều lần, điều này không được phép cho một finalbiến. Vì vậy, trình biên dịch quyết định xây dựng là sự lựa chọn tốt! Bạn có thể chỉ định một giá trị cho một biến cuối cùng chỉ một lần.

Kịch bản 2

private static final List foo = new ArrayList();

foobây giờ là một biến tĩnh . Khi chúng ta tạo một thể hiện của Testlớp, foosẽ không được sao chép vào đối tượng vì foolà tĩnh. Bây giờ fookhông phải là một tài sản độc lập của mỗi đối tượng. Đây là một tài sản của Testlớp. Nhưng foocó thể được nhìn thấy bởi nhiều đối tượng và nếu mọi đối tượng được tạo bằng cách sử dụng newtừ khóa cuối cùng sẽ gọi hàm Testtạo sẽ thay đổi giá trị tại thời điểm tạo nhiều đối tượng (Ghi nhớ static fookhông được sao chép trong mọi đối tượng, nhưng được chia sẻ giữa nhiều đối tượng .)

Kịch bản 3

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

Trên đây Modification-2là từ câu hỏi của bạn. Trong trường hợp trên, bạn không thay đổi đối tượng được tham chiếu đầu tiên, nhưng bạn đang thêm nội dung bên trong foođược phép. Trình biên dịch phàn nàn nếu bạn cố gắng gán a new ArrayList()cho foobiến tham chiếu.
Quy tắc Nếu bạn đã khởi tạo một finalbiến, thì bạn không thể thay đổi nó để tham chiếu đến một đối tượng khác. (Trong trường hợp này ArrayList)

các lớp cuối cùng không thể được phân lớp, các phương thức
cuối cùng không thể bị ghi đè. (Phương thức này nằm trong lớp bậc trên) phương thức
cuối cùng có thể ghi đè. (Đọc điều này theo cách ngữ pháp. Phương pháp này nằm trong một lớp con)


1
Chỉ để được rõ ràng. Trong Kịch bản 2, bạn có nói rằng foosẽ được đặt nhiều lần mặc dù chỉ định cuối cùng nếu foođược đặt trong lớp Kiểm tra và nhiều phiên bản Kiểm tra được tạo không?
Rawr

Không hiểu dòng cuối cùng cho kịch bản 2: Nhưng foocó thể là .. nhiều đối tượng.) Điều đó có nghĩa là, nếu tôi tạo nhiều đối tượng cùng một lúc, thì đối tượng nào đang khởi tạo biến cuối cùng phụ thuộc vào thực thi?
Saumya Suhagiya

1
Tôi nghĩ rằng một cách hữu ích để suy nghĩ về kịch bản 3 là bạn đang gán finalcho địa chỉ bộ nhớ được tham chiếu bởi foođó là một ArrayList. Bạn không được gán finalcho địa chỉ bộ nhớ được tham chiếu bởi phần tử đầu tiên của foo(hoặc bất kỳ phần tử nào cho vấn đề đó). Do đó, bạn không thể thay đổi foonhưng bạn có thể thay đổi foo[0].
Pinkerton

@Rawr Như hiện tại, Kịch bản 2 sẽ gây ra lỗi thời gian biên dịch vì foo = new ArrayList();- foođề cập đến biến tĩnh vì chúng ta ở trong cùng một lớp.
Flow2k

Tôi là một nhà phát triển C ++ đang học Java. Có an toàn khi nghĩ finalvề một biến giống như consttừ khóa trong C ++ không?
Doug Barbieri

213

Từ khóa cuối cùng có nhiều cách để sử dụng:

  • Một lớp cuối cùng không thể được phân lớp.
  • Một phương thức cuối cùng không thể bị ghi đè bởi các lớp con
  • Một biến cuối cùng chỉ có thể được khởi tạo một lần

Cách sử dụng khác:

  • Khi một lớp bên trong ẩn danh được định nghĩa trong phần thân của một phương thức, tất cả các biến được khai báo cuối cùng trong phạm vi của phương thức đó có thể truy cập được từ bên trong lớp bên trong

Một biến lớp tĩnh sẽ tồn tại từ đầu JVM và nên được khởi tạo trong lớp. Thông báo lỗi sẽ không xuất hiện nếu bạn làm điều này.


24
Đây là câu trả lời yêu thích của tôi. Đơn giản và dễ hiểu, đây là những gì tôi mong đợi để đọc trong các tài liệu trực tuyến về java.
RAnders00

Vậy trong biến tĩnh chúng ta có thể khởi tạo bao nhiêu lần tùy ý?
jorge saraiva

1
@jorgesaraiva có, biến tĩnh không phải là hằng số.
czupe

1
@jorgesaraiva Bạn có thể gán các trường (không khởi tạo ) static(miễn là chúng không final) bao nhiêu lần bạn muốn. Xem wiki này để biết sự khác biệt giữa chuyển nhượngkhởi tạo .

56

Các finaltừ khóa có thể được hiểu theo hai cách khác nhau tùy thuộc vào những gì nó được sử dụng trên:

Các loại giá trị: Đối với ints, s, doublev.v., nó sẽ đảm bảo rằng giá trị không thể thay đổi,

Các kiểu tham chiếu: Đối với các tham chiếu đến các đối tượng, finalđảm bảo rằng tham chiếu sẽ không bao giờ thay đổi, có nghĩa là nó sẽ luôn tham chiếu đến cùng một đối tượng. Nó không đảm bảo bất cứ điều gì về các giá trị bên trong đối tượng được giữ nguyên.

Như vậy, final List<Whatever> foo;đảm bảo fooluôn luôn đề cập đến cùng một danh sách, nhưng nội dung của danh sách nói trên có thể thay đổi theo thời gian.


23

Nếu bạn tạo footĩnh, bạn phải khởi tạo nó trong hàm tạo của lớp (hoặc nội tuyến nơi bạn định nghĩa nó) như các ví dụ sau.

Trình xây dựng lớp (không phải ví dụ):

private static final List foo;

static
{
   foo = new ArrayList();
}

Nội tuyến:

private static final List foo = new ArrayList();

Vấn đề ở đây không phải là cách công cụ finalsửa đổi hoạt động, mà là cách công cụ staticsửa đổi hoạt động.

Công cụ finalsửa đổi thực thi khởi tạo tham chiếu của bạn trước thời điểm cuộc gọi đến hàm tạo của bạn hoàn thành (nghĩa là bạn phải khởi tạo nó trong hàm tạo).

Khi bạn khởi tạo một thuộc tính trong dòng, nó sẽ được khởi tạo trước khi mã bạn đã xác định cho hàm tạo được chạy, do đó bạn nhận được các kết quả sau:

  • if foois static, foo = new ArrayList()sẽ được thực thi trước khi hàm static{}tạo mà bạn đã xác định cho lớp của bạn được thực thi
  • nếu fookhông static, foo = new ArrayList()sẽ được thực thi trước khi hàm tạo của bạn chạy

Khi bạn không khởi tạo một thuộc tính trong dòng, final khởi tạo sửa đổi thực thi rằng bạn khởi tạo nó và bạn phải làm như vậy trong hàm tạo. Nếu bạn cũng có một công cụ staticsửa đổi, hàm tạo bạn sẽ phải khởi tạo thuộc tính trong là khối khởi tạo của lớp : static{}.

Lỗi bạn nhận được trong mã của bạn là từ thực tế là static{} được chạy khi lớp được tải, trước thời điểm bạn khởi tạo một đối tượng của lớp đó. Vì vậy, bạn sẽ không khởi tạo fookhi lớp được tạo.

Hãy nghĩ về static{}khối như một hàm tạo cho một đối tượng kiểuClass . Đây là nơi bạn phải thực hiện việc khởi tạo các static finalthuộc tính lớp của mình (nếu không được thực hiện nội tuyến).

Lưu ý bên:

Các final sửa đổi đảm bảo chỉ liên tục cho các kiểu nguyên thủy và tham chiếu.

Khi bạn khai báo một finalđối tượng, những gì bạn nhận được là một final tham chiếu đến đối tượng đó, nhưng bản thân đối tượng đó không phải là hằng số.

Những gì bạn thực sự đạt được khi khai báo một finalthuộc tính là, một khi bạn khai báo một đối tượng cho mục đích cụ thể của bạn (như final Listbạn đã khai báo), và chỉ đối tượng đó sẽ được sử dụng cho mục đích đó: bạn sẽ không thể thay đổi List foothành khác List, nhưng bạn vẫn có thể thay đổi Listbằng cách thêm / xóa các mục (mục Listbạn đang sử dụng sẽ giống nhau, chỉ với nội dung của nó bị thay đổi).


8

Đây là một câu hỏi phỏng vấn rất tốt. Đôi khi, họ thậm chí có thể hỏi bạn sự khác biệt giữa một đối tượng cuối cùng và đối tượng bất biến.

1) Khi ai đó đề cập đến một đối tượng cuối cùng, điều đó có nghĩa là tham chiếu không thể thay đổi, nhưng trạng thái của nó (các biến thể hiện) có thể được thay đổi.

2) Một đối tượng bất biến là một đối tượng có trạng thái không thể thay đổi, nhưng tham chiếu của nó có thể thay đổi. Ví dụ:

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

biến ref x có thể được thay đổi để trỏ một chuỗi khác, nhưng giá trị của "abc" không thể thay đổi.

3) Biến sơ thẩm (trường không tĩnh) được khởi tạo khi hàm tạo được gọi. Vì vậy, bạn có thể khởi tạo các giá trị cho các biến trong một hàm tạo.

4) "Nhưng tôi thấy rằng bạn có thể thay đổi giá trị trong hàm tạo / phương thức của lớp". - Bạn không thể thay đổi nó trong một phương thức.

5) Một biến tĩnh được khởi tạo trong khi tải lớp. Vì vậy, bạn không thể khởi tạo bên trong một hàm tạo, nó phải được thực hiện ngay cả trước khi nó. Vì vậy, bạn cần gán giá trị cho một biến tĩnh trong khi khai báo chính nó.


7

Các finaltừ khóa trong java được sử dụng để hạn chế người sử dụng. finalTừ khóa java có thể được sử dụng trong nhiều ngữ cảnh. Chung kết có thể là:

  1. Biến đổi
  2. phương pháp
  3. lớp học

Các finaltừ khóa có thể được áp dụng với các biến, một finalbiến mà không có giá trị, được gọi là trống finalbiến hoặc chưa được khởi tạo finalbiến. Nó chỉ có thể được khởi tạo trong hàm tạo. Trống finalbiến có thể staticcũng sẽ được khởi tạo trongstatic khối duy nhất.

Biến cuối cùng của Java:

Nếu bạn thực hiện bất kỳ biến nào final, bạn không thể thay đổi giá trị của finalbiến (Nó sẽ là hằng số).

Ví dụ về finalbiến

Có một biến tốc độ cuối cùng, chúng ta sẽ thay đổi giá trị của biến này, nhưng nó không thể thay đổi vì biến cuối cùng một khi được gán một giá trị không bao giờ có thể thay đổi.

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  

Lớp cuối cùng của Java:

Nếu bạn thực hiện bất kỳ lớp nào final, bạn không thể mở rộng nó.

Ví dụ về lớp cuối cùng

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();  
      }  
  }  

Phương thức cuối cùng của Java:

Nếu bạn thực hiện bất kỳ phương thức nào là cuối cùng, bạn không thể ghi đè lên nó.

Ví dụ về finalphương thức (run () trong Honda không thể ghi đè run () trong Bike)

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();  
   }  
}  

đã chia sẻ từ: http://www.javatpoint.com/final-keyword


7

Đáng để đề cập đến một số định nghĩa đơn giản:

Lớp học / Phương pháp

Bạn có thể khai báo một số hoặc tất cả các phương thức lớp như final, để chỉ ra rằng phương thức đó không thể bị ghi đè bởi các lớp con.

Biến

Khi một finalbiến đã được khởi tạo, nó luôn chứa cùng một giá trị.

final về cơ bản tránh ghi đè / ghi lại bởi bất cứ điều gì (lớp con, biến "gán lại"), tùy thuộc vào trường hợp.


1
Tôi nghĩ định nghĩa cuối cùng về các biến là một chút ngắn; "Trong Java, khi từ khóa cuối cùng được sử dụng với một biến các kiểu dữ liệu nguyên thủy (int, float, .. vv), giá trị của biến không thể thay đổi nhưng Khi cuối cùng được sử dụng với các biến không nguyên thủy (Lưu ý rằng các biến không nguyên thủy luôn được tham chiếu đến các đối tượng trong Java), các thành viên của đối tượng được tham chiếu có thể được thay đổi. Cuối cùng cho các biến không nguyên thủy chỉ có nghĩa là chúng không thể được thay đổi để tham chiếu đến bất kỳ đối tượng nào khác ". geekforgeek.org/g-fact-48
ceyun

Cũng hợp lệ, đặc biệt để đề cập đến như trường hợp nguyên thủy và không nguyên thủy. Tks.
ivanleoncz

4

finallà một từ khóa dành riêng trong Java để hạn chế người dùng và nó có thể được áp dụng cho các biến thành viên, phương thức, lớp và biến cục bộ. Các biến cuối cùng thường được khai báo bằng statictừ khóa trong Java và được coi là hằng số. Ví dụ:

public static final String hello = "Hello";

Khi chúng ta sử dụng finaltừ khóa với khai báo biến, giá trị được lưu trữ bên trong biến đó không thể thay đổi sau này.

Ví dụ:

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

Lưu ý : Một lớp được khai báo là cuối cùng không thể được mở rộng hoặc kế thừa (nghĩa là không thể có một lớp con của siêu lớp). Cũng cần lưu ý rằng các phương thức được khai báo là cuối cùng không thể bị ghi đè bởi các lớp con.

Lợi ích của việc sử dụng từ khóa cuối cùng được đề cập trong chủ đề này .


2
the value stored inside that variable cannot be changed latterlà một phần đúng. Nó chỉ đúng với các loại dữ liệu nguyên thủy. Trong trường hợp bất kỳ đối tượng nào được tạo ra final, như danh sách mảng, giá trị của nó có thể thay đổi nhưng không phải là tham chiếu. Cảm ơn bạn!
GS

3

Giả sử bạn có hai hộp đựng tiền, đỏ và trắng. Bạn chỉ gán những hộp tiền này cho hai đứa trẻ và chúng không được phép trao đổi hộp của chúng. Vì vậy, bạn có các hộp tiền màu đỏ hoặc trắng (cuối cùng), bạn không thể sửa đổi hộp nhưng bạn có thể đặt tiền vào hộp của mình.Nobody quan tâm (Sửa đổi-2).


2

Đọc tất cả các câu trả lời.

Có một trường hợp người dùng khác mà finaltừ khóa có thể được sử dụng tức là trong một đối số phương thức:

public void showCaseFinalArgumentVariable(final int someFinalInt){

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

}

Có thể được sử dụng cho biến không nên thay đổi.


1

Khi bạn làm cho nó tĩnh cuối cùng, nó sẽ được khởi tạo trong một khối khởi tạo tĩnh

    private static final List foo;

    static {
        foo = new ArrayList();
    }

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

1

Các finaltừ khóa chỉ ra rằng một biến chỉ có thể được khởi tạo một lần. Trong mã của bạn, bạn chỉ thực hiện một lần khởi tạo cuối cùng để các điều khoản được thỏa mãn. Tuyên bố này thực hiện việc khởi tạo đơn độc của foo. Lưu ý rằng final! = Bất biến, điều đó chỉ có nghĩa là tham chiếu không thể thay đổi.

foo = new ArrayList();

Khi bạn khai báo foostatic finalbiến phải được khởi tạo khi lớp được tải và không thể dựa vào khởi tạo (còn gọi là hàm tạo) để khởi tạo foovì các trường tĩnh phải có sẵn mà không có phiên bản của lớp. Không có gì đảm bảo rằng hàm tạo sẽ được gọi trước khi sử dụng trường tĩnh.

Khi bạn thực hiện phương thức của mình theo static finalkịch bản, Testlớp được tải trước khi khởi tạo ttại thời điểm này, không có foonghĩa là nó không được khởi tạo nên foođược đặt thành mặc định cho tất cả các đối tượng null. Tại thời điểm này, tôi giả sử mã của bạn ném một NullPointerExceptionkhi bạn cố gắng thêm một mục vào danh sách.


1

Trước hết, vị trí trong mã của bạn nơi bạn đang khởi tạo (nghĩa là gán lần đầu tiên) foo ở đây:

foo = new ArrayList();

foo là một đối tượng (có kiểu List) vì vậy nó là kiểu tham chiếu , không phải kiểu giá trị (như int). Như vậy, nó giữ một tham chiếu đến một vị trí bộ nhớ (ví dụ 0xA7D2A834) nơi các thành phần Danh sách của bạn được lưu trữ. Những dòng như thế này

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

không thay đổi giá trị của foo (một lần nữa, chỉ là tham chiếu đến vị trí bộ nhớ). Thay vào đó, họ chỉ cần thêm các yếu tố vào vị trí bộ nhớ được tham chiếu. Để vi phạm từ khóa cuối cùng , bạn sẽ phải thử gán lại foo như sau:

foo = new ArrayList();

Điều đó sẽ cung cấp cho bạn một lỗi biên dịch.


Bây giờ, với cách đó, hãy nghĩ về những gì sẽ xảy ra khi bạn thêm từ khóa tĩnh .

Khi bạn KHÔNG có từ khóa tĩnh, mỗi đối tượng khởi tạo lớp có bản sao foo riêng. Do đó, hàm tạo gán một giá trị cho một bản sao mới, trống của biến foo, điều này hoàn toàn tốt.

Tuy nhiên, khi bạn có từ khóa tĩnh, chỉ có một foo tồn tại trong bộ nhớ được liên kết với lớp. Nếu bạn đã tạo hai hoặc nhiều đối tượng, hàm tạo sẽ cố gắng gán lại một foo đó mỗi lần, vi phạm từ khóa cuối cùng .


1
  1. Do biến cuối cùng là không tĩnh, nên nó có thể được khởi tạo trong hàm tạo. Nhưng nếu bạn làm cho nó tĩnh, nó không thể được khởi tạo bởi hàm tạo (vì các hàm tạo không tĩnh).
  2. Bổ sung vào danh sách dự kiến ​​sẽ không dừng lại bằng cách đưa ra danh sách cuối cùng. finalchỉ liên kết các tham chiếu đến đối tượng cụ thể. Bạn có thể tự do thay đổi 'trạng thái' của đối tượng đó, nhưng không phải chính đối tượng đó.

1

Sau đây là các bối cảnh khác nhau, nơi cuối cùng được sử dụng.

Biến cuối cùng Một biến cuối cùng chỉ có thể được gán một lần. Nếu biến là tham chiếu, điều này có nghĩa là biến không thể được ràng buộc lại để tham chiếu đối tượng khác.

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
   }
}

biến cuối cùng có thể được gán giá trị sau (không bắt buộc gán giá trị khi khai báo), nhưng chỉ một lần.

Các lớp cuối cùng Một lớp cuối cùng không thể được mở rộng (kế thừa)

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

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

Các phương thức cuối cùng Một phương thức cuối cùng không thể bị ghi đè bởi các lớp con.

//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();
    }
}

1

Tôi nghĩ về việc viết một câu trả lời cập nhật và chuyên sâu ở đây.

final từ khóa có thể được sử dụng ở một số nơi.

  1. các lớp học

Một final classnghĩa là không có lớp nào khác có thể mở rộng lớp cuối cùng đó. Khi Thời gian chạy Java ( JRE ) biết một tham chiếu đối tượng thuộc loại của lớp cuối cùng (giả sử F), nó biết rằng giá trị của tham chiếu đó chỉ có thể thuộc loại F.

Ví dụ:

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

Vì vậy, khi nó thực thi bất kỳ phương thức nào của đối tượng đó, phương thức đó không cần phải giải quyết trong thời gian chạy bằng bảng ảo . tức là đa hình thời gian chạy không thể được áp dụng. Vì vậy, thời gian chạy không bận tâm về điều đó. Có nghĩa là nó tiết kiệm thời gian xử lý, sẽ cải thiện hiệu suất.

  1. phương pháp

Một final methodlớp bất kỳ có nghĩa là bất kỳ lớp con nào mở rộng lớp đó không thể ghi đè (các) phương thức cuối cùng đó. Vì vậy, hành vi thời gian chạy trong kịch bản này cũng khá giống với hành vi trước đây tôi đã đề cập cho các lớp.

  1. các trường, biến cục bộ, tham số phương thức

Nếu một trong những chỉ định bất kỳ loại nào ở trên final, điều đó có nghĩa là giá trị đã được hoàn thành, do đó giá trị không thể thay đổi .

Ví dụ:

Đối với các trường, tham số cục bộ

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

Đối với tham số phương thức

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

Điều này đơn giản có nghĩa là giá trị của finalgiá trị tham chiếu không thể thay đổi. tức là chỉ cho phép một khởi tạo. Trong kịch bản này, trong thời gian chạy, vì JRE biết rằng các giá trị không thể thay đổi, nó tải tất cả các giá trị được hoàn thành này (của các tham chiếu cuối cùng) vào bộ đệm L1 . Bởi vì nó không cần phải tải lại một lần nữa và một lần nữa từ bộ nhớ chính . Nếu không, nó tải vào bộ đệm L2 và thời gian tải từ bộ nhớ chính. Vì vậy, nó cũng là một cải tiến hiệu suất.

Vì vậy, trong tất cả 3 trường hợp trên, khi chúng tôi chưa chỉ định finaltừ khóa ở những nơi chúng tôi có thể sử dụng, chúng tôi không cần phải lo lắng, tối ưu hóa trình biên dịch sẽ làm điều đó cho chúng tôi. Ngoài ra còn có rất nhiều thứ khác mà tối ưu hóa trình biên dịch làm cho chúng ta. :)


0

Trên hết là chính xác. Hơn nữa nếu bạn không muốn người khác tạo các lớp con từ lớp của bạn, thì hãy khai báo lớp của bạn là cuối cùng. Sau đó, nó trở thành cấp độ lá của hệ thống phân cấp cây lớp của bạn mà không ai có thể mở rộng thêm. Đó là một thực hành tốt để tránh hệ thống phân cấp lớn của các lớp.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.