文章目录
一、反射机制
1.1 概念
当一个类加载进内存后,会生成相应的字节码文件,我们可以通过这个字节码文件获得类的一些信息,来得到类的属性,调用类的一些功能。
1.2 获取类的字节码文件对象
- 通过Object类中的 getClass()
所有该类的实例通过该方法获得的字节码对象是一样的
public class text1 {
String name;
String sge;
public static void main(String[] args) {
text1 a = new text1();
text1 b = new text1();
Class class1 = a.getClass();
Class class2 = b.getClass();
System.out.println(class1 == class2);
}
}
- 通过类的静态属性获得
通过类的静态class属性与类的getclass()方法获得的字节码文件对象时一样的。
public class text1 {
String name;
String sge;
public static void main(String[] args) {
text1 a = new text1();
Class class1 = a.getClass();
Class class3 = text1.class;
System.out.println(class1 == class3);
}
}
-
通过 Class 类中的一个静态方法来获取
-
static Class<?> forName (String className)
返回与带有给定字符串名的类或接口相关联的 Class 对象。
通过一个类的全路径,就可以获取到该类的字节码文件对象
类的全路径: 类的包名+类的名称:
public class text1 {
String name;
String sge;
public static void main(String[] args) throws ClassNotFoundException {
Class class3 = text1.class;
Class class4 = Class.forName("net.text1");
System.out.println(class3 == class4);
}
}
结果:
true
1.3 进一步通过类的字节码文件对象获取类的构造方法
- public Constructor<?>[] getConstructors ()
获取所有的构造方法不包含私有的
示例:
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException {
Class class3 = text1.class;
Constructor[] constructor = class3.getConstructors();
for(Constructor cons: constructor) {
System.out.println(cons);
}
}
结果:
public net.text1()
public net.text1(java.lang.String,java.lang.String)
- public Constructor<?>[] getDeclaredConstructors ()
获取所有的构造方法 包括私有的 - public Constructor getConstructor (Class < ? >…parameterTypes)
获取单个的构造方法 不包含私有的 - public Constructor getDeclaredConstructor (Class < ? >…parameterTypes)
获取单个的构造方法包含私有的
示例:
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException {
Class class3 = text1.class;
System.out.println(class3.getDeclaredConstructor());
System.out.println(class3.getConstructor(String.class, String.class));
}
结果:
public net.text1()
public net.text1(java.lang.String,java.lang.String)
注意:
获取单个构造方法时,你要获什么构造方法,里面就是对应类型的class类型。
1.4 进一步通过构造方法创建类对象
- newInstance(Object…initargs)
使用此 Constructor 对象表示的构造方法来创建该类的实例,并用指定的初始化方法初始化该实例。
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
Class class3 = text1.class;
Constructor constructor = class3.getDeclaredConstructor();
Constructor constructor1 = class3.getConstructor(String.class, String.class);
Object o = constructor.newInstance();
Object o1 = constructor1.newInstance("张三","10");
text1 text = (text1)o;
text1 text2 = (text1)o1;
System.out.println(text.name + " " + text.age);
System.out.println(text2.name + " " + text2.age);
}
结果:
null null
张三 10
- void setAccessible(boolean value)
该方法可以取消语法规则,如下例在使用了该方法后,我们可以通过类的私有构造方法来创建实例。
public static void main(String[] args) throws Exception{
Class class1 = text1.class;
Constructor constructor = class1.getDeclaredConstructor(String.class, String.class);
constructor.setAccessible(false);
Object o = constructor.newInstance("张三", "19");
text1 te = (text1)o;
System.out.println(te.name + " " + te.age);
}
1.5 通过构造方法获取类的属性(成员变量)
- public Field[] getFields ()
获取所有的成员变量包含从父类继承过来的 - public Field[] getDeclaredFields ()
获取所有的成员变量 包含私有的 也包含从父类继承过来的成员变量
public static void main(String[] args) throws Exception{
Class class1 = text1.class;
Field[] fields = class1.getFields();
for(Field field : fields) {
System.out.println(field);
}
}
- public Field getField (String name)
获取单个成员变量 - public Field getDeclaredField (String name)
获取单个成员变量,包括私有的
public static void main(String[] args) throws Exception{
Class class1 = text1.class;
Field one = class1.getField("TWO");
System.out.println(one);
}
注意:获取单个成员变量时,参数是成员变量的名称。
1.6 给对象的成员变量设置
- void set (Object obj, Object value)
给指定类的实例的成员变量设值。
步骤:
- 通过类的字节码文件对象创建类的实例
- 通过反射机制获取类的成员变量的对象
- 通过set方法给指定类的实例的成员变量设值
- 通过get方法得到设置后的成员变量的值
注意:哪个成员变量的对象调用的set方法只能设值该成员变量的值
示例:
public static void main(String[] args) throws Exception{
Class class1 = student.class;
Field name = class1.getDeclaredField("name");
name.setAccessible(true);
Object o =class1.newInstance();
name.set(o, "张三");
Object o1 = name.get(o);
System.out.println(o1);
}
1.8 获取类的成员方法
-
public Method[] clazz.getMethods()
获取类的所有成员方法,不包括私有的 -
public Method[] clazz.getDeclaredMethods()
获取类的所有的成员方法,包括私有的,不包括从父类继承的。 -
public Method getMethod(String method_name, 参数类型的class类型列表)
获取类的某一个成员方法,不包括私有的 -
public Method getDeclaredMethod(String method_name, 参数类型的class类型列表)
获取类的某一个成员方法,包括私有的
示例:
public static void main(String[] args) throws Exception {
Class class1 = student.class;
Method[] methods = class1.getMethods();
for (Method method : methods) {
System.out.println(method);
}
System.out.println("---------------------------------------");
Method[] method1 = class1.getDeclaredMethods();
for (Method method2 : method1) {
System.out.println(method2);
}
System.out.println("---------------------------------------");
Method method = class1.getDeclaredMethod("show2");
System.out.println(method);
Method method2 = class1.getMethod("show1", int.class);
System.out.println(method2);
}
1.9 调用类的成员方法
- Object invoke (Object obj, Object…args)
对带有指定参数的指定对象调用由此 Method 对象表示的底层方法。
public static void main(String[] args) throws Exception {
Class class1 = student.class;
Method method = class1.getDeclaredMethod("show2");
method.setAccessible(true);
Object o = class1.newInstance();
method.invoke(o);
}
结果:
show2方法
注意:如果调用的方法要传参数的话,就正常的传参即可。
1.10 总结
通过反射机制获取类的成员方法参数是参数类型的class类型,获取类的属性的参数是属性名称。
二、通过反射运行配置文件的内容
2.1 读取简单配置文件案例
内容:通过读取配置文件里的内容,来执行配置文件中的方法。
public class text {
public static void main(String[] args) throws Exception {
Properties pro = new Properties();
pro.load(new FileReader("config"));
Class class1 = Class.forName(pro.getProperty("classname"));
Method method = class1.getDeclaredMethod(pro.getProperty("methodname"));
method.setAccessible(true);
Object o = class1.newInstance();
method.invoke(o);
}
}
2.2 通过反射越过泛型检查
案例:
我给你ArrayList 的一个对象,我想在这个集合中添加一个字符串数据,如何实现呢?
思路:
泛型:只在编译期有效,运行期就擦除了
反射机制在运行期发挥作用
public class txet {
public static void main(String[] args) throws Exception {
ArrayList<Integer> list = new ArrayList();
list.add(90);
list.add(20);
list.add(19);
Class class1 = list.getClass();
Method add = class1.getMethod("add", Object.class);
add.invoke(list,"张三");
System.out.println(list);
}
}
结果:
[90, 20, 19, 张三]
还可以这样遍历:
for(Object obj : list) {
System.out.println(obj);
}
2.3 写一个工具类设置某个对象的某个成员变量为指定的值
工具类:
public class Set {
public static void set(Object obj, String old_valeue, Object new_value) throws Exception {
Class class1 = obj.getClass();
Field field = class1.getDeclaredField(old_valeue);
field.setAccessible(true);
field.set(obj, new_value);
}
}
测试:
public class txet {
public static void main(String[] args) throws Exception {
student stu = new student();
Set.set(stu, "name", "张三");
Set.set(stu, "age", "18");
System.out.println(stu.name + " " + stu.age);
}
}
结果:
张三 18