通过java语言的反射机制可以操作字节码文件,让程序更加灵活。与反射机制相关的重要的类有:
1.java.lang.Class:每个类运行时的类型信息是用Class对象表示的,每个类都有一个Class对象。
2.java.lang.reflect.Method:代表字节码中的方法字节码。代表类中的方法。
3.java.lang.reflect.Constructor:代表字节码中的构造方法字节码。代表类中的构造方法。
4.java.lang.reflect.Field:代表字节码中的属性字节码。代表类中的成员变量。
获取Class对象的三种方式
1.Class c = Class.forName(“全限定类名”);
Class.forName()的执行会导致类的加载
try {
Class c1 = Class.forName("java.lang.String");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
2.Class c = 对象.getClass();
String s = "a";
Class c2 = s.getClass();
3.Class c = 任何类型.class;
Class c3 = String.class;
字节码文件装载到JVM的时候只装载一份
System.out.println(c1 == c2); // true
System.out.println(c2 == c3); // true
创建由Class类对象表示的类的新实例
Object o = c.newInstance(); c为Class对象
通过反射机制获取实例化对象的方法更加灵活,只需要改变配置文件,就可以实例化出不同的对象。
public static void instance() {
// 绑定配置文件,通过修改classinfo.properties文件中的value值可以实例化不同的对象
ResourceBundle bundle = ResourceBundle.getBundle("classinfo");
// classinfo.properties文件中的内容为:className=java.util.Date
// className = java.util.Date(任意全限定类名)
String className = bundle.getString("className");
try {
Class c = Class.forName(className);
// o为Date类的实例
Object o = c.newInstance();
} catch (Exception e) {
e.printStackTrace();
}
}
newInstance()底层调用的是该类的无参构造方法。如果没有无参构造方法会出现“实例化”异常。
使用反射机制获取目标类的成员变量
package com.xs.bean;
public class Student {
public int no;
private String name;
protected int age;
boolean sex;
private static final Double MATH_PI = 3.1415926;
}
package com.xs.reflection;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
public class ReflectionField {
public static void main(String[] args) {
try {
// 获取整个类
Class studentClass = Class.forName("com.xs.bean.Student");
// obj就是Student对象
Object obj = studentClass.newInstance();
// 给成员变量赋值
Field noField = studentClass.getDeclaredField("no");
System.out.println(noField); // public int com.xs.bean.Student.no
noField.set(obj,15); // 给obj对象的no属性赋值
System.out.println(noField.get(obj)); // 获取obj对象的no属性的值:15
Field nameField = studentClass.getDeclaredField("name");
// 打破封装 (反射机制的缺点,打破封装可能给不法分子留下机会)
// 这样设置完后,在外部也可以访问private。
nameField.setAccessible(true);
nameField.set(obj,"张三"); // 给obj对象的name属性赋值
System.out.println(nameField.get(obj)); // 获取obj对象的name属性的值:张三
// 获取类名
String name1 = studentClass.getName();
System.out.println("完整类名:" + name1); // 完整类名:com.xs.bean.Student
String name2 = studentClass.getSimpleName();
System.out.println("简单类名:" + name2); // 简单类名:Student
// 获取类中所有的public修饰的Field
Field[] fields = studentClass.getFields();
System.out.println(fields.length); // 1
System.out.println(fields[0]); // public int com.xs.bean.Student.no
String name3 = fields[0].getName();
System.out.println(name3); // no
// 获取所有的Field
Field[] fields1 = studentClass.getDeclaredFields();
System.out.println(fields1.length); // 5
for (Field field : fields1) {
// 获取属性的修饰符列表,返回的是一个数字,每个数字对应不同的修饰符
int i = field.getModifiers();
System.out.println(i); // 1 、2 、4 、0 、26
// 将数字转换成对应的修饰符
String modifierString = Modifier.toString(i);
System.out.println(modifierString); // public、 private、 protected、空 、private static final
// 获取属性的类型
Class<?> type = field.getType();
System.out.println(type); // int、class java.lang.String、 int、 boolean、 class java.lang.Double
System.out.println(type.getSimpleName()); // int、 string、 int、 boolean、 Double
// 获取属性的名字
System.out.println(field.getName()); // no、 name、 age、 sex、 MATH_PI
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
将代码中的字符串替换成配置文件,修改属性的值只需要修改配置文件,不需要修改源代码。
使用反射机制调用目标类的方法
目标类
package com.xs.service;
public class UserService {
public boolean login(String username, String password) {
return "admin".equals(username) && "123456".equals(password);
}
public void logout() {
System.out.println("系统已经安全退出");
}
}
使用反射机制调用UserService类中的方法
package com.xs.reflection;
import java.lang.reflect.Method;
public class ReflectionMethod2 {
public static void main(String[] args) {
try {
Class methodClass = Class.forName("com.xs.service.UserService");
// 获取对象
Object obj = methodClass.newInstance();
// 获取Method
Method loginMethod = methodClass.getDeclaredMethod("login", String.class, String.class);
// 调用方法
Object retValue = loginMethod.invoke(obj, "admin", "123456");
System.out.println(retValue); // true
} catch (Exception e) {
e.printStackTrace();
}
}
}
使用反射机制调用目标类构造方法
目标类
package com.xs.bean;
public class Account {
private int id;
private String name;
public Account() {
}
public Account(int id, String name) {
this.id = id;
this.name = name;
}
@Override
public String toString() {
return "Account{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
使用反射机制调用Account类的构造方法
package com.xs.reflection;
import java.lang.reflect.Constructor;
public class ReflectionConstructor {
public static void main(String[] args) {
try {
Class constructorClass = Class.forName("com.xs.bean.Account");
// 调用无参构造方法
Object obj1 = constructorClass.newInstance();
System.out.println(obj1); // Account{id=0, name='null'}
// 调用有参数的构造方法
// 获取有参数的构造方法
Constructor con = constructorClass.getDeclaredConstructor(int.class, String.class);
// 调用构造方法new对象
Object obj2 = con.newInstance(20, "李四");
System.out.println(obj2); // Account{id=20, name='李四'}
// 获取无参数的构造方法
Constructor con2 = constructorClass.getDeclaredConstructor();
// 调用无参数的构造方法
Object obj3 = con2.newInstance();
System.out.println(obj3); // Account{id=0, name='null'}
} catch (Exception e) {
e.printStackTrace();
}
}
}
通过反射机制获取目标类的父类及其实现的所有接口
package com.xs.reflection;
public class ReflectionTest {
public static void main(String[] args) {
try {
Class stringClass = Class.forName("java.lang.String");
// 获取String的父类
Class superClass = stringClass.getSuperclass();
System.out.println(superClass.getSimpleName());
// 获取String类实现的所有接口
Class[] interfaces = stringClass.getInterfaces();
for (Class in : interfaces) {
System.out.println(in.getName());
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
运行结果:
Object
java.io.Serializable
java.lang.Comparable
java.lang.CharSequence