反射的定义
能够分析类能力的程序称为反射,反射的本质就是为了获取类信息。
在某些时刻,无法通过new来实例化对象,比如tomcat,这个时候就需要反射通过代码来获取类信息,并且无需new来生成对象。
反射获取类信息的三种方式
通过类:类.class
通过对象:对象.getClass();
通过路径:class.forName(里面传的是类的路径)
利用反射分析类的能力
java.lang.reflect 包中有三个类 Field、Method 和 Constructor
Field 类:----获取属性信息的类
常用的方法有四个:
getFields():获取所有公共类域---public
getDeclaredFields():忽略修饰符,获取所有类里的域
getField:获取指定名字的公共域
getDeclaredField():忽略修饰符,获取指定名字的域;
我们定义一个类:
public class Peo {
private String name;
protected String adeeress;
public int age;
public String sex;
public String password;
char a;
}
然后我们在测试类里尝试获取这些属性的信息,注释为输出的结果
public class TestP {
public static void main(String[] args) throws Exception {
Class p = Peo.class;
Field[] fields = p.getFields();
for (Field f:fields){
System.out.println(f);
}
//public int reflection.Peo.age
//public java.lang.String reflection.Peo.sex
//public java.lang.String reflection.Peo.password
System.out.println("----------------------------------");
Field[] fields1 = p.getDeclaredFields();
for (Field f2 :fields1){
System.out.println(f2);
}
//private java.lang.String reflection.Peo.name
//protected java.lang.String reflection.Peo.adeeress
//public int reflection.Peo.age
//public java.lang.String reflection.Peo.sex
//public java.lang.String reflection.Peo.password
//char reflection.Peo.a
System.out.println("----------------------------------");
Field age = p.getField("age");
System.out.println(age);
//public int reflection.Peo.age
System.out.println("----------------------------------");
Field name = p.getDeclaredField("name");
System.out.println(name);
//private java.lang.String reflection.Peo.name
}
}
Method类-----获取类中方法信息
常用方法类似于Field也是四个,获取的权限与Field基本类似;
getMethods()
getDeclaredMethods()
getMethod(String name,args....)
getDeclaredMethod(String name,ares....)
这里需要强调一下后两个方法,我们需要显示给出方法的名字和参数的类型,比如:
public int run(int a,int b,String c){
return 2;
}//这是我们定义的方法;
Class p = Peo.class;
Method run2 = p.getMethod("run",int.class,int.class,String.class);//这是我们获取的方式
下面我们来测试一下:
首先我们在刚才定义的Peo类中添加一些方法:
public class Peo {
private String name;
protected String adeeress;
public int age;
public String sex;
public String password;
char a;
public int run(){
return 1;
}
public int run(int a,int b,String c){
return 2;
}
private String eat(){
return "";
}
protected String hh(String ss){
return "ss";
}
我们在主方法中进行测试:
public static void main(String[] args) throws Exception {
Class p = Peo.class;
Method[] methods = p.getMethods();
for (Method m :methods){
System.out.println(m);
}
//public int reflection.Peo.run()
//public int reflection.Peo.run(int,int,java.lang.String)
System.out.println("-------------------------------");
Method[] methods1 = p.getDeclaredMethods();
for (Method m2:methods1){
System.out.println(m2);
}
//private java.lang.String reflection.Peo.eat()
//protected java.lang.String reflection.Peo.hh(java.lang.String)
//public int reflection.Peo.run()
//public int reflection.Peo.run(int,int,java.lang.String)
System.out.println("----------------------------------");
Method run1 = p.getMethod("run");
System.out.println(run1);
Method run2 = p.getMethod("run",int.class,int.class,String.class);
System.out.println(run2);
//public int reflection.Peo.run()
//public int reflection.Peo.run(int,int,java.lang.String)
System.out.println("-----------------------------------");
Method eat = p.getDeclaredMethod("eat");
Method hh = p.getDeclaredMethod("hh",String.class);
System.out.println(eat);
System.out.println(hh);
//private java.lang.String reflection.Peo.eat()
//protected java.lang.String reflection.Peo.hh(java.lang.String)
}
Constructor类
在我们已经了解过以上两个类之后,Constructor类便很容易理解了
同样有四个常用的方法:getConstructor、getConstructors、getDeclaredConstructor、getDeclaredConstructors,用法基本一致。
下面我们直接来测试。
我们定义一个私有类型的构造器
private Peo(String name){ this.name = name; }
Class p = Peo.class; Constructor c = p.getDeclaredConstructor(String.class);
//private reflection.Peo(java.lang.String)
可以看到 我们可以轻松获取构造器的信息。
以上我们了解了如何获取类的一些主要信息,我们知道了如何获取域、方法和构造器,接下来我们来介绍一下如何通过反射实例化对象,并对属性赋值。
在这里为了便于理解,我们将域和构造器稍作修改:
private String name; protected String address; public int age; private Peo(String name,String address,int age){ this.name = name; this.address = address; this.age = age; }
首先通过类名.class来获取当前类,并用一个Class变量接收:
Class peo = Peo.class;
然后通过getDeclaredConstructor方法来获取到构造器,并用Constructor类型的变量接收,这里注意我们需要显式给出参数类型,以此来明确获取的构造器:
Constructor constructor = peo.getDeclaredConstructor(String.class,String.class,int.class);
然后我们通过newInstance方法来实例化对象:
Peo peo1 = (Peo) constructor.newInstance("张三","保定",19);
这里会报错,原因是因为我们定义的构造器是私有类型的,直接调用newInstance是无法使用构造器的。
在实例化之前,我们需要暴力反射构造器,以此来屏蔽安全访问修饰符的检查,这里特指private
constructor.setAccessible(true); Peo peo1 = (Peo) constructor.newInstance("张三","保定",19);
我们可以看到,在暴力反射构造器之后,我们可以成功实例化一个Peo类型的对象了。
现在我们完成了对对象的实例化,而在我们的项目过程中,我们有时候需要修改对象的属性值。
对于非私有类型的变量,我们可以通过set方法直接对其修改,通过get方法直接获取到。
set(Object,value);
get(Object)
而对于私有属性的变量我们是没有办法直接获取的,
看到这里的小伙伴应该也明白了,如果我们想直接拿到私有类型的属性,我们需要对其暴力反射。
上面介绍的是通过暴力反射来获取到private类型的属性,我们也可以通过更改器和访问器来获取到private类型的属性:
//请注意这里的方法我们设置为public类型
public String getName() { return name; } public void setName(String name) { this.name = name; }
思考:假如更改器和访问器我们也设置为私有,如何通过这两个方法来获取到私有属性值?