2016-09-16 12 views
30

xem xét:Biến 'this' trong Java thực sự được đặt thành đối tượng hiện tại như thế nào?

class TestParent{ 
    public int i = 100; 
    public void printName(){ 
    System.err.println(this); //{[email protected]} according to the Debugger. 
    System.err.println(this.i); //this.i is 100. 
    } 
} 

class TestChild extends TestParent{ 
    public int i = 200; 
} 

public class ThisTest { 
    public static void main(String[] args) { 
    new TestChild().printName(); 
    } 
} 

Tôi biết rằng câu hỏi tương tự đã được yêu cầu, nhưng tôi không thể có được một sự hiểu biết vững chắc của 'này' biến trong Java.

Hãy để tôi giải thích cách tôi hiểu kết quả của hình ảnh ở trên.

  1. Kể từ đó là một đối tượng new TestChild() đó là cách gọi phương thức printName(), các this biến trong dòng 6 được thiết lập để một đối tượng TestChild - {TestChild @ 428} theo Debugger. Tuy nhiên, vì Java không có trường ảo - tôi không hoàn toàn chắc chắn điều này có nghĩa là gì, nhưng tôi hiểu khái niệm đó là ngược lại với các phương thức Java, hỗ trợ Đa hình - this.i được đặt thành 100 trong số TestParent tại thời gian biên dịch.

  2. Vì vậy, không có vấn đề gì this, this.i trong một phương pháp TestParent sẽ luôn luôn là i biến trong lớp TestParent.

Tôi không chắc rằng sự hiểu biết của tôi là chính xác vì vậy hãy sửa tôi nếu tôi sai.

Và cũng có thể, câu hỏi chính của tôi là,

Làm thế nào là this biến các thiết lập để các đối tượng hiện tại mà đang gọi phương pháp này? Làm thế nào nó thực sự được thực hiện?

+0

Khi bạn làm, cố gắng thêm một 'toString() 'phương pháp (http://www.javapractices.com/topic/TopicAction.do?Id=55) cho' TestParent 'và xem những gì' System.out.println (điều này); sản lượng.' – c0der

+0

IMO' this' đại diện cho lớp hiện tại vì vậy nếu bạn đang ràng buộc bất cứ điều gì với 'this', nghĩa là bạn đang ràng buộc với' class' hiện tại và cho rằng bạn cần 'variable/instance/member/method ...' của lớp hiện tại. – emotionlessbananas

+4

Kevin Park, sự hiểu biết của bạn trong các mục từ 1. đến 3. là chính xác. @AsteriskNinja, 'this' đề cập đến một đối tượng, không phải là một lớp (trong một số trường hợp sự khác biệt là không quan trọng, trong những người khác nó là rất quan trọng). –

Trả lời

38

Về bản chất, không có sự khác biệt giữa

this.foo() 

anyObject.foo() 

như cả được "thực hiện" theo cùng một cách. Hãy ghi nhớ rằng "cuối cùng" "định hướng đối tượng chỉ là một trừu tượng, và trong 'thực tế' những gì xảy ra là một cái gì đó như:

foo(callingObject) 

Nói cách khác: bất cứ khi nào bạn sử dụng một số tài liệu tham khảo đối tượng để gọi một phương pháp ... cuối cùng không có một cuộc gọi trên một số đối tượng.Vì sâu xuống trong lắp ráp và mã máy, một cái gì đó giống như "một cuộc gọi trên một cái gì đó" không tồn tại.

Điều gì thực sự xảy ra là gọi đến một hàm và tham số đầu tiên (ẩn/ẩn trên cấp mã nguồn) là đối tượng đó

BTW: bạn thực sự có thể viết rằng xuống Java như:

class Bar { 
    void foo(Bar this) { ... } 

và sau đó sử dụng

new Bar().foo(); 

Và cho việc này.fieldA, cuối cùng: bạn có một tham chiếu đến một số vị trí trong bộ nhớ; và một bảng cho bạn biết "bù đắp" nào bạn sẽ tìm thấy fieldA.

Chỉnh sửa - chỉ để lưu nội dung trò chuyện. Nếu bạn muốn biết thêm chi tiết về foo (Bar this) - bạn có thể chuyển sang số question này; đưa ra các chi tiết trong thông số Java đằng sau nó!

+9

Chính xác. Một số ngôn ngữ làm cho cơ chế này rõ ràng hơn (nói Python, nơi bạn * có * để xác định đối số 'tự' trong phương thức) – Bakuriu

+8

CÁI GÌ! Tôi không biết bạn có thể viết một tham số 'this' như thế (mặc dù tôi đã biết rằng nó đã được sử dụng trong nội bộ với các lớp bên trong). Ví dụ đó vẫn không hợp pháp mặc dù vì nó thiếu một kiểu trả về (và một thân phương thức). – Pokechu22

+2

@ Pokechu22 Cảm ơn bạn đã làm cho tôi biết; cố định ví dụ của tôi. Và có, rất ít người biết rằng bạn có thể viết ra các phương pháp như thế. – GhostCat

1

Khi đối tượng mới được tạo, đối tượng có địa chỉ trong bộ nhớ để bạn có thể nghĩ nó như thể đối tượng có thành viên riêng this được đặt thành địa chỉ khi đối tượng được tạo. Bạn cũng có thể nghĩ về nó như thế này: obj.method(param) chỉ là cú pháp đường cho method(obj, param);this thực sự là một tham số của method.

18

Điều đang xảy ra ở đây là có hai trường hoàn toàn khác nhau được gọi là i; để sử dụng tên đầy đủ của họ, một là TestParent::i và một là TestChild::i.

Bởi vì phương pháp printName được định nghĩa trong TestParent, khi nó đề cập đến i, nó chỉ có thể nhìn thấy TestParent::i, được thiết lập để 100.

Trong khi đó khi bạn thiết i-200 trong TestChild, cả hai lĩnh vực được gọi là i hiển thị, nhưng vì chúng có cùng tên, TestChild::ihidesTestParent::i và bạn kết thúc thiết lập TestChild::i và để lại TestParent::i không bị ảnh hưởng.

+5

Đây là câu trả lời thực sự cho câu hỏi. Tôi không biết tại sao các câu trả lời khác lại nói về các đối số phương thức. – Nayuki

+0

@Nayuki Bởi vì câu trả lời này sẽ không đầy đủ mà không cần lưu ý rằng từ khóa này chỉ là một đối số bình thường – Taemyr

0

Để trực tiếp địa chỉ những gì bạn nhìn thấy trong đầu ra: Cuộc gọi để in 'this.i' này chuyển làm đối số 'print()' giá trị của trường 'i' trong phạm vi hiện tại, là phạm vi của lớp cha. Ngược lại, lời gọi để in 'this' sẽ được dịch dưới mui xe sang một cuộc gọi để in 'this.getClass(). GetName()' [nói gần], và cuộc gọi 'getClass()' nhận đối tượng lớp thực tế , dành cho lớp trẻ em.

0

Thêm một số thông tin khác trên đầu trang của câu trả lời @Tom Anderson, giải thích khái niệm ẩn một cách độc đáo.

Tôi đã thêm một hàm tạo khác trong Child (TestChild) in các giá trị của i trong cả cha và mẹ.

Nếu bạn muốn nhận được giá trị của i từ trẻ em (TestChild), ghi đè phương pháp này trong TestChild.

class TestParent{ 
    public int i = 100; 
    public void printName(){ 
    System.err.println("TestParent:printName()"); 
    System.err.println(this); //{[email protected]_NUM} according to the Debugger. 
    System.err.println(this.i); //this.i is 100. 
    } 
} 

class TestChild extends TestParent{ 
    public int i = 200; 
    public TestChild(){ 
    System.out.println("TestChild.i and TestParent.i:"+this.i+":"+super.i); 
    } 
    public void printName(){ 
     //super.printName(); 
     System.err.println("TestChild:printName()"); 
     System.err.println(this); //{[email protected]_NUM} according to the Debugger. 
     System.err.println(this.i); //this.i is 200. 
    } 
} 

public class ThisTest { 
    public static void main(String[] args) { 
    TestParent parent = new TestChild(); 
    parent.printName(); 
    } 
} 

Trường hợp 1: Nếu tôi nhận xét super.printName() cuộc gọi từ đứa trẻ, phiên bản trẻ em của TestChild.printName() in giá trị của i trong TestChild

Output:

TestChild.i and TestParent.i:200:100 
TestChild:printName() 
[email protected] 
200 

Trường hợp 2: TestChild.printName() gọi super.printName() làm dòng đầu tiên trong phương thức printName(). Trong trường hợp này, i giá trị từ cả cha và con được hiển thị trong các phương thức tương ứng.

Output:

TestChild.i and TestParent.i:200:100 
TestParent:printName() 
[email protected] 
100 
TestChild:printName() 
[email protected] 
200 
Các vấn đề liên quan