2013-12-13 46 views
6

Tôi đã kết thúc kịch bản sau đây trong mã ngày hôm nay (mà tôi thừa nhận là kinda lạ và tôi đã được refactored). Khi tôi chạy thử nghiệm đơn vị của tôi, tôi thấy rằng một khởi tạo trường không được thiết lập bởi thời gian mà các nhà xây dựng siêu lớp đã chạy. Tôi nhận ra rằng tôi không hoàn toàn hiểu được thứ tự khởi tạo của nhà xây dựng/lĩnh vực, vì vậy tôi đăng bài với hy vọng rằng ai đó giải thích cho tôi thứ tự mà trong đó những điều này xảy ra.Khởi tạo trường đối tượng và thứ tự khởi tạo trong Java

class Foo extends FooBase { 
    String foo = "foobar"; 

    @Override 
    public void setup() { 
     if (foo == null) { 
      throw new RuntimeException("foo is null"); 
     } 
     super.setup(); 
    } 
} 

class FooBase { 
    public FooBase() { 
     setup(); 
    } 

    public void setup() { 

    } 
} 

@Test 
public void testFoo() { 
    new Foo(); 
} 

Chữ backtrace viết tắt từ JUnit như sau, tôi đoán tôi dự kiến ​​$ Foo. <init> để đặt foo.

$Foo.setup 
$FooBase.<init> 
$Foo.<init> 
.testFoo 

Trả lời

7

Có, trong trình khởi tạo trường Java (không giống như C#, ví dụ) được gọi là sau hàm tạo siêu lớp. Điều đó có nghĩa là bất kỳ cuộc gọi phương thức ghi đè nào từ hàm tạo sẽ được gọi là trước trình khởi tạo trường được thực hiện.

Trật tự là:

  • Initialize lớp cha (đệ quy gọi các bước sau)
  • Execute initializers lĩnh vực
  • Execute cơ thể constructor (sau bất kỳ chaining constructor, mà đã xảy ra ở bước 1)

Về cơ bản, bạn nên gọi các phương thức không phải cuối cùng trong các nhà xây dựng. Nếu bạn định làm như vậy, hãy ghi rõ rất rõ ràng để mọi người ghi đè phương thức biết rằng phương thức sẽ được gọi trước khi các trình khởi tạo trường (hoặc phần tử khởi tạo) được thực hiện.

Xem JLS section 12.5 để biết thêm chi tiết.

3

Hoạt động đầu tiên của nhà xây dựng luôn là yêu cầu của hàm tạo siêu lớp. Không có hàm tạo nào được định nghĩa rõ ràng trong một lớp tương đương với việc có

public Foo() { 
    super(); 
} 

Trình xây dựng của lớp cơ sở được gọi trước khi bất kỳ trường nào của lớp con được khởi tạo. Và lớp cơ sở của bạn làm điều gì đó cần phải tránh: gọi một phương thức có thể ghi đè.

Vì phương pháp này được ghi đè trong lớp con, nó được gọi trên một đối tượng chưa được xây dựng hoàn chỉnh và do đó thấy trường con là rỗng.

0

Dưới đây là một ví dụ về đa hình trong pseudo-C#/Java:

class Animal 
{ 
    abstract string MakeNoise(); 
} 

class Cat : Animal { 
    string MakeNoise() { 
    return "Meow"; 
    } 
} 

class Dog : Animal { 
    string MakeNoise() { 
    return "Bark"; 
    } 
} 

Main() { 
    Animal animal = Zoo.GetAnimal(); 
    Console.WriteLine (animal.MakeNoise()); 
} 

Chức năng chính không biết loại động vật và phụ thuộc vào hành vi thực hiện cụ thể của phương pháp MakeNoise().

class A 
{ 
A(int number) 
    { 
    System.out.println("A's" + " "+ number); 
    } 
} 

class B 
{ 
A aObject = new A(1); 
B(int number) 
    { 
    System.out.println("B's" + " "+ number);   
    } 
A aObject2 = new A(2); 
} 

public class myFirstProject { 
public static void main(String[] args) { 
    B bObj = new B(5); 
    } 
} 

ra: Một của 1 Một của 2 B 5

Quy tắc của tôi: 1. Không khởi tạo với các giá trị mặc định trong tuyên bố (null, false, 0, 0.0 ...) . 2.Ưu tiên khởi tạo khai báo nếu bạn không có tham số hàm tạo thay đổi giá trị của trường. 3. Nếu giá trị của trường thay đổi do tham số hàm tạo khởi tạo trong các hàm tạo. 4. Hãy nhất quán trong thực hành của bạn. (Quy tắc quan trọng nhất)

public class Dice 
{ 
private int topFace = 1; 
private Random myRand = new Random(); 

public void Roll() 
    { 
    // ...... 
    } 
} 

hoặc

public class Dice 
{ 
private int topFace; 
private Random myRand; 

public Dice() 
    { 
    topFace = 1; 
    myRand = new Random(); 
    } 

public void Roll() 
    { 
    // ..... 
    } 
} 
Các vấn đề liên quan