当前位置: 首页 > 科技观察

盘点Java创建对象的x操作

时间:2023-03-22 15:06:54 科技观察

我们在日常生活中创建了很多对象,但是这个对象跟你理解的对象不一样,因为作者不是女娲,不能创建人。作者只是一个程序员,他只会用Java创建对象。那么让我问你一个问题,你知道如何在Java中创建对象吗?这道题好像是写给Java初学者的,好像看不起这里的每一个人,嗯。..好吧,让我换一种方式问这个问题。毕竟看我公众号的都是未来月入十万的大佬。你知道Java有多少种创建对象的方法吗?嗯?这个问题有点意思。通常我们使用new来创建对象。这是第一种方式;如果我们使用框架,我们可以直接提交只需要让Spring来管理它。Spring底层使用反射来创建对象。这是第二种方式;进而。......有点想不起来了,别着急,这篇文章帮你复习一下。使用new创建对象使用new创建对象是最简单的方式,new是Java中的一个关键字,new通过为新对象分配内存并返回该内存的引用来实例化一个类,这个实例化一个类其实就等同于创建对象,因为类也是对象;new还负责调用对象的构造函数,下面是使用new创建对象的代码Objectobj=newObject();在这段代码中,我们在heap区分配了一块内存,然后obj对象指向了这块内存区。不知道大家有没有看到new的字节码?以下是这段代码的字节码。在Java中,我们认为创建一个对象就是调用它的构造函数,所以我们使用newObject()构造的对象,其实就是调用Object类的无参构造函数。但是通过字节码,我们发现一个对象的创建和调用它的构造方法是分开的。字节码new的意思是在堆上创建一个对象,并将该对象的引用压入栈中。invokespecial表示不带参数调用对象的构造函数。实际上JVM提供了5条方法调用指令,即invokestatic:该指令用于调用静态方法,即用static关键字修饰的方法;invokespecial:该指令用于三种场景:调用实例构造方法、调用私有方法(即private关键字修饰的方法)和父类方法(即super关键字调用的方法);invokeinterface:该指令用于调用接口方法,然后在运行时确定一个实现该接口的对象;invokevirtual:该指令使用Invokingvirtualmethods(即除上述三种情况之外的方法);invokedynamic:在运行时动态解析调用点限定符引用的方法后调用该方法;在JDK1.7中提出,主要用于支持JVM上的动态脚本语言(如Groovy、Jython等);因此,如果您在堆栈上有一个对象引用,并且调用了dup,那么您现在在堆栈上有两个对该对象的引用。好像不知道为什么,于是在网上求助的时候,找到了R的解释的出处:https://www.zhihu.com/question/52749416后面的astore会消耗参考上的操作数栈顶将其放下并保存到指定的局部变量中。如果不创建局部变量而直接使用newObject(),请注意其字节码。你能看出细微的差别吗?上图中的astore_1变成了pop,也就是说newObject()不保存对象的局部变量,而是直接消费。好吧,正如预期的那样。所以这是第一种创建方式,也就是使用new来创建。使用newInstance方法创建这个newInstance方法引用class类中的方法。newInstance方法将调用无参数构造函数来创建对象。我们可以使用newInstance方法来创建对象,下面是示例代码Useruser=(User)Class.forName("com.cxuan.test.User").newInstance();//或者使用Useruser=User.class。newInstance();让我们分析一下这个字节码。其实第一种方法和第二种方法的区别就是Class.forName的字节码。这是一个静态方法,应该使用invokestatic。我们来验证一下。第一种方法的字节码可以看到第二种方法的字节码,我们验证的是正确的。那么这个字节码是什么意思呢?ldc的意思是将常量池中的引用压入当前栈,我们上面解释过的invokestatic和invokevirtual,然后checkcast,这个字节码的意思就是进行类型转换,因为newInstance生成的是一个Object对象,所以我们需要把它转换成我们需要的User类型,这个字节码就是为了这个工作。使用反射创建对象使用反射创建对象其实就是使用了newInstance方法,只不过这个方法是Constructor,Java反射中的构造方法,我们可以通过这种方式创建一个新的对象。如下代码所示Constructorconstructor=User.class.getConstructor();Useruser=constructor.newInstance();下面是它的字节码这里是对iconst_0的解释,意思是加载int值0到stack,相当于为getConstructor方法准备参数分配的字节码。为了验证这个结论,我们简化优化一下,看看其他方法User.class.getDeclaredField("id")的字节码;它的字节码如下:可以看到,第二个ldc其实就是getDeclaredField中的参数,是String类型的,所以使用了ldc,将引用压入栈中。使用对象克隆创建对象这是第四种创建方法,使用Cloneable类中的clone()方法创建。它的前提是你需要实现Cloneable接口,并实现它定义的clone方法。使用克隆方法创建对象不会调用任何构造函数。Constructorconstructor=User.class.getConstructor();Useruser=constructor.newInstance();user.setName("cxuan");Useruser2=(User)user.clone();System.out。println(user2.getName());输出cxuan的字节码如下。这个字节码有点长,但是上面已经介绍了字节码的概念和含义。最重要的是压栈,调用对应的实例方法。对象克隆是面试官非常喜欢考的一个点。后面我会分析浅拷贝和深拷贝的区别。使用反序列化创建对象当我们使用序列化和反序列化时,JVM也会帮我们创建一个单独的对象。反序列化时,JVM创建一个对象,不调用任何构造函数,如下代码所示newFileInputStream("xxx"));Useruser3=(User)in.readObject();in.close();user3.setName("cxuan003");System.out.println(user3+",hashcode:"+user3.hashCode());这个反编译后的字节码文件比较长,这里就不贴了。读者可自行编译。其实并没有什么特殊的字节码指令,大部分我们上面已经讲过了。提及。本文转载自微信公众号“JavaBuilder”,可通过以下二维码关注。如需转载本文,请联系Java开发者公众号。