探讨Java中类加载到完成一个对象创建的过程
Java作为面向对象的一门语言,在程序中最重要的也是最基础的就是通过类创建对象,在本文中我简单聊一聊对于这个过程我个人的理解。有不足之处还请多多指正。
一个Java类中有什么?
那么当我们创建一个对象时执行了哪些操作呢?
类加载以及对象创建及其初始化
类加载:发生在类第一次被虚拟机调用,类会被虚拟机加载一次,一般来说,也只被加载一次
类加载执行的操作:
1.初始化类变量
2.分配类方法的函数入口
3.执行静态块
对象创建以及对象初始化晚于类加载的时间
因为静态部分是依赖于类的,不依赖于对象存在的,所以静态部分的加载先于类的加载。
对象创建及实例化的步骤
1.初始化实例变量
2.为实例方法分配函数入口(在第一个对象创建时分配)
3.执行对象块(对象块是每创建一个对象,执行一次)
4.执行构造函数(注意super()),以及剩余部分
执行完上述步骤后才算是完成了一次对象的创建。
具体解释
public class Test1{
//1.初始化类变量
static String staticVar = "我是静态变量";
//2.分配类方法的函数入口
public static void statFunc(){
System.out.println("我是静态方法,我被执行啦!");
}
//3.执行静态块
static {
//只执行一次,多数用于在程序启动的时候,初始化一些静态资源,如图片,音乐等等
}
//4.执行main()方法
public static void main(String args[]){
}
}
小小总结一下:
因为静态部分是依赖于类,而不是依赖于对象存在的,所以静态部分的加载优先于对象存在。
当找到main方法后,因为main方法虽然是一个特殊的静态方法,但是还是静态方法,此时JVM会加载main方法所在的类,试图找到类中其他静态部分,即首先会找main方法所在的类。
test2:
public calss Test2{
//1.初始化实例变量,注意:初始化实例变量 != 给实例变量赋值
String name="实例变量";
//2.为实例方法分配函数入口,注意第一个对象创建的时候分配入口地址
public void init(){
System.out.print("实例方法被调用");
}
//3.执行对象块
{
//每创建一个对象,都会执行一次对象块
//可以在创建每一个对象的时候,给对象设置状态,属性等等
}
//4.执行构造函数(super();剩余语句才是第四部分执行的)
public static void main(String args[]){
Test2 t2=new Test2();
....
}
}
关于上面的第四点:
4.执行构造函数(super();剩余语句才是第四部分执行的)
注意:构造方法的调用:
以派生的顺序调用,从父类到子类
可以理解为:(灵魂画手…希望读者能看明白)
看看一些经典例子(太经典了,侵删)
package test;
class A {
public A() {
System.out.println("A的构造方法");
}
public int j = print();
public static int print() {
System.out.println("A print");
return 521;
}
}
public class Test extends A {
public Test() {
System.out.println("Test的构造方法");
}
public static int k = print();
public static int print() {
System.out.println("Test print");
return 522;
}
public static void main(String[] args) {
System.out.println("main start");
Test test = new Test();
}
}
输出:(可以自己更改程序多运行几次熟悉熟悉)
另一个经典例子:
public class Text {
public static int k = 0;
public static Text t1 = new Text("t1");
public static Text t2 = new Text("t2");
public static int i = print("i");
public static int n = 99;
public int j = print("j");
{
print("构造块");
}
static {
print("静态块");
}
public Text(String str) {
System.out.println((++k) + ":" + str + " i=" + i + " n=" + n);
++i;
++n;
}
public static int print(String str) {
System.out.println((++k) + ":" + str + " i=" + i + " n=" + n);
++n;
return ++i;
}
public static void main(String args[]) {
Text t = new Text("init");
}
}
输出:
这里补充下知识点:
super关键字:
1. 调用构造方法
类似this(),super()必须是子类构造函数第一个执行的语句,如果super()没用到,每个父类的默认构造或无参构造函数将被执行
2. 调用被隐藏变量及方法
可通过super关键字调用被隐藏的父类中的变量及方法
this关键字:
this在方法用,指向当前对象,属于调用这个方法的当前对象
用法
- this.变量/方法 ---->指代的是通过当前对象调用的变量/方法(不能用于静态方法中)
- this(【参数】) ------>一般用于构造器中,调用当前类的其他构造方法(必须是构造方法中的第一条语句)