JavaSE基础之反射,javase反射
分享于 点击 14839 次 点评:212
JavaSE基础之反射,javase反射
->理解 Class 类->理解 Java 的类加载机制
->学会使用 ClassLoader 进行类加载
->理解反射的机制
->掌握 Constructor、Method、Field 类的用法
->理解并掌握动态代理
反射概述
Reflection(反射)是Java被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API取得任何类的內部信息,并能直接操作任意对象的内部属性及方法。
Java反射机制主要提供了以下功能:
- 在运行时构造任意一个类的对象
- 在运行时获取任意一个类所具有的成员变量和方法
- 在运行时调用任意一个对象的方法(属性)
- 生成动态代理
Class 类
对象照镜子后可以得到的信息:某个类的数据成员名、方法和构造器、某个类到底实现了哪些接口。对于每个类而言,JRE 都为其保留一个不变的 Class 类型的对象。一个 Class 对象包含了特定某个类的有关信息。Class 对象只能由系统建立对象
一个类在 JVM 中只会有一个Class实例
每个类的实例都会记得自己是由哪个 Class 实例所生成
获取 Class 对象的方式
/**
* 关于 Class:
* 1. Class 是一个类
* 2. 对象照镜子后可以得到的信息:某个类的数据成员名、方法和构造器、 某个类到底实现了哪些接口。
* 3. 对于每个类而言,JRE 都为其保留一个不变的 Class 类型的对象。 一个 Class 对象包含了特定某个类的有关信息。
* 4. Class对象只能由系统建立对象
* 5. 一个类在 JVM 中只会有一个Class实例
*
* @throws ClassNotFoundException
*/
@Test
public void testClass() throws ClassNotFoundException {
Class clazz = null;
// *1. 得到 Class 对象
// 1.1 直接通过 类名.class 的方式得到
clazz = Person.class;
// 1.2 通过对象调用 getClass() 方法来获取
Object obj = new Person();
clazz = obj.getClass();
// 1.3 通过全类名的方式获取. 用的较多。
String className = "com.zto.reflection.entity.Person";
clazz = Class.forName(className);
// Field[] fields = clazz.getDeclaredFields();
System.out.println();
}
获取Class对象的一个实例
/**
* *Class 类的 newInstance() 方法.
*
* @throws ClassNotFoundException
* @throws IllegalAccessException
* @throws InstantiationException
*/
@Test
public void testNewInstance() throws ClassNotFoundException,
InstantiationException, IllegalAccessException {
String className = "com.zto.reflection.entity.Person";
Class clazz = Class.forName(className);
// 利用 Class 对象的 newInstance() 方法来创建类的一个对象.
// 实际调用的是类的那个 无参数的 构造器!
// 一般地, 一个类若声明了带参数的构造器, 也要声明一个无参数的构造器.
Object obj = clazz.newInstance();
System.out.println(obj);
}
ClassLoader类加载器
类装载器是用来把类(class)装载进 JVM 的。JVM 规范定义了两种类型的类装载器:启动类装载器(bootstrap)和用户自定义装载器(user-defined class loader)。
JVM在运行时会产生3个类加载器组成的初始化加载器层次结构 ,如下图所示://表示系统类装载器实例化自类sun.misc.Launcher$AppClassLoader
sun.misc.Launcher$AppClassLoader@19821f
//表示系统类装载器的parent实例化自类sun.misc.Launcher$ExtClassLoader
sun.misc.Launcher$ExtClassLoader@addbf1
//表示系统类装载器parent的parent为bootstrap,无法直接获取
null
@Test
public void testClassLoader() throws ClassNotFoundException, FileNotFoundException {
// 1. 获取一个系统的类加载器
ClassLoader classLoader = ClassLoader.getSystemClassLoader();
System.out.println(classLoader);
// 2. 获取系统类加载器的父类加载器.扩展类加载器
classLoader = classLoader.getParent();
System.out.println(classLoader);
// 3. 获取扩展类加载器的父类加载器.引导类加载器
classLoader = classLoader.getParent();
System.out.println(classLoader);
// 4. 测试当前类由哪个类加载器进行加载:
classLoader = Class.forName("com.zto.reflection.ReflectionTest").getClassLoader();
System.out.println(classLoader);
// 5. 测试 JDK 提供的 Object 类由哪个类加载器负责加载
classLoader = Class.forName("java.lang.Object").getClassLoader();
System.out.println(classLoader);
// 6*. 关于类加载器的一个主要方法.
// 通过系统类加载器获取classpath下的文件对应的输入流
// 调用 getResourceAsStream 获取类路径下的文件对应的输入流.
InputStream in = null;
in = this.getClass().getClassLoader().getResourceAsStream("test.properties");
System.err.println(in);
// new FileInputStream("test.properties");
}
Method
类的方法: Method
/**
* Class 是对一个类的描述
* 类的属性: Field
* 类的方法: Method
* 类的构造器: Constrctor
*
* Method: 对应类中的方法.
* 1. 获取 Method:
* 1.1 获取类的方法的数组: clazz.getDeclaredMethods();
* 1.2 获取类的指定的方法: getDeclaredMethod(String name,Class<?>... parameterTypes)
* name: 方法名
* parameterTypes: 方法的参数类型(使用Class来来描述)的列表
*
* Method method = clazz.getDeclaredMethod("setName", String.class);
*
* method = clazz.getDeclaredMethod("setName", String.class, Integer.class);
*
* 1.3 通过 method 对象执行方法:
* public Object invoke(Object obj, Object... args)
*
* obj: 执行哪个对象的方法?
* args: 执行方法时需要传入的参数.
*
* Object obj = clazz.newInstance();
* //method 对应的原方法为:
* public void setName(String name, Integer age)
* method.invoke(obj, "xyc", 24);
*
*/
@Test
public void testMethod() throws Exception{
Class clazz = Class.forName("com.zto.reflection.entity.Person");
//1. 得到 clazz 对应的类中有哪些方法, 不能获取 private 方法.
Method [] methods = clazz.getMethods();
for(Method method: methods){
System.out.println("^" + method.getName());
}
//2. 获取所有的方法, 包括 private 方法, 且只获取当前类声明的方法.
Method [] methods2 = clazz.getDeclaredMethods();
for(Method method: methods2){
System.out.println("~" + method.getName());
}
//3. 获取指定的方法.
Method method = clazz.getDeclaredMethod("setName", String.class);
System.out.println(method);
method = clazz.getDeclaredMethod("test");
System.out.println(method);
method = clazz.getDeclaredMethod("setName", String.class, Integer.class);
System.out.println(method);
//4. 执行方法!
Object obj = clazz.newInstance();
method.invoke(obj, "xyc", 24);
}
练习
/**
*
* @param className: 某个类的全类名
* @param methodName: 类的一个方法的方法名. 该方法也可能是私有方法.
* @param args: 调用该方法需要传入的参数
* @return: 调用方法后的返回值
*/
public Object invoke(String className, String methodName, Object ... args){
Object obj = null;
try {
obj = Class.forName(className).newInstance();
return invoke(obj, methodName, args);
}catch(Exception e) {
e.printStackTrace();
}
return null;
}
/**
*
* @param obj: 方法执行的那个对象.
* @param methodName: 类的一个方法的方法名. 该方法也可能是私有方法.
* @param args: 调用该方法需要传入的参数
* @return: 调用方法后的返回值
*/
public Object invoke(Object obj, String methodName, Object ... args){
//1. 获取 Method 对象
Class [] parameterTypes = new Class[args.length];
for(int i = 0; i < args.length; i++){
parameterTypes[i] = args[i].getClass();
System.out.println(parameterTypes[i]);
}
try {
Method method = obj.getClass().getMethod(methodName, parameterTypes);
//2. 执行 Method 方法
//3. 返回方法的返回值
return method.invoke(obj, args);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
@Test
public void testInvoke(){
Object obj = new Person();
invoke(obj, "setName", "xyc", 24);
invoke("com.atguigu.javase.lesson12.Person",
"setName", "xyc", 24);
Object result =
invoke("java.text.SimpleDateFormat", "format", new Date());
System.out.println(result);
}
获取当前类的父类
/**
* 获取当前类的父类:
* 直接调动 Class 对象的 getSuperClass() 方法.
* @throws Exception
*/
@Test
public void testGetSuperClass() throws Exception{
String className = "com.zto.reflection.entity.Student";
Class clazz = Class.forName(className);
Class superClazz = clazz.getSuperclass();
System.out.println(superClazz);
}
执行类的私有方法
/**
* 执行类的私有方法
* 若通过 Method 的 invoke() 方法调用方法, 而访问权限不足, 则可以先使该方法
* 变为可被访问的:
*
* method.setAccessible(true);
*
* @throws Exception
*/
@Test
public void testInvokePrivateMethod() throws Exception{
Object obj = new Student();
Class clazz = obj.getClass();
Method method = clazz.getDeclaredMethod("method1", Integer.class);
System.out.println(method);
//若需要通过反射执行私有方法
method.setAccessible(true);
method.invoke(obj, 10);
}
练习:执行给定类的私有方法,如果当前类中没有次方法,从父类中找到执行
/**
* @throws Exception
*
*/
@SuppressWarnings("unchecked")
@Test
public void testClassMethod() throws Exception{
//1. 全类名
String className = "com.zto.reflection.entity.Student";
//2. 方法名: 可能在 1 给的类中, 也可能在父类中. 可能是私有方法, 也可能是公有方法.
String methodName = "method3";
//3. 执行 2 对应的方法时需要传入的参数列表.
Object [] args = {"邢宇超", 24};
//根据以下条件, 执行 methodName 对应的方法, 并打印返回值.
Class clazz = Class.forName(className);
Class [] parameterTypes = getParamerTypes(args);
Method method = getMethod(clazz, methodName, parameterTypes);
Object result = invokeMethod(clazz.newInstance(), method, args);
System.out.println(result);
}
/**
* 由 Object 数组得到其对应的 Class 数组.
* @param args
* @return
*/
public Class [] getParamerTypes(Object ... args){
Class [] parameterTypes = new Class[args.length];
for(int i = 0; i < args.length; i++){
parameterTypes[i] = args[i].getClass();
}
return parameterTypes;
}
/**
* 执行 obj 对象的 method 方法, 参数值为 args
* @param obj
* @param method
* @param args
* @return
* @throws InstantiationException
* @throws IllegalAccessException
* @throws InvocationTargetException
*/
public Object invokeMethod(Object obj, Method method, Object[] args)
throws InstantiationException, IllegalAccessException,
InvocationTargetException {
method.setAccessible(true);
Object result = method.invoke(obj, args);
return result;
}
/**
* 获取 clazz 的 methodName 方法. 该方法可能是私有方法, 还可能在父类中(私有方法)
* @param clazz
* @param methodName
* @param parameterTypes
* @return
*/
public Method getMethod(Class clazz, String methodName,
Class ... parameterTypes){
for(Class clazz2 = clazz; clazz2 != Object.class; clazz2 = clazz2.getSuperclass()){
try {
Method method = clazz2.getDeclaredMethod(methodName, parameterTypes);
return method;
} catch (Exception e) {}
}
return null;
}
Field
/**
* Field: 封装了字段的信息.
* 1. 获取字段:
* 1.1 Field [] fields = clazz.getDeclaredFields();
* 1.2 Field field2 = clazz.getDeclaredField("age");
*
* 2. 获取指定对象的指定字段的值.
* public Object get(Object obj)
* obj 为字段所在的对象.
*
* Object val = field.get(person);
*
* 注意: 若该字段是私有的, 需先调用 setAccessible(true) 方法
*
* 3. 设置指定对象的指定字段的值
* public void set(Object obj, Object value)
* obj: 字段所在的对象
* value: 要设置的值.
*
* field.set(person, "xyc");
*
*
*/
@Test
public void testField() throws ClassNotFoundException, NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException{
String className = "com.zto.reflection.entity.Person";
Class clazz = Class.forName(className);
//1. 获取字段
//1.1 获取 Field 的数组
Field [] fields = clazz.getDeclaredFields();
for(Field field: fields){
System.out.println(field.getName());
}
//1.2 获取指定名字的 Field
Field field = clazz.getDeclaredField("name");
System.out.println(field.getName());
Person person = new Person("ABC", 12);
field.setAccessible(true);
//2. 获取指定对象的 Field 的值
Object val = field.get(person);
System.out.println(val);
//3. 设置指定对象的 Field 的值
field.set(person, "xyc");
System.out.println(person.getName());
//4. 若该字段是私有的, 需要调用 setAccessible(true) 方法
Field field2 = clazz.getDeclaredField("age");
field2.setAccessible(true);
System.out.println(field2.get(person));
}
练习:创建 className 对应类的对象, 并为其 fieldName 赋值为 val
/**
*
* @Title: testClassField
* @Description: 创建 className 对应类的对象, 并为其 fieldName 赋值为 val
* @param: @throws ClassNotFoundException
* @param: @throws InstantiationException
* @param: @throws IllegalAccessException
* @return: void
* @throws
*/
@Test
public void testClassField() throws ClassNotFoundException, InstantiationException, IllegalAccessException{
String className = "com.zto.reflection.entity.Student";
String fieldName = "age"; //可能为私有, 可能在其父类中.
Object val = 20;
//创建 className 对应类的对象, 并为其 fieldName 赋值为 val
Object obj = null;
Class clazz = Class.forName(className);
Field field = getField(clazz, fieldName);
obj = clazz.newInstance();
setFieldValue(obj, field, val);
Object fieldValue = getFieldValue(obj, field);
System.out.println(fieldValue);
}
public Object getFieldValue(Object obj, Field field) throws IllegalArgumentException, IllegalAccessException{
field.setAccessible(true);
return field.get(obj);
}
public void setFieldValue(Object obj, Field field, Object val)
throws IllegalAccessException {
field.setAccessible(true);
field.set(obj, val);
}
public Field getField(Class clazz, String fieldName) {
Field field = null;
for(Class clazz2 = clazz; clazz2 != Object.class;
clazz2 = clazz2.getSuperclass()){
try {
field = clazz2.getDeclaredField(fieldName);
} catch (Exception e) {}
}
return field;
}
构造器
/**
* Constructor: 代表构造器
* @throws ClassNotFoundException
* @throws SecurityException
* @throws NoSuchMethodException
* @throws InvocationTargetException
* @throws IllegalArgumentException
* @throws IllegalAccessException
* @throws InstantiationException
*/
@Test
public void testConstructor() throws SecurityException, ClassNotFoundException, NoSuchMethodException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException{
String className = "com.zto.reflection.entity.Person";
Class<Person> clazz = (Class<Person>) Class.forName(className);
//1. 获取 Constructor 对象
Constructor<Person> [] constructors =
(Constructor<Person>[]) Class.forName(className).getConstructors();
for(Constructor<Person> constructor: constructors){
System.out.println(constructor);
}
Constructor<Person> constructor = clazz.getConstructor(String.class, int.class);
System.out.println(constructor);
//2. 调用构造器的 newInstance() 方法创建对象
Object obj = constructor.newInstance("xyc", 24);
}
Annotation 和 反射:
@Retention(RetentionPolicy.RUNTIME)
@Target(value={ElementType.METHOD})
public @interface AgeValidator {
public int min();
public int max();
}
@AgeValidator(max = 80,min = 20)
public void setAge(int age) {
this.age = age;
}
/**
* Annotation 和 反射:
* 1. 获取 Annotation
*
* getAnnotation(Class<T> annotationClass)
* getDeclaredAnnotations()
*
*/
@Test
public void testAnnotation() throws Exception{
String className = "com.zto.reflection.entity.Person";
Class clazz = Class.forName(className);
Object obj = clazz.newInstance();
Method method = clazz.getDeclaredMethod("setAge", int.class);
int val = 6;
Annotation annotation = method.getAnnotation(AgeValidator.class);
if(annotation != null){
if(annotation instanceof AgeValidator){
AgeValidator ageValidator = (AgeValidator) annotation;
if(val < ageValidator.min() || val > ageValidator.max()){
throw new RuntimeException("年龄非法");
}
}
}
method.invoke(obj, 1000);
System.out.println(obj);
}
相关文章
- 暂无相关文章
用户点评