Java的对象头由以下三部分组成:MarkWord、指向类的指针、数组长度(只有数组对象才有)
MarkWord包含:哈希码、GC分代年龄、锁标识状态、
线程持有的锁、偏向线程ID(一般占32/64 bit)。
MarkWord记录了对象锁相关的信息。
当这个对象被synchronized关键字当成同步锁时,围绕这个锁的一系列操作都和MarkWord有关。
MarkWord在32位JVM中的长度是32bit,在64位JVM中长度是64bit。
32位JVM中,MarkWord在不同的锁状态下存储的内容:
其中无锁和偏向锁的锁标志位都是01,只是在前面的1bit区分了这是无锁状态还是偏向锁状态。
JDK1.6以后的版本在处理同步锁时存在锁升级的概念,JVM对于同步锁的处理是从偏向锁开始的,随着竞争越来越激烈,处理方式从偏向锁升级到轻量级锁,最终升级到重量级锁。
锁升级流程:
Java对象的类数据保存在方法区。
该指针在32位JVM中的长度是32bit,在64位JVM中长度是64bit。
只有数组对象保存了这部分数据。
该数据在32位和64位JVM中长度都是32bit。
对象的实例数据就是在Java代码中能看到的属性
和他们的属性值
。
因为JVM要求Java的对象占的内存大小应该是8bit的倍数,所以后面有几个字节用于把对象的大小补齐至8bit的倍数,没有特别的功能。
最常见的也是最简单的创建对象的方式,通过这种方式我们可以调用任意的构造函数(无参的和有参的)去创建对象。
public static void main(String[] args) {
User = new User();
}
通过Java的反射机制使用Class类的newInstance方法来创建对象。这个newInstance方法调用无参的构造器创建对象。
public static void main(String[] args) throws Exception {
// 方法1
User user1 = (User)Class.forName("com.joker.pojo.User").newInstance();
// 方法2
User user2 = User.class.newInstance();
}
事实上Class的newInstance方法内部调用的是Constructor的newInstance方法。
通过Java的反射机制使用Constructor类的newInstance方法来创建对象。
java.lang.relect.Constructor类里的newInstance方法比Constructor类的newInstance方法更加强大些,我们可以通过这个newInstance方法调用有参数的和私有的构造函数。
public static void main(String[] args) throws Exception {
Constructor<User> constructor = User.class.getConstructor(Integer.class);
User user3 = constructor.newInstance(123);
}
通过实现Cloneable接口,重写Object类的clone方法来创建对象(浅拷贝)。
Java为所有对象提供了clone方法(Object类),又出于安全考虑,将它设置为了保护属性。
protected native Object clone() throws CloneNotSupportedException;
我们可以通过反射(reflect)机制在任意对象中调用该方法。
如果不通过反射的方式,我们要如何实现对象克隆呢?可以通过实现Cloneable接口,重写Object类的clone方法来实现对象的克隆。
实现原理:
Java API采用判断是否实现空接口Cloneable的方法来判断对象所属的类是否支持克隆。如果被克隆对象所属类没有实现该接口,则抛出NotDeclareCloneMethod 异常。当支持克隆时,通过重写Object类的clone方法,并把方法的修饰符改为public,就可以直接调用该类的实例对象的clone方法实现克隆。
我们常用的很多类都是通过这种方式来实现的,如:ArrayList、HashMap等。
@Data
public class User implements Cloneable {
private String id;
private String userName;
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
public static void main(String[] args) throws CloneNotSupportedException {
User user = new User();
User user1 = (User)user.clone();
}
}
当我们反序列化一个对象时,JVM会给我们创建一个单独的对象,在此过程中,JVM并不会调用任何构造函数。为了反序列化一个对象,我们需要让我们的类实现Serializable接口。
public static void main(String[] args) throws Exception {
User user = new User();
user.setId("1");
user.setUserName("haha");
// 写对象
ObjectOutputStream output = new ObjectOutputStream(
new FileOutputStream("F:\\joker\\text.txt"));
output.writeObject(user);
output.close();
// 读对象
ObjectInputStream input = new ObjectInputStream(new FileInputStream(
"F:\\joker\\text.txt"));
User user1 = (User) input.readObject();
}
这里以new关键字方式创建对象为例。
对象创建过程分为以下几步:
检查类是否已经被加载;
new关键字时创建对象时,首先会去运行时常量池中查找该引用所指向的类有没有被虚拟机加载,如果没有被加载,那么会进行类的加载过程。类的加载过程需要经历:加载、链接、初始化三个阶段。
具体过程可参考文章:Java类的加载机制
为对象分配内存空间;
此时,对象所属类已经加载,现在需要在堆内存中为该对象分配一定的空间,该空间的大小在类加载完成时就已经确定下来了。
为对象分配内存空间有两种方式:
为对象的字段赋默认值;
分配完内存后,需要对对象的字段进行零值初始化(赋默认值),对象头除外。
零值初始化意思就是对对象的字段赋0值,或者null值,这也就解释了为什么这些字段在不需要进程初始化时候就能直接使用。
设置对象头;
对这个将要创建出来的对象,进行信息标记,包括是否为新生代/老年代,对象的哈希码,元数据信息,这些标记存放在对象头信息中。
执行实例的初始化方法lint
linit方法包含成员变量、构造代码块的初始化,按照声明的顺序执行。
执行构造方法。
执行对象的构造方法。至此,对象创建成功。
上述为无父类的对象创建过程。对于有父类的对象创建过程,还需满足如下条件:
对象创建源码
public class ClassA {
private static int y = 1;
private static String s = "1";
static {
y=2;
}
private static int x = 1;
static {
s="2";
}
static {
x=2;
}
public ClassA() {
x = x+1;
y = y+1;
s = "3";
}
public static void main(String[] args) {
ClassA classA = new ClassA();
}
}
具体创建步骤
类未加载,先加载类;
链接阶段时,准备阶段,为静态变量赋默认值;
y = 0;
s = null;
x = 0;
初始化阶段时,为静态变量赋初始值(执行类的初始化方法clinit);
clinit方法包含静态变量、静态代码块,按照声明的顺序执行。
y = 1;
s = "1";
y = 2;
x = 1;
s = "2";
x = 2;
为成员变量赋默认值;
aa = 0;
bb = 0;
对象初始化,为成员变量赋初始值。
执行实例的初始化方法lint(成员变量、构造代码块)。
aa = 1;
aa = 2;
bb = 1;
bb = 2;
执行构造方法。
aa = 3;
bb = 3;
至此,对象创建完成。
对象创建源码
父类
@Data
public class ClassParent {
public static int p1 = 1;
public int p2 = 1;
{
p2 = 2;
}
static {
p1 = 2;
}
public ClassParent() {
p2 = 3;
}
}
子类
@Data
public class ClassChild extends ClassParent {
private static int c1 = 1;
private int c2 = 1;
{
c2 = 2;
}
static {
c1 = 2;
}
public ClassChild() {
super();
c2 = 3;
}
public static void main(String[] args) {
ClassChild classA = new ClassChild();
}
}
具体创建步骤
类未加载,先加载类;
先加载父类,再加载子类
p1 = 0;
p1 = 1;
p1 = 2;
c1 = 0;
c1 = 1;
c1 = 2;
为成员变量赋默认值;
这里的父类之类执行顺序没去验证,个人认为是先父类后子类。
p2 = 0;
c2 = 0;
ClassParent类:对象初始化,为成员变量赋初始值。
p2 = 1;
p2 = 2;
ClassParent类:执行构造方法。
p2 = 3;
ClassChild类:对象初始化,为成员变量赋初始值。
c2 = 1;
c2 = 2;
ClassChild类:执行构造方法。
c2 = 3;
至此,对象创建完成。
https://blog.csdn.net/justloveyou_/article/details/72466416