浏览 11777 次
锁定老帖子 主题:不用构造方法也能创建对象
精华帖 (0) :: 良好帖 (6) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2010-12-23
最后修改:2010-12-30
import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.ObjectInputStream; import java.io.Serializable; public class TestClass implements Serializable{ private static final long serialVersionUID = 0L; public TestClass() throws Exception { throw new Exception("哎呀妈呀,异常啦!!!!"); } public static void main(String[] args) throws Exception { byte[] head = { -84, -19, 0, 5, 115, 114, 0 }; byte[] ass = { 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 120, 112 }; String name = TestClass.class.getName(); ByteArrayOutputStream baos = new ByteArrayOutputStream(); baos.write(head); baos.write(name.length()); baos.write(name.getBytes()); baos.write(ass); baos.flush(); baos.close(); ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray())); TestClass o = (TestClass) ois.readObject(); ois.close(); System.out.println("创建对象: " + o); } } 看到这里先别着急执行,先看看.你觉得能够正常运行吗? 结果是什么? ----------------------------------------------------------------------------------- 运行结果: 创建对象: TestClass@743399 CSDN某帖子 写道 对象创建的几种方法:
1.使用new关键字 2.使用clone方法 3.反射机制 4.反序列化 其中1,3都会明确的显式的调用构造函数 2是在内存上对已有对象的影印 所以不会调用构造函数 4是从文件中还原类的对象 也不会调用构造函数 上述代码就是反序列化的结果 RednaxelaFX 写道 嗯顺带推荐Effective Java, Second Edition的第74条
引用 Normally, objects are created using constructors;
serialization is an extralinguistic mechanism for creating objects. Whether you accept the default behavior or override it, deserialization is a “hidden constructor” with all of the same issues as other constructors. 大体的意思是反序列化有一定的风险.破坏了封装.相当于一个隐藏的构造器. RednaxelaFX 写道 1、Java的实例构造器只负责初始化,不负责创建对象;Java虚拟机的字节码指令的设计也反映了这一点,有一个new指令专门用于创建对象实例,而调用实例构造器则使用invokespecial指令。
2、“this”是作为实例构造器的第一个实际参数传入的。 Java反序列化实际上是调用了基类的构造方法. ObjectStreamClass中调用ReflectionFactory.newConstructorForSerialization(cl, cons); cons = reflFactory.newConstructorForSerialization(cl, cons); 根据cl参数(TestClass类的Class对象)和cons参数(基类Object的构造器)创建了一个新的构造器 打印生成的构造器对象,输出信息为public java.lang.Object(),表面生成的构造器是TestClass父类Object的无参构造器. 但是调用newInstance()的结果却产生了TestClass类的对象.(おかしい......) ReflectionFactory.newConstructorForSerialization()例子 Constructor superCons = TestClass.class.getSuperclass().getConstructor(); System.out.println(superCons); ReflectionFactory reflFactory = ReflectionFactory.getReflectionFactory(); Constructor c = reflFactory.newConstructorForSerialization(TestClass.class,superCons); System.out.println(c.newInstance()); public java.lang.Object() TestClass@fd13b5 通过基类的方法调用生成了子类.绕开了TestClass类的构造方法.生成了看似不可能存在的TestClass对象 ----------------------------------------------------------------------------------- 感谢FX,之前一篇关于构造器的详细论述.传送门http://rednaxelafx.iteye.com/blog/652719 FX写的关于本篇的更详细讲解在这里,传送门http://rednaxelafx.iteye.com/blog/850082 ----------------------------------------------------------------------------------- PS:这不是提问帖,是技术分享,不要求大家回答,只是个人理解还不是很透彻,如果研究明白了会补充,管理员勿擅自转帖. 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2010-12-23
Java类的构造器只能通过两种方式调用,一个是通过new表达式,另一个是通过反射调用构造器。这两种方式对Java程序员来说都是“整体”的,但实际新建对象的动作分两步走:
1、创建出空对象(此时类型已经是正确的了),对应字节码是new 2、调用某个版本的构造器,对应字节码是invokespecial "<init>"。 默认的反序列化机制同样是分两步走,但变成: 1、创建出空对象(此时类型已经是正确的了); 2、调用用户定义的反序列化方法(readObject,如果有的话)或者调用默认反序列化方法。 这就是为什么反序列化可以看作是“隐藏的构造器”。 如果想自己试试去玩创建出空对象但却不调用构造器的,可以试试sun.misc.Unsafe.allocateInstance() 用Groovy控制台来演示一下: Groovy Shell (1.7.2, JVM: 1.6.0_23) Type 'help' or '\h' for help. ---------------------------------------------------------------- groovy:000> class Foo { groovy:001> int value = 12345; groovy:002> Foo() { println "foo ctor!" } groovy:003> int getValue() { println "getValue"; value } groovy:004> } ===> true groovy:000> f1 = new Foo() foo ctor! ===> Foo@10f0625 groovy:000> f1.value getValue ===> 12345 groovy:000> f2 = sun.misc.Unsafe.theUnsafe.allocateInstance(Foo) ===> Foo@38fff7 groovy:000> f2.value getValue ===> 0 groovy:000> quit 可以看到,创建f2指向的Foo对象时,构造器并没有被调用(没有输出"foo ctor!"),实例的状态(value)也并未按用户指定的值初始化(12345),整个对象的所有字段都处于默认状态(0或者null或者false之类)。 |
|
返回顶楼 | |
发表时间:2010-12-23
最后修改:2010-12-23
RednaxelaFX 写道 Java类的构造器.......
FX果然NB, 原来关键点在这里..... public static Unsafe getUnsafe() { Class localClass = Reflection.getCallerClass(2); if (localClass.getClassLoader() != null) throw new SecurityException("Unsafe"); return theUnsafe; } ........... public native Object allocateInstance(Class paramClass) throws InstantiationException; FX大的Groovy对应的Java代码 package go.test.com; import java.lang.reflect.Field; import sun.misc.Unsafe; public class TestClass{ public TestClass() throws Exception { throw new Exception("哎呀妈呀,异常啦!!!!"); } public static void main(String[] args) throws Exception { Field f = Unsafe.class.getDeclaredField("theUnsafe"); f.setAccessible(true); Unsafe us = (Unsafe)f.get(null); TestClass o = (TestClass)us.allocateInstance(TestClass.class); System.out.println(o); } } 结果 写道 go.test.com.TestClass@1bab50a |
|
返回顶楼 | |
发表时间:2010-12-23
只是借这个话题用Unsafe举例说明Java对象的创建是分两步走、调用构造器只是其中一步。并不是说反序列化的时候就一定用了Unsafe哦,这个请注意区分 ^_^
实际上在Sun JDK的实现里,Java层面的反射类库与JVM层面的反射实现相互配合来完成反序列化。java.io.ObjectStreamClass通过跟反射方法/构造器调用类似的机制获取所谓的“序列化构造器”,在反序列化的时候调用这个版本的构造器。 创建这个“序列化构造器”时要在继承链里从最具体向最抽象的方向搜索,找出第一个不可序列化的类(没有实现Serializable接口的类),并找出它的无参构造器来调用。也就是说,反序列化的时候并不是完全不调用用户代码里声明的构造器,只是不调用实现了Serializable的类的而已。 |
|
返回顶楼 | |
发表时间:2010-12-23
RednaxelaFX 写道 只是借这个话题用Unsafe举例说明Java对象的创建是分两步走、调用构造器只是其中一步。并不是说反序列化的时候就一定用了Unsafe哦,这个请注意区分 ^_^
实际上在Sun JDK的实现里,Java层面的反射类库与JVM层面的反射实现相互配合来完成反序列化。java.io.ObjectStreamClass通过跟反射方法/构造器调用类似的机制获取所谓的“序列化构造器”,在反序列化的时候调用这个版本的构造器。 创建这个“序列化构造器”时要在继承链里从最具体向最抽象的方向搜索,找出第一个不可序列化的类(没有实现Serializable接口的类),并找出它的无参构造器来调用。也就是说,反序列化的时候并不是完全不调用用户代码里声明的构造器,只是不调用实现了Serializable的类的而已。 证明我上面的推测是正确的 引用 分配内存的貌似是JVM,我咋觉得和构造函数没啥关系呢
引用 目前推测是创建父类的对象,然后在此基础上构建子类对象.再进行转换.
详细内容还在看.弄懂了会整理出来.谢谢提醒 |
|
返回顶楼 | |
发表时间:2010-12-23
发了更详细的讲解在这里,欢迎参考:http://rednaxelafx.iteye.com/blog/850082
|
|
返回顶楼 | |