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


1766

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?


65
Câu trả lời của Joshua Bloch là trong bài đọc Java hiệu quảitem 22 : Favor static member classes over non static
Raymond Chenon

4
Đối với hồ sơ, đó là mục 24 trong phiên bản thứ 3 của cùng một cuốn sách.
ZeroCool

Câu trả lời:


1696

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 nhau 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 InternalClass 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 outerObject = new OuterClass()
OuterClass.InnerClass innerObject = outerObject.new InnerClass();

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

Đối với tính đầy đủ lưu ý rằng cũng có một thứ như một lớp bên trong mà 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 một bối cảnh tĩnh và không có một thể hiện kèm theo.


130
Lưu ý rằng bạn cũng có thể nhập trực tiếp một lớp lồng tĩnh, tức là bạn có thể làm (ở đầu tệp): import OuterClass.StaticNestedClass; sau đó tham chiếu lớp giống như OuterClass.
Camilo Díaz Repka

4
@Martin có tên kỹ thuật cụ thể nào cho thành ngữ này để tạo một lớp bên trong OuterClass.InnerClass innerObject = outerObject.new InnerClass();không?
Geek

7
Vì vậy, việc sử dụng lớp lồng tĩnh riêng là gì? Tôi nghĩ rằng chúng ta không thể khởi tạo nó từ bên ngoài như OuterClass.StaticNestedClass NestedObject = new OuterClass.StaticNestedClass ();
RoboAlex

1
@Ilya Kogan: Ý tôi là tôi biết rằng chúng ta có thể truy cập các trường lớp của cha mẹ tĩnh hoặc không tĩnh (nếu lớp con là lớp không tĩnh) tôi đã sử dụng các tính năng của chúng vài lần, nhưng tôi đã tự hỏi rằng loại mô hình bộ nhớ nào họ làm theo? các khái niệm OOP bao gồm chúng như một khái niệm riêng biệt? nếu không phải nơi họ thực sự phù hợp trong OOP. Có vẻ như là một lớp bạn với tôi :)
Mubashar

4
@martin, tôi biết tôi đến chủ đề này cực kỳ muộn nhưng: Đó là biến của bạn, a, đó là trường tĩnh của lớp, A. Điều đó không có nghĩa là lớp ẩn danh được tạo bởi 'new A () {int t () {trả lại 2; }} 'là tĩnh bất kỳ nếu tôi chỉ định bất kỳ đối tượng nào khác cho trường tĩnh, a, như trong: class B {static void main (chuỗi s) {Aa = new A ()}} (A & B giống nhau gói) Điều này không làm cho A một lớp tĩnh. Cụm từ "bối cảnh tĩnh", trong tài liệu tham khảo được trích dẫn trong chuỗi mà bạn liên kết đến, là vô cùng mơ hồ. Trong thực tế, điều này đã được ghi nhận trong nhiều ý kiến ​​trong chủ đề đó.
GrantRobertson

599

Các hướng dẫn Java nói :

Thuật ngữ: 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 nhau tĩnh. Các lớp lồng không tĩnh được gọi là các lớp bên trong.

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 vào nhau vô hạn , 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 này 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 thể hiện 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 sử dụng lớp chứa làm không gian tên của nó, ví dụ: Lớp Dê đượ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 nó, 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 thể hiện của một lớp bên trong được gắn với một trường hợp cụ thể của lớp chứa nó: trên, được tạo trong jerry , được ngầm gắn liền với Rhino dụ này trong jerry . Mặt khác, chúng tôi làm cho cá thể 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 chỉ là trong cú pháp mới lạ : Java nhập vào loại có chứa từ phần tê giác . Và, vâng, mới tê giác . Goat () cũng có ý nghĩa hơn đối 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 chỉ qua các tên đơn giản của chúng, không phải thông qua cái này ( cái này trong lớp bên trong đề cập đến thể hiện của lớp bên trong, không phải là thể hiện của lớp có 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 này của lớp chứa như Rhino.this , và bạn có thể sử dụng này để tham khảo cho các thành viên của nó, ví dụ như Rhino.this.barry .


Lớp học địa phương

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 lớp bên trong được gắn với thể hiện được giữ bởi phương thức chứa này tại thời điểm tạo cá thể, và do đó, các thành viên thể hiện của lớp chứa có thể truy cập được như trong một thể hiện lớp bên trong. Một lớp bên trong cục bộ được khởi tạo đơn giản thông qua tên của nó, ví dụ lớp bên trong cục bộ Cat được khởi tạo là Cat mới () , không phải new.Cat () như bạn mong đợi.


Lớp học ẩn danh

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 thật tuyệt nếu chúng ta không phải nghĩ ra một tên cho lớp (càng ít hữu í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*}

Đây là một biểu thức trả về một thể hiện mới của một lớp chưa được đặt tên mở rộng ParentClassName . Bạn không thể cung cấp nhà xây dựng của riêng bạn; đúng hơn, một cái được cung cấp ngầm mà chỉ đơn giản gọi siêu cấu trúc, vì vậy các đối số được cung cấp phải phù hợp với siêu cấu trúc. (Nếu cha mẹ chứa nhiều hàm tạo, thì đơn giản nhất là một hàm được gọi là, đơn giản nhất được xác định bởi một bộ quy tắc khá phức tạp không đáng bận tâm để tìm hiểu chi tiết - chỉ cần chú ý đến những gì NetBeans hoặc Eclipse nói với bạn.)

Ngoài ra, bạn có thể chỉ định một giao diện để thực hiện:

new *InterfaceName*() {*members*}

Một khai báo như vậy tạo ra một thể hiện mới của một lớp không tên mở rộng Object và thực hiện InterfaceName . Một lần nữa, bạn không thể cung cấp hàm tạo của riêng bạn; trong trường hợp này, Java ngầm cung cấp một hàm tạo không có đối số, không làm gì (vì vậy sẽ không bao giờ có các đối số hàm tạo trong trường hợp này).

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.


39
Câu chuyện tuyệt vời, cảm ơn. Nó có một sai lầm mặc dù. Bạn có thể truy cập các trường của một lớp bên ngoài từ một lớp bên trong thể hiện bởi Rhino.this.variableName.
Thirler

2
Trong khi bạn không thể cung cấp hàm tạo của riêng mình cho các lớp bên trong ẩn danh, bạn có thể sử dụng khởi tạo cú đúp. c2.com/cgi/wiki?DoubleBraceInitialization
Casebash

30
Giải thích tuyệt vời, nhưng tôi không đồng ý với các lớp bên trong tĩnh là vô giá trị. Xem rwhansen.blogspot.com/2007/07/ trên để biết một biến thể lớn của mẫu trình xây dựng phụ thuộc nhiều vào việc sử dụng các lớp bên trong tĩnh.
Mansoor Siddiqui

9
Tôi cũng không đồng ý rằng lớp bên trong tĩnh là vô giá trị: nếu bạn muốn một enum trong lớp bên trong, bạn sẽ phải làm cho lớp bên trong tĩnh.
Ai đó ở đâu đó vào

9
Các lớp lồng tĩnh riêng cũng khá hữu ích: bạn muốn có chúng, nhưng bạn không muốn phơi bày chúng. Giống như Mục nhập <T> trong Danh sách được liên kết <T> hoặc
AsyncT task

150

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 công cụ sửa đổi tĩnh .
  • 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 đượ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 nhau. 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 danh mộ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 này.


17
"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." Ngoại trừ lớp lồng nhau có thể thấy các trường / phương thức riêng của cha mẹ và lớp cha có thể thấy các trường / phương thức riêng của lồng nhau.
Brad Cupit

Lớp bên trong không tĩnh có khả năng gây rò rỉ bộ nhớ lớn không? Như trong, mỗi khi bạn tạo một người nghe, bạn tạo ra một rò rỉ?
G_V

3
@G_V chắc chắn có khả năng rò rỉ bộ nhớ vì một thể hiện của lớp bên trong giữ một tham chiếu đến lớp bên ngoài. Đây có phải là một vấn đề thực tế hay không phụ thuộc vào vị trí và cách tham chiếu đến các thể hiện của lớp bên ngoài và lớp bên trong được tổ chức.
jrudolph

94

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:

Tổng quat

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ó í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 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 không có thể có thể truy cập thể hiện của lớp container, trong khi tĩnh không thể .


: từ kết luận của bạn "trong khi tĩnh không thể", thậm chí không phải là các trường hợp tĩnh của container? Chắc chắn rồi?
VdeX 21/07/2015

Một cách sử dụng phổ biến của lớp lồng nhau tĩnh là mẫu thiết kế ViewHolder trong RecyclerView và ListView.
Hamzeh Soboh

1
Trong nhiều trường hợp, câu trả lời ngắn gọn rõ ràng hơn và tốt hơn. Đây là một ví dụ như vậy.
Eric Wang

32

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 publichoặ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 {
    }
}

Inner1là lớp bên trong tĩnh của chúng tôi và Inner2là 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 Inner2cá thể mà không có Ngoại mà bạn có thể tạo một Inner1đối tượng 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 mà Class AClass Bcó liên quan, Class Bcần phải truy cập Class Acác thành viên và Class Bchỉ liên quan đến Class A. Các lớp học 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à một 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.


3
ý bạn là OuterClass.Inner2 inner = outer.new Inner2();sao
Erik Kaplun

4
static innerlà một mâu thuẫn trong điều khoản.
Hầu tước Lorne

Và các lớp bên trong không còn được gọi là 'các lớp không ngăn xếp'. Không sử dụng định dạng mã cho văn bản không phải là mã và sử dụng nó cho văn bản.
Hầu tước Lorne

30

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

Hy vọng nó giúp!

Lớp học bên trong

  • Có thể truy cập vào lớp bên ngoài cả trường và phương thức tĩnh và trường
  • Liên kết với thể hiện của lớp kèm theo để 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 ý vị trí từ khóa mới ):

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

  • Không thể có khai báo Class hoặc Interface

Lớp lồng tĩnh

  • Không thể truy cập lớp bên ngoài ví dụ phương pháp hoặc các lĩnh vực

  • 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ả hai lớp bên trong có thể truy cập ngay cả các trường riêng và các phương thức của lớp bên ngoài
  • Ngoài ra, lớp ngoài có quyền truy cập vào các trường riêng và phương thức 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 đủ ):

  • Đó 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: Nếu một lớp chỉ hữu ích cho một lớp khác, thì việc nhúng nó vào lớp đó và giữ hai lớp lại với nhau là hợp lý. 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 sự đó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 mà 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

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

  • lớp tĩnh trong lớp cấp cao nhất là lớp lồng nhau
  • lớp không tĩnh trong lớp cấp cao nhất là lớp bên trong , lớp này có thêm hai dạng:
    • 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 [cha] bên ngoài 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 class [Anonymous] extends YourClass {}
    • new YourInterface(){}; có nghĩa 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.


15

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 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ì từ "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);

13

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à 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à 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, 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> = bảo vệ> = công khai), sau đó chuyển đổi nó sang lớp bên trong (bằng cách xóa định danh "tĩnh") và nới lỏng phạm vi, nếu nó thực sự cần thiết.


2
Câu đầu tiên không chính xác. Không có những điều như ' sự thể hiện của các lớp bên trong, và trường hợp của nó có thể được tạo ra bất cứ lúc nào sau khi lớp bên ngoài đã được khởi tạo. Câu thứ hai không theo từ câu đầu tiên.
Hầu tước Lorne

11

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 được lồng dường như không được khởi tạo cho đến khi hàm tạo của lớp được gọi, hoặc ít nhất là cho đến khi các thuộc tính được tham chiếu lần đầu, ngay cả khi chúng được đánh dấu là "trận chung kết".

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à 'InternalItem' đều được khai báo là 'cuối cùng tĩnh'. cài đặt của Nested.innerItem không diễn ra cho đến khi lớp được khởi tạo (hoặc ít nhất là cho đến khi mục tĩnh được lồng lần đầu tiên được tham chiếu), như bạn có thể tự mình nhìn thấy bằng cách nhận xét và bỏ qua các dòng mà tôi đề cập đến, ở trên. Điều tương tự không đúng với 'outsItem'.

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


10

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 nhau" để chỉ một lớp bên trong tĩnh, một lớp không có trường hợp 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.


2
"Nội tĩnh" là một mâu thuẫn trong các điều khoản.
Hầu tước Lorne

5
Nó không phải là quy ước định nghĩa lớp bên trong là một lớp lồng không tĩnh, mà là JLS. docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-8.1.3
Lew Bloch

1
Và các điều khoản không 'được sử dụng thay thế cho nhau'.
Hầu tước Lorne

tại sao tĩnh trong ví dụ trên đưa ra lỗi trong ide
DeV

10

Trong trường hợp tạo cá thể, thể hiện của lớp bên trong không 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. Điều này có nghĩa là nó có ví dụ bao gồm. Nhưng thể hiện của lớp bên trong tĩnh đượ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….
  }
}

1
"Nội tĩnh" là một mâu thuẫn trong các điều khoản. Một lớp lồng nhau là tĩnh hoặc bên trong.
Hầu tước Lorne

9

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, điều đó có nghĩa là chúng GIỮ một con trỏ tới thể hiện của lớp kèm theo của chúng có thể dẫn đến tràn bộ nhớ hoặc ngoại lệ chồng chéo 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 trên // inner = null;Chương trình sẽ đưa ra " Tôi bị phá 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à bởi 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 cá thể mà liên quan đến lớp. Chương trình trên có thể in " Tôi bị hủy! " Nếu bạn tạo lớp bên trong tĩnh và khởi tạo vớiOuter.Inner i = new Outer.Inner();




8

Ummm ... một lớp bên trong LÀ 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 ... chưa kể không có tài liệu tham khảo nào ở đó để sao lưu yêu cầu ... phía dưới dò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.


3
Theo tài liệu Java, có một sự khác biệt giữa lớp bên trong và lớp lồng tĩnh - các lớp lồng tĩnh không có tham chiếu đến lớp kèm theo của chúng và được sử dụng chủ yếu cho mục đích tổ chức. Bạn sẽ thấy câu trả lời của Jegschemech để có một mô tả sâu hơn.
mipadi

1
Tôi nghĩ rằng sự khác biệt về ngữ nghĩa chủ yếu là lịch sử. Khi tôi viết trình biên dịch C # -> Java 1.1, tham chiếu ngôn ngữ Java rất rõ ràng: lớp lồng nhau là tĩnh, lớp bên trong không (và do đó có $ 0). Dù sao thì nó cũng khó hiểu và tôi mừng vì nó không còn là vấn đề nữa.
Tomer Gabel

2
JLS định nghĩa "lớp bên trong" trong docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-8.1.3 và đó là lý do tại sao không thể có "bên trong" không tĩnh lớp "trong Java. "Lồng nhau" KHÔNG "chỉ là một thuật ngữ khác có nghĩa tương tự" và KHÔNG ĐÚNG rằng "một lớp bên trong là một lớp được định nghĩa trong một lớp (tĩnh hay không)". Đó là thông tin TUYỆT VỜI.
Lew Bloch

6

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
Ví 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 KHÔNG thể nó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á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:
Ví 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"
  }

}

Chuyên ngành của 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.

Kết luận:
Câu hỏi: 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ì?
Trả lời: chỉ cần đi qua chi tiết cụ thể của từng lớp được đề cập ở trên.



6

Cả lớp bên trong và lớ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 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 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 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ỳ cá thể 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 các thành viên dữ liệu tĩnh của lớp bên 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 hoặc phương thức dữ liệu không tĩnh (thể hiện) .

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ụ


2
"Lớp lồng nhau tĩnh không thể truy cập thành viên hoặc phương thức dữ liệu không tĩnh (thể hiện)." là không chính xác, và gây nhầm lẫn . Họ hoàn toàn có quyền truy cập vào thông tin cá nhân - miễn là họ tạo một thể hiện để truy cập thông tin cá thể đó. Họ không có một cá thể kèm theo như các lớp bên trong, nhưng họ có quyền truy cập vào các thành viên riêng của cá thể lớp kèm theo của họ.
TJ Crowder

5

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ụ).


2
Đây là tất cả nonense. Tất cả điều này cho thấy rằng một lớp bên trong không thể chứa một lớp tĩnh. Phần về 'không phụ thuộc vào lớp nào chứa' là vô nghĩa, như câu sau đây.
Hầu tước Lorne

5

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

"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" Điều đó không có nghĩa. "Một lớp cấp cao nhất là một lớp không phải là một lớp lồng nhau." Không có thứ gọi là "lớp lồng nhau cấp cao nhất".
Radiodef

3

Sau đây là một 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

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 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 lồng 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 của 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
        }
    }
}

2

Một biểu đồ

nhập mô tả hình ảnh ở đây

Sự khác biệt chính giữa static nestednon-static nestedcác lớp là static nested không có quyền truy cập vào các thành viên lớp ngoài không tĩnh


0

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ó bất kỳ thành viên tĩnh nào khác. 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.


1
"... nói rằng đó là một thành viên tĩnh của Lớp ngoài có nghĩa là ....": Không có gì sai khi nghĩ về một lớp lồng tĩnh là một "lớp thành viên" của Lớp ngoài, nhưng sự tương đồng với các trường tĩnh và phương pháp kết thúc ở đó. Một lớp lồng tĩnh không "thuộc" lớp ngoài. Trong hầu hết mọi cách quan trọng, một lớp lồng tĩnh là một lớp cấp cao nhất miễn phí có định nghĩa lớp được lồng bên trong lớp ngoài để thuận tiện cho việc đóng gói (và, hy vọng, bởi vì có một mối liên hệ logic giữa lớp lồng nhau và Lớp ngoài ... mặc dù không cần phải có một cái).
scottb

1
"Nội tĩnh" là một mâu thuẫn trong các điều khoản. Các lớp tĩnh tồn tại, ở cấp độ lồng đầu tiên, và chúng không phải là các lớp bên trong, theo định nghĩa. Rất bối rối.
Hầu tước Lorne

0

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 nhau 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. Như với 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 nhau tĩnh, hãy 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


-1

Sự khác biệt là một khai báo lớp lồng nhau cũng là tĩnh có thể được khởi tạo bên ngoài lớp kèm theo.

Khi bạn có một khai báo lớp lồng nhau không tĩnh, còn được gọi là lớp bên trong , Java sẽ không cho phép bạn khởi tạo nó ngoại trừ thông qua lớp kèm theo. Đối tượng được tạo ra từ lớp bên trong được liên kết với đối tượng được tạo từ lớp bên ngoài, vì vậy lớp bên trong có thể tham chiếu các trường bên ngoài.

Nhưng nếu nó là tĩnh, thì liên kết không tồn tại, các trường bên ngoài không thể được truy cập (ngoại trừ thông qua một tham chiếu thông thường như bất kỳ đối tượng nào khác) và do đó bạn có thể tự khởi tạo lớp lồng nhau.


1
Điều này là sai sự thật. Có một cú pháp đặc biệt để tạo một lớp bên trong bên ngoài phạm vi của lớp kèm theo.
Hầu tước Lorne

-1

Nó khá đơn giản, so sánh các lớp cục bộ tĩnh và các lớp bên trong không tĩnh
Khác biệt:
Lớp cục bộ tĩnh:
Chỉ có thể truy cập các thành viên tĩnh của lớp bên ngoài.
Không thể có bộ khởi tạo tĩnh
Không thể truy cập trực tiếp từ bên ngoài hàm được khai báo


-2

Tôi đã minh họa nhiều kịch bản đúng và lỗi có thể xảy ra trong mã java.

    class Outter1 {

        String OutStr;

        Outter1(String str) {
            OutStr = str;
        }

        public void NonStaticMethod(String st)  {

            String temp1 = "ashish";
            final String  tempFinal1 = "ashish"; 

            //  below static attribute not permitted
            // static String tempStatic1 = "static";    

            //  below static with final attribute not permitted         
            // static final String  tempStatic1 = "ashish";  

            // synchronized keyword is not permitted below          
            class localInnerNonStatic1 {            

                synchronized    public void innerMethod(String str11) {
                    str11 = temp1 +" sharma";
                    System.out.println("innerMethod ===> "+str11);
                }

                /* 
        //  static method with final not permitted
          public static void innerStaticMethod(String str11) { 

                    str11 = temp1 +" india";
                    System.out.println("innerMethod ===> "+str11);
                }*/
            }

            // static class not permitted below
            //  static class localInnerStatic1 {   }                            

        }

        public static  void StaticMethod(String st)     {

            String temp1 = "ashish";
            final String  tempFinal1 = "ashish"; 

            // static attribute not permitted below
            //static String tempStatic1 = "static";     

            //  static with final attribute not permitted below
            // static final String  tempStatic1 = "ashish";                         

            class localInnerNonStatic1 {
                public void innerMethod(String str11) {
                    str11 = temp1 +" sharma";
                    System.out.println("innerMethod ===> "+str11);
                }

                /*
    // static method with final not permitted
    public static void innerStaticMethod(String str11) {  
                    str11 = temp1 +" india";
                    System.out.println("innerMethod ===> "+str11);
                }*/
            }

            // static class not permitted below
            //  static class localInnerStatic1 {   }    

        }

        // synchronized keyword is not permitted
        static  class inner1 {          

            static String  temp1 = "ashish";
            String  tempNonStatic = "ashish";
            // class localInner1 {

            public void innerMethod(String str11) {
                str11 = temp1 +" sharma";
                str11 = str11+ tempNonStatic +" sharma";
                System.out.println("innerMethod ===> "+str11);
            }

            public static void innerStaticMethod(String str11) {
                //  error in below step
                str11 = temp1 +" india";    
                //str11 = str11+ tempNonStatic +" sharma";
                System.out.println("innerMethod ===> "+str11);
            }
            //}
        }

        //synchronized keyword is not permitted below
        class innerNonStatic1 {             

//This is important we have to keep final with static modifier in non
// static innerclass below
            static final String  temp1 = "ashish";  
            String  tempNonStatic = "ashish";
            // class localInner1 {

            synchronized    public void innerMethod(String str11) {
                tempNonStatic = tempNonStatic +" ...";
                str11 = temp1 +" sharma";
                str11 = str11+ tempNonStatic +" sharma";
                System.out.println("innerMethod ===> "+str11);
            }

            /*
            //  error in below step
            public static void innerStaticMethod(String str11) {   
                            //  error in below step
                            // str11 = tempNonStatic +" india";                     
                            str11 = temp1 +" india";
                            System.out.println("innerMethod ===> "+str11);
                        }*/
                    //}
                }
    }

1
Rõ ràng là phần mã. Và trong trường hợp bạn không nhận thấy: ví dụ mã của bạn rất khó đọc. Ngay cả trên màn hình máy tính để bàn khổng lồ của tôi, tôi có một thanh cuộn ngang. Xem xét đưa ý kiến ​​của bạn lên trên hoặc bên dưới điều họ đang bình luận - thay vì phía sau .
GhostCat
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.