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 đượ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.
import OuterClass.StaticNestedClass;
sau đó tham chiếu lớp giống như OuterClass.
OuterClass.InnerClass innerObject = outerObject.new InnerClass();
không?
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:
Có bốn loại lớp lồng nhau trong Java . Tóm lại, chúng là:
Hãy để tôi giải thích chi tiết hơn.
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.
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, Dê đượ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 Dê :
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à Dê 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 .
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.
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.
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:
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 là 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.
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:
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.
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.
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ể .
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 Inner2
cá 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 A
và Class B
có liên quan, Class B
cần phải truy cập Class A
các thành viên và Class B
chỉ 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.
OuterClass.Inner2 inner = outer.new Inner2();
sao
static inner
là một mâu thuẫn trong điều khoản.
Đâ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!
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ể 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();
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.
Tôi nghĩ rằng, quy ước thường được tuân theo là:
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.
Lớp lồng nhau: lớp bên trong lớp
Các loại:
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);
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.
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.
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.
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….
}
}
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();
Lớp lồng nhau là một thuật ngữ rất chung chung: mỗi lớp không phải là cấp cao nhất là một lớp lồng nhau. Một lớp bên trong là một lớp lồng không tĩnh. Joseph Darcy đã viết một lời giải thích rất hay về các lớp học lồng nhau, nội tâm, thành viên và cấp cao nhất .
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.
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:
Chuyên ngành của lớp Nội:
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:
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.
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ụ
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ụ).
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);
}
}
}
Sau đây là một ví dụ về static nested class
và inner 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());
}
}
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ì
Ví dụ lớp tổ tĩnh có thể được tạo trước lớp bên ngoài.
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.
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ì:
Lớp bên trong MyAdOG cần truy cập vào thành viên lớp bên ngoài.
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
}
}
}
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.
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
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.
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);
}*/
//}
}
}
item 22 : Favor static member classes over non static