利用反射生成一个没有Nullary Constructor的类的对象

这篇博客探讨了如何在没有Nullary Constructor的情况下,利用反射和特定方法(如ObjectStreamClass或ReflectionFactory)动态创建类的对象。通过实现Serializable接口,即使类没有无参构造器,也能借助ObjectInputStream或反射工厂类生成对象。这种方法绕过了对特定构造器的依赖,扩大了动态编程的可能性。
摘要由CSDN通过智能技术生成

在一些动态程序中,时常有这种情况:通过记录的类型信息,动态构造一个类的对象。通常的方法就是采用Class.newInstance()方法来做到。但是这个方法要求这个Class一定具有一个Nullary Constructor,即没有参数的构造函数。如果此Class明确的声明了一个带有参数的Constructor,newInstance就会报出InstantiationException。类似的,还可以通过Class.getConstructor(...)来获取某个构造函数,再newInstance(...)。然而,这还是要求使用者一定提前知道目标Class具有某个特别的Constructor才可以。这在一定程度上限制动态程序的范围。

然而,很明显的,实现Serializable接口的Class明显不受这个限制的约束。任凭你怎么随便定义Constructor,ObjectInputStream.readObject()都能很好的把那个对象生成出来。这个过程是通过ObjectStreamClass这个类来完成的。ObjectStreamClass内部维护一组类型数据,来获取某个Class对应的ObjectStreamClass对象(lookup方法)。当readObject时,就获取这个类型信息,并newInstance()。通过查看代码可以发现这也是通过找到某个无参构造函数得到的。这就是ObjectStreamClass。getSerializableConstructor(Class cl)。此方法查找目标类型的继承体系中第一个没有实现Serilizable的类的Nullary Constructor。这对绝大部分的Class来讲都会找到Object类的那个无参构造函数。此构造函数一定存在。因此,一定能找到。然而,Object的构造函数怎么能生成目标Class的对象呢?这实际上是靠反射直接操作二进制码类实现的。在看到的两个版本的ObjectStreamClass中,java.io.ObjectClassStream使用了sun.reflect.ReflectionFactory#newConstructorForSerialization(Class cl, Constructor cons)方法;而com.sun.corba.se.impl.io.ObjectClassStream使用了sun.corba.Bridge#newConstructorForSerialization(Class cl, Constructor cons)方法。这两个方法都能得到一个可以生成目标类型的,但是是无参的构造函数(newConstructorForSerialization(Class cl, Constructor cons)的第一个参数是目标类型,第二个是无参的构造函数,例如Object的那个)。因此,尽管ObjectClassStream的关键方法都是package的或者private的访问权限,无法直接使用,还是可以利用ReflectionFactory或者Bridge来达到目的。下面是使用Bridge的例子代码。

  1. import java.io.Serializable;
  2. import java.lang.reflect.Constructor;
  3. import sun.corba.Bridge;
  4. public class TestNewInstance {
  5.     public static void main(String[] args) throws Exception {
  6.         Bridge b = Bridge.get();
  7.         Constructor cons = b.newConstructorForSerialization(A.class, Object.class.getConstructor());
  8.         A a = (A)cons.newInstance();
  9.         a.x = 5;
  10.         System.out.println(a.x);
  11.     }
  12.     
  13.     static class A implements Serializable {
  14.         public A(int x) {
  15.             this.x = x;
  16.         }
  17.         int x = 2;
  18.     }
  19. }

 

这份代码最后输出数字5。请注意,如果上面代码第10行被注掉,输出结果不是2,而是0。说明这个特殊的"for serializable"的Constructor不会按照类声明中的数据初始化来填充数据。(本来嘛,正常的使用在对象被生成后,就会利用反串行化得到的数据来填充对象)。

 

然后再换ReflectionFactory来实现一下。

  1. import java.io.Serializable;
  2. import java.lang.reflect.Constructor;
  3. import sun.reflect.ReflectionFactory;
  4. public class TestNewInstance {
  5.     public static void main(String[] args) throws Exception {
  6.         ReflectionFactory relfactory = ReflectionFactory.getReflectionFactory();
  7.         Constructor cons = relfactory.newConstructorForSerialization(A.class, Object.class.getConstructor());
  8.         A a = (A)cons.newInstance();
  9.         a.x = 5;
  10.         System.out.println(a.x);
  11.     }
  12.     
  13.     static class A implements Serializable {
  14.         public A(int x) {
  15.             this.x = x;
  16.         }
  17.         int x = 2;
  18.     }
  19. }

 

结果依旧。因此,根据上面的代码,可以写出动态生成任何类型的对象而无需做任何对构造函数样式的假设的代码。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值