在java中new一个对象的流程是怎样的
对象怎么创建,这个太熟悉了,new一下(其实还有很多途径,比如反射、反序列化、clone等,这里拿最简单的new来讲):
Dog dog = new Dog();
我们总是习惯于固定语句的执行,却对于背后的实现过程缺乏认知,而理解这个过程对后面晦涩难懂的反射和代理其实会有很大帮助,所以请务必学好这块内容。
在看这篇文章之前,啰嗦一句:如果你死记硬背下面所说的流程等于白看,就算现在记住了,一个礼拜后呢,一个月后你又能记得多少,因为对象创建过程这个知识点平常的工作中基本不会涉及到,太底层了,背熟的知识点不经常加以运用容易遗忘,所以我的建议是什么呢,流程做到心里大概有个数,其中涉及到关键的知识点记牢就可以了。
JVM内存
先简单说下java虚拟机内存模型和存放内容的区别,两部分:
- 栈内存 存放基本类型数据和对象的引用变量,数据可以直接拿来访问,速度比堆快
- 堆内存 存放创建的对象和数组,会由java虚拟机的自动垃圾回收来管理(GC),创建一个对象放入堆内的同时也会在栈中创建一个指向该对象堆内存中的地址引用变量,下面说的对象就是存在该内存中
下面我们就按照对象生成的过程来一一讲解参与其中过程的各个概念
首先有这么一个类,后面的初始化基于这个讲解:
/*** @author 炜哥* @since 2021-04-18 11:01:41** 执行顺序:(优先级从高到低。)静态代码块>构造代码块>构造方法>普通方法。* 其中静态代码块只执行一次。构造代码块在每次创建对象是都会执行。*/
public class Dog {//默认狗狗的最大年龄是16岁private static int dog_max_age = 16;//狗狗的名字private String dog_name;{System.out.println("狗狗的构造代码块");}static {System.out.println("狗狗的静态代码块");}//无参构造器故意没设//有参构造器public Dog(String dog_name) {this.dog_name = dog_name;}public void getDogInfo(){System.out.println("名字是:"+dog_name + " 年龄:" + dog_max_age);}//狗叫public static void barking(){System.out.println("汪汪汪~~~");}
}
JVM生成.class文件
一个java文件会在编译期间被初始化生成.class字节码文件,字节码文件是专门给JVM阅读的,我们平时吭哧吭哧写的一行行代码最终都会被编译成机器能看懂的语句,这个文件后面会被类加载器加载到内存。
类加载器加载.class文件
《深入理解Java的虚拟机》中大概有这么一句话:在虚拟机遇到一条new的指令时,会去检查一遍在静态常量池中能否定位到一个类的符号引用 (就这个类的路径+名字),并且检查这个符号引用代表的类是否已被加载、解析和初始化过。如果不是第一次使用,那必须先执行相应的类加载过程,这个过程由类加载器来完成。
类加载字面意思就可以理解成加载class文件,更准确点的说法就是会把class文件变成一个二进制流加载到内存中,即把类的描述信息加载到Metaspace,至于类加载器如何找到并把一个class文件转成IO流加载到内存中,我后面会专门写一篇关于类加载器的文章,这里就只要理解创建对象中有这么一步就行了。不过这里面有很重要的概念不得不讲:Class对象
知识扩展:Class对象
划重点,这是个非常重要的概念,理解它对于理解后面的反射和代理会有很大的帮助
类加载器 ClassLoader
加载class文件时,会把类里的一些数值常量、方法、类信息等加载到内存中,称之为类的元数据,最终目的是为了生成一个Class对象用来描述类,这个对象会被保存在.class文件里,可能有新手看到这里会比较懵逼,class也有对象?当然了,Class是个实实在在的类(用来描述类的类,比较拗口),有构造方法( private
,意味着可以生成对象,但不能手动生成,由JVM自动创建Class对象),类加载器会给每个java文件都创建一个Class对象,用来描述类,我画个图:
//以下操作只能由jvm完成,我们手动做不了
Class cls1 = new Class(Dog.class.getClassLoader());
Class cls2 = new Class(Cat.class.getClassLoader());
Class cls3 = new Class(People.class.getClassLoader());
这个Class对象除了描述对应的类之外还有什么作用呢?也可以生成对象,就是java的反射概念(通过Class实例获取类信息)
上面说了,Class类是用来描述像People.Class类的类,那么它里面肯定包含了所有能够描述该class的所有属性,比如类名、方法、接口等,我们先到Class类源码中瞄一眼:
这里面有个方法 newInstance()
,即创建对象, 我把源代码贴出来并简单解析下:
@CallerSensitive
public T newInstance()throws InstantiationException, IllegalAccessException{if (System.getSecurityManager() != null) {checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), false);}if (cachedConstructor == null) {if (this == Class.class) {throw new IllegalAccessException("Can not call newInstance() on the Class for java.lang.Class");}try {Class<?>[] empty = {};//声明无参构造对象final Constructor<T> c = getConstructor0(empty, Member.DECLARED);// Disable accessibility checks on the constructor// since we have to do the security check here anyway// (the stack depth is wrong for the Constructor's// security check to work)java.security.AccessController.doPrivileged(new java.security.PrivilegedAction<Void>() {public Void run() {c.setAccessible(true);return null;}});cachedConstructor = c;} catch (NoSuchMethodException e) {//如果class中没有无参构造方法,那么抛InstantiationException错误throw (InstantiationException)new InstantiationException(getName()).initCause(e);}}Constructor<T> tmpConstructor = cachedConstructor;// Security check (same as in java.lang.reflect.Constructor)int modifiers = tmpConstructor.getModifiers();if (!Reflection.quickCheckMemberAccess(this, modifiers)) {Class<?> caller = Reflection.getCallerClass();if (newInstanceCallerCache != caller) {Reflection.ensureMemberAccess(caller, this, null, modifiers);newInstanceCallerCache = caller;}}// Run constructortry {//最终还是调用了无参构造器对象的newInstance方法return tmpConstructor.newInstance((Object[])null);} catch (InvocationTargetException e) {Unsafe.getUnsafe().throwException(e.getTargetException());// Not reachedreturn null;}}
首先搞清楚 newInstance
两种方法区别:
- Class.newInstance() 只能够调用无参的构造函数,即默认的构造函数,我们在Class源码里也看到了其实最终还是调用了无参构造器对象
Constructor
的newInstance
方法,举个栗子:Dog.class
中是没有无参构造方法,那么会直接抛出InstantiationException
异常:
//Dog类中只有一个dog_name的有参构造方法
Class c = Class.forName("com.service.ClassAnalysis.Dog");
Dog dog = (Dog) c.newInstance();//直接抛InstantiationException异常
- Constructor.newInstance() 可以根据传入的参数,调用任意构造构造函数,也可以反射私有构造器(了解就行)
//Dog类中只有一个dog_name的有参构造方法
Constructor cs = Dog.class.getConstructor(String.class);
Dog dog = (Dog) cs.newInstance("小黑");//执行没有问题
但是这里面的 newInstance
跟我们这次要说的 new
方法存在区别,两者创建对象的方式不同,创建条件也不同:
- 使用
newInstance
时必须要保证这类已经加载并且已经建立连接,就是已经被类记载器加载完毕,而new
不需要 - class对象的
newInstance
方法只能用无参构造,上面已经提到了,而new
不需要 - 前者使用的是类加载机制,是一种方法,后者是创建一个新类,一种关键字
这个不能说newInstance
不方便,相反它在反射、工厂设计模式、代理中发挥了重要作用,后续我也会写下代理和反射,因为理解起来确实有点绕。还有一点需要注意,不管以哪种方式创建对象,对应的Class对象都是同一个
Dog dog1 = new Dog("旺财");
Dog dog2 = new Dog("小黑");
Class c = Class.forName("com.service.classload.Dog");//为了测试,加了无参构造
Dog dog3 = (Dog) c.newInstance();
System.out.println(dog1.getClass() == dog2.getClass());
System.out.println(dog1.getClass() == dog3.getClass());
连接和初始化
在此阶段首先为静态static变量内存中分配存储空间,设立初始值(还未被初始化)比如:
public static int i = 666;//被类加载器加载到内存时会执行,赋予一个初始值
public static Integer ii = new Integer(666);//也被赋值一个初始值
但请注意,实际上i
的初始值是0,不是666,其他基本数据类型比如boolean的初始值就是false,以此类推。如果是引用类型的成员变量 ii
那么初始值就是null。(对基本数据类型和引用类型不熟悉的可以看我的另外一篇文章《 java的数据类型&自动装箱/拆箱浅谈》,这里就不多说了)
Dog dog = new Dog("旺财");//在这里打个断点
执行,首先会执行静态成员变量初始化,默认值是0:
但有例外,如果加上了 final
修饰词那么初始值就是设定的值。
接着对已经分配存储空间的静态变量真正赋值,比如为上面的dog_max_age
赋值16,还有执行静态代码块,也就是类似下面的代码:
static {System.out.println("狗狗的静态代码块");
}
到这为止,类的加载过程才算完成。
创建实例
在加载类完毕后,对象的所需大小根据类信息就可以确认了,具体创建的步骤如下:
- 先给对象分配内存(包括本类和父类的所有实例变量,不包括上面的静态变量),并设置默认值,如果有引用对象那么会在栈内存中申请一个空间用来指向的实际对象。
- 执行初始化代码实例化,先初始化父类再初始化子类,赋予给定值(尊重长辈是java的传统美德)
- 对象实例化完毕后如果存在引用对象的话还需要**把第一步的栈对象指向到堆内存中的实际对象,**这样一个真正可用的对象才被创建出来。
**说了这么多估计很多人都没概念,懵逼状态中,其实很简单,我们只要记住new的创建对象就两步:初始化和实例化,再给你们搞一张图:可以简单理解②③④为初始化⑤实例化(可恶,我这该死的责任感!)
本文不指望你能使劲弄懂java虚拟机底层加载创建对象的过程(其实有些步骤我都懒得讲了,因为说出来也都非常理论化,没多大意思),是想让你知道对象的诞生过程有哪几个重要概念参与了,弄懂这些概念比起单单知道对象创建的过程有意义的多:
- 类加载器,可以找找网上的资料,蛮多的,这块内容做个了解就行
- Class类和Class对象的概念,请重点掌握,不然理解反射和动态代理很费劲,spring的源码也会难以理解
- 栈内存和堆内存以及对应的基本类型和引用类型,也很重要,争取能基本理解
OVER
在java中new一个对象的流程是怎样的相关推荐
- 在 Java 中 new 一个对象的流程是怎样的?
对象怎么创建,这个太熟悉了,new一下(其实还有很多途径,比如反射.反序列化.clone等,这里拿最简单的new来讲): Dog dog = new Dog(); 我们总是习惯于固定语句的执行,却对于 ...
- java类怎么删除对象_在java中删除一个对象?
在java中删除一个对象? 我想删除一个我创build的对象(跟随着你的一个椭圆),但是我怎么做呢? delete follower1; 没有工作. 编辑: 好吧,我会给更多的上下文. 我正在制作一个 ...
- java中new一个对象时具体都发生了什么?
java是一门面向对象的程序设计语言,在java中一切皆为对象.在java中创建一个新对象的方式有很多种如new+构造方法,克隆,反射,反序列化等.那么当我们new一个对象时,java底层都经过了怎样 ...
- java中new一个对象要经历哪些过程
我们要了解new一个对象经历哪些过程,就要知道new对象之前会做什么事情. 首先Java在new一个对象的时候,会先查看对象所属的类有没有被加载到内存,如果没有的话,就会先通过类的全限定名将对象所属的 ...
- 第 1-4 课:Java 中的运算符和流程控制 + 面试题
算术运算符 Java 中的算法运算符,包括以下几种: 算术运算符 名称 举例 + 加法 1+2=3 - 减法 2-1=1 * 乘法 2*3=6 / 除法 24/8=3 % 求余 24%7=3 ++ 自 ...
- Java程序设计语言基础03:Java中的程序执行流程
目录 1. 简记部分 1.1 条件与循环 1.2 switch语句 1.3 continue与break 2. 变量的作用域 3. 从标准输入读取数据 4. 生成随机数 1. 简记部分 1.1 条件与 ...
- java中遍历一个对象的所有属性
分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow 也欢迎大家转载本篇文章.分享知识,造福人民,实现我们中华民族伟大复兴! 本文转载 ...
- 初识Java ~ (二) # Java 中程序的执行流程,(万字长文)特别细~ 可收藏~
大家好~ ,我是 清汉 时隔一月,懒惰的我终于将 Java 初识系列文章,第二篇程序执行流程码出来了~ 初识Java ~ (一) 从0开始,Java基础知识,双手奉上~ 可收藏!! 应该不算太晚吧,这 ...
- jstack处理Java中CPU100%的思路流程
模拟问题代码 构造一个死循环,造成CPU使用率100%. > vim InfiniteLoop.java public class InfiniteLoop {public static voi ...
- 【26天高效学完Java编程】Day03:Java中的运算符与流程控制语句的基本概念与实操
本专栏将从基础开始,循序渐进,由浅入深讲解Java的基本使用,希望大家都能够从中有所收获,也请大家多多支持. 专栏地址:26天高效学完Java编程 相关软件地址:链接地址 所有代码地址:链接地址 如果 ...
最新文章
- 【翻译】Oracle不同版本之间Export Import的兼容性矩阵
- 解决Pytohn安装第三方库出现read timed out 问题
- 【文摘】 雪念——作者:蓝色妖姬
- linux 移动硬盘 mnt,linux 移动硬盘 mnt
- python操作符op_详解python中的 is 操作符
- beast php,windows php-beast 安装
- Github部署+Hexo搭建免费博客 next主题美化
- php携程 线程,244,android线程与协程以及携程的使用方法
- ScintillaNET的应用
- 2016,不能忽视的IBM闪存新思维下的新战略
- react給變量賦值并列元素
- 重新认识margin-top和margin-bottom
- 埃文科技教你如何验证IP地址定位的准确率
- 关于计算机组件游戏,老司机带你解决游戏运行提示缺少组件问题
- OpenG 分化基础知识
- java外文文库_java外文文献毕业设计
- 二十九-使用RealSenseD435进行ORBSLAM2实时三维重建
- 【每日蓝桥】15、一三年省赛Java组真题“错误票据”
- Kubernetes(K8s)基本概念:Volume(存储卷)、Persistent Volume
- 传奇开服教程完整版GOM引擎超详细的单机架设图文教程(小白一看就会)