Câu hỏi 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 nhau tĩnh trong Java là gì? Việc thiết kế / thực hiện có đóng vai trò trong việc lựa chọn một trong số này không?


1468
2017-09-16 08:22


gốc


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


Các câu trả lời:


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 đơn giản được gọi là các lớp lồng nhau tĩnh. Các lớp lồng nhau 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 bao quanh:

OuterClass.StaticNestedClass

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

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

Các đối tượng là các cá thể của một lớp bên trong tồn tại trong một cá thể của lớp bên ngoài. 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 cá thể 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 - Lớp lồng nhau

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


1482
2017-09-16 08:28



Lưu ý rằng bạn cũng có thể nhập trực tiếp lớp lồng nhau tĩnh, tức là bạn có thể thực hiện (ở đầu tệp): import OuterClass.StaticNestedClass;  sau đó tham khảo lớp học chỉ như OuterClass. - Camilo Díaz Repka
@Martin là có bất kỳ tên kỹ thuật cụ thể cho thành ngữ này của việc tạo ra một lớp bên trong OuterClass.InnerClass innerObject = outerObject.new InnerClass();? - Geek
Vì vậy, việc sử dụng lớp lồng nhau tĩnh tư nhân là gì? Tôi nghĩ chúng ta không thể khởi tạo nó từ bên ngoài như OuterClass.StaticNestedClass nestedObject = new OuterClass.StaticNestedClass (); - RoboAlex
@ Ilya Kogan: Tôi có nghĩa là tôi biết chúng ta có thể truy cập vào các 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? 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ư một người bạn lớp với tôi :) - Mubashar Ahmad
@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à một 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 () {return 2; }} 'là tĩnh hơn bất kỳ giá trị nào nếu tôi chỉ đơn giản là gán bất kỳ đối tượng nào khác vào trường tĩnh, a, như trong: class B {static void main (chuỗi s) {Aa = new A ()}} (A & B trong cùng một gói) Điều này không làm cho A là một lớp tĩnh. Cụm từ "ngữ cảnh tĩnh", trong tài liệu tham khảo được trích dẫn trong chuỗi bạn đã liên kết đến, cực kỳ mơ hồ. Trong thực tế, điều này đã được ghi nhận trong nhiều ý kiến ​​trong chủ đề đó. - GrantRobertson


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

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

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

Các lớp học có thể được lồng vào 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, nhiều cấp độ lồng lớp là hiếm, vì nó thường là thiết kế kém.

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 khi nó không được sử dụng trong bất kỳ ngữ 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à các biến / trường nào phụ thuộc vào loại lớp lồng nhau, cho dù bên trong hay tĩnh).
  • tiện lợi: phải tạo một tệp mới cho mọi loại mới là khó chịu, một lần nữa, đặc biệt 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, họ là:

  • lớp tĩnh: được khai báo là thành viên tĩnh của một lớp khác
  • lớp bên trong: được khai báo là thành viên của một 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 một lớp khác
  • lớp bên trong vô danh: giống như lớp bên trong cục bộ, nhưng được viết dưới dạng biểu thức trả về một đối tượng một lần

Hãy để tôi xây dựng chi tiết hơn.


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 cá thể của lớp chứa.

Một lớp tĩnh là một lớp được khai báo là một thành viên tĩnh của một 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 có chứa như là không gian tên của nó, ví dụ. lớp Con dê khai báo là một thành viên tĩnh của lớp Tê giác trong gói pizza được gọi bằng cá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 gói. Lý do thực sự duy nhất để tạo ra 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 của nó, nhưng tôi thấy đây là một lý do khá lame cho tính năng lớp tĩnh tồn tại.


Lớp bên trong

Một lớp bên trong là một lớp được khai báo là một thành viên không tĩnh của một 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 của nó, pizza.Rhino.Goat, nhưng bên trong lớp chứa, nó có thể được biết bằng tên đơn giản của nó. Tuy nhiên, mọi cá thể của một lớp bên trong được gắn với một cá thể cụ thể của lớp chứa của nó: ở trên, Con dê được tạo ra tại jerry, được gắn hoàn toàn với Tê giác ví dụ điều này trong jerry. Nếu không, chúng tôi sẽ liên kết Tê giác ví dụ rõ ràng khi chúng tôi khởi tạo Con dê:

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

(Lưu ý rằng bạn tham khảo kiểu bên trong như chỉ Con dê trong kỳ lạ Mới cú pháp: Java xâm nhập vào loại có chứa từ tê giác phần. Và vâng new rhino.Goat () cũng sẽ có ý nghĩa hơn với tôi.)

Vậy điều này làm chúng ta đạt được điều gì? Vâng, cá thể lớp bên trong có quyền truy cập vào các cá thể thành viên của cá thể lớp chứa. Những thành phần thể hiện bao quanh này được gọi bên trong lớp bên trong thông qua chỉ là tên đơn giản của họ, không phải thông qua  điều này (điều này trong lớp bên trong tham chiếu đến cá thể lớp bên trong, không phải là cá thể lớp có chứa):

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 điều này của lớp chứa Rhino.thisvà bạn có thể sử dụng điều này để chỉ các thành viên của nó, ví dụ. Rhino.this.barry.


Lớp nội bộ đị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 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 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 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 của nó. Khi cá thể sử dụng một địa phương cuối cùng của phương thức chứa của nó, biến giữ lại giá trị nó được giữ tại thời điểm tạo cá thể, ngay cả khi biến đã nằm ngoài phạm vi (đây là phiên bản bị đóng của phiên bản giới hạn).

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 một gói, nó không được khai báo với một 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 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 thể hiện, một sự khởi tạo của lớp bên trong được gắn với cá thể được giữ bởi phương thức chứa điều này tại thời điểm tạo cá thể của cá thể, và vì vậy các cá thể thành viên của lớp chứa có thể truy cập giống như trong lớp bên trong của cá thể. 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ộ Con mèo được khởi tạo là new Cat (), không phải mới this.Cat () như bạn có thể mong đợi.


Lớp bên trong ẩn danh

Một lớp bên trong vô danh là một cách thuận tiện về cú pháp để viết một lớp bên trong cục bộ. Thông thường, 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. Nó sẽ là tốt đẹp, sau đó, nếu chúng ta có thể kết hợp định nghĩa lớp bên trong nội bộ và sự khởi tạo đơn của nó thành một dạng cú pháp thuận tiện, và nó cũng sẽ tốt đẹp nếu chúng ta không phải nghĩ ra tên cho lớp đó (ít hữu ích hơn tên mã của bạn chứa, càng tốt). Một lớp bên trong vô danh cho phép cả hai thứ 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 hàm tạo của riêng bạn; thay vào đó, một cái được cung cấp ngầm mà chỉ đơn giản gọi các hàm tạo siêu, do đó các đối số được cung cấp phải phù hợp với hàm tạo siêu. (Nếu cha mẹ chứa nhiều hàm tạo, cái "đơn giản nhất" đượ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 để học chi tiết - chỉ chú ý đến những gì NetBeans hoặc Eclipse cho bạn biết.)

Ngoài ra, bạn có thể chỉ định giao diện để triển khai:

new *InterfaceName*() {*members*}

Một khai báo như vậy tạo ra một cá thể mới của một lớp chưa được đặt tên, mở rộng Object và các công cụ Tên giao diện. 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 no-arg, do-nothing (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ể đưa ra một lớp bên trong vô danh một hàm tạo, nhưng 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 vô danh đơn giản là một cách ít linh hoạt hơn để tạo ra một lớp bên trong cục bộ với một cá thể. 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 với Vật hoặc chỉ định hàm tạo riêng của nó, bạn đang mắc kẹt tạo một lớp bên trong được đặt tên thường xuyên cục bộ.


525
2017-09-16 09:24



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 vào 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
Mặc dù bạn không thể cung cấp hàm tạo riêng cho các lớp bên trong vô danh, bạn có thể sử dụng khởi tạo dấu ngoặc kép. c2.com/cgi/wiki?DoubleBraceInitialization - Casebash
Lời 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/… cho một biến thể lớn của mô hình xây dựng phụ thuộc rất nhiều vào việc sử dụng các lớp bên trong tĩnh. - Mansoor Siddiqui
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. - Someone Somewhere
Riêng lớp lồng nhau tĩnh cũng khá hữu ích: bạn muốn có chúng, nhưng bạn không muốn để lộ chúng. Giống như Entry <T> trong một LinkedList <T>, hoặc AsyncTasks bên trong một Activity (Android), v.v ... - Lorenzo Dematté


Tôi không nghĩ 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 tĩnh bổ nghĩa.
  • Lớp lồng nhau không tĩnh được gọi là lớp bên trong. (Tôi ở lại với lớp lồng nhau 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 lồng nhau là tĩnh hay không?

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

Lớp lồng nhau không tĩnh là một con thú khác. Tương tự như các lớp bên trong vô danh, các lớp lồng nhau như vậy thực sự bị đóng. Điều đó có nghĩa là họ nắm bắt phạm vi xung quanh của họ và trường hợp kèm theo của họ và làm cho có thể truy cập. Có lẽ một ví dụ sẽ làm rõ điều đó. Xem phần này của một 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 tới vùng chứa cha. Sử dụng lớp lồng nhau không tĩnh, thao tác này không hoạt động. Bạn có thể truy cập cá thể kèm theo của Container bằng cú pháp Container.this.

Nhiều lời giải thích khác về hardcore sau:

Nếu bạn nhìn vào các bytecode Java, trình biên dịch tạo ra một lớp lồng nhau (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 thiết lập trong hàm tạo có một tham số bổ sung của kiểu Container để chỉ định cá thể kèm theo. Bạn không thể nhìn thấy tham số này trong nguồn nhưng trình biên dịch ngầm tạo ra nó cho một lớp lồng nhau.

Ví dụ của Martin

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

như vậy sẽ được biên soạn thành một cuộc gọi của một cái gì đó như (trong bytecodes)

new InnerClass(outerObject)

Vì mục đích hoàn chỉnh:

Một lớp ẩn danh  một ví dụ hoàn hảo của một lớp lồng nhau không tĩnh mà không có tên liên kết với nó và không thể được tham chiếu sau này.


124
2017-09-16 09:12



"Không có sự khác biệt ngữ nghĩa giữa một lớp lồng nhau 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 tư của lồng nhau. - Brad Cupit
Liệu lớp bên trong không tĩnh có thể gây ra 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
@G_V chắc chắn có tiềm năng cho 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. Cho dù đây là một vấn đề thực tế phụ thuộc vào nơi và làm thế nào tham chiếu đến các trường hợp của các lớp bên ngoài và bên trong được tổ chức. - 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 nhau tĩnh trong thiết kế ứng dụng:

Xem qua

Một lớp lồng nhau có thể 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 lớp khác. Một lớp lồng nhau nên tồn tại chỉ để phục vụ là kèm theo lớp, nếu một lớp lồng nhau là hữu ích bởi các lớp khác (không chỉ các lớp kèm theo), nên được khai báo là một lớp cấp cao nhất.

Sự khác biệt

Lớp lồng nhau không tĩnh : được liên kết hoàn toàn với cá thể 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 cá thể kèm theo. Một cách sử dụng phổ biến của một lớp lồng nhau không phải là để định nghĩa một lớp Adapter.

Lớp lồng nhau tĩnh : không thể truy cập kèm theo cá thể lớp 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 nhau không yêu cầu quyền truy cập vào một cá thể của lớp kèm theo. Việc sử dụng phổ biến của lớp lồng nhau 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 tĩnh có thể truy cập cá thể của lớp container, trong khi tĩnh không thể.


82
2018-05-27 07:43



: từ kết luận của bạn "trong khi tĩnh không thể", thậm chí không trường hợp tĩnh của container? Chắc chắn rồi? - VedX
Việc 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
Trong nhiều trường hợp, câu trả lời ngắn gọn là rõ ràng hơn và tốt hơn. Đây là một ví dụ. - Eric Wang


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 bao quanh 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 bao quanh, do đó bạn có thể chỉ định bất kỳ bộ lọc truy cập nào - 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, mà chỉ có thể được khai báo public hoặc gói riêng tư.

Các lớp bên trong aka Các lớp Non-stack có quyền truy cập vào các thành viên khác của lớp hàng đầu, ngay cả khi chúng được khai báo riêng trong khi các lớp lồng nhau 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 ta và Inner2 là lớp bên trong của chúng ta không tĩnh. Sự khác biệt chính giữa chúng, bạn không thể tạo ra Inner2 ví dụ mà không có bên ngoài nơi bạn có thể tạo Inner1 đối tượng độc lập.

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

Hãy nghĩ về một tình huống Class A và Class 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 của bạn.

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 Inner 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 cá thể của lớp bao quanh / 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 các trường của lớp bên ngoài, nó 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, sử dụng cú pháp này:

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

Ưu điểm của một 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 ra khi chạy.


29
2017-10-17 22:35



ý bạn là OuterClass.Inner2 inner = outer.new Inner2();? - Erik Allik
static inner là một mâu thuẫn về mặt. - user207421
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. - user207421


Tôi nghĩ, quy ước thường được theo sau là:

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

Tuy nhiên, một số khác điểm để ghi nhớ là:

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

  • Các lớp bên trong có quyền truy cập vào các biến cá thể của cá thể kèm theo của lớp Outer [parent]. Tuy nhiên, không phải tất cả các lớp bên trong đều có các cá thể bao quanh, ví dụ các lớp bên trong trong các ngữ cảnh tĩnh, giống như một lớp ẩn danh được sử dụng trong một 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ó thêm mệnh đề nào để mở rộng bất kỳ lớp nào khác hoặc thực hiện thêm bất kỳ giao diện nào. 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 mở ra cái nào để sử dụng và khi nào? Vâng mà chủ yếu phụ thuộc vào những gì kịch bản bạn đang đối phó với nhưng đọc trả lời được đưa ra bởi @ jrudolph có thể giúp bạn thực hiện một số quyết định.


24
2017-12-16 11:38





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

Hy vọng nó giúp!

Lớp bên trong

  • Có thể truy cập đến lớp ngoài cả hai trường hợp và tĩnh phương pháp 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 ngoài (lưu ý Mới vị trí từ khóa):

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

  • Không thể có Lớp học hoặc là Giao diện tờ khai

Lớp lồng nhau tĩnh

  • Không thể truy cập lớp ngoài ví dụ phương pháp hoặc trường

  • Không liên kết với bất kỳ cá thể 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ả trường và phương thức riêng tư của lớp ngoài
  • Cũng là Lớp ngoài có quyền truy cập vào trường và phương thức riêng tư của lớp bên trong
  • Cả hai lớp đều có thể có công cụ sửa đổi truy cập riêng, được bảo vệ hoặc công khai

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

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

  • Nó là một cách phân nhóm các lớp logic chỉ được sử dụng ở một nơi: Nếu một lớp chỉ có ích cho một lớp khác, thì nó hợp lý để nhúng nó vào lớp đó và giữ hai lớp lại với nhau. Việc lồng ghép "các lớp trợ giúp" làm cho gói của họ đượ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, A và B, trong đó B cần truy cập vào các thành viên của A mà nếu không sẽ được tuyên bố 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 riêng và B có thể truy cập chúng. Ngoài ra, B chính nó có thể được ẩn từ thế giới bên ngoài.

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


23
2017-12-31 20:53





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

Các loại:

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

Sự khác biệt:

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

Trong đối tượng lớp lồng nhau không tĩnh của lớp bên trong tồn tại 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 ngoài trước tiên.

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, bởi vì từ "tĩnh" cho biết không cần phải 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, sau đó viết phương thức bên trong sau

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

13
2018-04-04 10:14





Ví dụ của lớp bên trong được tạo ra khi cá thể của lớp ngoài được tạo ra. 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 cá thể (đối tượng) của lớp bên ngoài. Khi cá thể của lớp ngoài vượt ra ngoài phạm vi, các cá thể lớp bên trong cũng không tồn tại.

Lớp lồng nhau tĩnh không có một cá thể cụ thể. Nó chỉ được nạp 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à các biến không có quyền truy cập vào các cá thể 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ì nó không cần thiết để tạo ra cá thể của lớp đó. Do đó, quy tắc là cố gắng định nghĩa lớp lồng nhau tĩnh, với phạm vi giới hạn nhất có thể (private> = class> = protected> = public), và sau đó chuyển nó thành lớp bên trong (bằng cách loại bỏ định danh "tĩnh") và nới lỏng phạm vi, nếu nó thực sự cần thiết.


10
2017-09-16 08:58



Câu đầu tiên không đúng. Không có thứ gì như 'các thể hiện của lớp bên trong ', và các thể hiện 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 tuân theo câu đầu tiên. - user207421


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 constructor của lớp được gọi, hoặc ít nhất là không cho đến sau khi các thuộc tính được tham chiếu lần đầ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à 'innerItem' đề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 sau khi lớp được khởi tạo (hoặc ít nhất là không cho đến sau khi mục tĩnh lồng nhau được tham chiếu lần đầu), như bạn có thể thấy cho chính mình bằng cách bình luận và bỏ ghi chú các dòng mà tôi đề cập ở trên. Điều tương tự cũng không giữ đúng cho 'outerItem'.

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


10
2018-03-26 12:38