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 为什么理解这个过程如此重要?想象一下,如果您不知道“幕后”到底发生了什么,那么创建一个普通对象的结果会是多么出乎意料:)现在是时候返回课程并完成一些任务了!祝你好运,很快见到你!:)
评论
  • 受欢迎
你必须先登录才能发表评论
此页面还没有任何评论