持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第18天,点击查看活动详情
前言
首先先看一下这三道,大脑回忆一下是否能够回答,如果不能的话,浏览下面的内容吧。
- 对象在VM中是怎么存储的?
- 对象头信息里面有哪些东西?
- Java创建实例对象是不是必须要通过构造函数?
创建对象的方式
一般来说,大多数人最为熟悉的就是new 一个对象,但实际上,有五种方式:
1.new关键字
Phone phone = new Phone();
这是我们最常见的也是最简单的创建对象的方式,通过这种方式我们还可以调用任意的构造器(无参的和有参的)。
2.Class.newInstance
反射创建对象时最常用的方法,调用的是类的无参构造方法,因此必须保证类声明了有参构造后同时声明一个无参构造。
Phone phone1 = Phone.class.newInstance();
3.Constructor.newInstance
Person person = constructor.newInstance("luffy","20");
我们可以通过这个newInstance
方法调用有参数(不再必须是无参)的和私有的构造函数(不再必须是public)。
4.Clone方式
package com.hong.com.hong.juejin;
/**
* @author hong
* @date 2022年10月28日 14:51
*/
public class Phone implements Cloneable{
private String brand;
private float price;
public Phone(String brand,float price){
this.brand = brand;
this.price = price;
}
public Phone clone() throws CloneNotSupportedException {
return (Phone) super.clone();
}
@Override
public String toString() {
return "Phone{" +
"brand='" + brand + ''' +
", price=" + price +
'}';
}
}
public static void main(String[] args) throws CloneNotSupportedException {
Phone phone = new Phone("mi",1999);
Object clone = phone.clone();
System.out.println(phone);
System.out.println(clone);
}
要使用clone方法,我们必须先实现Cloneable接口并复写Object的clone方法。调用一个对象的clone
方法,JVM就会创建一个新的对象,将前面的对象的内容全部拷贝进去,用clone
方法创建对象并不会调用任何构造函数。
5.反序列化
当我们序列化和反序列化一个对象,JVM会给我们创建一个单独的对象,在反序列化时,JVM创建对象并不会调用任何构造函数。
前三种方式都会调用类的构造器,后两者则不会调用构造函数。
创建对象的步骤
- 判断对象对应的类是否加载、链接、初始化。
- 为对象分配内存
如果内存规整,则可以采用碰撞指针的方式分配内存。
如果内存不规整,则jvm会维护一个内存块空闲列表,根据空闲列表进行分配。 - 处理并发安全问题
每个线程预先分配一块TLAB
采用CAS和失败重试保证更新的原子性 - 初始化分配的空间
为属性设置初始值 - 设置对象的对象头
- 执行init方法进行初始化
对象在内存中的存储
HotSpot虚拟机中,对象在内存中存储的布局可以分为三块区域:对象头(Header)、实例数据(Instance Data)和对齐填充(Padding)。
对象头
在maven项目中的pom文件引入查看对象布局的依赖
<dependency>
<groupId>org.openjdk.jol</groupId>
<artifactId>jol-core</artifactId>
<version>0.9</version>
</dependency>
public static void main(String[] args) throws CloneNotSupportedException {
Phone phone = new Phone("mi",1999);
System.out.println(ClassLayout.
parseInstance(phone).toPrintable());
}
- OFFSET:偏移地址,单位字节;
- SIZE:占用的内存大小,单位为字节;
- TYPE DESCRIPTION:类型描述,其中
object header
为对象头; - VALUE:对应内存中当前存储的值;
HotSpot虚拟机的对象头包括两部分信息,第一部分用于存储对象自身的运行时数据, 如哈希码(HashCode)、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等等,这部分数据的长度在32位和64位的虚拟机(暂不考虑开启压缩指针的场景)中分别为32个和64个Bits,官方称它为“Mark Word”。
实例数据
它是对象真正存储的有效信息,包括程序代码中定义的各种类型的字段(包括从父类继承下来的和本身拥有的字段)
规则
- 相同宽度的字段总是被分配在一起
- 父类中定交的变量会出现在子类之前
- 如果CompactFields参数为true(默认为true):子类的窄变量可能播入到父类变量的空隙