文章目录
一、反射机制的作用
反射机制是指动态获取对象信息和调用对象方法的功能。Java 反射说的是在运行状态中,对于任何一个类,我们都能够知道这个类有哪些方法和属性。对于任何一个对象,我们都能够对它的方法和属性进行调用。
反射可以通过操作字节码文件(.class)实例化对象、调用方法、获取和设置变量值,还可以在运行时检查类,接口,方法和变量。比如当我们不知道一个类中是否拥有某个方法时,我们就可以使用反射来检查是否拥有这个方法。
二、获取Class的三种方式
Class 类表示正在运行的 Java 程序中的类和接口。
- 调用
java.lang.Class<T>
中的static 类<?> forName(String className)
方法,该方法导致类加载,执行类中的静态代码块,其中作为参数的字符串需要的是一个完整的类名,不能省略类的包名; - 调用任何一个对象的
类<?> getClass()
方法; - 调用类型的
class
属性。
示例如下:
public static void main(String[] args) {
//方法1
Class string1 = null;
try {
string1 = Class.forName("java.lang.String");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
//方法2
String s = "xzy";
Class string2 = s.getClass();
//方法3
Class string3 = String.class;
//其中,string1==string2==string3,说明它们均指向String.class字节码文件
}
注释
字节码文件装载到JVM
中的时候只有一份。
三、实例化对象
一般的实例化类对象方法都是通过new
,反射机制可以获取字节码文件之后通过调用newInstance()
方法,该方法实际调用的是对应类的无参构造方法,必须保证无参构造存在才可以。后者看似复杂,实则非常的灵活!
src/class.properties文件信息如下:
classname=java.util.Date
public static void main(String[] args) throws Exception {
//新建文件输入流对象
FileReader reader=new FileReader("src/class.properties");
//新建属性类对象
Properties ppt=new Properties();
//加载配置信息到Map集合中
ppt.load(reader);
reader.close();
//通过key获取value
String classname=ppt.getProperty("classname");
//利用Class的静态方法
Class c=Class.forName(classname);
Object obj=c.newInstance();
}
本例创建了一个Date对象,在不更改代码的前提下修改配置文件就可以创建其它的对象。(符合OCP开闭原则:对扩展开发,对修改关闭)
注释
- 更加通用的获取类路径(
src
路径)下文件的绝对路径的方法如下,好处在于具备了可移植性。
//getContextClassLoader()获取到当前线程的类加载器对象
//getResource()默认从类的根路径下加载资源
String filepath=Thread.currentThread().getContextClassLoader().getResource("class.properties").getPath();
//如果乱码,往往是路径上出现了空格、中文等字符,解决方法如下:
filepath = java.net.URLDecoder.decode(filepath, "utf-8");
- 还可以通过引流的方法获取输入流
InputStream reader=Thread.currentThread().getContextClassLoader().getResourceAsStream("class.properties");
- 通过资源绑定器获取类路径下的属性配置文件:
ResourceBundle pptresource=ResourceBundle.getBundle("class");
String classname=pptresource.getString("classname");
三、获取和设置Field
1、获取Field
类 | 方法 | 作用 |
---|---|---|
Class | Field[] getDeclaredFields() | 获取指定类的所有实例域 |
Class | String getSimpleName() | 获取源代码中给出的基础类的简单名称 |
Field | int getModifiers() | 获取由该 Field对象表示的字段的Java语言修饰符对应的访问修饰符标志 |
Field | String getName() | 获取由此 Field对象表示的字段的名称 |
Field | Class<?> getType() | 获取实例域中变量类型所对应的Class |
Modifier | static String toString(int mod) | 获取描述指定修饰符中的访问修饰符标志的字符串 |
示例:通过反射机制反编译一个类的属性
public static void main(String[] args) throws ClassNotFoundException {
StringBuilder message=new StringBuilder();
Class stringClass=Class.forName("java.lang.String");
message.append(Modifier.toString(stringClass.getModifiers())+" class "+stringClass.getSimpleName()+" {\n");
Field[] stringField=stringClass.getDeclaredFields();
for(Field field:stringField){
message.append("\t"+Modifier.toString(field.getModifiers())+" "+field.getType().getSimpleName()+" "+field.getName()+";\n");
}
message.append("}");
System.out.println(message);
}
输出结果如下:
public final class String {
private final char[] value;
private int hash;
private static final long serialVersionUID;
private static final ObjectStreamField[] serialPersistentFields;
public static final Comparator CASE_INSENSITIVE_ORDER;
}
2、设置Field
类 | 方法 | 作用 |
---|---|---|
Class | Field getDeclaredField(String name) | 根据属性的名字获取到对应的Field对象 |
Field | Object get(Object obj) | 获取指定对象对应属性的指 |
Field | void set(Object obj, Object value) | 设置指定对象对应属性的值 |
Field | void setAccessible(boolean flag) | 打破封装,使私有域可访问 |
示例
//package From0To100.Reflect.Field下的学生类
public class Student {
public static final String schoolname="xx小学";
public boolean gender;
protected String name;
private int age;
}
//package From0To100.Reflect.Field下的测试类
public class TestField {
public static void main(String[] args) throws Exception {
Class studentClass=Class.forName("From0To100.Reflect.Field.Student");
Object student=studentClass.newInstance();
Field genderField=studentClass.getDeclaredField("age");
//反射机制的缺点:打破封装
genderField.setAccessible(true);
//设置属性,需要用到对象、属性、值
genderField.set(student,18);
System.out.println(genderField.get(student));
//最终输出18
}
}
四、获取和调用Method
1、获取Method
类 | 方法 | 作用 |
---|---|---|
Class | Method[] getDeclaredMethods() | 获取对应类中的方法 |
Method | int getModifiers() | 获取对应方法修饰符的访问修饰符标志 |
Method | Class<?> getReturnType() | 获取方法返回类型对应的Class对象 |
Method | String getName() | 获取方法名称 |
Method | Class<?>[] getParameterTypes() | 获取方法的参数列表 |
示例:通过反射机制反编译一个类的方法
public static void main(String[] args) throws Exception {
StringBuilder message=new StringBuilder();
Class stringClass = Class.forName("java.lang.String");
message.append(Modifier.toString(stringClass.getModifiers())+" class "+stringClass.getSimpleName()+" {\n");
Method[] stringMethods=stringClass.getDeclaredMethods();
for(Method method:stringMethods){
//获取方法体
message.append("\t"+Modifier.toString(method.getModifiers())+" "+method.getReturnType().getSimpleName()+" "+method.getName()+"(");
//获取参数
Class[] parametersType=method.getParameterTypes();
for(Class parameter:parametersType){
message.append(parameter.getSimpleName()+",");
}
//考虑无参的方法
if(parametersType.length!=0)
message.deleteCharAt(message.length()-1);
message.append("){}\n");
}
message.append("}");
System.out.println(message);
}
2、调用Method
类 | 方法 | 作用 |
---|---|---|
Class | Method getDeclaredMethod(String name, Class<?>... parameterTypes) | 根据方法名和参数获取对应的方法 |
Method | Object invoke(Object obj, Object... args) | 根据参数和对象调用指定的方法 |
注释
Class<?>... parameterTypes和Object... args
都是可变长度参数,可以当成数组来对待,每个方法的参数列表中最多有一个可变长度参数,且必须要放在最后的位置上!
示例
//package From0To100.Reflect.Method下的用户类
public class User {
/**
* @param name 用户名
* @param password 密码
* @return 登陆成功返回true,否则返回false
*/
public boolean login(String name,String password) {
if ("xzy".equals(name) && "001".equals(password)) {
return true;
} else
return false;
}
}
//package From0To100.Reflect.Method下的测试类
public class TestMethod {
public static void main(String[] args) throws Exception {
Class userClass=Class.forName("From0To100.Reflect.Method.User");
Object user=userClass.newInstance();
Method loginMethod=userClass.getDeclaredMethod("login",String.class,String.class);
//调用方法,需要用到对象、方法、参数、返回值
Object result=loginMethod.invoke(user,"xzy","001");
System.out.println(result);
//结果为true
}
五、获取和调用构造方法
1、获取Constructor
类 | 方法 | 作用 |
---|---|---|
Class | Constructor<?>[] getDeclaredConstructors() | 获取对应类的构造器 |
Constructor | int getModifiers() | 获取对应构造器修饰符的访问修饰符标志 |
Constructor | Class<?>[] getParameterTypes() | 获取构造方法的参数列表 |
示例:通过反射机制反编译一个类的构造方法
public static void main(String[] args) throws Exception {
StringBuilder message=new StringBuilder();
Class stringClass = Class.forName("java.lang.String");
Constructor[] stringConstructors=stringClass.getDeclaredConstructors();
message.append(Modifier.toString(stringClass.getModifiers())+" class "+stringClass.getSimpleName()+" {\n");
for(Constructor constructor:stringConstructors){
message.append("\t"+Modifier.toString(constructor.getModifiers())+" "+stringClass.getSimpleName()+"(");
//获取参数
Class[] parametersType=constructor.getParameterTypes();
for(Class parameterType:parametersType){
message.append(parameterType.getSimpleName()+",");
}
//删除没用的,
if(parametersType.length!=0)
message.deleteCharAt(message.length()-1);
message.append("){}\n");
}
message.append("}");
System.out.println(message);
}
2、调用构造方法创建类对象
类 | 方法 | 作用 |
---|---|---|
Class | Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) | 根据参数对应的Class获取相应的构造器 |
Constructor | Object newInstance(Object... initargs) | 根据构造器传入参数创建类对象 |
示例:反射机制创建String对象
public static void main(String[] args) throws Exception {
Class stringClass=Class.forName("java.lang.String");
//无参构造方法
//获取到无参的构造器
Constructor constructor1=stringClass.getDeclaredConstructor();
Object string1=constructor1.newInstance();
System.out.println(string1);
//有参构造方法
char[] chars={'x','z','y'};
//获取到有参的构造器
Constructor constructor2=stringClass.getDeclaredConstructor(char[].class);
Object string2=constructor2.newInstance(chars);
System.out.println(string2);
}
六、获取父类和接口
类 | 方法 | 作用 |
---|---|---|
Class | Class<? super T> getSuperclass() | 获取父类 |
Class | Class<?>[] getInterfaces() | 获取实现的接口 |
示例:获取String的父类和接口
public static void main(String[] args) throws Exception {
Class stringClass=Class.forName("java.lang.String");
//获取父类
Class superClass=stringClass.getSuperclass();
System.out.println("父类:"+superClass.getName());
//获取接口
Class[] interfaces=stringClass.getInterfaces();
for(Class itf:interfaces)
System.out.println("接口:"+itf.getName());
}
输出结果如下:
父类:java.lang.Object
接口:java.io.Serializable
接口:java.lang.Comparable
接口:java.lang.CharSequence
欢迎评论区留言交流👍