关于反射的知识点总结
反射
相关github链接:反射
1.是JAVA API,是java提供的现成的类
2.是java提供的动态执行机制,可以动态加载类,动态创建对象,动态访问属性,动态调用方法.
静态与动态
静态:事先约定的规则,执行期间按照固定的规则执行.
java中的静态执行:编译时已经就确定执行规则(执行次序),在运行期间按照编译结果顺序执行.
动态:事先没有约定,在执行期间动态确定执行规则.
java中的动态执行:运行期间才能确定加载哪些类,创建哪些对象,执行哪些方法...
反射的作用
import java.lang.reflect.Method; import java.util.Scanner; public class Main { public static void main(String[] args) throws Exception { Scanner input = new Scanner(System.in); System.out.println("input Class:"); String className = input.nextLine(); //动态加载类 Class clazz = Class.forName(className); System.out.println(clazz); //动态创建对象 Object obj = clazz.newInstance(); System.out.println(obj); //动态查询类的方法信息 //从clazz代表的类信息中获取全部的方法信息 //每一个Method代表一个方法信息 //方法的所有要素都在这个对象中 Method[] ary = clazz.getDeclaredMethods(); for (Method method : ary) { System.out.println(method); //获取方法的详细信息 System.out.println(method.getName()); System.out.println(method.getReturnType()); //查找与test开头的方法 String name = method.getName(); if (name.startsWith("test")) { System.out.println("find"); } } input.close(); } }
动态加载类
class clazz = Class.forName(类名);
import java.util.Scanner; public class Main { public static void main(String[] args) throws Exception { Scanner input = new Scanner(System.in); System.out.println("input Class:"); String className = input.nextLine(); Class clazz = Class.forName(className); System.out.println(clazz); } }
动态创建对象
Object obj = clazz.newInstance();
执行clazz引用的类信息中的无参构造器,动态创建实例.如果没有无参构造器,则抛出异常.
反射可以调用有参构造器.
几乎所有的高级流都没有无参构造器.
动态获取类的方法信息
//动态查询类的方法信息 //从clazz代表的类信息中获取全部的方法信息 //每一个Method代表一个方法信息 //方法的所有要素都在这个对象中 Method[] ary = clazz.getDeclaredMethods(); for (Method method : ary) { //获取方法的详细信息 System.out.println(method); System.out.println(method.getName()); System.out.println(method.getReturnType()); //查找与test开头的方法 String name = method.getName(); if (name.startsWith("test")) { System.out.println("find"); } }
动态执行方法
invoke: 调用 method:方法
语法:
Object obj = method.invoke(执行方法的对象, 传递的参数1, 参数2, ...);
必须在对象上执行一个非静态方法,调用方法的时候必须有对象.
在invoke方法执行的时候,必须传递包含当前方法的对象.
invoke可以调用私有方法.
package reflect; public class Test { public String fun1() { return "fun(1)"; } private String fun2(String str) { return str; } }
package reflect;
import java.lang.reflect.Method;
import java.util.Scanner;
public class Main {
public static void main(String[] args) throws Exception {
//动态加载类
Scanner in = new Scanner(System.in);
System.out.println("input Class:");
String className = in.nextLine();
Class cls = Class.forName(className);
//1.找到指定方法
//Class提供了根据方法签名找到指定的方法信息的API
String name = "fun2";
//类型列表 Class[]
//X.class ==> X类型
Class[] types={String.class}; // --> 方法签名
//根据方法签名在cls查找方法信息 与参数共有与否无关
Method method = cls.getDeclaredMethod(name, types);
//找到该方法
System.out.println(method);
//执行私有方法
//打开方法的执行权限!!违反封装
method.setAccessible(true);
Object obj = cls.newInstance();
Object value = method.invoke(obj, "fun2..");
System.out.println(value);
}
}
运行: input Class: reflect.Test private java.lang.String reflect.Test.fun2(java.lang.String) fun2..
反射用途
1.eclipse中解析类的结构使用了反射
2.JUnit识别被测试的方法使用了反射
-JUnit3利用反射查找test开头的方法
-JUnit4利用反射解析@Test查找测试方法
3.Spring管理Bean对象,注入了Bean属性使用了反射
-利用反射创建Bean对象实例
-利用反射注入Bean属性
4.注解的解析使用了反射
-利用反射API支持注解
5.强行执行私有方法(访问私有属性)
案例一: 实现JUnit4中的筛选带有@Test标签的功能(以@Demo为例)
TestDemo.java:
//执行一个类中全部以@Demo标注的方法 public class TestDemo { public String fun1() { return "fun1()"; } private String fun2() { return "fun2()"; } public void fun3() { System.out.println("fun3()"); } }
Demo.java:
import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; RetentionPolicy.RUNTIME) (/* * @Retention 修饰注解的注解 * RetentionPolicy.SOURCE:默认值(编译完了只在源代码中有) * RetentionPolicy.CLASS 保留到class中 * RetentionPolicy.RUNTIME:保留到运行时(保留到方法区) * 反射在运行时动态加载类 */ public @interface Demo { }
Demo1.java:
import java.lang.reflect.Method; import java.util.Scanner; public class Demo1 { public static void main(String[] args) throws Exception { Scanner in = new Scanner(System.in); System.out.print("input Class name:"); String className = in.nextLine(); //动态加载类 Class cls = Class.forName(className); //动态获取全部方法 Method[] ary = cls.getDeclaredMethods(); //动态检查方法的注解信息 for (Method method : ary) { //检查一个方法的注解信息 //method.getAnnotation(被检查的注解类型); Demo ann = method.getAnnotation(Demo.class); //返回注册类型,如果为空表示没有注解 //不为空表示找到了被检查的注解Annotation if (ann != null) { System.out.println(method); } } } }
Console:
input Class name:reflect.TestCase public java.lang.String reflect.TestCase.fun1() private java.lang.String reflect.TestCase.fun2()
案例二: 用反射实现Spring的原型
ApplicationContext(ClassPathXMLApplicationContext).java:
import java.io.InputStream; import java.util.HashMap; import java.util.List; import java.util.Map; import org.dom4j.Document; import org.dom4j.DocumentException; import org.dom4j.Element; import org.dom4j.io.SAXReader; public class ApplicationContext { //缓存Spring容器中的Bean对象 private Map<String, Object> beans = new HashMap<>(); /* * 利用配置文件初始化当前容器 * 利用xml配置文件,初始化全部的Bean对象 */ public ApplicationContext(String xml) throws Exception { //利用DOM4j读取xml文件 //解析XML文件内容,得到Bean的类名和Bean的id //根据类名动态加载类并且创建对象 //将对象和对应的id添加到Map缓存中 //从Resourse(classpath)中读取流 InputStream in = getClass().getClassLoader().getResourceAsStream(xml); SAXReader reader = new SAXReader(); //相当于一个高级流,高级流需依附于低级流 Document doc = reader.read(in); in.close(); //解析xml:<beans><bean><bean>...<beans> Element root = doc.getRootElement(); //Element就是beans //读取根元素中全部的bean子元素. List<Element> list = root.elements("bean"); for (Element e : list) { //e就是bean元素 属性: id, class String id = e.attributeValue("id"); //获取属性 String className = e.attributeValue("class"); //动态加载类,动态创建对象 Class clazz = Class.forName(className); Object bean = clazz.newInstance(); beans.put(id, bean); } } public Object getBean(String id) { //根据id在Map中查找对象,并返回对象 return beans.get(id); } //泛型方法 public <T> T getBean(String id, Class<T> cls) { return (T) beans.get(id); } }
ApplicationContext.xml:
<?xml version="1.0" encoding="UTF-8"?> <beans> <bean id="date" class="java.util.Date"></bean> <bean id="testCase" class="reflect.TestCase"></bean> <bean id="demo1" class="reflect.Demo1"></bean> </beans>
Demo.java:
import java.util.Date; public class Test { public static void main(String[] args) throws Exception { ApplicationContext ctx = new ApplicationContext("ApplicationContext.xml"); Date date = (Date) ctx.getBean("date"); System.out.println(date); } }