Java对象创建步骤?

2021-04-26 19:58发布

5条回答
py大白
2楼 · 2021-04-28 09:31

1. 类加载检查
虚拟机遇到一条 new 指令时,首先将去检查这个指令的参数是否能在常量池中定位到这个类的符号引用,并且检查这个符号引用代表的类是否已被加载过、解析和初始化过。如果没有,那必须先执行相应的类加载过程。

2. 分配内存
在类加载检查完成后,虚拟机为new出来的对象分配内存。
新对象需要的内存大小在类加载完成后就已经确定,现在要做的是吧一块确定大小的内存从Java堆中划分出来。
分配方式涉及到两种:

指针碰撞

空闲列表

堆内存规整时    堆内存不规整时    

用过的内存放在一边,没用的放一边,中间有个分界值指针    JVM维护一个列表记录划分的内存    

GC收集器:Serial、ParNew    

GC收集器:CMS

   

创建对象时涉及到的问题:
线程安全:
CAS+失败重试: CAS 是乐观锁的一种实现方式。所谓乐观锁就是,每次不加锁而且假设没有冲突而去完成某项操作,如果因为冲突失败就重试,直到成功为止。虚拟机采用 CAS 配上失败重试的方式保证更新操作的原子性。
TLAB:为每一个线程预先在 Eden 区分配一块儿内存,JVM 在给线程中的对象分配内存时,首先在 TLAB 分配,当对象大于 TLAB 中的剩余内存或 TLAB 的内存已用尽时,再采用上述的 CAS 进行内存分配


3. 初始化零值
内存分配完成后,虚拟机需要将分配到的内存空间都初始化为零值(不包括对象头),这一步操作保证了对象的实例字段在 Java 代码中可以不赋初始值就直接使用,程序能访问到这些字段的数据类型所对应的零值。

4. 设置对象头
虚拟机要对对象进行必要的设置,例如这个对象是那个类的实例、如何才能找到类的元数据信息、对象的哈希码、对象的 GC 分代年龄等信息。这些信息存放在对象头中。另外,根据虚拟机当前运行状态的不同,如是否启用偏向锁等,对象头会有不同的设置方式。

5. 执行init方法
经过1-4步骤,从JVM的角度看一个新的对象已经产生,但从Java程序上看,对象创建才刚开始。执行 new 指令之后会接着执行 方法,把对象按照程序员的意愿进行初始化,这样一个真正可用的对象才算完全产生出来。


回答: 2022-02-17 14:44

1new指令

虚拟机遇到一条new指令时,首先去检查这个指令的参数是否能在常量池中定位到一个类的符号引用,并且检查这个符号引用代表的类是否已被加载、解析和初始化过。如果没有,那么须先执行相应的类加载过程。

2分配内存

接下来虚拟机将为新生代对象分配内存。对象所需的内存的大小在类加载完成后便可完全确定。分配方式有“指针碰撞(Bump the Pointer)”和“空闲列表(Free List)”两种方式,具体由所采用的垃圾收集器是否带有压缩整理功能决定。

3初始化

内存分配完成后,虚拟机需要将分配到的内存空间都初始化为零值(不包括对象头),这一步操作保证了对象的实例字段在Java代码中可以不赋初始值就直接使用,程序能访问到这些字段的数据类型所对应的零值。

4对象的初始设置

接下来虚拟机要对对象进行必要的设置,例如这个对象是哪个类的实例、如何才能找到类的元数据信息、对象的哈希码、对象的GC分代年龄等信息。这些信息存放在对象的对象头(Object Header)之中。根据虚拟机当前的运行状态的不同,如对否启用偏向锁等,对象头会有不同的设置方式。

在上面的工作都完成了之后,从虚拟机的角度看,一个新的对象已经产生了,但是从Java程序的角度看,对象创建才刚刚开始—


Java研究室
3楼 · 2021-05-10 08:33

三步:

  1. 分配空间

  2. init初始化

  3. 对象地址赋值

    这里有一一个关于单例模式的面试一并奉上.jvm可以优化指令重排序.上述步骤如果按1,3,2的步骤来执行。恰好赶上执行到3的时候,另一个线程进来询问对象的地址,此时是 不为null的,所有还没有来得及执行2,对象就返回了,调用时对象就会报错.因为还没有初始化.

    解决:用voliate关键字修饰对象不让虚拟机指令重排,只能按1,2,3顺序执行,配合同步锁,在锁年后分别进行检查,即可实现单例模式

路小雨xiaoyu
4楼 · 2021-05-24 16:15

1.Java普通对象的创建

这里讨论的仅仅是普通Java对象,不包含数组和Class对象。


1.1new指令

虚拟机遇到一条new指令时,首先去检查这个指令的参数是否能在常量池中定位到一个类的符号引用,并且检查这个符号引用代表的类是否已被加载、解析和初始化过。如果没有,那么须先执行相应的类加载过程。


1.2分配内存

接下来虚拟机将为新生代对象分配内存。对象所需的内存的大小在类加载完成后便可完全确定。分配方式有“指针碰撞(Bump the Pointer)”和“空闲列表(Free List)”两种方式,具体由所采用的垃圾收集器是否带有压缩整理功能决定。


1.3初始化

内存分配完成后,虚拟机需要将分配到的内存空间都初始化为零值(不包括对象头),这一步操作保证了对象的实例字段在Java代码中可以不赋初始值就直接使用,程序能访问到这些字段的数据类型所对应的零值。


1.4对象的初始设置

接下来虚拟机要对对象进行必要的设置,例如这个对象是哪个类的实例、如何才能找到类的元数据信息、对象的哈希码、对象的GC分代年龄等信息。这些信息存放在对象的对象头(Object Header)之中。根据虚拟机当前的运行状态的不同,如对否启用偏向锁等,对象头会有不同的设置方式。


1.5方法

在上面的工作都完成了之后,从虚拟机的角度看,一个新的对象已经产生了,但是从Java程序的角度看,对象创建才刚刚开始—方法还没有执行,所有的字段都还为零。所以,一般来说,执行new指令后悔接着执行init方法,把对象按照程序员的意愿进行初始化(应该是将构造函数中的参数赋值给对象的字段),这样一个真正可用的对象才算完全产生出来。


2.Java对象内存布局

在HotSpot虚拟机中,对象在内存中存储的布局可以分为3块区域:对象头(Header)、实例数据(Instance Data)、对其填充(Padding)。



2.1对象头

HotSpot虚拟机的对象头包含两部分信息,第一部分用于存储对象自身的运行时数据,如哈希码(HashCode)、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等。

对象的另一部分类型指针,即对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例(并不是所有的虚拟机实现都必须在对象数据上保留类型指针,也就是说,查找对象的元数据信息并不一定要经过对象本身)。

如果对象是一个Java数组,那在对象头中还必须有一块用于记录数组长度的数据。


元数据:描述数据的数据。对数据及信息资源的描述信息。在Java中,元数据大多表示为注解。

2.2实例数据

实例数据部分是对象真正存储的有效信息,也是在程序代码中定义的各种类型的字段内容,无论从父类继承下来的,还是在子类中定义的,都需要记录起来。这部分的存储顺序会虚拟机默认的分配策略参数和字段在Java源码中定义的顺序影响(相同宽度的字段总是被分配到一起)。


2.3对齐填充

对齐填充部分并不是必然存在的,也没有特别的含义,它仅仅起着占位符的作用。由于HotSpot VM的自动内存管理系统要求对象的起始地址必须是8字节的整数倍,也就是说,对象的大小必须是8字节的整数倍。而对象头部分正好是8字节的倍数(1倍或者2倍),因此,当对象实例数据部分没有对齐时,就需要通过对齐填充来补全。

靓猴一枚
5楼 · 2021-08-29 09:28

1,创建对象给成员变量赋值的两种方式的区别:


p1 = new Person("张天一",23);    每次new 相当于重新创建一个对象


Person p2 = new Person();        //空参构造创建对象


构造方法: 给属性进行初始化


setXxx方法


修改属性值


这两种方式,在开发中用setXxx更多一些,因为比较灵活


class Demo3_Person {

    public static void main(String[] args) {

        Person p1 = new Person("张三",23);


        p1 = new Person("张天一",23);    //这种方式看运行结果貌似是改名了,其实是将原对象变成垃圾


        System.out.println(p1.getName() + "..." + p1.getAge());


 


 


        System.out.println("--------------------");


        Person p2 = new Person();        //空参构造创建对象


        p2.setName("李四");


        p2.setAge(24);


 


 


        p2.setName("李鬼");


        System.out.println(p2.getName() + "..." + p2.getAge());


    }


}


class Person {

    private String name;                //姓名


    private int age;                    //年龄


 


 


    public Person() {                    //空参构造


    }


 


 


    public Person(String name,int age) {//有参构造


        this.name = name;


        this.age = age;


    }


    


    public void setName(String name) {    //设置姓名


        this.name = name;


    }


 


 


    public String getName() {            //获取姓名


        return name;


    }


 


 


    public void setAge(int age) {        //设置年龄


        this.age = age;


    }


 


 


    public int getAge() {                //获取年龄


        return age;


    }


}


 


2,创建一个对象的步骤


【1】代码


class Demo4_Student {

    public static void main(String[] args) {

        Student s1 = new Student();                    //使用空参构造


        s1.setName("张三");                            //设置姓名


        s1.setAge(23);                                //设置年龄


 


 


        System.out.println("我的姓名是:" + s1.getName() + ",我的年龄是:" + s1.getAge());


        //getXxx()获取属性值,可以打印,也可以赋值给其他的变量,做其他的操作


        Student s2 = new Student("李四",24);


        s2.setName("王五");


        s2.show();                                    //只是为了显示属性值


    }


}


/*


* A:案例演示


    * 学生类:


        * 成员变量:


            * name,age


        * 构造方法:


            * 无参,带两个参


        * 成员方法:


            * getXxx()/setXxx()


            * show():输出该类的所有成员变量值


* B:给成员变量赋值:


    * a:setXxx()方法


    * b:构造方法


    


* C:输出成员变量值的方式:


    * a:通过getXxx()分别获取然后拼接


    * b:通过调用show()方法搞定


*/


 


 


class Student {

    private String name;                            //姓名


    private int age;                                //年龄


 


 


    public Student(){}                                //空参构造


 


 


    public Student(String name,int age) {            //有参构造


        this.name = name;


        this.age = age;


    }


 


 


    public void setName(String name) {                //设置姓名


        this.name = name;


    }


 


 


    public String getName() {                        //获取姓名


        return name;


    }


 


 


    public void setAge(int age) {                    //设置年龄


        this.age = age;


    }


 


 


    public int getAge() {                            //获取年龄


        return age;


    }


 


 


    public void show() {

        System.out.println("我的姓名是:" + name +  ",我的年龄是:" +  age);


    }


}


【2】图解


Student s = new Student();


Student.class加载进内存


声明一个Student类型引用s


在堆内存创建对象,


给对象中属性默认初始化值


属性进行显示初始化


 构造方法进栈,对对象中的属性赋值,构造方法弹栈


将对象的地址值赋值给s




 


3,创建Rectangle矩形


class Test1_Rectangle {                            //Rectangle矩形


    public static void main(String[] args) {

        Rectangle r = new Rectangle(10,20);


        System.out.println(r.getLength());        //周长


        System.out.println(r.getArea());        //面积


    }


}


/*


* A:案例演示


    * 需求:


        * 定义一个长方形类,定义 求周长和面积的方法,


        * 然后定义一个测试类进行测试。


    分析:


        成员变量:


            宽width,高high


        空参有参构造


        成员方法:


            setXxx和getXxx


            求周长:getLength()


            求面积:getArea()


*/

 

class Rectangle {

 

    private int width;                //宽

 

    private int high;                //高

 

 

 

 

 

    public Rectangle(){}            //空参构造

 

 

 

 

 

    public Rectangle(int width,int high) {

 

        this.width = width;            //有参构造

 

        this.high = high;

 

    }

 

 

 

 

 

    public void setWidth(int width) {//设置宽

 

        this.width = width;

 

    }

 

 

 

 

 

    public int getWidth() {            //获取宽

 

        return width;

 

    }

 

 

 

 

 

    public void setHigh(int high) {    //设置高

 

        this.high = high;

 

    }

 

 

 

 

 

    public int getHigh() {            //获取高

 

        return high;

 

    }

 

 

 

 

 

    public int getLength() {        //获取周长

 

        return 2 * (width + high);

 

    }

 

 

 

 

 

    public int getArea() {            //获取面积

 

        return width * high;

 

    }

 

}

 


关于对象的创建过程一般是从new指令(我说的是JVM的层面)开始的(具体请看图1),JVM首先对符号引用进行解析,如果找不到对应的符号引用,那么这个类还没有被加载,因此JVM便会进行类加载过程(具体加载过程可参见我的另一篇博文)。符号引用解析完毕之后,JVM会为对象在堆中分配内存,HotSpot虚拟机实现的JAVA对象包括三个部分:对象头、实例字段和对齐填充字段(具体内容请看图2),其中要注意的是,实例字段包括自身定义的和从父类继承下来的(即使父类的实例字段被子类覆盖或者被private修饰,都照样为其分配内存)。相信很多人在刚接触面向对象语言时,总把继承看成简单的“复制”,这其实是完全错误的。JAVA中的继承仅仅是类之间的一种逻辑关系(具体如何保存记录这种逻辑关系,则设计到Class文件格式的知识,具体请看我的另一篇博文),唯有创建对象时的实例字段,可以简单的看成“复制”。

    为对象分配完堆内存之后,JVM会将该内存(除了对象头区域)进行零值初始化,这也就解释了为什么JAVA的属性字段无需显示初始化就可以被使用,而方法的局部变量却必须要显示初始化后才可以访问。最后,JVM会调用对象的构造函数,当然,调用顺序会一直上溯到Object类。


相关问题推荐

  • 回答 2

    Statement的execute(String query)方法用来执行任意的SQL查询,如果查询的结果是一个ResultSet,这个方法就返回true。如果结果不是ResultSet,比如insert或者update查询,它就会返回false。我们可以通过它的getResultSet方法来获取ResultSet,或者通过getUpda...

  • 回答 22

    忙的时候项目期肯定要加班 但是每天加班应该还不至于

  • 回答 108
    已采纳

    虽然Java人才越来越多,但是人才缺口也是很大的,我国对JAVA工程师的需求是所有软件工程师当中需求大的,达到全部需求量的60%-70%,所以Java市场在短时间内不可能饱和。其次,Java市场不断变化,人才需求也会不断增加。马云说过,未来的制造业要的不是石油,...

  • 回答 5
    已采纳

    工信部证书含金量较高。工信部是国务院的下属结构,具有发放资质、证书的资格。其所发放的证书具有较强的权威性,在全国范围内收到认可,含金量通常都比较高。 工信部证书,其含义也就是工信部颁发并承认的某项技能证书,是具有法律效力的,并且是国家认可的...

  • 回答 70
    已采纳

    学Java好不好找工作?看学完Java后能做些什么吧。一、大数据技术Hadoop以及其他大数据处理技术都是用Java或者其他,例如Apache的基于Java 的 HBase和Accumulo以及ElasticSearchas。但是Java在此领域并未占太大空间,但只要Hadoop和ElasticSearchas能够成长壮...

  • 回答 16
    已采纳

    就是java的基础知识啊,比如Java 集合框架;Java 多线程;线程的五种状态;Java 虚拟机;MySQL (InnoDB);Spring 相关;计算机网络;MQ 消息队列诸如此类

  • 回答 12

    #{}和${}这两个语法是为了动态传递参数而存在的,是Mybatis实现动态SQL的基础,总体上他们的作用是一致的(为了动态传参),但是在编译过程、是否自动加单引号、安全性、使用场景等方面有很多不同,下面详细比较两者间的区别:1.#{} 是 占位符 :动态解析 ...

  • 回答 62

    没问题的,专科学历也能学习Java开发的,主要看自己感不感兴趣,只要认真学,市面上的培训机构不少都是零基础课程,能跟得上,或是自己先找些资料学习一下。

  • 回答 4

    1、反射对单例模式的破坏采用反射的方式另辟蹊径实例了该类,导致程序中会存在不止一个实例。解决方案其思想就是采用一个全局变量,来标记是否已经实例化过了,如果已经实例化过了,第 二次实例化的时候,抛出异常2、clone()对单例模式的破坏当需要实现单例的...

  • 回答 5

     优点: 一、实例控制  单例模式会阻止其他对象实例化其自己的单例对象的副本,从而确保所有对象都访问唯一实例。 二、灵活性  因为类控制了实例化过程,所以类可以灵活更改实例化过程。 缺点: 一、开销  虽然数量很少,但如果每次对象请求引用时都要...

  • 回答 4

    这个主要是看你数组的长度是多少, 比如之前写过的一个程序有个数组存的是各个客户端的ip地址:string clientIp[4]={XXX, xxx, xxx, xxx};这个时候如果想把hash值对应到上面四个地址的话,就应该对4取余,这个时候p就应该为4...

  • 回答 6

     哈希表的大小 · 关键字的分布情况 · 记录的查找频率 1.直接寻址法:取关键字或关键字的某个线性函数值为散列地址。即H(key)=key或H(key) = a·key + b,其中a和b为常数(这种散列函数叫做自身函数)。...

  • 回答 6

    哈希表的大小取决于一组质数,原因是在hash函数中,你要用这些质数来做模运算(%)。而分析发现,如果不是用质数来做模运算的话,很多生活中的数据分布,会集中在某些点上。所以这里最后采用了质数做模的除数。 因为用质数做了模的除数,自然存储空间的大小也用质数了...

  • 回答 2

    是啊,哈希函数的设计至关重要,好的哈希函数会尽可能地保证计算简单和散列地址分布均匀,但是,我们需要清楚的是,数组是一块连续的固定长度的内存空间

  • 回答 3

     解码查表优化算法,seo优化

  • 回答 5

    1.对对象元素中的关键字(对象中的特有数据),进行哈希算法的运算,并得出一个具体的算法值,这个值 称为哈希值。2.哈希值就是这个元素的位置。3.如果哈希值出现冲突,再次判断这个关键字对应的对象是否相同。如果对象相同,就不存储,因为元素重复。如果对象不同,就...

没有解决我的问题,去提问