反射机制用通俗的话来说就是Java程序可以加载一个在编译运行时才能够得知类名的class(也叫运行时类),从而
可以访问其
属性,方法,构造方法等
而反射的源头在Java.lang.Class包下,意味着我们要获取了Class类的实例,才能够创建对应的运行时类的对象并且访问其结构或者调用其指定的结构(也就是能够创建运行时类对象或者访问其属性,方法,构造方法等),下面是Class实例的获取
一、Class实例的获取
public void test4() throws Exception{
//1、通过运行时类的对象获取
Animal A = new Animal();
Class clazz = A.getClass();
System. out.println(clazz );
//2、通过运行时类的本身.class属性获取
Class clazz1 = Animal. class;
System. out.println(clazz1 );
//3、通过Class的静态方法获取
String ClassName = "TestReflection.Animal" ;
Class clazz2 = Class.forName(ClassName );
System. out.println(clazz2 );
//4、通过类的加载器
ClassLoader classLoader = getClass().getClassLoader();
Class clazz3 = classLoader.loadClass( ClassName);
System. out.println(clazz3 );
}
通过这四个方式都可以获取Class实例,但是通过类的加载器,我们不仅可以获取Class的实例,还可以获取一个文件中的信息,具体操作方式如下:
//读取类路径下的jdbc.properties文件
InputStream In =getClass().getClassLoader().getResourceAsStream("jdbc.properties" );//输入流读取文件
Properties properties = new Properties();
properties.load(In );
像上面这样只需要调用Properties中getProperty()方法就可以读取一个文件中的信息了
二、运行时类的创建
既然已经获取了Class类的实例,那么我们就可以通过它来创建运行时类的对象,通过反射创建运行时类的对象可以用Class类中的newInstance()方法来完成创建,该方法返回一个默认为Object类对象,需要进行强制转换为你所希望的运行时类对象,但是在使用这个方法之前需要注意两点:①对应的运行时类必须要有无参的构造器②构造器的权限要够(不能为private,否则创建失败),下面是具体的创建步骤:
假设在我的TestReflection包下有一个名为Animal的类,那么通过反射创建其运行时类的对象的方法为:
public void test() throws Exception{
Class clazz = Class.forName( "TestReflection.Animal");
Animal A1 = (Animal) clazz.newInstance(); //创建运行时类的对象,使用了Class类中的newInstance()方法,相当于调用了运行时类的构造器
System. out.println(A1 );
}
三、获取类的完整性结构及其方法
当通过一个运行时类获取了Class类的实例之后,就能够调用Class类中的一些方法获取该运行时类的方法及完整性结构了,具体方法如下:
1、先获取Class类的实例(这里假设有一个Animal类):
Class clazz = Animal.class;
2、获取对应运行时类的属性
①通过Field[] getFields() 方法获取运行时类及其父类中中声明位public的属性 由于可能有多个属性 所以返回了一个Field型的数组
②通过Field[] getDeclaredFields() 方法获取运行时类本身声明的所有属性
3、获取对应运行时类的权限修饰符、变量类型、变量名
获取了运行时类的属性之后,可以通过以下方法获取其权限修饰符、属性的变量类型、变量名
①通过int getModifiers()获取每个属性的权限修饰符所对应的编号 然后可以利用Modifiers.toString(int i)获取相应的权限修饰符名字
②通过Class getType()获取属性的类型
③通过String getName()获取属性名
4、获取对应运行时类的方法以及其注解、异常、形参类型、返回类型
先获取运行时类的方法之后,才能够获取其注解、异常、形参类型、返回类型,获取运行时类的方法的步骤如下:
①Method[] getMethods() 获取运行时类及其父类中所有声明位public的方法
②Method[] getDeclaredMethods() 获取运行时类本身的所有方法
获取了运行时类的方法之后,就可以通过以下方法去获取运行时类方法的一些属性:
①通过Annotation[] getAnnotations() 获取注解
②通过Class getReturnType() 获取返回类型
③通过Class[] getParameterTypes() 获取形参类型
④Class[] getExceptionTypes() 获取异常类型
5、获取运行时类的父类
获取了运行时类后,使用Class getSuoerclass() 方法可以获取运行时类的父类
6、获取带泛型的父类
当运行时类所继承的父类是泛型时,可以使用Class getGenericSuperclass()获取带泛型的父类
7、获取父类的泛型
现在一个运行时类Animal继承了其父类Creater,并使用了String类型的泛型,想要获取该父类的泛型,可以通过以下步骤
Class clazz = Animal.class;
Type type = clazz.getGenericSuperclass();
ParameterizedType param = (ParameterizedType)type;
Type[] ars = param.getActualTypeArguments();
System.out.println(((Class)ars[0]).getName());//java.lang.String
8、获取实现的接口
当当前运行时类实现了一些接口时,可以调用Class[] getInterfaces() 方法来获取实现的接口
9、获取运行时类所在的包
想要获取当前运行时类所在的包时,可以使用 Package getPackage() 方法来获取所在包
三、通过反射获取指定的属性、方法、构造器
1、获取指定的属性
获取指定属性可以通过getField(String fieldname)以及getDeclaredField(String fieldname)这两个方法,前者只能够获取当前运行时类下权限修饰符为public的指定名为fieldname的属性,后者可以获取当前运行时类下任何指定名为fieldname的属性,但是为了一些权限为public之外的属性可以被操作,因此在使用了getDeclaredField(String fieldname)方法获取了属性之后而又需要操作相应的属性的时候需要先使用setAccessible(boolean flag)方法并将其参数设置为true,使得属性可被操作,下面是具体的例子
现在有一个Animal类,其中包括了两个属性:
public class Animal{
public String name;
private int age;
public Animal(){
super();
}
private Animal(String name,int age){
this.name = name;
this.age = age;
@Override
public String toString() {
return "Animal [name=" + name + ", age=" + age + "]";
}
}
现在想用反射分别获取这两个属性并对其赋值,相应的操作如下:
@Test
public void test() throws Exception{
Class clazz = Class.forName("TestReflection.Animal");
//1、创建运行时类对象
Animal p = (Animal)clazz.newInstance();
//2、获取指定属性 通过getField(String fieldname)方法只能够获取到当前运行时类中声明为public的指定名为fieldname的属性
Field name = clazz.getField("name");
name.set(p, "cat");//将运行时类的指定属性赋值
System.out.println(p);//Animal [name=cat, age=0]
//3、通过getDeclaredField()方法能够获取到当前运行时类中指定名为fieldname的属性
Field age = clazz.getDeclaredField("age");
//4、由于权限修饰符的原因,在给属性赋值之前使用setAccessible()方法使得属性可被操作(即权限为public之外的其他权限修饰符)
age.setAccessible(true);
age.set(p, 6);
System.out.println(p);//Animal [name=cat, age=6]
}
2、获取指定的方法并调用
获取指定的方法可以通过getMethod(String methodname,Class .....type) 方法以及getMethod(String methodname,Class .....type )方法,同获取指定的属性一样,前者只能够获取权限修饰符为public的指定名为methodanme的方法;后者能够获取任何方法,只是需要使用setAccessible(boolean flag)方法并将其参数设置为true,使得方法能够被操作,可以看到这两个方法中除了String类型的methodname之外还有另一个参数,另一个参数为Class类,它表示的是被获取的方法的所有形参,所以在获取时,需要将方法中所有形参的类型全部写入
获取到指定的方法后就可以调用该方法了,调用该方法是通过Object invoke(Object obj1,Object ...args)方法来调用该方法,该方法会返回一个值,返回的值为被调用的方法的返回值,而其中的参数obj1就是对应的运行时类的对象,另一个参数就是传入该方法的实际参数(可能没有也可能为多个),具体使用例子如下:
先定义一个Animal类:
public class Animal{
public String name;
private int age;
public Animal(){
super();
}
private Animal(String name,int age){
this.name = name;
this.age = age;
}
public void show(){
System.out.println("我是一只动物");
}
public void display(String type){
System.out.println("我是"+type);
}
private Integer info(String type,Integer i){
System.out.println(type);
return i;
}
}
然后开始获取指定的方法,步骤如下
class TestGetMethod {
@Test
public void test() throws Exception{
//1、通过加载器获取对应运行时类
Class clazz = getClass().getClassLoader().loadClass("TestReflection.Animal");
//2、创建运行时类对象
Animal p = (Animal)clazz.newInstance();
//3、获取运行时类中权限修饰符为public的指定的方法
Method show = clazz.getMethod("show");
//4、调用指定的方法 invoke()返回的值为被调用方法的返回值,没有返回值则为null
Object obj = show.invoke(p);//我是一只动物
Method display = clazz.getMethod("display", String.class);//display(String type)只有一个形参
Object obj1 = display.invoke(p, "猫");//我是猫
//-------------------获取运行时类的指定方法(可以为public也可以不为public)----------
Method info = clazz.getDeclaredMethod("info", String.class,Integer.class);//info(String type,integer i)有两个形参
info.setAccessible(true);//使得该方法可被操作
Object obj2 = info.invoke(p, "dog",12);//dog
System.out.println(obj2);//12
}
}
3、获取指定的构造器
上面的例子中获取的构造器都是默认的空参构造器,如果想要获取另一个有参数的构造器,就得使用getConstructor(Class ....type)方法或者getDeclaredConstructor(Class ....type)方法来获取指定的构造器,前者只能够获取权限修饰符为public的构造器,后者能够获取任意权限的构造器,只是在操作构造器之前同样要使用setAccessible(boolean flag)方法并将其参数设置为true,使得构造器能够被操作,具体的获取方法如下:
@Test
public void test() throws Exception{
//1、获取运行时类
Class clazz = getClass().getClassLoader().loadClass("TestReflection.Animal");
//2、获取指定的构造器
Constructor con = clazz.getDeclaredConstructor(String.class,int.class);
con.setAccessible(true);
//3、调用指定的构造器
Animal p = (Animal)con.newInstance("狼",12);
System.out.println(p);
}