it-swarm-vi.tech

Lớp bên trong Java và lớp lồng nhau tĩnh

Sự khác biệt chính giữa một lớp bên trong và một lớp lồng tĩnh trong Java là gì? Có thiết kế/thực hiện đóng một vai trò trong việc lựa chọn một trong số này?

1585
Omnipotent

Từ Hướng dẫn Java :

Các lớp lồng nhau được chia thành hai loại: tĩnh và không tĩnh. Các lớp lồng nhau được khai báo tĩnh được gọi đơn giản là các lớp lồng tĩnh. Các lớp lồng không tĩnh được gọi là các lớp bên trong. 

Các lớp lồng nhau tĩnh được truy cập bằng cách sử dụng tên lớp kèm theo:

OuterClass.StaticNestedClass

Ví dụ, để tạo một đối tượng cho lớp lồng nhau tĩnh, hãy sử dụng cú pháp này:

OuterClass.StaticNestedClass nestedObject = new OuterClass.StaticNestedClass();

Các đối tượng là các thể hiện của một lớp bên trong tồn tại trong một thể hiện của lớp bên ngoài. Hãy xem xét các lớp sau:

class OuterClass {
    ...
    class InnerClass {
        ...
    }
}

Một thể hiện của InnerClass chỉ có thể tồn tại trong một thể hiện của OuterClass và có quyền truy cập trực tiếp vào các phương thức và các trường của thể hiện kèm theo của nó.

Để khởi tạo một lớp bên trong, trước tiên bạn phải khởi tạo lớp bên ngoài. Sau đó, tạo đối tượng bên trong bên trong đối tượng bên ngoài với cú pháp này:

OuterClass.InnerClass innerObject = outerObject.new InnerClass();

xem: Hướng dẫn Java - Các lớp lồng nhau

Để hoàn chỉnh lưu ý rằng cũng có một thứ như lớp bên trong không có một thể hiện kèm theo :

class A {
  int t() { return 1; }
  static A a =  new A() { int t() { return 2; } };
}

Ở đây, new A() { ... } là một lớp bên trong được xác định trong ngữ cảnh tĩnh và không có một thể hiện kèm theo.

1564
Martin

Hướng dẫn Java nói :

Thuật ngữ: Các lớp lồng nhau là được chia thành hai loại: tĩnh và không tĩnh. Các lớp lồng nhau mà được khai báo tĩnh được gọi đơn giản là các lớp lồng nhau tĩnh. Không tĩnh các lớp lồng nhau được gọi là bên trong các lớp học.

Theo cách nói chung, các thuật ngữ "lồng nhau" và "bên trong" được sử dụng thay thế cho nhau bởi hầu hết các lập trình viên, nhưng tôi sẽ sử dụng thuật ngữ chính xác "lớp lồng nhau" bao gồm cả bên trong và tĩnh.

Các lớp có thể được lồng nhau ad infinitum, ví dụ: lớp A có thể chứa lớp B chứa lớp C chứa lớp D, v.v. Tuy nhiên, hơn một cấp độ lồng lớp là rất hiếm, vì nó thường có thiết kế xấu.

Có ba lý do bạn có thể tạo một lớp lồng nhau:

  • tổ chức: đôi khi có vẻ hợp lý nhất khi sắp xếp một lớp vào không gian tên của một lớp khác, đặc biệt là khi nó sẽ không được sử dụng trong bất kỳ bối cảnh nào khác
  • truy cập: các lớp lồng nhau có quyền truy cập đặc biệt vào các biến/trường của các lớp chứa của chúng (chính xác là biến/trường nào phụ thuộc vào loại lớp lồng, cho dù bên trong hay tĩnh).
  • thuận tiện: phải tạo một tệp mới cho mọi loại mới thật khó chịu, đặc biệt là khi loại đó sẽ chỉ được sử dụng trong một ngữ cảnh

bốn loại lớp lồng nhau trong Java. Tóm lại, chúng là:

  • lớp tĩnh: được khai báo là thành viên tĩnh của lớp khác
  • lớp bên trong: được khai báo là thành viên thể hiện của lớp khác
  • lớp bên trong cục bộ: được khai báo bên trong một phương thức cá thể của lớp khác
  • lớp bên trong ẩn danh: giống như một lớp bên trong cục bộ, nhưng được viết dưới dạng một biểu thức trả về một đối tượng một lần

Hãy để tôi giải thích chi tiết hơn.


Các lớp tĩnh

Các lớp tĩnh là loại dễ hiểu nhất vì chúng không liên quan gì đến các thể hiện của lớp chứa.

Một lớp tĩnh là một lớp được khai báo là thành viên tĩnh của lớp khác. Cũng giống như các thành viên tĩnh khác, một lớp như vậy thực sự chỉ là một móc treo trên đó sử dụng lớp chứa làm không gian tên của nó, ví dụ: lớp được khai báo là thành viên tĩnh của lớp Rhino trong gói pizza được biết đến với tên pizza.Rhino.Goat.

package pizza;

public class Rhino {

    ...

    public static class Goat {
        ...
    }
}

Thành thật mà nói, các lớp tĩnh là một tính năng khá vô giá trị vì các lớp đã được chia thành các không gian tên theo các gói. Lý do thực sự duy nhất có thể hiểu được để tạo một lớp tĩnh là một lớp như vậy có quyền truy cập vào các thành viên tĩnh riêng của lớp chứa, nhưng tôi thấy đây là một lý do khá khập khiễng cho tính năng lớp tĩnh tồn tại.


Lớp học bên trong

Một lớp bên trong là một lớp được khai báo là thành viên không tĩnh của lớp khác:

package pizza;

public class Rhino {

    public class Goat {
        ...
    }

    private void jerry() {
        Goat g = new Goat();
    }
}

Giống như với một lớp tĩnh, lớp bên trong được gọi là đủ điều kiện bởi tên lớp chứa, pizza.Rhino.Goat, nhưng bên trong lớp chứa, nó có thể được gọi bằng tên đơn giản. Tuy nhiên, mọi phiên bản của một lớp bên trong được gắn với một thể hiện cụ thể của lớp chứa nó: ở trên, được tạo trong jerry, được gắn hoàn toàn với Rhino dụ this trong jerry. Mặt khác, chúng tôi làm cho thể hiện Rhino được liên kết rõ ràng khi chúng tôi khởi tạo :

Rhino rhino = new Rhino();
Rhino.Goat goat = rhino.new Goat();

(Lưu ý rằng bạn đề cập đến loại bên trong là chỉ trong cú pháp lạ new: Java nhập loại chứa từ phần rhino. Và, có new rhino.Goat () cũng có ý nghĩa hơn với tôi.)

Vì vậy, những gì này đạt được chúng ta? Chà, cá thể lớp bên trong có quyền truy cập vào các thành viên thể hiện của cá thể lớp chứa. Các thành viên thể hiện kèm theo này được tham chiếu bên trong lớp bên trong via chỉ tên đơn giản của chúng, không phải via _ ​​= this [] (this trong lớp bên trong đề cập đến thể hiện của lớp bên trong, không phải ví dụ chứa lớp liên quan): 

public class Rhino {

    private String barry;

    public class Goat {
        public void colin() {
            System.out.println(barry);
        }
    }
}

Trong lớp bên trong, bạn có thể tham khảo this của lớp chứa là Rhino.this và bạn có thể sử dụng this để chỉ các thành viên của nó, ví dụ: Tê giác.this.barry.


Lớp nội địa

Một lớp bên trong cục bộ là một lớp được khai báo trong phần thân của một phương thức. Một lớp như vậy chỉ được biết đến trong phương thức chứa của nó, vì vậy nó chỉ có thể được khởi tạo và có các thành viên của nó được truy cập trong phương thức chứa của nó. Lợi ích là một thể hiện của lớp bên trong cục bộ được gắn với và có thể truy cập các biến cục bộ cuối cùng của phương thức chứa nó. Khi cá thể sử dụng một phương thức cục bộ cuối cùng của phương thức chứa của nó, biến đó sẽ giữ lại giá trị mà nó giữ tại thời điểm tạo cá thể, ngay cả khi biến đó nằm ngoài phạm vi (đây thực sự là phiên bản đóng, giới hạn của Java).Bởi vì một lớp bên trong cục bộ không phải là thành viên của một lớp hoặc gói, nó không được khai báo với mức truy cập. (Tuy nhiên, hãy rõ ràng rằng các thành viên của chính nó có các cấp truy cập như trong một lớp học bình thường.).

Nếu một lớp bên trong cục bộ được khai báo trong một phương thức cá thể, thì việc khởi tạo của lớp bên trong được gắn với thể hiện được giữ bởi phương thức chứa this tại thời điểm tạo cá thể, và vì vậy các thành viên thể hiện của lớp chứa có thể truy cập được giống như trong một lớp bên trong. Một lớp bên trong cục bộ được khởi tạo đơn giản là via tên của nó, ví dụ: lớp bên trong cục bộ Cat được khởi tạo là new Cat (), không phải new.Cat () như bạn mong đợi.

.


Một lớp bên trong ẩn danh là một cách thuận tiện về mặt cú pháp để viết một lớp bên trong cục bộ. Thông thường nhất, một lớp bên trong cục bộ được khởi tạo nhiều nhất chỉ một lần mỗi khi phương thức chứa của nó được chạy. Sau đó, thật tuyệt nếu chúng ta có thể kết hợp định nghĩa lớp bên trong cục bộ và khởi tạo đơn lẻ của nó thành một dạng cú pháp thuận tiện, và cũng sẽ rất tuyệt nếu chúng ta không phải nghĩ ra một tên cho lớp (càng ít có ích Tên mã của bạn chứa, tốt hơn). Một lớp bên trong ẩn danh cho phép cả hai điều này:

new *ParentClassName*(*constructorArgs*) {*members*}

new *InterfaceName*() {*members*}

Mặc dù bạn không thể cung cấp cho lớp bên trong ẩn danh một hàm tạo, bạn vẫn có thể thực hiện bất kỳ thiết lập nào bạn muốn bằng cách sử dụng một khối khởi tạo (một khối {} được đặt bên ngoài bất kỳ phương thức nào).

Hãy rõ ràng rằng một lớp bên trong ẩn danh chỉ đơn giản là một cách kém linh hoạt hơn để tạo một lớp bên trong cục bộ với một thể hiện. Nếu bạn muốn một lớp bên trong cục bộ thực hiện nhiều giao diện hoặc thực hiện các giao diện trong khi mở rộng một số lớp khác ngoài Object hoặc chỉ định hàm tạo của chính nó, bạn bị kẹt khi tạo một lớp bên trong cục bộ có tên thông thường.

Be clear that an anonymous inner class is simply a less flexible way of creating a local inner class with one instance. If you want a local inner class which implements multiple interfaces or which implements interfaces while extending some class other than Object or which specifies its own constructor, you're stuck creating a regular named local inner class.

553
Jegschemesch

Tôi không nghĩ rằng sự khác biệt thực sự đã trở nên rõ ràng trong các câu trả lời ở trên. 

Đầu tiên để có được các điều khoản đúng: 

  • Một lớp lồng nhau là một lớp được chứa trong một lớp khác ở cấp mã nguồn.
  • Nó là tĩnh nếu bạn khai báo nó với static modifier.
  • Một lớp lồng không tĩnh được gọi là lớp bên trong. (Tôi ở lại với lớp lồng không tĩnh.)

Câu trả lời của Martin là đúng cho đến nay. Tuy nhiên, câu hỏi thực tế là: Mục đích của việc khai báo một lớp tĩnh là gì hay không?

Bạn sử dụng các lớp lồng nhau tĩnh nếu bạn chỉ muốn giữ các lớp của mình với nhau nếu chúng thuộc về nhau hoặc nếu lớp lồng nhau được sử dụng riêng trong lớp kèm theo. Không có sự khác biệt về ngữ nghĩa giữa một lớp lồng tĩnh và mọi lớp khác.

Các lớp lồng không tĩnh là một con thú khác. Tương tự như các lớp bên trong ẩn danh, các lớp lồng nhau như vậy thực sự là các bao đóng. Điều đó có nghĩa là họ nắm bắt phạm vi xung quanh và trường hợp kèm theo của họ và làm cho nó có thể truy cập được. Có lẽ một ví dụ sẽ làm rõ điều đó. Xem phần còn lại của Container:

public class Container {
    public class Item{
        Object data;
        public Container getContainer(){
            return Container.this;
        }
        public Item(Object data) {
            super();
            this.data = data;
        }

    }

    public static Item create(Object data){
        // does not compile since no instance of Container is available
        return new Item(data);
    }
    public Item createSubItem(Object data){
        // compiles, since 'this' Container is available
        return new Item(data);
    }
}

Trong trường hợp này, bạn muốn có một tham chiếu từ một mục con đến vùng chứa cha. Sử dụng một lớp lồng không tĩnh, điều này hoạt động mà không có một số công việc. Bạn có thể truy cập vào thể hiện kèm theo của Container với cú pháp Container.this.

Nhiều lời giải thích khó hiểu sau:

Nếu bạn nhìn vào mã byte Java, trình biên dịch tạo ra cho một lớp lồng (không tĩnh), nó có thể trở nên rõ ràng hơn:

// class version 49.0 (49)
// access flags 33
public class Container$Item {

  // compiled from: Container.Java
  // access flags 1
  public INNERCLASS Container$Item Container Item

  // access flags 0
  Object data

  // access flags 4112
  final Container this$0

  // access flags 1
  public getContainer() : Container
   L0
    LINENUMBER 7 L0
    ALOAD 0: this
    GETFIELD Container$Item.this$0 : Container
    ARETURN
   L1
    LOCALVARIABLE this Container$Item L0 L1 0
    MAXSTACK = 1
    MAXLOCALS = 1

  // access flags 1
  public <init>(Container,Object) : void
   L0
    LINENUMBER 12 L0
    ALOAD 0: this
    ALOAD 1
    PUTFIELD Container$Item.this$0 : Container
   L1
    LINENUMBER 10 L1
    ALOAD 0: this
    INVOKESPECIAL Object.<init>() : void
   L2
    LINENUMBER 11 L2
    ALOAD 0: this
    ALOAD 2: data
    PUTFIELD Container$Item.data : Object
    RETURN
   L3
    LOCALVARIABLE this Container$Item L0 L3 0
    LOCALVARIABLE data Object L0 L3 2
    MAXSTACK = 2
    MAXLOCALS = 3
}

Như bạn có thể thấy trình biên dịch tạo ra một trường ẩn Container this$0. Điều này được đặt trong hàm tạo có tham số bổ sung kiểu Container để chỉ định thể hiện kèm theo. Bạn không thể thấy tham số này trong nguồn nhưng trình biên dịch hoàn toàn tạo ra nó cho một lớp lồng nhau. 

Ví dụ của Martin

OuterClass.InnerClass innerObject = outerObject.new InnerClass();

vì vậy sẽ được biên dịch thành một cuộc gọi của một cái gì đó như (bằng mã byte)

new InnerClass(outerObject)

Vì lợi ích của sự hoàn chỉnh:

Một lớp ẩn danhmột ví dụ hoàn hảo về một lớp lồng không tĩnh mà chỉ không có tên liên quan đến nó và không thể được tham chiếu sau.

131
jrudolph

Tôi nghĩ rằng không có câu trả lời nào ở trên giải thích cho bạn sự khác biệt thực sự giữa một lớp lồng nhau và một lớp lồng tĩnh về mặt thiết kế ứng dụng: 

Xem qua

Một lớp lồng nhau có thể là không tĩnh hoặc tĩnh và trong mỗi trường hợp là một lớp được định nghĩa trong một lớp khác . Một lớp lồng chỉ tồn tại để phục vụ là bao quanh lớp , nếu một lớp lồng được hữu ích bởi các lớp khác (không chỉ lớp kèm theo), nên được khai báo là lớp cấp cao nhất.

Sự khác biệt

Lớp không lồng nhau : được liên kết ngầm với thể hiện kèm theo của lớp chứa, điều này có nghĩa là có thể gọi các phương thức và các biến truy cập của thể hiện kèm theo. Một cách sử dụng phổ biến của lớp lồng nhau không định hướng là định nghĩa lớp Adaptor.

Lớp lồng nhau tĩnh : không thể truy cập thể hiện của lớp kèm theo và gọi các phương thức trên nó, vì vậy nên được sử dụng khi lớp lồng không yêu cầu quyền truy cập vào một thể hiện của lớp kèm theo. Một cách sử dụng phổ biến của lớp lồng tĩnh là để thực hiện một thành phần của đối tượng bên ngoài.

Phần kết luận

Vì vậy, sự khác biệt chính giữa hai từ quan điểm thiết kế là: lớp lồng nhau không có thể có thể truy cập thể hiện của lớp container, trong khi tĩnh không thể.

88
aleroot

Nói một cách đơn giản, chúng ta cần các lớp lồng nhau chủ yếu vì Java không cung cấp các bao đóng.

Các lớp lồng nhau là các lớp được định nghĩa bên trong phần thân của một lớp kèm theo khác. Chúng có hai loại - tĩnh và không tĩnh.

Chúng được coi là thành viên của lớp kèm theo, do đó bạn có thể chỉ định bất kỳ trong số bốn chỉ định truy cập - private, package, protected, public. Chúng tôi không có sự sang trọng này với các lớp cấp cao nhất, chỉ có thể được khai báo public hoặc gói riêng.

Các lớp bên trong hay còn gọi là các lớp không ngăn xếp có quyền truy cập vào các thành viên khác của lớp trên cùng, ngay cả khi chúng được khai báo là riêng tư trong khi các lớp lồng tĩnh không có quyền truy cập vào các thành viên khác của lớp trên cùng.

public class OuterClass {
    public static class Inner1 {
    }
    public class Inner2 {
    }
}

Inner1 là lớp bên trong tĩnh của chúng tôi và Inner2 là lớp bên trong của chúng tôi không tĩnh. Sự khác biệt chính giữa chúng, bạn không thể tạo một cá thể Inner2 mà không có Bên ngoài khi bạn có thể tạo một đối tượng Inner1 một cách độc lập.

Khi nào bạn sẽ sử dụng lớp Nội?

Hãy nghĩ về một tình huống trong đó Class AClass B có liên quan, Class B cần truy cập Class A thành viên và Class B chỉ liên quan đến Class A. Các lớp bên trong đi vào hình ảnh.

Để tạo một thể hiện của lớp bên trong, bạn cần tạo một thể hiện của lớp bên ngoài.

OuterClass outer = new OuterClass();
OuterClass.Inner2 inner = outer.new Inner2();

hoặc là

OuterClass.Inner2 inner = new OuterClass().new Inner2();

Khi nào bạn sẽ sử dụng lớp Nội tĩnh?

Bạn sẽ định nghĩa một lớp bên trong tĩnh khi bạn biết rằng nó không có bất kỳ mối quan hệ nào với thể hiện của lớp/lớp trên cùng. Nếu lớp bên trong của bạn không sử dụng các phương thức hoặc trường của lớp bên ngoài, thì đó chỉ là sự lãng phí không gian, vì vậy hãy làm cho nó tĩnh.

Ví dụ, để tạo một đối tượng cho lớp lồng nhau tĩnh, hãy sử dụng cú pháp này:

OuterClass.Inner1 nestedObject = new OuterClass.Inner1();

Ưu điểm của lớp lồng nhau tĩnh là nó không cần một đối tượng của lớp chứa/lớp trên cùng để làm việc. Điều này có thể giúp bạn giảm số lượng đối tượng mà ứng dụng của bạn tạo khi chạy.

31
Thalaivar

Tôi nghĩ rằng, quy ước thường được tuân theo là:

  • lớp tĩnh trong một lớp cấp cao nhất là một lớp lồng nhau
  • lớp không tĩnh trong lớp cấp cao nhất là lớp lớp bên trong, mà hơn nữa có hai dạng nữa:
    • lớp cục bộ - các lớp được đặt tên được khai báo bên trong một khối như thân phương thức hoặc hàm tạo
    • lớp ẩn danh - các lớp không tên có phiên bản được tạo trong biểu thức và câu lệnh

Tuy nhiên, một vài điểm khác cần nhớ là:

  • Các lớp cấp cao nhất và lớp lồng tĩnh giống nhau về mặt ngữ nghĩa ngoại trừ trong trường hợp lớp lồng tĩnh, nó có thể tạo tham chiếu tĩnh đến các trường/phương thức tĩnh riêng của lớp Ngoài [cha] và ngược lại.

  • Các lớp bên trong có quyền truy cập vào các biến đối tượng của thể hiện kèm theo của lớp ngoài [cha]. Tuy nhiên, không phải tất cả các lớp bên trong đều có các thể hiện kèm theo, ví dụ các lớp bên trong trong bối cảnh tĩnh, giống như một lớp ẩn danh được sử dụng trong khối khởi tạo tĩnh, thì không.

  • Lớp ẩn danh theo mặc định mở rộng lớp cha hoặc thực hiện giao diện cha và không có mệnh đề nào nữa để mở rộng bất kỳ lớp nào khác hoặc thực hiện bất kỳ giao diện nào nữa. Vì thế,

    • new YourClass(){}; có nghĩa là class [Anonymous] extends YourClass {}
    • new YourInterface(){}; có nghĩa là class [Anonymous] implements YourInterface {}

Tôi cảm thấy rằng câu hỏi lớn hơn vẫn còn mở nên sử dụng cái nào và khi nào? Chà, điều đó chủ yếu phụ thuộc vào kịch bản bạn đang xử lý nhưng đọc phản hồi được đưa ra bởi @jrudolph có thể giúp bạn đưa ra quyết định.

26
sactiw

Dưới đây là sự khác biệt và điểm tương đồng chính giữa Java lớp bên trong và lớp lồng tĩnh.

Hy vọng nó giúp!

Lớp trong

  • Có thể truy cập vào lớp ngoài cả phương thức và trường tĩnh
  • Liên kết với thể hiện của lớp kèm theo vì vậy để khởi tạo nó trước tiên cần một thể hiện của lớp bên ngoài (lưu ý new nơi từ khóa):

    Outerclass.InnerClass innerObject = outerObject.new Innerclass();
    
  • Không thể xác định bất kỳ thành viên tĩnh

  • Không thể có Giao diện Lớp hoặc khai báo

Lớp lồng tĩnh

  • Không thể truy cập lớp ngoài thể hiện

  • Không được liên kết với bất kỳ trường hợp nào của lớp kèm theo Vì vậy, để khởi tạo nó:

    OuterClass.StaticNestedClass nestedObject = new OuterClass.StaticNestedClass();
    

Điểm tương đồng

  • Cả Các lớp bên trong có thể truy cập ngay cả các trường và phương thức riêng tư của lớp ngoài
  • Ngoài ra Lớp ngoài có quyền truy cập vào các trường và phương thức riêng của các lớp bên trong
  • Cả hai lớp có thể có sửa đổi truy cập riêng tư, được bảo vệ hoặc công khai

Tại sao nên sử dụng các lớp lồng nhau?

Theo tài liệu của Oracle, có một số lý do ( tài liệu đầy đủ ):

  • Đây là cách phân nhóm hợp lý các lớp chỉ được sử dụng ở một nơi: Nếu một lớp chỉ hữu ích cho một lớp khác, thì nó hợp lý với nhúng nó vào lớp đó và giữ hai cái lại với nhau Việc lồng các "lớp trợ giúp" như vậy làm cho gói của chúng được sắp xếp hợp lý hơn.

  • Nó làm tăng đóng gói: Hãy xem xét hai lớp cấp cao nhất là A và B, trong đó B cần quyền truy cập vào các thành viên của A nếu không sẽ được khai báo là riêng tư. Bằng cách ẩn lớp B trong lớp A, các thành viên của A có thể được khai báo là riêng tư và B có thể truy cập chúng. Ngoài ra, bản thân B có thể bị ẩn khỏi thế giới bên ngoài.

  • Nó có thể dẫn đến mã dễ đọc và dễ bảo trì hơn: Lồng các lớp nhỏ trong các lớp cấp cao nhất đặt mã gần hơn với nơi nó được sử dụng.

26
Behzad Bahmanyar

Lớp lồng nhau: lớp bên trong lớp

Các loại:

  1. Lớp lồng tĩnh
  2. Lớp lồng không tĩnh [Lớp bên trong]

Sự khác biệt:

Lớp lồng không tĩnh [Lớp bên trong]

Trong đối tượng lớp lồng không tĩnh của lớp bên trong tồn tại bên trong đối tượng của lớp bên ngoài. Vì vậy, thành viên dữ liệu của lớp bên ngoài có thể truy cập vào lớp bên trong. Vì vậy, để tạo đối tượng của lớp bên trong, chúng ta phải tạo đối tượng của lớp bên ngoài trước.

outerclass outerobject=new outerobject();
outerclass.innerclass innerobjcet=outerobject.new innerclass(); 

Lớp lồng nhau tĩnh

Trong đối tượng lớp lồng nhau tĩnh của lớp bên trong không cần đối tượng của lớp bên ngoài, vì Word "tĩnh" biểu thị không cần tạo đối tượng.

class outerclass A {
    static class nestedclass B {
        static int x = 10;
    }
}

Nếu bạn muốn truy cập x, hãy viết phương thức bên trong sau

  outerclass.nestedclass.x;  i.e. System.out.prinltn( outerclass.nestedclass.x);
15
tejas

Có một sự tinh tế về việc sử dụng các lớp tĩnh lồng nhau có thể hữu ích trong các tình huống nhất định.

Trong khi các thuộc tính tĩnh được khởi tạo trước khi lớp được khởi tạo thông qua hàm tạo của nó, Các thuộc tính tĩnh bên trong các lớp tĩnh lồng nhau dường như không được khởi tạo cho đến sau khi được tham chiếu đầu tiên, ngay cả khi chúng được đánh dấu là 'cuối cùng'.

Xem xét ví dụ này:

public class C0 {

    static C0 instance = null;

    // Uncomment the following line and a null pointer exception will be
    // generated before anything gets printed.
    //public static final String outerItem = instance.makeString(98.6);

    public C0() {
        instance = this;
    }

    public String makeString(int i) {
        return ((new Integer(i)).toString());
    }

    public String makeString(double d) {
        return ((new Double(d)).toString());
    }

    public static final class nested {
        public static final String innerItem = instance.makeString(42);
    }

    static public void main(String[] argv) {
        System.out.println("start");
        // Comment out this line and a null pointer exception will be
        // generated after "start" prints and before the following
        // try/catch block even gets entered.
        new C0();
        try {
            System.out.println("retrieve item: " + nested.innerItem);
        }
        catch (Exception e) {
            System.out.println("failed to retrieve item: " + e.toString());
        }
        System.out.println("finish");
    }
}

Mặc dù 'lồng nhau' và 'bên trong' đều được khai báo là 'cuối cùng tĩnh'. cài đặt của lồng nhau.innerItem không diễn ra cho đến khi lớp được khởi tạo (hoặc ít nhất là và bỏ qua các dòng mà tôi đề cập ở trên. Điều tương tự không giữ Đúng cho 'outsItem'.

Ít nhất đây là những gì tôi thấy trong Java 6.0.

11
HippoMan

Thể hiện của lớp bên trong được tạo khi thể hiện của lớp bên ngoài được tạo. Do đó, các thành viên và các phương thức của lớp bên trong có quyền truy cập vào các thành viên và các phương thức của thể hiện (đối tượng) của lớp bên ngoài. Khi thể hiện của lớp bên ngoài đi ra khỏi phạm vi, thì các thể hiện của lớp bên trong sẽ không còn tồn tại.

Lớp lồng nhau tĩnh không có một cá thể cụ thể. Nó chỉ được tải khi nó được sử dụng lần đầu tiên (giống như các phương thức tĩnh). Đó là một thực thể hoàn toàn độc lập, có các phương thức và biến không có quyền truy cập vào các thể hiện của lớp bên ngoài.

Các lớp lồng nhau tĩnh không được kết hợp với đối tượng bên ngoài, chúng nhanh hơn và chúng không lấy bộ nhớ heap/stack, bởi vì không cần thiết phải tạo ra thể hiện của lớp đó. Do đó, nguyên tắc chung là cố gắng xác định lớp lồng tĩnh, với phạm vi giới hạn nhất có thể (private> = class> = reserved> = public), sau đó chuyển đổi nó sang lớp bên trong (bằng cách xóa định danh "tĩnh") phạm vi, nếu nó thực sự cần thiết.

11
rmaruszewski

Tôi không nghĩ có nhiều điều để thêm ở đây, hầu hết các câu trả lời giải thích hoàn hảo sự khác biệt giữa lớp lồng nhau tĩnh và lớp bên trong. Tuy nhiên, hãy xem xét vấn đề sau khi sử dụng các lớp lồng nhau và các lớp bên trong. Như đã đề cập trong một vài câu trả lời, các lớp bên trong không thể được khởi tạo mà không có và ví dụ của lớp kèm theo của chúng có nghĩa là chúng GIỮ a con trỏ với thể hiện của bao vây lớp có thể dẫn đến tràn bộ nhớ hoặc ngoại lệ ngăn xếp tràn do thực tế là GC sẽ không thể thu gom rác các lớp kèm theo ngay cả khi chúng không được sử dụng nữa. Để làm rõ điều này, hãy kiểm tra đoạn mã sau: 

public class Outer {


    public  class Inner {

    }


    public Inner inner(){
        return new Inner();
    }

    @Override
    protected void finalize() throws Throwable {
    // as you know finalize is called by the garbage collector due to destroying an object instance
        System.out.println("I am destroyed !");
    }
}


public static void main(String arg[]) {

    Outer outer = new Outer();
    Outer.Inner inner = outer.new Inner();

    // out instance is no more used and should be garbage collected !!!
    // However this will not happen as inner instance is still alive i.e used, not null !
    // and outer will be kept in memory until inner is destroyed
    outer = null;

    //
    // inner = null;

    //kick out garbage collector
    System.gc();

}

Nếu bạn xóa nhận xét về // inner = null; Chương trình sẽ đưa ra "Tôi bị hủy!", Nhưng giữ bình luận này thì không.
Lý do là vì thể hiện bên trong màu trắng vẫn được tham chiếu, GC không thể thu thập nó và vì nó tham chiếu (có một con trỏ tới) thể hiện bên ngoài nên nó cũng không được thu thập. Có đủ các đối tượng này trong dự án của bạn và có thể hết bộ nhớ.
So với các lớp bên trong tĩnh không giữ một điểm đến thể hiện của lớp bên trong vì nó không liên quan đến thể hiện mà liên quan đến lớp. Chương trình trên có thể in "Tôi bị phá hủy!" Nếu bạn làm cho lớp bên trong tĩnh và được khởi tạo với Outer.Inner i = new Outer.Inner(); 

10
Adelin

Trong trường hợp tạo cá thể, thể hiện của lớp không Lớp bên trong tĩnh được tạo với tham chiếu của Đối tượng của lớp bên ngoài mà nó được định nghĩa. Cái này Có nghĩa là nó có bao gồm . Nhưng thể hiện của lớp bên trong tĩnhis được tạo với tham chiếu của lớp ngoài, không phải với Tham chiếu của đối tượng của lớp bên ngoài. Điều này có nghĩa là nó Không bao gồm ví dụ.

Ví dụ:

class A
{
  class B
  {
    // static int x; not allowed here…..    
  }
  static class C
  {
    static int x; // allowed here
  }
}

class Test
{
  public static void main(String… str)
  {
    A o=new A();
    A.B obj1 =o.new B();//need of inclosing instance

    A.C obj2 =new A.C();

    // not need of reference of object of outer class….
  }
}
10
Ankit Jain

Các thuật ngữ được sử dụng thay thế cho nhau. Nếu bạn muốn thực sự mô phạm về nó, thì bạn có thể định nghĩa "lớp lồng" để chỉ một lớp bên trong tĩnh, một lớp không có thể hiện kèm theo. Trong mã, bạn có thể có một cái gì đó như thế này:

public class Outer {
    public class Inner {}

    public static class Nested {}
}

Đó thực sự không phải là một định nghĩa được chấp nhận rộng rãi.

8
Daniel Spiewak

Lớp lồng nhau là một thuật ngữ rất chung chung: mỗi lớp không thuộc cấp cao nhất là lớp lồng nhau . Lớp bên trong là lớp lồng không tĩnh . Joseph Darcy đã viết một lời giải thích rất hay về Lồng nhau, Bên trong , Thành viên và Lớp học cấp cao nhất .

8
Wouter Coekaerts

Nhắm mục tiêu người học, những người mới làm quen với Java và/hoặc Các lớp lồng nhau 

Các lớp lồng nhau có thể là:
1. Các lớp lồng nhau tĩnh.
2. Các lớp lồng nhau không tĩnh. (còn được gọi là Lớp bên trong ) => Hãy nhớ điều này


1. Lớp học bên trong
Thí dụ:

class OuterClass  {
/*  some code here...*/
     class InnerClass  {  }
/*  some code here...*/
}


Các lớp bên trong là tập hợp con của các lớp lồng nhau:

  • lớp bên trong là một loại cụ thể của lớp lồng
  • các lớp bên trong là tập con của các lớp lồng nhau
  • Bạn có thể nói rằng một lớp bên trong cũng là một lớp lồng nhau, nhưng bạn có thểKH&OCIRC;NGnói rằng một lớp lồng nhau cũng là một lớp bên trong

Chuyên ngành của lớp Nội:

  • ví dụ của một lớp bên trong có quyền truy cập vào tất cả của các thành viên của lớp bên ngoài, ngay cả những người được đánh dấu là riêng tư


2. Các lớp lồng nhau:
Thí dụ:

class EnclosingClass {
  static class Nested {
    void someMethod() { System.out.println("hello SO"); }
  }
}

Trường hợp 1: Khởi tạo một lớp lồng tĩnh từ một lớp không kèm theo

class NonEnclosingClass {

  public static void main(String[] args) {
    /*instantiate the Nested class that is a static
      member of the EnclosingClass class:
    */

    EnclosingClass.Nested n = new EnclosingClass.Nested(); 
    n.someMethod();  //prints out "hello"
  }
}

Trường hợp 2: Khởi tạo một lớp lồng tĩnh từ một lớp kèm theo

class EnclosingClass {

  static class Nested {
    void anotherMethod() { System.out.println("hi again"); } 
  }

  public static void main(String[] args) {
    //access enclosed class:

    Nested n = new Nested(); 
    n.anotherMethod();  //prints out "hi again"
  }

}

Đặc sản của các lớp tĩnh:

  • Lớp bên trong tĩnh sẽ chỉ có quyền truy cập vào các thành viên tĩnh của lớp bên ngoài và không có quyền truy cập vào các thành viên không tĩnh.

Phần kết luận:
Câu hỏi: Sự khác biệt chính giữa lớp bên trong và lớp lồng tĩnh trong Java là gì?
Trả lời: chỉ cần đi qua chi tiết cụ thể của từng lớp được đề cập ở trên.

7
VdeX

Ummm ... một lớp bên trong IS một lớp lồng nhau ... bạn có nghĩa là lớp ẩn danh và lớp bên trong?

Chỉnh sửa: Nếu bạn thực sự có nghĩa là bên trong so với ẩn danh ... một lớp bên trong chỉ là một lớp được xác định trong một lớp, chẳng hạn như:

public class A {
    public class B {
    }
}

Trong khi đó một lớp ẩn danh là một phần mở rộng của một lớp được định nghĩa ẩn danh, do đó không có lớp "thực tế nào được định nghĩa, như trong:

public class A {
}

A anon = new A() { /* you could change behavior of A here */ };

Chỉnh sửa thêm:

Wikipedia tuyên bố có một sự khác biệt trong Java, nhưng tôi đã làm việc với Java được 8 năm và đó là lần đầu tiên tôi nghe thấy một sự khác biệt như vậy ... không đề cập đến việc không có tài liệu tham khảo nào để sao lưu khiếu nại ... dòng dưới cùng, một lớp bên trong là một lớp được định nghĩa trong một lớp (tĩnh hoặc không) và lồng nhau chỉ là một thuật ngữ khác có nghĩa tương tự.

Có một sự khác biệt tinh tế giữa lớp lồng tĩnh và không tĩnh ... về cơ bản các lớp bên trong không tĩnh có quyền truy cập ngầm vào các trường và các phương thức của lớp kèm theo (do đó chúng không thể được xây dựng trong ngữ cảnh tĩnh, nó sẽ là một trình biên dịch lỗi). Mặt khác, các lớp lồng nhau tĩnh không có quyền truy cập ngầm định vào các trường và phương thức thể hiện và CÓ THỂ được xây dựng trong một bối cảnh tĩnh.

7
Mike Stone

Lớp bên tronglớp tĩnh lồng nhau trong Java đều là các lớp được khai báo bên trong một lớp khác, được gọi là lớp cấp cao nhất trong Java. Trong thuật ngữ Java, nếu bạn khai báo một lớp tĩnh lồng nhau, nó sẽ được gọi là lớp tĩnh lồng nhau trong Java trong khi lớp lồng không tĩnh được gọi đơn giản là Lớp bên trong. 

Lớp bên trong trong Java là gì?

Bất kỳ lớp nào không phải là mức cao nhất hoặc được khai báo bên trong một lớp khác được gọi là lớp lồng nhau và trong số các lớp lồng nhau đó, lớp được khai báo không tĩnh được gọi là lớp bên trong Java. Có ba loại lớp bên trong trong Java:

1) Lớp bên trong cục bộ - được khai báo bên trong một khối mã hoặc phương thức.
2) Lớp bên trong ẩn danh - là lớp không có tên để tham chiếu và khởi tạo tại cùng nơi mà nó được tạo.
3) Lớp bên trong thành viên - được khai báo là thành viên không tĩnh của lớp bên ngoài.

public class InnerClassTest {
    public static void main(String args[]) {      
        //creating local inner class inside method i.e. main() 
        class Local {
            public void name() {
                System.out.println("Example of Local class in Java");

            }
        }      
        //creating instance of local inner class
        Local local = new Local();
        local.name(); //calling method from local inner class

        //Creating anonymous inner class in Java for implementing thread
        Thread anonymous = new Thread(){
            @Override
            public void run(){
                System.out.println("Anonymous class example in Java");
            }
        };
        anonymous.start();

        //example of creating instance of inner class
        InnerClassTest test = new InnerClassTest();
        InnerClassTest.Inner inner = test.new Inner();
        inner.name(); //calling method of inner class
    }

     //Creating Inner class in Java
    private class Inner{
        public void name(){
            System.out.println("Inner class example in Java");
        }
    }
}

Lớp tĩnh lồng nhau trong Java là gì?

Lớp tĩnh lồng nhau là một lớp khác được khai báo bên trong một lớp là thành viên và được tạo tĩnh. Lớp tĩnh lồng nhau cũng được khai báo là thành viên của lớp ngoài và có thể được đặt ở chế độ riêng tư, công khai hoặc được bảo vệ như bất kỳ thành viên nào khác. Một trong những lợi ích chính của lớp tĩnh lồng nhau so với lớp bên trong là thể hiện của lớp tĩnh lồng nhau không được gắn với bất kỳ thể hiện kèm theo nào của lớp ngoài. Bạn cũng không cần bất kỳ phiên bản nào của lớp ngoài để tạo cá thể của lớp tĩnh lồng nhau trong Java.

1) Nó có thể truy cập thành viên dữ liệu tĩnh của lớp ngoài bao gồm cả riêng tư.
2) Lớp lồng nhau tĩnh không thể truy cập thành viên dữ liệu không tĩnh (thể hiện) hoặc phương thức.

public class NestedStaticExample {
    public static void main(String args[]){  
        StaticNested nested = new StaticNested();
        nested.name();
    }  
    //static nested class in Java
    private static class StaticNested{
        public void name(){
            System.out.println("static nested class example in Java");
        }
    }
}

Tham chiếu: Lớp bên trong và Lớp tĩnh lồng trong Java với Ví dụ

6
roottraveller

Khi chúng ta khai báo lớp thành viên tĩnh bên trong một lớp, nó được gọi là lớp lồng cấp cao nhất hoặc lớp lồng tĩnh. Nó có thể được chứng minh như sau: 

class Test{
    private static int x = 1;
        static class A{
        private static int y = 2;
        public static int getZ(){
            return B.z+x;
        }
    }
    static class B{
        private static int z = 3;
        public static int getY(){
            return A.y;
        }
    }
}

class TestDemo{
     public static void main(String[] args){
        Test t = new Test();
        System.out.println(Test.A.getZ());
        System.out.println(Test.B.getY());
    }
}

Khi chúng ta khai báo lớp thành viên không tĩnh bên trong một lớp, nó được gọi là lớp bên trong. Lớp bên trong có thể được chứng minh như sau: 

    class Test{
        private int i = 10;
        class A{
            private int i =20;
            void display(){
            int i = 30;
            System.out.println(i);
            System.out.println(this.i);
            System.out.println(Test.this.i);
        }
    }
}
5
Pankti

Tôi nghĩ mọi người ở đây nên chú ý đến Poster rằng: Lớp Nest tĩnh chỉ là lớp bên trong đầu tiên . Ví dụ:

 public static class A {} //ERROR

 public class A {
     public class B {
         public static class C {} //ERROR
     }
 }

 public class A {
     public static class B {} //COMPILE !!!

 }

Vì vậy, tóm tắt, lớp tĩnh không phụ thuộc vào lớp nào. Vì vậy, họ không thể trong lớp học bình thường. (vì lớp bình thường cần một ví dụ).

5
hqt

Sau đây là ví dụ về static nested classinner class:

OuterClass.Java

public class OuterClass {
     private String someVariable = "Non Static";

     private static String anotherStaticVariable = "Static";  

     OuterClass(){

     }

     //Nested classes are static
     static class StaticNestedClass{
        private static String privateStaticNestedClassVariable = "Private Static Nested Class Variable"; 

        //can access private variables declared in the outer class
        public static void getPrivateVariableofOuterClass(){
            System.out.println(anotherStaticVariable);
        }
     }

     //non static
     class InnerClass{

         //can access private variables of outer class
         public String getPrivateNonStaticVariableOfOuterClass(){
             return someVariable;
         }
     }

     public static void accessStaticClass(){
         //can access any variable declared inside the Static Nested Class 
         //even if it private
         String var = OuterClass.StaticNestedClass.privateStaticNestedClassVariable; 
         System.out.println(var);
     }

}

OuterClassTest:

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

        //access the Static Nested Class
        OuterClass.StaticNestedClass.getPrivateVariableofOuterClass();

        //test the private variable declared inside the static nested class
        OuterClass.accessStaticClass();
        /*
         * Inner Class Test
         * */

        //Declaration

        //first instantiate the outer class
        OuterClass outerClass = new OuterClass();

        //then instantiate the inner class
        OuterClass.InnerClass innerClassExample =  outerClass. new InnerClass();

        //test the non static private variable
        System.out.println(innerClassExample.getPrivateNonStaticVariableOfOuterClass()); 

    }

}
3
Pritam Banerjee

Một biểu đồ

enter image description here

Sự khác biệt giữa static nestednon-static nested lớp

  • Các lớp lồng tĩnh không có quyền truy cập trực tiếp vào các thành viên khác (các biến và phương thức không tĩnh) của lớp kèm theo vì vì nó là tĩnh, nên nó phải truy cập vào các thành viên không tĩnh của lớp kèm theo thông qua một đối tượng. Đó là, nó không thể đề cập trực tiếp đến các thành viên không tĩnh của lớp kèm theo. Do hạn chế này, các lớp lồng nhau hiếm khi được sử dụng.
  • Các lớp lồng nhau không tĩnh (các lớp bên trong) có quyền truy cập vào tất cả các thành viên (các biến và phương thức tĩnh và không tĩnh, bao gồm cả riêng tư) của lớp bên ngoài của nó và có thể tham chiếu trực tiếp đến chúng giống như các thành viên không tĩnh khác của bên ngoài Lớp làm.

Đọc thêm tại đây

1
yoAlex5

Tôi nghĩ rằng không có câu trả lời nào ở trên đưa ra ví dụ thực tế cho bạn về sự khác biệt giữa lớp lồng nhau và lớp lồng tĩnh về mặt thiết kế ứng dụng. Và sự khác biệt chính giữa lớp lồng nhau tĩnh và lớp bên trong là khả năng truy cập vào trường đối tượng lớp bên ngoài.

Chúng ta hãy xem hai ví dụ sau đây.

Lớp tổ tĩnh: Một ví dụ điển hình về việc sử dụng các lớp lồng tĩnh là mẫu trình xây dựng ( https://dzone.com/articles/design-potypes-the-builder-potype ).

Đối với BankAccount, chúng tôi sử dụng lớp lồng nhau tĩnh, chủ yếu là vì

  1. Ví dụ lớp tổ tĩnh có thể được tạo trước lớp bên ngoài.

  2. Trong mẫu trình xây dựng, trình xây dựng là lớp trình trợ giúp được sử dụng để tạo BankAccount.

  3. BankAccount.Builder chỉ được liên kết với BankAccount. Không có lớp nào khác liên quan đến BankAccount.Builder. Vì vậy, tốt hơn là tổ chức chúng lại với nhau mà không sử dụng quy ước tên.
public class BankAccount {

    private long accountNumber;
    private String owner;
    ...

    public static class Builder {

    private long accountNumber;
    private String owner;
    ...

    static public Builder(long accountNumber) {
        this.accountNumber = accountNumber;
    }

    public Builder withOwner(String owner){
        this.owner = owner;
        return this; 
    }

    ...
    public BankAccount build(){
            BankAccount account = new BankAccount(); 
            account.accountNumber = this.accountNumber;
            account.owner = this.owner;
            ...
            return account;
        }
    }
}

Lớp bên trong: Một cách sử dụng phổ biến của các lớp bên trong là để định nghĩa một trình xử lý sự kiện. https://docs.Oracle.com/javase/tutorial/uiswing/events/generalrules.html

Đối với MyClass, chúng tôi sử dụng lớp bên trong, chủ yếu là vì:

  1. Lớp bên trong MyAdOG cần truy cập vào thành viên lớp bên ngoài.

  2. Trong ví dụ này, MyAd CHƯƠNG chỉ được liên kết với MyClass. Không có lớp nào khác liên quan đến MyAd CHƯƠNG. Vì vậy, tốt hơn là tổ chức chúng lại với nhau mà không sử dụng quy ước tên

public class MyClass extends Applet {
    ...
        someObject.addMouseListener(new MyAdapter());
    ...
    class MyAdapter extends MouseAdapter {
        public void mouseClicked(MouseEvent e) {
            ...// Event listener implementation goes here...
            ...// change some outer class instance property depend on the event
        }
    }
}
1
Leogao

Ngôn ngữ lập trình Java cho phép bạn định nghĩa một lớp trong một lớp khác. Một lớp như vậy được gọi là một lớp lồng nhau và được minh họa ở đây:

class OuterClass {
...
class NestedClass {
    ...
    }
}

Các lớp lồng nhau được chia thành hai loại: tĩnh và không tĩnh. Các lớp lồng nhau được khai báo tĩnh được gọi là các lớp lồng tĩnh. Các lớp lồng không tĩnh được gọi là các lớp bên trong . Một điều mà chúng ta nên ghi nhớ là các lớp lồng không tĩnh (các lớp bên trong) có quyền truy cập vào các thành viên khác của lớp kèm theo, ngay cả khi chúng được khai báo là riêng tư. Các lớp lồng nhau tĩnh chỉ có quyền truy cập vào các thành viên khác của lớp kèm theo nếu chúng là tĩnh. Nó không thể truy cập các thành viên không tĩnh của lớp bên ngoài . Cũng như các phương thức và biến lớp, một lớp lồng tĩnh được liên kết với lớp bên ngoài của nó. Ví dụ, để tạo một đối tượng cho lớp lồng tĩnh, sử dụng cú pháp này:

OuterClass.StaticNestedClass nestedObject =
 new OuterClass.StaticNestedClass(); 

Để khởi tạo một lớp bên trong, trước tiên bạn phải khởi tạo lớp bên ngoài. Sau đó, tạo đối tượng bên trong bên trong đối tượng bên ngoài với cú pháp này:

OuterClass.InnerClass innerObject = new OuterClass().new InnerClass();

Tại sao chúng ta sử dụng các lớp lồng nhau

  1. Đó là một cách phân nhóm hợp lý các lớp chỉ được sử dụng ở một nơi.
  2. Nó làm tăng đóng gói.
  3. Nó có thể dẫn đến mã dễ đọc và dễ bảo trì hơn.

Nguồn: Hướng dẫn Java ™ - Các lớp lồng nhau

0
Mithun Debnath

Trước hết Không có lớp nào được gọi là Lớp tĩnh. Công cụ sửa đổi tĩnh sử dụng với lớp bên trong (được gọi là Lớp lồng nhau) nói rằng nó là thành viên tĩnh của Lớp ngoài, có nghĩa là chúng ta có thể truy cập nó như với các thành viên tĩnh khác và không cần ví dụ của lớp ngoài. (Đó là lợi ích của tĩnh ban đầu.) 

Sự khác biệt giữa việc sử dụng lớp Nested và lớp Nội bộ thông thường là:

OuterClass.InnerClass inner = new OuterClass().new InnerClass();

Đầu tiên Chúng ta có thể khởi tạo Outerclass sau đó chúng ta có thể truy cập vào Nội.

Nhưng nếu Class được lồng nhau thì cú pháp là:

OuterClass.InnerClass inner = new OuterClass.InnerClass();

Mà sử dụng Cú pháp tĩnh như triển khai bình thường của từ khóa tĩnh.

0
Sohi