1、反射案例:
需求
写一个"框架",不能改变该类的任何代码的前提下,可以帮我们创建任意类的对象,并且执行其中任意方法
实现:
- 配置文件
- 反射
步骤:
- 创建对象
- 将需要创建的对象的全类名和需要执行的方法定义在配置文件中
- 在程序中加载读取配置文件
- 使用反射技术来加载类文件进内存
- 执行方法
第一步:Person类(创建对象)
package cn.demo1015JUnit_Reflect_Anno.demo03ReflectTest;
public class Person{
public void eat(){
System.out.println("Person.eat");
}
//私有方法需要暴力反射
private void sleep(){
System.out.println("Person.sleep");
}
}
第二步:配置文件 pro.properties(将需要创建的对象的全类名和需要执行的方法定义在配置文件中)
className=cn.demo1015JUnit_Reflect_Anno.demo03ReflectTest.Person
methodName=eat
第三四五步:假设框架类ReflectTestQiang
package cn.demo1015JUnit_Reflect_Anno.demo03ReflectTest;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.Properties;
/**
* 假设框架类
* 3. 在程序中加载读取配置文件
* 4. 使用反射技术来加载类文件进内存
* 5. 执行方法
*/
public class ReflectTestQiang {
public static void main(String[] args) throws Exception {
//可以创建任意类的对象,可移植性任意方法
/**
* 前提:不能改变该类的任意代码,可以创建任意类的对象,可以执行任意方法。
* ReflectTest.clas获取该类字节码对象
* ReflectTest.class.getClassLoader()获取类加载器
* classLoader.getResourceAsStream():获取资源对应的字节流
*/
//1、加载配置文件
//1.1创建Properties对象
Properties pro = new Properties();
//1.2加载配置文件,转换为一个集合
//1.2.1获取class目录下的配置文件
ClassLoader classLoader = ReflectTestQiang.class.getClassLoader();
InputStream is = classLoader.getResourceAsStream("cn/demo1015JUnit_Reflect_Anno/demo03ReflectTest/pro.properties");
pro.load(is);
//2.获取配置文件中定义的数据
String className = pro.getProperty("className");
String methodName = pro.getProperty("methodName");
//3.获取该类进内存
Class cls = Class.forName(className);
//4、创建对象
Object o = cls.newInstance();
//5.获取方法对象
//Method method = cls.getMethod(methodName);
//暴力反射:可以执行私有方法
Method method = cls.getDeclaredMethod(methodName);
method.setAccessible(true);
//6.执行方法
method.invoke(o);
}
}
执行结果
2、反射注解案例
需求
写一个"框架",自定义注解Pro。根据注解属性设置需要反射的className和methodName。在程序中获得注解的className,methodName。执行该method。
实现
- 自定义注解类
- 测试类
- 包含方法的对象
步骤:
- 创建对象
- 将需要创建的对象的全类名和需要执行的方法定义在配置文件中
- 在程序中加载读取配置文件
- 使用反射技术来加载类文件进内存
- 执行方法
第一步:创建Demo1对象
package cn.demo1015JUnit_Reflect_Anno.demo04Anno.demo05Parse;
public class Demo1 {
public void show(){
System.out.println("Demo1.show");
}
}
第二步:自定义注解类Pro
package cn.demo1015JUnit_Reflect_Anno.demo04Anno.demo05Parse;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 描述需要执行的类名和方法名
*/
@Target(value = {ElementType.TYPE})//描述注解作用在类上
@Retention(RetentionPolicy.RUNTIME)//保留在RunTime时期
public @interface Pro {
String className();
String methodName();
}
第三步:测试类ReflectTest
package cn.demo1015JUnit_Reflect_Anno.demo04Anno.demo05Parse;
import java.lang.reflect.Method;
/**
* 假设框架类
*/
@Pro(className = "cn.demo1015JUnit_Reflect_Anno.demo04Anno.demo05Parse.Demo2",methodName = "show")
public class ReflectTest {
public static void main(String[] args) throws Exception {
//可以创建任意类的对象,可移植性任意方法
/**
* 前提:不能改变该类的任意代码,可以创建任意类的对象,可以执行任意方法。
*/
//1.解析注解
//1.1获取该类的字节码文件对象
Class<ReflectTest> reflectTestClass = ReflectTest.class;
//2.获取上边的注解类对象(其实返回内存中生成了一个该注解接口的子类实现对象)
Pro anno = reflectTestClass.getAnnotation(Pro.class);
//3.调用注解对象中定义的抽象方法,获取属性值
String className = anno.className();
String methodName = anno.methodName();
System.out.println(className);
System.out.println(methodName);
//3.获取该类进内存
Class cls = Class.forName(className);
//4、创建对象
Object o = cls.newInstance();
//5.获取方法对象
Method method = cls.getMethod(methodName);
//6.执行方法
method.invoke(o);
}
}
3、注解案例:简单的测试框架
需求:
当主方法执行后,会自动自行被检测的所有方法(加了Check注解的方法)判断方法是否有异常,记录到文件中。
第一步:定义Check注解
package cn.demo1015JUnit_Reflect_Anno.demo04Anno.demo06Test;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Check {
}
第二步:Calculator 小明定义的计算器类
package cn.demo1015JUnit_Reflect_Anno.demo04Anno.demo06Test;
/**
* 小明定义的计算器类
*/
public class Calculator {
//加法
@Check
public void add(){
String str = null;
str.toString();//空指针异常
System.out.println("1 + 0 =" + (1 + 0));
}
//减法
@Check
public void sub(){
System.out.println("1 - 0 =" + (1 - 0));
}
//乘法
@Check
public void mul(){
System.out.println("1 * 0 =" + (1 * 0));
}
//除法
@Check
public void div(){
System.out.println("1 / 0 =" + (1 / 0));
}
public void show(){
System.out.println("永无bug...");
}
}
第三步:编写测试类
1.创建计算器对象
2.获取计算器类的字节码文件的对象
3.获取所有方法
4.判断方法上是否有Check
5.有,执行
6.捕获异常
package cn.demo1015JUnit_Reflect_Anno.demo04Anno.demo06Test;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Method;
/**
* 简单的测试框架
* 当主方法执行后,会自动自行被检测的所有方法(加了Check注解的方法)
* 判断方法是否有异常,记录到文件中
*/
public class TestCheck {
public static void main(String[] args) throws IOException {
//1.创建计算器对象
Calculator calculator = new Calculator();
//2.获取计算器类的字节码文件的对象
Class aClass = calculator.getClass();
//3.获取所有方法
Method[] methods = aClass.getMethods();
int number= 0 ;//出现异常的次数
BufferedWriter bw = new BufferedWriter(new FileWriter("D:\\Java_IDEA\\JAVAData\\JavaBase\\src\\cn\\demo1015JUnit_Reflect_Anno\\demo04Anno\\demo06Test\\bug.txt"));
for (Method method : methods) {
//4.判断方法上是否有Check
if (method.isAnnotationPresent(Check.class)){
//5.有,执行
try {
method.invoke(calculator);
} catch (Exception e) {
//6.捕获异常
//记录到文件中
number++;
bw.write(method.getName()+"方法出异常了");
bw.newLine();
bw.write("异常名称"+e.getCause().getClass().getSimpleName());
bw.newLine();
bw.write("异常的原因"+e.getCause().getMessage());
bw.newLine();
bw.write("-----------------------");
bw.newLine();
}
}
}
bw.write("本次测试一共出现"+number+"次异常!!");
bw.flush();
bw.close();
}
}
运行结果:
bug.txt
小结:
- 以后大多数时候,我们会使用注解,而不是自定义注解
- 注解给谁用:编译器、给解析程序用
- 注解不是程序的一部分,可以理解为注解就是一个标签