概述
java对象创建分为两个过程:声明对象引用和创建对象实体。类信息、对象引用、对象实体均在内存的不同区域。
内存结构
每一个java应用程序均会唯一的对应一个jvm实例,而这个jvm实例将会完成对象的内存分配、程序运行、垃圾回收等工作。JVM将其内存分为三个区域:方法区、栈区、堆区。
- 方法区:是jvm在装载类文件时,用于存储类的所有描述信息的,这些信息主要包括,类的基本信息(访问修饰符、类名等)、字段信息(修饰符、类型、字段名)、方法信息(修饰符、返回值类型、方法名、参数列表类型、异常、方法体字节码等)、常量池、静态区、classloader以及class的引用
- 栈区:存放局部变量,基本数据类型和对象引用类型
- 堆区:用于存放new产生对象的实体,每一个对象实体均会有自己的内存地址,一旦这个内存地址不被任何栈区对象引用,就会成为垃圾,随时被gc回收
对象创建过程
package ccnu.allTest;
public class TestObject {
public static void main(String[] args) {
YyPpLl ypl1 = new YyPpLl();
YyPpLl ypl2 = new YyPpLl(48);
System.out.println((ypl1.ii == ypl2.ii) + " "+ YyPpLl.ii);
System.out.println(ypl1.str == ypl2.str);
}
static class YyPpLl {
private String str = "ypl"; // 显示初始化字段
private int i; // 未显示初始化字段
private static int ii = 24; // 静态字段
private static int iii;
static { // 静态初始化块
System.out.println("ii=" + ii + ", iii=" + iii);
}
{ // 实例初始化块
// iii = 100;
System.out.println("str=" + str + ",i=" + i + ",ii=" + ii + ",iii=" + iii);
}
public YyPpLl(){
ii = 72;
System.out.println("non-arguments");
}
public YyPpLl(int i){
this.i = i;
System.out.println("two arguments");
}
}
}
/*
ii=24, iii=0
str=ypl,i=0,ii=24,iii=0
non-arguments
str=ypl,i=0,ii=72,iii=0
two arguments
true 72
true
*/
- 步骤:1、将JVM加载到内存
2、jvm首先检查public类是否存在,存在就会将它加载到内存方法区中,否则抛出异常,结束进程
3、对类中静态字段进行初始化(显示初始化就为指定的值,否则为默认值),接着执行静态代码块
4、在栈中创建局部变量,堆中创建对象实体,为堆对象实体的成员变量(非静态)初始化,对于静态字段只需要从方法区的静态区中引用其值即可,执行构造代码块,接着执行构造函数(当然,调用顺序会一直上溯到Object类,每一个构造方法的第一行为super()语句),最后将对象实体堆地址给栈中引用变量
要注意的是,实例字段包括自身定义的和从父类继承下来的(即使父类的实例字段被子类覆盖或者被private修饰,都照样为其分配内存)
C++中的引用和JAVA的引用作对比,其实他们两个只是“名称”一样,本质并没什么关系,C++中的引用只是给现存变量起了一个别名(引用变量只是一个符号引用而已,编译器并不会给引用分配新的内存),而JAVA中的引用变量却是真真正正的变量,具有自己的内存空间,只是不同的引用变量可以“指向”同一个对象而已。因此,如果要拿C++和JAVA引用对象的方式相对比,C++中的指针倒和JAVA中的引用如出一辙,毕竟,JAVA中的引用其实就是对指针的封装