反射
java中提供的动态执行API,可以动态检查对象的类型,对象类型的内部结构,还能动态创建对象,动态执行方法等.
public static void main(String[] args) {
test(123);//class java.lang.Integer
test("wome");//class java.lang.String
test(new File("./"));//class java.io.File
test(new ArrayList<Double>());//class java.util.ArrayList
Collection<String> c = new ArrayList<>();
Iterator<String> it = c.iterator();
//it的具体对象的类型是什么?
test(it);//class java.util.ArrayList$Itr,ArrayList的内部类Itr
}
public static void test(Object obj){
/*
* obj的具体类型是什么?
* 如何在方法中检查对象的具体类型?
* 利用反射API可以动态检查对象的类型
* Object类上定义了getClass,用于检查软件执行期间对象的具体类型
*/
Class cls = obj.getClass();
System.out.println(cls);
/*
* 进一步动态检查类型的内部结构
* 1:检查类型声明的全部成员变量
* Declared 声明 Fields 字段,成员变量
* getDeclaredFields()的功能是检查cls代表的类型中全部的字段(成员变量)
*/
Field[] fields = cls.getDeclaredFields();
for (Field field : fields) {
System.out.println("****" + field);
}
/*
* 2:检查类中声明的方法
* getDeclaredMethods():动态获取当前类中声明的方法信息,不包含继承的方法
* getMethods():获取当前类和从父类中继承的方法
*/
Method[] methods = cls.getDeclaredMethods();
for (Method m : methods) {
System.out.println("---" + m);
}
/*
* 3:检查类中声明的构造方法
* getDeclaredConstructors():动态获取当前类中声明的构造方法,不包含继承的
* getConstructors():动态获取当前类和父类中继承的方法
*/
Constructor[] constructors = cls.getDeclaredConstructors();
for (Constructor constructor : constructors) {
System.out.println("~~~~"+constructor);
}
}
动态检查对象的类型
Class cls = obj.getClass();
对象类型的内部结构
Field[] fields = cls.getDeclaredFields();
Method[] methods = cls.getDeclaredMethods();
Constructor[] constructors = cls.getDeclaredConstructors();
反射API动态加载类,动态创建对象
Class cls = Class.forName(类名);
/**
* 动态加载类,创建对象
* 如下方法可以动态加载任何类,创建其对象
* 注意:类型必须有无参构造器
* @author Mr.Huang
* @date 2018年8月8日
*/
Scanner scan = new Scanner(System.in);
System.out.println("请输入一个类全名package.className:");
String className = scan.nextLine();
// Class.forName(类名)动态加载类到方法区,类名错误抛异常
try {
Class cls = Class.forName(className);// 类找不到报异常
// 显示加载的结果
System.out.println(cls);
// 动态创建对象
Object obj = cls.newInstance();// 默认使用的是空参构造类没有空参构造就会抛异常
// 显示创建好的对象
System.out.println(obj);
} catch (ClassNotFoundException e) {//类找不到
e.printStackTrace();
} catch (InstantiationException e) {//没有空参构造
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
反射动态调用对象的方法
Foo f = new Foo();
f.test();
动态调用:运行期间动态给出方法名,如果输入的是test则执行test()方法,输入demo就执行demo()方法
动态执行方法:invoke :调用
* 1:先在类中找到方法 method 对象 * 2:在method上调用invoke()就是执行方法,需要传递包含方法的对象作为参数.
method.invoke(包含方法的对象,传递参数...)
try {
System.out.println("请输入方法名:");
String methodName = scan.nextLine();
//根据方法名动态查找方法getDeclaredMethod,查找一个方法
Method method = cls.getDeclaredMethod(methodName);
//动态执行方法
method.invoke(obj);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
反射的用途
反射API用途之一:访问不可见的属性,方法
//在动态执行方法之前,打开访问权限,可以访问私有方法
method.setAccessible(true);
反射的核心用途: 1:将一段程序于另外一段程序进行解耦
案例:单元测试工具原型
执行一个类中的全部的以test为开头的方法
1:类有无参构造
2:方法都是无参数方法
解决方案:
1:动态输入类名
2:动态加载类
3:动态创建类型的实例
4:动态查找类型全部方法
5:遍历检查每个方法的名,如果是以test开头,则利用发射API执行方法
try {
//1:动态输入类名
System.out.println("请输入全类名:");
Scanner scan = new Scanner(System.in);
String className = scan.nextLine();
//2:动态加载类
Class cls = Class.forName(className);
//3:动态创建类型的实例
Object obj = cls.newInstance();
//4:动态查找类型的全部方法
Method[] methods = obj.getClass().getDeclaredMethods();
for (Method method : methods) {
//System.out.println("===" + method.getName());
//5:遍历检查每个方法的名,如果是以test开头,则利用发射API执行方法
if(method.getName().startsWith("test")){
//Method m = cls.getDeclaredMethod(method.getName());
method.setAccessible(true);
method.invoke(obj);
}
}
} catch (Exception e) {
e.printStackTrace();
}
利用反射调用有参数的方法
1:查找有参数方法 Method method = cls.getDeclaredMethod(方法名,参数类型1,参数类型2...)
2:动态调用执行 method.invoke(对象,参数1,参数2...)
Scanner scan = new Scanner(System.in);
System.out.print("请输入全类名;");
String className = scan.nextLine();
System.out.print("请输入方法名:");
String methodName = scan.nextLine();
try {
//1:动态加载类
Class cls = Class.forName(className);
//2:动态创建对象
Object obj = cls.newInstance();
//3:查找有参数的方法,第一个参数是方法名,随后参数是类型.class
//找不到就会抛异常
Method method = cls.getDeclaredMethod(methodName,int.class,String.class);
//4:动态获取参数
System.out.print("请输入第一个参数p1:");
String p1 = scan.nextLine();
System.out.print("请输入第一个参数p2:");
String p2 = scan.nextLine();
int n = Integer.parseInt(p1);
//5:动态执行方法
Object val = method.invoke(obj, n,p2);
System.out.println(val);
} catch (Exception e) {
e.printStackTrace();
}
紧耦合
组件和组件之间是紧密关联的关系.
松耦合
组件和组件之间不是紧密关联的关系,可以将组件进行重新组合,可以进行组件替换
解耦
将组件关系从紧耦合改编为松耦合称为解耦
利用反射实现松耦合
反射API可以动态解析注解=====解析的只有运行期的注解
自定义注解
注解可以分为SOURCE|CLASS|RUNTIME
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* 自定义注解
* Retention:用于声明注解的存在范围SOURCE|CLASS|RUNTIME
* 代表着注解的存在范围;
* SOURCE:存在于源代码中
* CLASS:存在于源代码和字节码中
* RUNTIME:存在于源代码.字节码以及运行期
* 反射API能获取到的注解形式只能是运行期注解
*
* @author Mr.Huang
* @date 2018年8月8日
*/
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
}
动态解析注解
try {
//动态加载类
Class cls = Class.forName(className);
//动态查找全部方法
Method[] methods = cls.getDeclaredMethods();
/*Annotation[] ann = cls.getAnnotations();
for (Annotation annotation : ann) {
System.out.println("类名" + annotation);
}*/
/*
* 方法上的注解
*/
for (Method method : methods) {
//method 代表cls 类上的每一个方法
//Annotation 注解,getAnnotations(),用于获取标注在方法上的全部注解
System.out.println("方法名" + method.getName());
Annotation[] annotations = method.getAnnotations();
for (Annotation annotation : annotations) {
System.out.println(" "+annotation);
}
}
} catch (Exception e) {
e.printStackTrace();
}
自定义Junit测试工具
import java.lang.reflect.Method;
/**
* 注解的用途演示
*
* 实现一个工具,自动执行类中标注了@MyAnnotation的方法
*
* @author Mr.Huang
* @date 2018年8月8日
*/
public class Junit {
public static void main(String[] args) {
try {
String className = "reflect.DemoAll";
Class cls = Class.forName(className);
Method[] methods = cls.getDeclaredMethods();
for (Method method : methods) {
//检查每个方法上是否包含@MyAnnotation注解
/*
* method.getAnnotation检查方法上是否包含 了MyAnnotation的注解,如果包含则返回注解类型,不包含则返回null
*/
MyAnnotation ann = method.getAnnotation(MyAnnotation.class);
if(ann != null){
//找到注解
method.invoke(cls.newInstance());
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}