三、怎么使用反射
想要使用反射机制,就必须要先获取到该类的字节码文件对象(.class),通过字节码文件对象,就能够通过该类中的方法获取到我们想要的所有信息(方法,属性,类名,父类名,实现的所有接口等等),每一个类对应着一个字节码文件也就对应着一个Class类型的对象,也就是字节码文件对象。
获取字节码文件对象的三种方式。
1、Class class1= Class.forName("全限定类名"); //通过Class类中的静态方法forName,直接获取到一个类的字节码文件对象,此时该类还是源文件阶段,并没有变为字节码文件。
2、Class class2= User.class; //当类被加载成.class文件时,此时Person类变成了.class,在获取该字节码文件对象,也就是获取自己, 该类处于字节码阶段。
3、Class class3= user.getClass(); //通过类的实例获取该类的字节码文件对象,该类处于创建对象阶段。
代码实例:
//User类还没有加载,在源文件阶段 获取其字节码文件对象
Class class1 =Class.forName("com.reflect.test.User");
System.out.println("第一种:"+class1.getName());
//可以根据 实例对象获取我们想要的信息
Class class2 = User.class;
System.out.println("第二种:"+class2.getName()); User user = new User();
Class class3 = user.getClass();
System.out.println("第三种:"+class3.getName());
} 输出:
第一种:com.reflect.test.User
第二种:com.reflect.test.User
第三种:com.reflect.test.User
获取字节码文件对象
有了字节码文件对象才能获得类中所有的信息,我们在使用反射获取信息时,也要考虑使用上面哪种方式获取字节码对象合理,视不同情况而定。下面介绍Class类的功能。
首先生成一个User类对象
package com.reflect.test; public class User { private String name ;
private int age;
private String sex; public User() {
} public User(String name, int age, String sex) {
this.name = name;
this.age = age;
this.sex = sex;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public int getAge() {
return age;
} public void setAge(int age) {
this.age = age;
} public String getSex() {
return sex;
} public void setSex(String sex) {
this.sex = sex;
} @Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
", sex='" + sex + '\'' +
'}';
}
}
User.java
3.1 通过字节码对象创建实例对象
public static void main(String[] args) throws ClassNotFoundException,InstantiationException,IllegalAccessException{
//User类还没有加载,在源文件阶段 获取其字节码文件对象
Class classz =Class.forName("com.reflect.test"); //forname 里面的参数完整文件路径,如果查不到改路径下的文件 会报 classNotFound异常
//创建实例,
User user = (User) classz.newInstance();
//可以根据 实例对象获取我们想要的信息
}
3.2获取指定构造器方法。constructor对象
知道对象构造函数的参数情况下,可以直接获取指定构造函数。
//User类还没有加载,在源文件阶段 获取其字节码文件对象
Class class1 =Class.forName("com.reflect.test.User");
//获取无参构造函数
Constructor constructor1 = class1.getConstructor();
//获取有参构造函数
Constructor constructor2 = class1.getConstructor(String.class,int.class,String.class);
User user1=(User)constructor1.newInstance();
System.out.println("user1: " + user1);
User user2 = (User) constructor2.newInstance("张三",23,"男");
System.out.println("user1: "+ user2); 输出:
user1: User{name='null', age=0, sex='null'}
user1: User{name='张三', age=23, sex='男'}
如果不知道 User类有哪些构造方法,以及参数,可以这样获取全部构造方法
//User类还没有加载,在源文件阶段 获取其字节码文件对象
Class class1 =Class.forName("com.reflect.test.User");
//获取所有构造方法
Constructor[] constructors = class1.getConstructors();
//遍历所有构造方法
int index = 0;
for(Constructor constructor : constructors){
Class[] parameterTypes = constructor.getParameterTypes();
index =index+1;
System.out.println("第" + index +"个构造函数");
for (Class parameterType : parameterTypes){
//获取构造函数中的参数类型
System.out.print(parameterType.getName()+",");
}
输出:
第1个构造函数
第2个构造函数
java.lang.String,int,java.lang.String,
3.2获取成员变量并使用。Field对象
获取指定成员变量
//User类还没有加载,在源文件阶段 获取其字节码文件对象
Class class1 =Class.forName("com.reflect.test.User");
//获取实例对象
User user = (User) class1.newInstance();
//获取成员变量class1.getField(name); 通过name获取指定变量
// 如果变量属性是私有的 那么应该使用class1.getDeclaredField(name);
Field field = class1.getDeclaredField("name");
//因为属性是私有的,获得对象属性后,还要打开其私有权限
field.setAccessible(true); // 这里也变相的破解了 面向对象的封装性
//对其变量进行操作
field.set(user,"张三");
//
System.out.println(field.get(user));
System.out.println(user.getName());
输出:
张三
张三
Class.getField(String)方法可以获取类中的指定字段(可见的), 如果是私有的可以用getDeclaedField("name")方法获取,通过set(obj, "李四")方法可以设置指定对象上该字段的值, 如果是私有的需要先调用setAccessible(true)设置访问权限,用获取的指定的字段调用get(obj)可以获取指定对象中该字段的值
获取全部属性 变量
//User类还没有加载,在源文件阶段 获取其字节码文件对象
Class class1 =Class.forName("com.reflect.test.User");
//获取实例对象
User user = (User) class1.newInstance();
user.setName("zhangsan");
user.setAge(23);
user.setSex("男");
//获取全部成员变量
Field[] fields = class1.getDeclaredFields(); for ( Field field : fields){
field.setAccessible(true);
System.out.println(field.get(user));
}
输出:
zhangsan
23
男
3.3 获得成员方法并使用。Method对象
//User类还没有加载,在源文件阶段 获取其字节码文件对象
Class class1 =Class.forName("com.reflect.test.User");
//获取实例对象
User user = (User) class1.newInstance();
user.setAge(23);
user.setSex("男");
/**
* Method getMethod(String name, Class<?>... parameterTypes)
* name : 为方法名字
* parameterTypes:方法的参数,为class类型,比如参数类型为String,则填string.class
* 没有则不填
*/
/**
* Object invoke(Object obj, Object... args)
* obj:方法的对象
* args:实际的参数值,没有则不填
*/
Method method1 = class1.getMethod("getAge");
System.out.println("method1 方法:" +method1.getName());
System.out.println("调用方法 : "+method1.invoke(user));
Method method2 = class1.getMethod("getSex");
System.out.println("method2 方法:" +method2.getName());
System.out.println("调用方法 : "+method2.invoke(user));
//我们将 User 类的setName()方法 改为私有的private void setName(String name) {this.name = name; } Method method3 = class1.getDeclaredMethod("setName", String.class);
method3.setAccessible(true);
method3.invoke(user,"zhangsan"); //可以调用 user对象的 私有方法setName
System.out.println("user.getName :" + user.getName()); // 获取 name属性值 输出:
method1 方法:getAge
调用方法 : 23
method2 方法:getSex
调用方法 : 男
user.getName :zhangsan
Class.getMethod(String, Class...) 和 Class.getDeclaredMethod(String, Class...)方法可以获取类中的指定方法,
如果为私有方法,则需要打开一个权限。setAccessible(true);
用invoke(Object, Object...)可以调用该方法,
同理可以 获取全部方法
//User类还没有加载,在源文件阶段 获取其字节码文件对象
Class class1 =Class.forName("com.reflect.test.User");
//获取实例对象
User user = (User) class1.newInstance();
user.setAge(23);
user.setSex("男"); Method [] methods = class1.getDeclaredMethods();
for (Method method : methods){
method.setAccessible(true);
System.out.println("方法名:"+method.getName());//获取 方法名
Class [] parameterTypes = method.getParameterTypes();//这里又回到了上面的获取参数代码
for (Class parameterType : parameterTypes){
System.out.println("获取参数名:"+parameterType.getName());
} }
输出:
方法名:toString
方法名:getName
方法名:setName
获取参数名:java.lang.String
方法名:getSex
方法名:getAge
方法名:setSex
获取参数名:java.lang.String
方法名:setAge
获取参数名:int
3.4 获得该类的所有接口
Class[] getInterfaces():确定此对象所表示的类或接口实现的接口
返回值:接口的字节码文件对象的数组