什么是java反射机制 :
对于一个类,可以知道类中的所有属性和方法,对于一个对象,可以调用他的属性和方法,这种动态获取信息和动态调用对象的方法就叫做反射机制。其实说白了就是通过java代码获取装载到方法区的类信息一种手段。
java反射机制有什么用 :
反射允许程序在运行时通过reflection的API取得任何一个已知名称的class内部信息,也可以称为java类的自审。
java反射机制的基础 :
1.Class类 。
当一个.class文件被装载到JVM后,JVM就会自动产生一个Class对象,通过这个Class对象,我们就能获得加载到JVM中这个Class对象方法,属性等信息。
2.java.lang.reflect包 。
在这个包中有很多的接口和类,正是因为有这个类库的支撑,正是因为有反射的API,我们才能得到JVM中类的信息。
下面是一些API的介绍:
得到所要操作的Class对象(这个查下Class的API中有很多的方法),如:
Class c = Class.forName("reflection.Demo");
是不是似曾相识,不错,在JDBC中得到驱动的时候用过。不过这里可能会抛出ClassNotFoundException异常。
1.得到类中的构造方法
getConstructor(Class<?>...parameterTypes);
getDeclaredConstructor(Class<?>...parameterTypes);
getConstructors();
getDeclaredContructors();
在这么多的方法中,其中带有Declared的表示获取所有申明的构造方法,不管是"public","protected",默认的还是"private",而不带Declared的表示获取的是申明为"public"的内容。不带参数的表示获取所有能获取的构造方法,而带参数的表示构造方法的参数类型。如:
Person类:
public class Person {
public String name;
protected int age;
String sex;
private boolean isStudent = true;
private Person(String name){
this.name = name;
}
Person(String name,int age){
this.name = name;
this.age = age;
}
protected Person(String name,int age,String sex){
this.name = name;
this.age = age;
this.sex = sex;
}
public Person(String name,int age,String sex,boolean isStudent){
this.name = name;
this.age = age;
this.sex = sex;
this.isStudent = isStudent;
}
}
测试类:
public class Test2 {
public static void main(String [] args) throws Exception{
Class<?> c = Class.forName("reflection.Person");
Constructor<?> []con1 = c.getConstructors();
Constructor<?> []con2 = c.getDeclaredConstructors();
Constructor<?> con3 = c.getDeclaredConstructor(String.class,int.class);
for(int i = 0;i<con1.length;i++){
System.out.println(con1[i].toString());
}
System.out.println("=============================");
for(int i = 0;i<con2.length;i++){
System.out.println(con2[i].toString());
}
System.out.println("=============================");
System.out.println(con3);
Person per = (Person) con3.newInstance("张三",20);//用得到的构造函数创建对象
}
}
结果为:
public reflection.Person(java.lang.String,int,java.lang.String,boolean)
=============================
public reflection.Person(java.lang.String,int,java.lang.String,boolean)
protected reflection.Person(java.lang.String,int,java.lang.String)
reflection.Person(java.lang.String,int)
private reflection.Person(java.lang.String)
=============================
reflection.Person(java.lang.String,int)
2.得到类中的属性
getField(String name);
getDeclaredField(String name);
getFields();
getDeclaredFields();
和构造方法差不多的,参数指的是属性的名字,如修改Person类中的私有isStudent属性:
public class Test2 {
public static void main(String [] args) throws Exception{
Class<?> c = Class.forName("reflection.Person");
Person per = (Person) c.newInstance();
Field field = c.getDeclaredField("isStudent");
field.setAccessible(true);
field.setBoolean(per, false);
}
}
在这里会报异常:
Exception in thread "main" java.lang.InstantiationException : reflection.Person
查API发现,当应用程序试图使用Class类中的newInstance()方法创建一个类的实例,而指定的类对象无法被实例化,就会抛出该异常。这是因为newInstance()只能调用无参构造方法,当我们在Person类中加个无参构造方法后,就不会报这个异常了。我们都知道当我们重载了构造方法后,无参构造方法不写的话就没有了。
这里有个方法,field.setAccessible(true),如果没这句,就会报下列异常,不允许改变private属性:
Exception in thread "main" java.lang.IllegalAccessException : Class reflection.Test2 can not access a member of class reflection.Person with modifiers "private" 。
要改变类中的属性,只要调用field.setXXX(Object,Object)方法,如果是该布尔属性就是setBoolean,第一个参数是要改变属性的对象,第二个是要改变成的值。如果是静态属性,第一个参数就是null。
3.得到类中的方法
getMethod(String name,Class<?>...parameterTypes);
getDeclaredMethod(String name,Class<?>...parameterTypes);
getMethods();
getDeclaredMethods();
和前面介绍的差不多,这里介绍一下参数,第一个String name表示的是方法名,第二个是参数类型:
Person类:
public class Person {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void print(){
System.out.println("测试一下");
}
}
测试类:
public class Test3 {
public static void main(String [] args) throws Exception{
Class<?> c = Class.forName("reflection.Person");
Person per = (Person) c.newInstance();
Method m1 = c.getMethod("print");
Method m2 = c.getMethod("setName", String.class);
Method m3 = c.getMethod("getName");
m1.invoke(per);
m2.invoke(per, "张三");
String name = (String) m3.invoke(per);
System.out.println(name);
}
}
结果:
测试一下
张三
通过调用getMethod()方法得到Method对象,然后调用invoke()方法,invoke()方法中,第一个参数代表的是哪个对象调用了类Person中的方法,第二个就是传的参数。
有了反射,一个类中的私有属性和方法也可以被我们调用。
这里只是简单的介绍了一下反射最基本的应用,其性能,安全性和AOP代理后面再做分析。