Java反射教程
什么是反射
反射是:指程序可以访问、检测和修改它本身状态或行为的一种能力 反射是一种能力,所以给的定义就是说明了它能干嘛。 我们平时用反射主要做: - 获取类型的相关信息 - 动态调用方法 - 动态构造对象 - 从程序集中获得类型。
通过反射机制实例化对象
基础实体
package com.haojishu.entity;
public class User {
public Integer age;
private String Username;
private String addr;
public User() {
System.out.println("构造参数");
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getUsername() {
return Username;
}
public void setUsername(String username) {
Username = username;
}
public String getAddr(String 河北石家庄) {
return addr;
}
public void setAddr(String addr) {
this.addr = addr;
}
@Override
public String toString() {
return "User{"
+ "age="
+ age
+ ", Username='"
+ Username
+ '\''
+ ", addr='"
+ addr
+ '\''
+ '}';
}
}
反射获取对象
// 获取类字节码文件
Class<?> aClass = Class.forName("com.haojishu.entity.User");
// 实例化类
Object newInstance = aClass.newInstance();
// 类转换实体
User user = (User) newInstance;
使用反射机制创建对象有什么作用呢?
- 反射机制让程序更加灵活。
- java代码写一遍,在不改变java源代码的基础上,可以做到不同对象的实例化。(符合OCP原则:对外开放,修改关闭。)
- 高级框架:底层实现原理:采用了反射机制。
- 举个栗子:(使用IO流,properties集合,通过属性文件来实例化对象)(重点)
代码优化
如何获取类路径下文件的绝对路径?
- 为什么聊这个?因为上述代码可能换一个位置,当前路径名就失效了,(即移植性差)所以我们的聊聊这个。
- 使用以下通用方式,这个文件必须保存在类路径下,(即src目录下)。
- String path = Thread.currentThread().getContextClassLoader().getResource(” “).getPath();(重点)使用这个方法可以获取文件的绝对路径。(适用于各种操作系统)
- 哈哈,看不懂?别急解释一下上述程序:
- Thread.currentThread()当前线程对象
- getContextClassLoader()使对象的方法,可以获取当前线程的类加载器对象。
- getResource()这是类加载器对象的方法,当前线程的类加载器默认从类的根路径下加载资源。
修改Pom
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
</includes>
</resource>
</resources>
编写配置文件pack.properties
User=com.haojishu.entity.User
最后优化代码:
Thread thread = Thread.currentThread();
ClassLoader contextClassLoader = thread.getContextClassLoader();
try {
InputStream resourceAsStream = contextClassLoader.getResourceAsStream("pack.properties");
Properties properties = new Properties();
properties.load(resourceAsStream);
resourceAsStream.close();
String userString = properties.getProperty("User");
// 获取类字节码文件
Class<?> aClass = Class.forName(userString);
// 实例化类
Object newInstance = aClass.newInstance();
// 类转换实体
User user = (User) newInstance;
System.out.println("优化后的代码实例化" + user);
} catch (Exception e) {
e.printStackTrace();
}
这样我们在修改包路径的时候只需要修改配置文件即可
反射Field(属性)获取字节码
- 反射属性之前,应该获取整个类。(
Class.forName(" ")
方法获取。) 获取整个类中的所有的Field。
getFields()
方法;用之前获取的整个类调用;其获取所有public修饰的属性,返回到Field[]数组中。getDeclaredFields()
方法;获取所有属性。不受修饰符现在,返回到Field[]数组中。getName()
方法:可以获取属性名字。对于类也有名字,也用这个方法,对于反射获得的类还有simpleName()
方法,来获取简单名字。getType()
方法:获取属性的类型,返回值为类,然后我们再通过这个得到的类用getName
方法获取属性类型的名字。getModifiers()
方法:获取属性的修饰符列表,返回的是int类型,每个修饰符的数字是,修饰符的代号。我们可以用toString
方法(Modifier.toString
),Modefier
为reflect的子类,返回一个字符串类型。举个栗子:
Thread thread = Thread.currentThread(); ClassLoader contextClassLoader = thread.getContextClassLoader(); try { InputStream resourceAsStream = contextClassLoader.getResourceAsStream("pack.properties"); Properties properties = new Properties(); properties.load(resourceAsStream); resourceAsStream.close(); String userString = properties.getProperty("User"); // 获取类字节码文件 Class<?> aClass = Class.forName(userString); String aClassName = aClass.getName(); newInstance = aClass.newInstance(); String name = newInstance.getClass().getName(); System.out.println("获取类名称: " + aClassName); System.out.println("获取类名称: " + name); Field[] declaredFields = aClass.getDeclaredFields(); System.out.println("获取反射类字段数量: " + declaredFields.length); for (Field decl : declaredFields) { System.out.println("获取字段名称: " + decl.getName()); System.out.println("获取字段类型: " + decl.getType().getName()); int modifiers = decl.getModifiers(); System.out.println("获取字段属性标识符: " + Modifier.toString(modifiers)); } } catch (Exception e) { e.printStackTrace(); }
设置属性字段
- 通过反射机制,反编译一个类的属性Field
怎么通过反射机制访问对象属性?(赋值,获取属性的值)
- 首先获取整个Class。
- 然后通过Class实例化对象。
- 获取对象的属性;依据名字。
- 属性.set(对象,222)方法:以获取的属性调用,给对象的属性赋值。
- 反射机制代码复杂,但灵活。
读取属性的值:属性.get(对象)。
Thread thread = Thread.currentThread(); ClassLoader contextClassLoader = thread.getContextClassLoader(); try { InputStream resourceAsStream = contextClassLoader.getResourceAsStream("pack.properties"); Properties properties = new Properties(); properties.load(resourceAsStream); resourceAsStream.close(); String userString = properties.getProperty("User"); // 获取类字节码文件 Class<?> aClass = Class.forName(userString); String aClassName = aClass.getName(); newInstance = aClass.newInstance(); String name = newInstance.getClass().getName(); System.out.println("获取类名称: " + aClassName); System.out.println("获取类名称: " + name); // 获取属性对象 Field age = aClass.getDeclaredField("age"); // 设置属性 age.set(newInstance, 12); System.out.println("打印属性设置后的对象值: " + newInstance); // 读取属性 System.out.println("读取属性值:" + age.get(newInstance)); } catch (Exception e) { e.printStackTrace(); }
反射Mothod
(方法)
- 首先获取Class。
- 获取所有的Method方法。
- Method[] methods=类.
getDeclaredMethod()
;方法:获取所有的Method. - 获取方法的返回值类型:方法.
getReturnType().getSimpleName()
; - 获取修饰符列表:
Modifer.toString(方法.getModifiers()
; - 获取方法参数的类型:方法
.getParameterTypes()
;返回Class[]数组。再调用getSimpleName()
即可。 - 通过反射机制怎么调用对象的方法。(重点)
- 1.获取
Calss
,并用CLass
实例化对象。 - 2.获取Method:使用
Method logMethod = 类.getDeclaredMethod("方法名“,类型名.class,类型名.class);
方法。 3.调用方法:(对象,方法名,参数,返回值)
Object value = 方法.invoke(“对象”,“参数值”,“参数值”);invoke方法返回一个Object类型。
Thread thread = Thread.currentThread(); ClassLoader contextClassLoader = thread.getContextClassLoader(); try { InputStream resourceAsStream = contextClassLoader.getResourceAsStream("pack.properties"); Properties properties = new Properties(); properties.load(resourceAsStream); resourceAsStream.close(); String userString = properties.getProperty("User"); // 获取类字节码文件 Class<?> aClass = Class.forName(userString); String aClassName = aClass.getName(); newInstance = aClass.newInstance(); Method[] methods = aClass.getMethods(); System.out.println(methods); for (Method method : methods) { String name = method.getName(); System.out.println("方法名称: " + name); int modifiers = method.getModifiers(); String toString = Modifier.toString(modifiers); System.out.println("方法修饰符: " + toString); Class<?>[] parameterTypes = method.getParameterTypes(); for (Class parme : parameterTypes) { System.out.println("支持参数: " + parme.getSimpleName()); } } // 调用方法名 Method setAge = aClass.getDeclaredMethod("setAge", Integer.class); setAge.invoke(newInstance, 12); System.out.println("反射应用结果: " + newInstance); } catch (Exception e) { e.printStackTrace(); }
反射Constructor(构造方法)
- 反射机制怎么调用构造方法?
使用反射机制创建对象?
- 1.调用无参构造方法。(
newInstance
方法) - 2.调用有参构造方法(也可以用这种方法调用无参构造方法)。
第一步:先获取这个有参构造方法;Constructor con = 类.
getDeclaredConstructor(int.class,String.classs)
第二步:调用构造方法new对象。Object aa =con.newInstance(参数列表)
。Thread thread = Thread.currentThread(); ClassLoader contextClassLoader = thread.getContextClassLoader(); try { InputStream resourceAsStream = contextClassLoader.getResourceAsStream("pack.properties"); Properties properties = new Properties(); properties.load(resourceAsStream); resourceAsStream.close(); String userString = properties.getProperty("User"); // 获取类字节码文件 Class<?> aClass = Class.forName(userString); String aClassName = aClass.getName(); newInstance = aClass.newInstance(); Constructor<?> declaredConstructor = aClass.getDeclaredConstructor(Integer.class, String.class, String.class); declaredConstructor.newInstance(12, "张三", "李四"); } catch (Exception e) { e.printStackTrace(); }
- 1.调用无参构造方法。(
通过反射获取父类和父接口
- 1.获取类的Class
- 2.获取Class父类:Class superClass = 子类.getSuperclass();
获取子类实现的所有接口:Class[] interfaces = 子类.getInterfaces();方法。
Thread thread = Thread.currentThread(); ClassLoader contextClassLoader = thread.getContextClassLoader(); try { InputStream resourceAsStream = contextClassLoader.getResourceAsStream("pack.properties"); Properties properties = new Properties(); properties.load(resourceAsStream); resourceAsStream.close(); String userString = properties.getProperty("User"); // 获取类字节码文件 Class<?> aClass = Class.forName(userString); // 实例化类 Object newInstance = aClass.newInstance(); // 获取父类 Class<?> superclass = aClass.getSuperclass(); System.out.println("获取父类名称: " + superclass.getSimpleName()); Class<?>[] interfaces = aClass.getInterfaces(); for (Class inter : interfaces) { // 不包含报名的接口简称 System.out.println(inter.getSimpleName()); } } catch (Exception e) { e.printStackTrace(); }