new和反射产生java内部类的实例化详解

前两天看到一道口试题,是关于内部类的知识,以为颇有意思。 java

这道题是这样的: 数组

根据注释填写(1),(2),(3)处的代码 测试

public class Test{ spa

?????? public static void main(String[] args){ 对象

????????????? // 初始化Bean1 接口

????????????? (1) 文档

????????????? bean1.I++; get

????????????? // 初始化Bean2 it

????????????? (2) io

????????????? 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

咱们知道,内部类分为两种,一种是静态内部类,一种是非静态内部类。前者不用产生外部类的实例化对象便可产生内部类的实例化对象,后者必须先产生外部类的实例化对象,才能产生内部类的实例化对象。

?

实例化静态内部类对象的模板是:? 外部类类名.内部类类名 *** = new 外部类类名.内部类类名()

实例化非静态内部类对象的模板是:外部类类名.内部类类名 *** = 外部类对象名.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类有一个方法,newInstance(Object...?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();
?????}

?

最后关于反射的一点点总结:

1.Java的反射是Java类的“自审”,能够经过反射探知类的结构,好比,得到类的属性,方法,修饰符,返回类型。是在程序运行时动态的天生的,那么有人会问,既然能够的到类的属性,方法等,那么是否能够增长或者删除它的属性或者方法呢?答案是否认的,因为Java是静态语言,不是动态语言,那么就不能修改类的属性,方法,只能探知类的属性,方法等。

2.既然能够经过反射获得类的属性及其父类的属性,方法等以及实现的接口的相关内容,那么是否能够经过接口获得他的实现类的属性,方法呢?答案是否认的,因为在子类中有个关键字implements,是经过他获得类所实现的接口,可是假如要经过接口获得子类的相关内容,是行不通的,因为在接口中没有任何与实现类相关联的代码(好比关键字)。

相关文章
相关标签/搜索