1.反射机制定义:
Java反射机制是在运行状态时,对于任意一个类,都能够知道这个类的所有属性和方法;对于任何一个对象,都能够调
用它的任意一个
方法和属性.
2.Java反射机制的类库支持
Class类和java.lang.reflect类库一起构成了对Java反射机制的支持.其中最常用的类是Constructor,Field,Method,
而这三个类都继承了一个接口Java.lang.reflect.Member.下面介绍一下java.lang.reflect类库中的类:
AccessibleObject: Field,Method,和Constructor对象的基类.提供了将反射的对象标记为在使用时取消默认Java语
言访问控制检查的能力
Array: 提供了动态创建和访问Java数组的方法
Constructor: 提供关于类的单个构造方法的信息以及对它的访问权限.
Field: 提供有关类或接口的单个字段的信息,以及对它的动态访问权限.反射的字段可能是一个类(静态)字段或实例字
段.
Method:提供关于类或接口上单独某个方法(以及如何访问该方法)的信息.所反映的方法可能是类方法或实例方法(包
括抽象方法)
在面向对象的世界中, 万事万物皆是对象,除了静态成员(因为静态成员属于某个类,而不是对象
)和普通数据类型.
在面向对象的语言中,我们擅长将现实世界中的一个实际存在的事物抽象并封装成一个类,并在类中添加相应的成员变量
(属性)和方法,然后我们就可以创建该类的对象,该对象持有属于自己的成员变量和方法.
其实,类也是对象,因为万事万物皆为对象,我们写的每一个类都是对象,是java.lang.Class 类的对象,也就是说每一个类都
既有自己的对象,同时也是Class类的对象.
Java.lang.Class类
先来看看Class类的构造方法:
***
由注释可知,Class类的构造方法是私有的,只有Java虚拟机可以创建该类的对象,因此我们无法显式地声明一个Class对象
class MyClass{}
MyClass myclass = new MyClass();
Class类与其他类的关系
我们无法像定义普通对象一样,通过new直接创建Class类的对象,但是可以通过其他方式得到Class的对象
-
通过类的静态成员表示,每个类都有一个隐含的静态成员Class,表示如下:
Class c1 = MyClass.class;
-
通过类对象的getclass()方法.由1不难理解,既然存在静态变量,那么通过对象的getter方法,就可以获取静态成员
class:
Class c2 = myclass.getClass();
-
通过Class类的静态方法forName()方法获取Class的对象.区别于通过new创建对象(编译时静态加载),在开发时如果我们
需要动态加载我们的功能模块,该方法可以帮助我们实现在程序运行时的动态加载
接下来先介绍forName方法
public static Class<?> forName(String className)
throw ClassNotFoundException{
return forName0(className,true,ClassLoader.getCallerClassLoader());
根据给定的类名参数className,查找与className相对应的Class实例,然后加载,连接该实例对象,之后返回这个Class实例.
注意,forName()需要传入类的全路径,如果当前类与参数类在同一包下即可省略包名,这个方法不会自动的去搜索,只会按照给出的
路径严格寻找,寻找不到就抛出ClassNotFoundException.
try {
//注意,forName()需要传入类的全路径
//如果当前类与参数类在同一包下即可省略包名
mClass = Class.forName("custom.OtherClass");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
由Class类的对象得到类的对象
我们可以通过类或类对象得到Class类的对象,反过来,我们也可以由Class类的对象得到类的对象
MyClass mClass2 = (MyClass)c2.newInstance();
c2.newInstance()需要调用MyClass类的无参构造方法!如果MyClass类中存在显示的有参构造方法,会覆盖默认的无参构造方法,
同时又没有显示的声明无参构造方法,那么执行这段代码会直接导致程序Crash掉.
Demo(一)--实例化Class对象,通过对象获得包名和类型
try {
Class c1 = Class.forName("java.lang.Integer");
System.out.println(c1.getName());
} catch (ClassNotFoundException e) {
System.out.println("forName出错");
}
Integer i = 1;
Class c2 = i.getClass();
System.out.println(c2.getName());
Class c3 = Integer.class;
System.out.println(c3.getName());
如果使用基本类型int,只有最后一种方法才好用.
Demo(二)--通过Class实例化任意类的对象
见上面
Demo(三)--获取类中的构造函数
我们可以使用Class类中的Constructor<?>[ ] getConstructor()方法,来获得这个类的构造函数
JDK API文档里,对这个方法的描述如下:
返回一个包含某些Constructor对象的数组,这些对象反映此Class对象所表示类的所有
公共构造方法
.如果该类没有公共构
造方法,或者该类是一个数组类,或者该类反映一个基本类型或void,则返回一个长度为0的数组.
注意:此方法返回Constructor<T>对象的数组(即取自此类构造方法的数组)时,此方法的返回类型是Constructor<?>[ ],不
是预期的Constructor<T>[ ].此少量信息的返回类型是必需的,因为从此方法返回之后,该数组可能被修改以保存不同类的
Constructor对象,而这将违反Constructor<T>[ ]的类型保证
Class
<?> c = MyClass.class;//获取Class对象
Constructor<?> cons[] = c.getConstructors();//获取构造函数的数组
//打印
for (Constructor<?> constructor : cons) {
System.out.println(constructor);
}
运行结果打印出了所有显示声明的,而且是公共的构造函数。
但是,这个顺序和我们类里的定义顺序是不一样的,因此,从这个方法精准地定位一个构造函数是不可取的。真的乱序的吗?API里没有提到。但是,另外一个方法的文档中提到了。这个方法是
Constuctor<?>[] getDeclaredConstructors()
这个方法返回Constructor对象的一个数组,这些对象包含了Class对象所表示的类中的
所有构造方法
。它们分别是公共,保护,默认(包),和私有构造。
返回数组中的元素没有排序,也没有任何特定的顺序。
如果该类存在一个默认构造方法,则它包含在返回的数组中。如果此Class对象表示一个接口,一个基本类型,一个数组类或者void,则这个方法返回一个长度为0的数组。
调用构造方法生成实例
Constructor<T> getConstructor(Class<?>... parameterTypes)
Class<?> c = MyClass.class;//获取Class对象
Constructor<T> getConstructor(int.class);//只有使用类字面常量才能获得int这种基本类型的Class对象。
UserInfo userInfo = (UserInfo) constructor.newInstance(1);//构造实例
Demo(四)获取一个类的父类和实现的接口
获取父类:
使用Class类中的getSuperClass()方法能够得到一个类的父类
如果此 Class 表示 Object 类、一个接口、一个基本类型或 void,则返回 null。如果此对象表示一个数组类,则返回表示该 Object 类的 Class 对象。
由于Java没有多继承,一个类最多只能有一个父类,因此返回类型并不是一个数组。
获取接口:
Class<?>[] getInterfaces()得到的数组中,接口对象顺序和这个对象所表示的类中implements子句中接口名的顺序,是一致的。
Demo(五)获得并调用一个类中的方法
获得一个类中的方法:
使用getMethods()这个方法,返回了一个包含某些Method对象的数组,Method对象已经介绍过,用来表示一个方法的对象,是对类中方法进行抽象的结果。
这些对象反映了Class对象所表示的类或者接口,当然,包括那些由该类或者接口声明的以及从超类和超接口继承的那些类或接口。举个例子来解释一下:
例如使用getMethods()获得Integer类的方法,会把Integer的父类,Number的所有方法,以及Number的父类Object的方法都获取出来。
这个返回数组,包括了从Object类继承的所有(公共)member方法。返回数组中的元素没有排序。如果这个Class对象表示没有公共成员方法的类或者接口,或者表示了一个基本类型或者表示void,则返回长度为0的数组。
调用类中的方法
Method m1 = herosClass.getMethod("setName",String.class);
m1.invoke(userInfo,"影魔");
Demo(六)—获得并操作一个类的属性
Field[] fields = c.getDeclaredFields();
Field field = c.getDeclaredField("name");
如果属性为private,则需要 field.setAccessible(true); 才能修改属性值