论坛首页 Java企业应用论坛

不用构造方法也能创建对象

浏览 11630 次
精华帖 (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:这不是提问帖,是技术分享,不要求大家回答,只是个人理解还不是很透彻,如果研究明白了会补充,管理员勿擅自转帖.
   发表时间: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之类)。
0 请登录后投票
   发表时间: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
0 请登录后投票
   发表时间:2010-12-23  
只是借这个话题用Unsafe举例说明Java对象的创建是分两步走、调用构造器只是其中一步。并不是说反序列化的时候就一定用了Unsafe哦,这个请注意区分 ^_^

实际上在Sun JDK的实现里,Java层面的反射类库与JVM层面的反射实现相互配合来完成反序列化。java.io.ObjectStreamClass通过跟反射方法/构造器调用类似的机制获取所谓的“序列化构造器”,在反序列化的时候调用这个版本的构造器。
创建这个“序列化构造器”时要在继承链里从最具体向最抽象的方向搜索,找出第一个不可序列化的类(没有实现Serializable接口的类),并找出它的无参构造器来调用。也就是说,反序列化的时候并不是完全不调用用户代码里声明的构造器,只是不调用实现了Serializable的类的而已。
0 请登录后投票
   发表时间:2010-12-23  
RednaxelaFX 写道
只是借这个话题用Unsafe举例说明Java对象的创建是分两步走、调用构造器只是其中一步。并不是说反序列化的时候就一定用了Unsafe哦,这个请注意区分 ^_^

实际上在Sun JDK的实现里,Java层面的反射类库与JVM层面的反射实现相互配合来完成反序列化。java.io.ObjectStreamClass通过跟反射方法/构造器调用类似的机制获取所谓的“序列化构造器”,在反序列化的时候调用这个版本的构造器。
创建这个“序列化构造器”时要在继承链里从最具体向最抽象的方向搜索,找出第一个不可序列化的类(没有实现Serializable接口的类),并找出它的无参构造器来调用。也就是说,反序列化的时候并不是完全不调用用户代码里声明的构造器,只是不调用实现了Serializable的类的而已。


证明我上面的推测是正确的
引用
分配内存的貌似是JVM,我咋觉得和构造函数没啥关系呢

引用
目前推测是创建父类的对象,然后在此基础上构建子类对象.再进行转换.

详细内容还在看.弄懂了会整理出来.谢谢提醒
0 请登录后投票
   发表时间:2010-12-23  
发了更详细的讲解在这里,欢迎参考:http://rednaxelafx.iteye.com/blog/850082
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics