gistfile1.txt 使用new和反射实例化内部类对象前两天看到一道面试题,是关于内部类的知识,觉得很有意思,现对其详细讲解,绝对原创!~这道题是这样的:根据注释填写(1),(2),(3)处的
使用new和反射实例化内部类对象 前两天看到一道面试题,是关于内部类的知识,觉得很有意思,现对其详细讲解,绝对原创!~ 这道题是这样的: 根据注释填写(1),(2),(3)处的代码 public class Test{ public static void main(String[] args){ // 初始化Bean1 (1) bean1.I++; // 初始化Bean2 (2) bean2.J++; //初始化Bean3 (3) bean3.k++; } class Bean1{ public int I = 0; } static class Bean2{ public int J = 0; } } public class Bean{ public class Bean3{ public int k = 0; } } 这其实就是实例化内部类对象的题。 从上面的题可以看出,Bean1为非静态内部类,Bean2为静态内部类,而Bean3则不是在Test类内部了,而是在一个外部类Bean的内部(是不是很拗口),呵呵。现通过new和反射来详细讲解其产生原理。 1.new 我们知道,内部类分为两种,一种是静态内部类,一种是非静态内部类。前者不用产生外部类的实例化对象即可产生内部类的实例化对象,后者必须先产生外部类的实例化对象,才能产生内部类的实例化对象。 实例化静态内部类对象的模板是: 外部类类名.内部类类名 xxx = new 外部类类名.内部类类名() 实例化非静态内部类对象的模板是:外部类类名.内部类类名 xxx = 外部类对象名.new 内部类类名() 1>>实例化非静态内部类Bean1 java代码 : Test test = new Test(); Test.Bean1 b1 = test.new Bean1(); b1.I++; 总共3行代码,是不是很简单呢。 2>>实例化静态内部类Bean2 java代码: Test.Bean2 b2 = new Test.Bean2(); b2.j++; 3>>实例化非静态内部类Bean3 Bean bean = new Bean(); Bean.Bean3 b3 = bean.new Bean3(); System.out.println(b3.k++); 总结:通过new方式产生内部类的实例化对象实现起来比较简单,也很容易理解,如果要深层次了解其产生,请用下面讲解的方式,反射。 2.反射 1>>反射产生非静态内部类Bean1的实例化对象 java代码: try { Class cla2 = Class.forName("com.lovo.nio.Test$Bean1"); } catch (ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } 解析:我们知道,内部类的class文件是以"外部类$内部类.class"的形式存在的,所以获取Class对象的时候,必须使用forName("包名+外部类$内部类")的形式才能得到Class对象 得到Class对象cla2后,肯定有人会说用下面的方法得到Bean1的实例化对象: Bean1 b6 = (Bean1)cla2.newInstance(); 运行上述代码,出现异常:InstantiationException,查看Java API文档,下面引用其原话: 当应用程序试图使用 Class 类中的 newInstance 方法创建一个类的实例,而指定的类对象无法被实例化时,抛出该异常。实例化失败有很多原因,包括但不仅限于以下原因: 类对象表示一个抽象类、接口、数组类、基本类型、void 类没有非 null 构造方法 在这里的原因是:Bean1的构造方法不公开,意思就是说,他的构造方法不是public的,不能通过newInstance()方式产生他的实例化对象。 那么我们是否可以查看其是什么访问修饰符的构造方法呢?答案是可以的,可以通过以下方式得到: Constructor [] c = cla2.getDeclaredConstructors(); int i = c[0].getModifiers(); //得到访问修饰符 String str = Modifier.toString(i); System.out.println(str+" aaaaaaaaa"); //注意:此处的aaaaaaaaaaa仅表示有这个输出 运行以上代码,输出“ aaaaaaaaa”,可以看出并没有输出str,实际上已经输出了,就是default,default在Java中可以省略不写的。现在该明白了吧!~ 那要如何才能实例化他的内部类对象呢,刚才我们说过,要实例化非静态的内部类对象,必须先实例化外部类的对象,可是我们任然没有实例化外部类的对象。我们查看JAVA PAI文档,发现Constructor类有一个方法,(... initargs),于是我们想到下面这种方式来构建: //反射产生非静态内部类Bean1的实例化对象 try { Class cla2 = Class.forName("com.lovo.nio.Test$Bean1"); // Bean1 b6 = (Bean1)cla2.newInstance(); // System.out.println(b6); Constructor [] c = cla2.getDeclaredConstructors(); int i = c[0].getModifiers(); //得到访问修饰符 String str = Modifier.toString(i); System.out.println(str+" aaaaaaaaa"); Bean1 bean1 = (Bean1)c[0].newInstance(new Test()); System.out.println(bean1.I++); } catch (ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IllegalArgumentException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (InstantiationException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IllegalAccessException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (InvocationTargetException e) { // TODO Auto-generated catch block e.printStackTrace(); } 运行以上代码,正常。 2>>反射产生静态内部类Bean2的实例化对象 Java代码: //反射产生静态内部类Bean2的实例化对象 try { // 初始化Bean2 Class cla = null; cla = Class.forName("com.lovo.nio.Test$Bean2"); Bean2 bean2 = (Bean2)cla.newInstance(); System.out.println(bean2.j++); } catch (ClassNotFoundException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } catch (InstantiationException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IllegalAccessException e) { // TODO Auto-generated catch block e.printStackTrace(); } 解析: 首先来看看static的相关知识: static内部类意味着: (1) 为创建一个static内部类的对象,我们不需要一个外部类对象。 (2) 不能从static内部类的一个对象中访问一个外部类对象. 倘若为了创建内部类的对象而不需要创建外部类的一个对象,那么可将所有东西都设为static。为了能正常工作,同时也必须将内部类设为static。 此外,也可考虑用一个static内部类容纳自己的测试代码。 在这里我们同样可以使用上面的方法获取他的构造方法的修饰符,也是default的,(静态内部类是有构造方法的,而且是无参的构造方法). java代码: try { // 初始化Bean2 Class cla = null; cla = Class.forName("com.lovo.nio.Test$Bean2"); Bean2 bean2 = (Bean2)cla.newInstance(); Constructor [] cs = cla.getDeclaredConstructors(); // Modifier.toString(cs[0]); System.out.println("******************"); System.err.println(cs.length); System.out.println(cs[0].getModifiers()); System.out.println("******************"); System.out.println(bean2.j++); } catch (ClassNotFoundException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } catch (InstantiationException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IllegalAccessException e) { // TODO Auto-generated catch block e.printStackTrace(); } 运行以上代码,红色部分代码输出: ****************** 1 ****************** 可以看出,有一个默认的(方法修饰符没写的,不是没有方法修饰符)构造方法。 如果我们要使用反射产生Bean2的实例化对象的话,只能用getDeclaredConstructors()方法。如上面的代码所示。 3>>反射产生 外部类的内部类Bean3 的实例化对象 分析:要产生外部类的内部类的实例化对象,则要先产生外部类的实例化对象。再通过getClass()方法得到外部类的实例化对象的Class对象,再通过getClasses()方法得到外部类的所有公共类和接口,包括内部类。 Java代码: try { Class c3 = bean.getClass(); Class [] cl = c3.getClasses(); // Bean3 b4 = (Bean3)c3.newInstance(); // System.out.println(b4); //使用反射产生Bean3实例化对象 Constructor [] cc = cl[0].getDeclaredConstructors(); //获取构造方法的个数,用以判定其构造方法是否是公共的,如果直接通过c3.newInstance()方法来实例化Bean3的话,则会包异常:java.lang.ClassCastException System.out.println(cc.length); Bean3 bb = (Bean3)cc[0].newInstance(new Bean()); System.out.println(bb.k++); } catch (IllegalArgumentException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (InstantiationException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IllegalAccessException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (InvocationTargetException e) { // TODO Auto-generated catch block e.printStackTrace(); }