CodeGym /Java 博客 /随机的 /对象创建期间的操作顺序
John Squirrels
第 41 级
San Francisco

对象创建期间的操作顺序

已在 随机的 群组中发布
你好!今天的课程将相当……呃……多面 :) 从某种意义上说,我们将涵盖广泛的主题,但它们都与对象创建过程相关。 对象创建期间的操作顺序 - 1我们会从头到尾分析一下:构造函数是怎么调用的,字段(包括静态字段)是怎么初始化的,按照什么顺序初始化的等等。文章中讨论的一些点我们之前已经触及到了,大家可以浏览一下关于基类构造函数的材料。首先,让我们回顾一下对象是如何创建的。你很清楚从开发者的角度来看这个过程是怎样的:他创建了一个类,写了new,然后一切就绪了:)这里我们将讨论当我们写的时候计算机和 Java 机器内部发生了什么,例如:

Cat cat = new Cat();
我们之前已经讨论过这个问题,但为了以防万一,我们会提醒您:
  • 首先,分配存储对象的内存。
  • 接下来,Java 机器创建对象的引用(在我们的例子中,引用是 Cat cat)。
  • 最后,初始化变量并调用构造函数(我们将更详细地研究这个过程)。
此外,从关于对象生命周期的课程中,您可能还记得只要至少有一次引用,一个对象就会持续存在。如果没有剩余,则该对象成为垃圾收集器的猎物。 对象创建期间的操作顺序 - 2前两点不应引起任何特殊问题。内存分配是一个简单的过程,只有两种可能的结果:要么有内存,要么没有 :) 而创建链接并不罕见。但第三点代表的是一整套严格按顺序执行的操作。我不喜欢死记硬背测试,但是你需要很好地理解这个过程并且你需要记住这个操作顺序. 前面几节课讲对象创建过程的时候,你对继承一无所知,所以有些东西解释起来有问题。现在你知道了很多,我们终于可以完整地考虑这个问题了:)所以第三点说“最后,变量被初始化,构造函数被调用。 ”但是这一切发生的顺序是什么?为了更好地理解,让我们创建两个超级简单的类——一个父类和一个子类:

public class Vehicle { 

   public static int vehicleCounter = 0; 

   private String description = "Vehicle"; 
   public Vehicle() { 
   } 

   public String getDescription() { 
       return description; 
   } 
} 

public class Truck extends Vehicle { 

   private static int truckCounter = 0; 

   private int yearOfManufacture; 
   private String model; 
   private int maxSpeed; 

   public Truck(int yearOfManufacture, String model, int maxSpeed) { 
       this.yearOfManufacture = yearOfManufacture; 
       this.model = model; 
       this.maxSpeed = maxSpeed; 

       Vehicle.vehicleCounter++; 
       truckCounter++; 
   } 
}
该类Truck是卡车的一个实现,其中包含代表其年份、型号和最大速度的字段。现在我们要创建一个这样的对象:

public class Main { 

   public static void main(String[] args) throws IOException { 

       Truck truck = new Truck(2017, "Scania S 500 4x2", 220); 
   } 
}
对于 Java 机器,该过程将如下所示:
  1. 发生的第一件事是类的静态变量Vehicle被初始化。是的,我说的是Vehicle类,不是Truck。静态变量在调用构造函数之前初始化,这从父类开始。让我们尝试验证这一点。我们将类vehicleCounter中的字段设置Vehicle为 10 并尝试在VehicleTruck构造函数中显示它。

    
    public class Vehicle { 
    
       public static int vehicleCounter = 10; 
       private String description = "Vehicle"; 
    
       public Vehicle() { 
           System.out.println(vehicleCounter); 
       } 
    
       public String getDescription() { 
           return description; 
       } 
    } 
    
    public class Truck extends Vehicle { 
    
       private static int truckCount = 0;
    
       private int yearOfManufacture; 
       private String model; 
       private int maxSpeed; 
    
       public Truck(int yearOfManufacture, String model, int maxSpeed) { 
           System.out.println(vehicleCounter); 
           this.yearOfManufacture = yearOfManufacture; 
           this.model = model; 
           this.maxSpeed = maxSpeed; 
    
           Vehicle.vehicleCounter++; 
           truckCount++; 
       } 
    }
    

    我们特意将 println 语句放在构造函数的最开头,以确保在显示Truck时卡车的字段尚未初始化。vehicleCounter

    结果如下:

    
    10 
    10
    
  2. 父类的静态变量初始化完成后,再对子类的静态变量进行初始化。在我们的例子中,这是班级truckCounter的领域Truck

    让我们做另一个实验,我们将尝试在初始化其他字段之前显示构造函数truckCounter内部的值:Truck

    
    public class Truck extends Vehicle { 
    
       private static int truckCounter = 10; 
    
       private int yearOfManufacture; 
       private String model; 
       private int maxSpeed; 
    
       public Truck(int yearOfManufacture, String model, int maxSpeed) { 
           System.out.println(truckCounter); 
           this.yearOfManufacture = yearOfManufacture; 
           this.model = model; 
           this.maxSpeed = maxSpeed; 
    
           Vehicle.vehicleCounter++; 
           truckCounter++; 
       } 
    }
    

    如您所见,在构造函数开始时,值 10已经分配给我们的静态变量Truck

  3. 现在还不是构造函数的时间!变量初始化继续。 第三次初始化父类的非静态变量。 如您所见,继承使创建对象的过程变得非常复杂,但是您对此无能为力:您只需要记住编程中的一些东西:)

    作为实验,我们可以descriptionVehicle类中为变量赋一些初始值,然后在构造函数中更改它。

    
    public class Vehicle { 
    
       public static int vehicleCounter = 10; 
    
       private String description = "Initial value of the description field"; 
    
       public Vehicle() { 
           System.out.println(description); 
           description = "Vehicle"; 
           System.out.println(description); 
       } 
    
       public String getDescription() { 
           return description; 
       } 
    }
    

    让我们运行 main()创建卡车的方法:

    
    public class Main { 
    
       public static void main(String[] args) throws IOException { 
    
           Truck truck = new Truck(2017, "Scania S 500 4x2", 220); 
       } 
    }
    

    我们得到以下结果:

    
    Initial value of the description field 
    Vehicle
    

    这证明在Vehicle构造函数开始时该description字段已经被赋值。

  4. 最后,是构造函数的时间了! 更准确地说,是基类构造函数的时候了。它在对象创建过程的第四步被调用。

    这也很容易验证。让我们尝试向控制台输出两行:一行在Vehicle基类构造函数中,第二行在Truck构造函数中。我们需要确信Vehicle首先显示里面的行:

    
    public Vehicle() { 
    
       System.out.println("Hello from the Vehicle constructor!"); 
    } 
    
    public Truck(int yearOfManufacture, String model, int maxSpeed) { 
    
       System.out.println("Hello from the Truck constructor!"); 
       this.yearOfManufacture = yearOfManufacture; 
       this.model = model; 
       this.maxSpeed = maxSpeed; 
    
       Vehicle.vehicleCounter++; 
       truckCounter++; 
    }
    

    我们将运行我们的main()方法并查看结果:

    
    Hello from the Vehicle constructor! 
    Hello from the Truck constructor! 
    

    出色的。这意味着我们没有弄错 :) 让我们继续。

  5. 现在是初始化子类(即我们的类)的非静态字段的Truck时候了。被实例化的类中的字段直到第五步才被初始化!令人惊讶,但确实如此 :) 同样,我们将做一个简单的检查——就像父类一样:我们将一些初始值赋给变量,maxSpeed并在Truck构造函数中检查该值是否在构造函数启动之前分配:

    
    public class Truck extends Vehicle { 
    
       private static int truckCounter = 10; 
    
       private int yearOfManufacture; 
       private String model; 
       private int maxSpeed = 150; 
    
       public Truck(int yearOfManufacture, String model, int maxSpeed) { 
    
           System.out.println("Initial value of maxSpeed = " + this.maxSpeed); 
           this.yearOfManufacture = yearOfManufacture; 
           this.model = model; 
           this.maxSpeed = maxSpeed; 
    
           Vehicle.vehicleCounter++; 
           truckCounter++; 
       } 
    }
    

    控制台输出:

    
    Initial value of maxSpeed = 150
    

    可以看到,  构造函数启动Truck ,maxSpeed 已经等于150了!

  6. Truck调用子类的构造函数。

    只有在这一点上,最后,我们正在实例化的类的构造函数才会被调用!

    只有在第六步中,这些字段才会被赋予我们作为参数传递给卡车的值。

    如您所见,“构建”一辆卡车,即对象创建过程,并不简单。但我们似乎已将其分解为最小的部分 :)

对象创建期间的操作顺序 - 3 为什么理解这个过程如此重要?想象一下,如果您不知道“幕后”到底发生了什么,那么创建一个普通对象的结果会是多么出乎意料:)现在是时候返回课程并完成一些任务了!祝你好运,很快见到你!:)
评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION