创建对象方法
1、用
new
语句创建对象
2、运用反射手段,调用Java.lang.Class
或者java.lang.reflect.Constructor
类的newInstance()
实例方法
3、调用对象的clone()
方法
4、运用反序列化手段
new一个对象的过程 (仅限普通Java对象
非数组
和Class对象
等)
1. 类加载检查
当Java虚拟机遇到一条字节码new
指令时,首先将去检查这个指令的参数
是否能在常量池
中定位到一个类的符号引用
,并且检查这个符号引用代表的类是否已被加载、解析和初始化过
。如果没有,那必须先执行相应的类加载
过程。
2. 分配内存
在类加载检查通过后,接下来虚拟机将为新生对象分配内存。对象所需内存的大小在类加载完成后便可完全确定
,为对象分配空间的任务实际上便等同于把一块确定大小的内存块从Java堆中划分出来。
3. 初始化零值
内存分配完成之后,虚拟机必须将分配到的内存空间都初始化为零值(但不包括对象头)
,如果使用了TLAB的话,这一项工作也可以提前至TLAB分配时顺便进行。这步操作保证了对象的实例字段在Java代码中可以不赋初始值就直接使用
,使程序能访问到这些字段的数据类型所对应的零值。
4. 设置对象头
接下来,Java虚拟机还要对对象进行必要的设置,例如这个对象是哪个类的实例
、如何才能找到类的元数据信息
、对象的哈希码
(实际上对象的哈希码会延后到真正调用Object::hashCode()
方法时才计算)、对象的GC分代年龄
等信息。这些信息存放在对象的对象头(Object Header)
之中。根据虚拟机当前运行状态的不同,如是否启用偏向锁等,对象头会有不同的设置方式。
5. 执行<init>()
方法
在上面工作都完成之后,从虚拟机的视角来看,一个新的对象已经产生了。但是从Java程序的视角看来,对象创建才刚刚开始—— 构造函数
,即Class文件中的 <init>()
方法 还没有执行,所有的字段都为默认的零值
,对象需要的其他资源和状态信息也还没有按照预定的意图构造好。new指令之后会接着执行<init>()方法
,按照程序员的意愿对对象进行初始化,这样一个真正可用的对象才算完全被构造出来。
-----------------------------------------------------------------------------读书笔记摘自 书名:深入理解Java虚拟机:JVM高级特性与最佳实践(第3版)作者:周志明
对象内存分配流程
Further Reading: 内存分配的两种方式
staticObj、instanceObj、localObj这三个变量本身(而不是它们所指向的对象)存放在哪里?
staticObj随着Test的类型信息存放在方法区
;
instanceObj随着Test的对象实例存放在Java堆
中;
localObject则是存放在foo()方法栈帧的局部变量表
中;
/**
* staticObj、instanceObj、localObj存放在哪里?
*/
public class JHSDB_TestCase {
static class Test {
static ObjectHolder staticObj = new ObjectHolder();
ObjectHolder instanceObj = new ObjectHolder();
void foo() {
ObjectHolder localObj = new ObjectHolder();
System.out.println("done"); // 这里设一个断点
}
}
private static class ObjectHolder {}
public static void main(String[] args) {
Test test = new JHSDB_TestCase.Test();
test.foo();
}
}
-----------------------------------------------------------------------------读书笔记摘自 书名:深入理解Java虚拟机:JVM高级特性与最佳实践(第3版)作者:周志明
什么是不可变对象? 好处是什么?
不可变对象指对象一旦被创建,状态就不能再改变,任何修改都会创建一个新的对象。
如
String
、Integer
及其它包装类
;
不可变对象最大的好处是线程安全
。
能否创建一个包含可变对象的不可变对象?
可以, 比如final Person[] persons = new Persion[]{}
,persons
是不可变对象的引用,但其数组中的Person
实例却是可变的。这种情况下需要特别谨慎,不要共享可变对象的引用。这种情况下,如果数据需要变化时,就返回原对象的一个拷贝。