前两天看到一道面试题,是关于内部类的知识,觉得很有意思。
这道题是这样的:
根据注释填写(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类有一个方法,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,是通过他得到类所实现的接口,但是如果要通过接口得到子类的相关内容,是行不通的,因为在接口中没有任何与实现类相关联的代码(比如关键字)。
不知道小弟所说是否正确,请大家指正,谢谢!
分享到:
相关推荐
对于框架设计者来说,便捷的代码,是很重要的一部分。 反射和泛型是一种重要的解决途径。 此代码是一个生成泛型对象的类。...希望能帮助那些为查找泛型构造器、非静态内部泛型类的对象生成而烦恼的童鞋。
掌握类和对象的概念,掌握面向对象编程的本质 49.静态变量和静态方法的意义,如何引用一个类的静态变量或者静态方法? 50.JAVA语言如何进行异常处理,关键字:thorws,throw,try,catch,finally 51.Object类(或者其...
生成这4条指令的最常见的Java代码场景是:使用new关键字实例化对象的时候、读取或者设置一个类的静态字段(被final修饰、已在编译器把结果放入常量池的静态字段除外)的时候,以及调用一个类静态方法的时候 - 使用...
龙卷风这是什么? 将 Java 对象序列化为 Json 并将 Json 反序列化为 Java 对象的实用程序为什么是另一个框架?... 使用 newInstance() 获取 Rjson 对象的实例使用 toJson 或 toObject 方法序列化或反序列化
类)的路径,我们可以利用 Class.forName() 方法获取到给的的类,然后通过调用该类的 newInstance() 方法实例化此类的对象,这样就取代了使用 new 关键字实例化对象的操作方式。 通过调用类的 newInstance() 方法...
(3)Java 中的动态数组——其实为 Java 中的集合类 为什么要应用静态数组而不使用 Java 中的集合类(如 ArrayList 等)产生动态数组?主要 是考虑到效率和类型两方面的问题。 1) 效率: 要想保存和随机访问一系列...
//获取类的默认构造函数对象并实例化对 象。 第五:得到car对象,然后调用car的方法:Method methd =car.getMethod("setName","String.class");//method声明,并指向car的setName这个方法,得到setName方 法。 2 用...
String----类---实例化成对象 设计一个方法,给一个字符串(类)返回一个对象,对象里面还有属性值 例如考试系统中 Question类型 ATM系统 User类型 Question和User类型都是我们自定义的,这两个类的目的是为了...
1、 Class类 和它的实例的产生: Class的对象是已经存在的类型, 所以不能够直接new一个Class对象出来,是通过Class类中的一个方法获取到的。 例如:通过全限定路径类名 2、同一种类型不管通过...
使用反射方式构造对象实例 第 7 章 在测试代码中使用Mock 静态mock,new MockUp的使用 mock构造函数和静态代码块 new MockUp和spring的集成 针对静态mock做断言 动态mock,new Expectations的使用
(2) 运用反射手段,调用java.lang.Class或者java.lang.reflect.Constructor类的newInstance()实例方法。 (3) 调用对象的clone()方法。 (4) 运用反序列化手段,调用java.io.ObjectInputStream对象的 ...
不能实例化泛型 T t = new T(); //error 数组不可用泛型限定 List[] list = new List[10]; //错误 E[] a = new E[10]; //错误 类的静态变量不能声明为类的泛型类型 public class GenClass...
java实例化对象的5种方式 String中对象实例化方法补充 静态变量与动态变量 为什么一个Java文件中只能包含一个public类 public/private/protected的具体区别 java的8种基础类型 static关键字 理解final关键字 java...
(1)New操作(2)调用类的静态方法(3)访问类的静态域(不是final的常值常量)(4)进行反射操作(4)创建对象的实例(1)调用父类的构造函数(如果在代码
2、同一种泛型可以对应多个版本(因为参数类型是不确定的),不同版本的泛型类实例是不兼容的。 3、泛型的类型参数可以有多个。 4、泛型的参数类型可以使用extends语句,例如。习惯上成为“有界类型”。 5、泛型的...
pvData 自省接口类和机制,其中使用 Java 反射 API 替换。 用法: 首先定义一个简单的Java POJO类: public static class MyData{double x, y;String name;int [] numbers;} 创建一个新实例并初始化数据MyData data...
再根据反射的Class.forName(全路径)获取类的class文件,再通过class.newInstance()实例化对象 从而实现解耦。 1、IOC思想基于IOC容器完成,IOC容器底层就是对象工厂 (1)BeanFactory:IOC容器基本实现,是Spring内部的...
nested [java] 嵌套的 ['nestid] '如:内部类(nested classes) Object [java] 对象 ['ɒbdʒekt] Overload [java] 方法的重载(不同参数列表的同名方法) [,әuvә'lәud] Override [java] 方法的覆盖(覆盖父类的...
对于一个 Java EE 应用来说,通常这些服务对象都是被容器管理的(例如,Spring 容器或 EJB 容器),更合适的方法是查找该服务对象而非直接实例化。因此,需要告诉 BlazeDS 通过 Factory 来查找指定的 FlexService ...