Java中Constructor类表示类的构造器,得到一个Constructor对象一般要通过Class对象的getConstructor(Class<?>...)方法,通过参数列表中类的顺序和类型来决定得到的是哪个重载的构造器,
比如
Constructor constructor=String.class.getConstructor(StringBuffer.class);
String str=(String) constructor.newInstance(stringBuffer);
就相当于调用
str=new String(stringBuffer);
无参的构造器构造对象
Constructor constructor=String.class.getConstructor();
String str=(String) constructor.newInstance();
相当于
str=new String();
也相当于
str=String.class.newInstance();
这里要格外注意的一点——newInstance参数列表是一个Object类型的可变参数,这是从java jdk1.5开始的用法,而在这之前要传参数只能用一个Class数组,新的JDK也兼容了这种用法,正是因为这种兼容会导致一些奇怪的问题。
比如我们想得到String(byte[] bytes,String charset)这个重载的构造器时,我们可以用可变参数这么做
Constructor constructor=String.class.getConstructor(byte[].class,String.class);
String str=(String) constructor.newInstance("data".getBytes(),"utf-8");
System.out.println(str);
也可以用Class[]数组这么做
Class[] clses={byte[].class,String.class};
Constructor constructor=String.class.getConstructor(clses);
Object[] params={"data".getBytes(),"utf-8"};
String str=(String) constructor.newInstance(params);
System.out.println(str);
这个例子很正常,那我们看一个能引起异常的例子:
我们想用URLClassLoader(URL[] urls)这个构造器实例化一个urlClassLoader,我们这么写代码:
Constructor constructor=URLClassLoader.class.getConstructor(URL[].class);
URL[] urls={new URL("http://www.baidu.com/"),new URL("http://www.tencent.com/")};
URLClassLoader urlClassLoader=(URLClassLoader) constructor.newInstance(urls);
看上去没问题吧,一运行就错了,异常是
Exception in thread "main" java.lang.IllegalArgumentException: wrong number of arguments
现在我们重新审视这段代码,在第三行new Instance(urls)这里,我们以为urls这一个参数,可按jdk1.5之前的解析方法,会把这个urls拆开,拆出了两个URL对象而不是一个URL[]数组对象,这就是兼容低版本jdk带来的潜在错误,解决方法有两种:
- 把urls强制转换成Object,这意思就是告诉编译器,这是一个对象,不要拆开,即
URLClassLoader urlClassLoader=(URLClassLoader) constructor.newInstance((Object)urls);
- 另一种方法是把urls再包装到一个Object数组中,让编译器拆开他,拆开一层后就是我们的那一个数组对象了,即
URLClassLoader urlClassLoader=(URLClassLoader) constructor.newInstance(new Object[]{urls});