Java 反射机制详解(下)
三、怎么使用反射
想要使用反射机制,就必须要先获取到该类的字节码文件对象(.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 + '\'' + '}'; } }
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():确定此对象所表示的类或接口实现的接口
返回值:接口的字节码文件对象的数组