对象的创建简述

对象的创建

对象的创建方式
  • 使用new关键字创建对象

    Java java = new Java();
    
  • 使用Class类的newInstance方法(反射机制)

    Java java = (Java)Class.forName("Java类全限定名").newInstance(); 
      
    
    Java java = Java.class.newInstance();
    
  • 使用Constructor类的newInstance方法(反射机制)

    java.lang.relect.Constructor类里也有一个newInstance方法可以创建对象,该方法和Class类中的newInstance方法很像,但是相比之下,Constructor类的newInstance方法更加强大些,我们可以通过这个newInstance方法调用有参数的和私有的构造函数

    public class Java {
    
        private int id;
    
        public Java(Integer id) {
            this.id = id;
        }
    
        public static void main(String[] args) throws Exception {
    
            Constructor<Java> constructor = Java.class
                    .getConstructor(Integer.class);
            Java java = constructor.newInstance(1024);
        }
    }
    
  • 使用clone方法(类需要实现Cloneable接口)

  • 使用(反)序列化机制创建对象(需要类实现Serializable接口)

对象的创建过程

没当一个对象创建时,虚拟机会为其分配内存,其次进行初始化.

  1. 虚拟机分配内存
  2. 对象初始化
虚拟机分配内存

存放类实例变量及其从父类继承过来的的实例变量(即使从超类继承过来的实例变量有可能被隐藏也会被分配空间),在为其分配内存时,这些变量也会被赋予其变量属性的默认值.

对象初始化
  1. 实例变量初始化

  2. 实例代码块初始化

  3. 构造函数初始化

    类初始化执行顺序
    /**
     * @ClassName Java
     * @Description 测试执行顺序
     * @Author XiaoZhe
     * @Date 2020/5/25 10:07
     * @Version 1.0
     **/
    public class Java {
        /**
         * 实例变量
         */
        private int i = 1024;
        /**
         * 静态变量
         */
        private static int j = 2048;
    
        /**
         * (构造代码块)实例代码块
         */
        {
            System.out.println("执行实例代码块 - ");
        }
    
        /**
         * 静态代码块
         */
        static {
            /**
             * 随着类的加载,只执行一次,并由于主函数(由类调用)
             * 静态代码块是类初始化时用, 构造代码块(实例代码块)是对象初始化
             */
            System.out.println("静态变量 - "+j);
            System.out.println("执行静态代码块 - ");
        }
    
        /**
         * 无参构造方法
         */
        public Java() {
            System.out.println("执行无参构造方法 - ");
        }
    
        public static void main(String[] args) {
            //Java java = new Java();
        }
    

    输出 :

    静态变量 - 2048
    执行静态代码块 - 
    
    对象初始化执行顺序

    将上面主函数中的创建对象的注释去掉,并执行,获得下方结果:

    静态变量 - 2048
    执行静态代码块 - 
    执行实例代码块 - 
    执行无参构造方法 -
    

    在上面主函数中再创建一个对象加入下面代码 :

     Java java2 = new Java();
    

    结果:

    静态变量 - 2048
    执行静态代码块 - 
    执行实例代码块 - 
    执行无参构造方法 - 
    执行实例代码块 - 
    执行无参构造方法 - 
    
    涉及到继承
    当涉及到继承时,按照如下顺序执行:
    
    执行父类的静态代码块,并初始化父类静态成员变量
    执行子类的静态代码块,并初始化子类静态成员变量
    执行父类的构造代码块,执行父类的构造函数,并初始化父类普通成员变量
    执行子类的构造代码块, 执行子类的构造函数,并初始化子类普通成员变量
    
    public class JavaA {
        public JavaA(){//构造函数
            System.out.println("A的构造函数");    
        }
        {//构造代码块
            System.out.println("A的构造代码块");    
        }
        static {//静态代码块
            System.out.println("A的静态代码块");        
        }
    }
    public class JavaB extends JavaA{
        public JavaB(){//构造函数
            System.out.println("B的构造函数");    
        }
        {//构造代码块
            System.out.println("B的构造代码块");    
        }
        static {//静态代码块
            System.out.println("B的静态代码块");        
        }
        public static void main(String[] args) {
            JavaB b=new JavaB();        
        }
    }
    运行结果:
    A的静态代码块
    B的静态代码块
    A的构造代码块
    A的构造函数
    B的构造代码块
    B的构造函数
    

    可以看出静态变量优于静态代码块执行,子类的静态变量或静态代码块优于父类构造代码块和构造函数执行.

实例化一个类的对象的过程是一个典型的递归过程

针对于向上无限继承,在初始化最底层的类时,便会如递归似得向上一个一个寻找父类直到寻到顶层父类,到达顶层父类后,不断向下归来并同时实例化各相关类.

对象的创建过程
  • 构建对象

    根据Java类元信息确定对象的大小,向JVM堆中申请一块内存区域并构建对象的默认信息(加载Java对象成员变量信息并赋默认值如 int类型为0,引用类型为null)。

  • 初始化对象:

    然后执行对象内部生成的init方法,初始化成员变量值,同时执行搜集到的{}代码块逻辑,最后执行对象构造方法。

  • 引用对象:

    对象实例化完毕后,再把栈中的Java对象引用地址指向Java对象在堆内存中的地址。

在内存区域中对象是什么形式的哪?

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

对象头

对象头里主要包括几类信息,分别是锁状态标志、持有锁的线程ID、,GC分代年龄、对象HashCode,类元信息地址、数组长度,这里并没有对对象头里的每个信息都列出而是进行大致的分类,下面是对其中几类信息进行说明。

  • 锁状态标志: 对象的加锁状态分为无锁、偏向锁、轻量级锁、重量级锁几种标记。

  • 持有锁的线程: 持有当前对象锁定的线程ID。

  • GC分代年龄: 对象每经过一次GC还存活下来了,GC年龄就加1。

  • 类元信息地址: 可通过对象找到类元信息,用于定位对象类型。

  • 数组长度: 当对象是数组类型的时候会记录数组的长度。

这里要特别关注的是**锁标志位,**锁标志位与是否偏向锁对应到唯一的锁状态。

由此可以了解Synchronized锁住的是对象,就是通过对象头中的这个锁状态标志来锁住对象.

实例对象

对象实例数据才是对象的自身真正的数据,主要包括自身的成员变量信息,同时还包括实现的接口、父类的成员变量信息.

对齐填充

根据JVM规范对象申请的内存地址必须是8的倍数,换句话说对象在申请内存大小时候8字节的倍数,如果对象自身的信息大小没有达到申请的内存大小,那么这部分是对剩余部分进行填充.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值