java中一个类如果想要初始化,是如何进行的呢?砍了编程思想,也结合了网上其他人的文章,最终有了一个新的认识。
一、初始化顺序
Java中类初始化顺序:(静态变量、静态初始化块)>(变量、初始化块)>构造器。
其中小括号内部之间的调用顺序是平行的,是按照代码的书写顺序来执行的。
二、静态变量、静态初始化块的初始化操作
我们知道,静态数据在内存中只占用一份存储空间。有几点需要注意:
1、 static关键字不能应用于局部变量中,也不能在构造方法里面是声明一个static关键字。
2、而且如果在使用之前没有对其初始化,jvm会先对其有一个默认的初始化值。
3、如果他是一个对象引用,那么他的默认初始化值就是null。
而静态初始化块,也是和静态变量一样,只执行一次。
static{
System.out.println("静态块");
}
三、变量、初始化块
首先是变量,变量的初始化很简单,其实概括起来也是几句话
1、若数据类型是基本类型,当没有在使用时进行初始化时,先有一个默认初始化值
2、局部变量在使用时如果没有初始化,则编译时直接报错、
3、一个类中含有对象,则在使用之前没有初始化时,就会默认有一个值null(和静态对象一样)
接下来是初始化块,先看一下初始化块
{
System.out.println("花括号括起来的内容就是初始化块");
}
1、其执行顺序,与普通变量是并列的。根据在程序中书写的前后顺序来执行。
四、构造器的初始化
1、类的初始化的几种方式:
1). 使用new关键字创建对象
这是我们最常见的也是最简单的创建对象的方式,通过这种方式我们可以调用任意的构造函数(无参的和有参的)去创建对象。比如:
Student student = new Student();
2). 使用Class类的newInstance方法(反射机制)
我们也可以通过Java的反射机制使用Class类的newInstance方法来创建对象,事实上,这个newInstance方法调用无参的构造器创建对象,比如:
Student student2 = (Student)Class.forName("Student类全限定名").newInstance();
或者:
Student stu = Student.class.newInstance();
3). 使用Constructor类的newInstance方法(反射机制)
java.lang.relect.Constructor类里也有一个newInstance方法可以创建对象,该方法和Class类中的newInstance方法很像,但是相比之下,Constructor类的newInstance方法更加强大些,我们可以通过这个newInstance方法调用有参数的和私有的构造函数,比如:
public class Student {
private int id;
public Student(Integer id) {
this.id = id;
}
public static void main(String[] args) throws Exception {
Constructor<Student> constructor = Student.class
.getConstructor(Integer.class);
Student stu3 = constructor.newInstance(123);
}
}
使用newInstance方法的这两种方式创建对象使用的就是Java的反射机制,事实上Class的newInstance方法内部调用的也是Constructor的newInstance方法。
4). 使用Clone方法创建对象
无论何时我们调用一个对象的clone方法,JVM都会帮我们创建一个新的、一样的对象,特别需要说明的是,用clone方法创建对象的过程中并不会调用任何构造函数。简单而言,要想使用clone方法,我们就必须先实现Cloneable接口并实现其定义的clone方法,这也是原型模式的应用。比如:
public class Student implements Cloneable{
private int id;
public Student(Integer id) {
this.id = id;
}
@Override
protected Object clone() throws CloneNotSupportedException {
// TODO Auto-generated method stub
return super.clone();
}
public static void main(String[] args) throws Exception {
Constructor<Student> constructor = Student.class
.getConstructor(Integer.class);
Student stu3 = constructor.newInstance(123);
Student stu4 = (Student) stu3.clone();
}
}
5). 使用(反)序列化机制创建对象
当我们反序列化一个对象时,JVM会给我们创建一个单独的对象,在此过程中,JVM并不会调用任何构造函数。为了反序列化一个对象,我们需要让我们的类实现Serializable接口,比如:
public class Student implements Cloneable, Serializable {
private int id;
public Student(Integer id) {
this.id = id;
}
@Override
public String toString() {
return "Student [id=" + id + "]";
}
public static void main(String[] args) throws Exception {
Constructor<Student> constructor = Student.class
.getConstructor(Integer.class);
Student stu3 = constructor.newInstance(123);
// 写对象
ObjectOutputStream output = new ObjectOutputStream(
new FileOutputStream("student.bin"));
output.writeObject(stu3);
output.close();
// 读对象
ObjectInputStream input = new ObjectInputStream(new FileInputStream(
"student.bin"));
Student stu5 = (Student) input.readObject();
System.out.println(stu5);
}
}
6). 完整实例
public class Student implements Cloneable, Serializable {
private int id;
public Student() {
}
public Student(Integer id) {
this.id = id;
}
@Override
protected Object clone() throws CloneNotSupportedException {
// TODO Auto-generated method stub
return super.clone();
}
@Override
public String toString() {
return "Student [id=" + id + "]";
}
public static void main(String[] args) throws Exception {
System.out.println("使用new关键字创建对象:");
Student stu1 = new Student(123);
System.out.println(stu1);
System.out.println("\n---------------------------\n");
System.out.println("使用Class类的newInstance方法创建对象:");
Student stu2 = Student.class.newInstance(); //对应类必须具有无参构造方法,且只有这一种创建方式
System.out.println(stu2);
System.out.println("\n---------------------------\n");
System.out.println("使用Constructor类的newInstance方法创建对象:");
Constructor<Student> constructor = Student.class
.getConstructor(Integer.class); // 调用有参构造方法
Student stu3 = constructor.newInstance(123);
System.out.println(stu3);
System.out.println("\n---------------------------\n");
System.out.println("使用Clone方法创建对象:");
Student stu4 = (Student) stu3.clone();
System.out.println(stu4);
System.out.println("\n---------------------------\n");
System.out.println("使用(反)序列化机制创建对象:");
// 写对象
ObjectOutputStream output = new ObjectOutputStream(
new FileOutputStream("student.bin"));
output.writeObject(stu4);
output.close();
// 读取对象
ObjectInputStream input = new ObjectInputStream(new FileInputStream(
"student.bin"));
Student stu5 = (Student) input.readObject();
System.out.println(stu5);
}
}/* Output:
使用new关键字创建对象:
Student [id=123]
---------------------------
使用Class类的newInstance方法创建对象:
Student [id=0]
---------------------------
使用Constructor类的newInstance方法创建对象:
Student [id=123]
---------------------------
使用Clone方法创建对象:
Student [id=123]
---------------------------
使用(反)序列化机制创建对象:
Student [id=123]
*///:~
从Java虚拟机层面看,除了使用new关键字创建对象的方式外,其他方式全部都是通过转变为invokevirtual指令直接创建对象的。
2、类的构造器的执行过程
实例化一个类的对象的过程是一个典型的递归过程,如下图所示。进一步地说,在实例化一个类的对象时,具体过程是这样的:
在准备实例化一个类的对象前,首先准备实例化该类的父类,如果该类的父类还有父类,那么准备实例化该类的父类的父类,依次递归直到递归到Object类。此时,首先实例化Object类,再依次对以下各类进行实例化,直到完成对目标类的实例化。具体而言,在实例化每个类时,都遵循如下顺序:先依次执行实例变量初始化和实例代码块初始化,再执行构造函数初始化。也就是说,编译器会将实例变量初始化和实例代码块初始化相关代码放到类的构造函数中去,并且这些代码会被放在对超类构造函数的调用语句之后,构造函数本身的代码之前。
代码实现一下:
第一步:定义爷爷类:
public class Grandpa {
Grandpa(){
System.out.println("爷爷类初始化,嘿嘿");
}
}
第二步:定义父类:
public class Father extends Grandpa {
Father(){
System.out.println("父类的初始化:");
}
}
第三步:定义子类:
public class Son extends Father{
Son(){
System.out.println("子类初始化");
}
public static void main(String[] args) {
Son son=new Son();
}
}
最后一步看结果:
从以上结果我们看到了类的执行过程。而在同一个类中构造器,是在最后执行的。
参考:https://blog.csdn.net/justloveyou_/article/details/72466416