一个需求,引出反射 | 完整的反射使用流程:
在不修改源码的情况下,来控制程序,也符合设计模式中的opc原则(开闭原则:不修改源码,扩容功能)
1、创建配置文件:re.properties
classfullpath=com.reflection.Cat
method=hi
2、创建Cat类
package com.reflection;public class Cat {public String name = "招财猫";public void hi(){System.out.println("hi!"+ name);}public void cry(){System.out.println(name + "喵喵叫!");}
}
3、创建调用类:quection,并回顾传统调用的方法,以及反射机制解决的完整流程
package com.reflection;import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Properties;public class quection {public static void main(String[] args) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {//问题:根据配置文件 re.properties 中的指定信息,创建Cat类 并 调用hi方法;//传统方法Cat cat = new Cat();cat.hi(); // 如果想调用cry方法,只能修改代码;System.out.println("===========================================");//反射//1. 使用Properties类 获取配置文件中的内容Properties properties = new Properties();properties.load(new FileInputStream("src\\main\\resources\\re.properties"));String classfullpath = properties.getProperty("classfullpath").toString();String method = properties.getProperty("method").toString();System.out.println("classfullpath:" + classfullpath);System.out.println("method:" + method);//2. 使用反射机制解决Class aClass = Class.forName(classfullpath); //加载类,返回Class类型的对象aClassObject o = aClass.newInstance(); //通过aClass得到 com.reflection.Cat 类的对象实例System.out.println("o的运行类型:" + o.getClass());//通过 aClass 得到 com.reflection.Cat 类的 methodName为“hi”的方法对象//即:在反射中,可把方法视为对象(万物接对象)Method method1 = aClass.getMethod(method);System.out.println("===========================================");//通过 方法对象 来实现 调用方法; 也就是 通过 method1对象 来调用 Cat类中的方法;method1.invoke(o);//传统方法:对象.方法(); 反射:方法.invoke(对象);}
}
在 传统方法调用 和 反射机制 中,若改为调用Cat类中的cry方法:
传统方法需要修改代码,而反射机制只需要修改配置文件即可,将re.properties配置文件中的,method=hi 改为 method=cry
反射机制:
反射机制允许程序在执行期借助于ReflectionAPI取得任何类的内部信息(比如成员变量,构造器,成员方法等),并能操作对象的属性及方法。反射在设计模式和框架底层都会用到。
加载完类后,在堆中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象包含了类的完整结构信息。通过这个对象得到类的结构。这个Class对象就像一面镜子,透过这个镜子看到类的结构,所以,形象的称之为:反射。
Java反射机制原理示意图:
Java反射机制可以完成:
1、在运行时判断任意一个对象所属的类
2、在运行时构造任意一个类的对象
3、在运行时得到任意一个类所具有的成员变量和方法
4、在运行时调用任意一个对象的成员变量和方法
5、生成动态代理
反射相关的主要类:这些类在java.lang.reflection包中
1、java.lang.Class:代表一个类,Class对象表示某个类加载后在堆中的对象
2、java.lang.reflect.Method:代表类的方法,Method对象表示某个类的方法
3、java.lang.reflect.Field:代表类的成员变量,Field对象表示某个类的成员变量
4、java.lang.reflect.Constructor:代表类的构造方法,Constructor对象表示构造器
反射相关的主要类的代码应用:
package com.reflection;public class Cat {private String name = "招财猫";public int age = 0;public Cat() {}public Cat(String name) {this.name = name;}public void hi(){System.out.println("hi!"+ name);}public void cry(){System.out.println(name + "喵喵叫!");}
}
package com.reflection;import java.io.FileInputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Properties;public class reflection01 {public static void main(String[] args) throws Exception {//反射//1. 使用Properties类 获取配置文件中的内容Properties properties = new Properties();properties.load(new FileInputStream("src\\main\\resources\\re.properties"));String classfullpath = properties.getProperty("classfullpath").toString();String method = properties.getProperty("method").toString();//2. 使用反射机制解决Class aClass = Class.forName(classfullpath); //加载类,返回Class类型的对象aClassObject o = aClass.newInstance(); //通过aClass得到 com.reflection.Cat 类的对象实例System.out.println("o的运行类型:" + o.getClass());//通过 aClass 得到 com.reflection.Cat 类的 methodName为“hi”的方法对象//即:在反射中,可把方法视为对象(万物接对象)Method method1 = aClass.getMethod(method);System.out.println("===========================================");//通过 方法对象 来实现 调用方法; 也就是 通过 method1对象 来调用 Cat类中的方法;method1.invoke(o);//传统方法:对象.方法(); 反射:方法.invoke(对象);//java.lang.reflect.Field:代表类的成员变量,Field对象表示某个类的成员变量//得到name字段;//getField不能得到私有的属性Field name = aClass.getField("age");System.out.println(name.get(o));//传统写法:对象.成员变量 反射:成员变量对象.get(对象)//java.lang.reflect.Constructor:代表类的构造方法,Constructor对象表示构造器Constructor constructor = aClass.getConstructor();//()中可以指定构造器参数类型,返回无参构造器System.out.println(constructor);//Cat()Constructor constructor1 = aClass.getConstructor(String.class);//这里传入的String.class 就是String类的Class对象System.out.println(constructor1);//Cat(String name)}
}
反射的优点和缺点:
优点: 可以动态的创建和使用对象(也是框架底层核心),使用灵活,没有反射机制,框架技术就失去底层支撑。
缺点: 使用反射基本是解释执行,对执行速度有影响。
package com.reflection;import java.io.FileInputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Properties;public class reflection02 {public static void main(String[] args) throws Exception {m1();m2();m3();}//传统方法调用hipublic static void m1() {Cat cat = new Cat();long start = System.currentTimeMillis();for (int i = 0; i < 900000000; i++) {cat.hi();}long end = System.currentTimeMillis();System.out.println("m1() 耗时:" + (end - start));}//反射机制调用方法hipublic static void m2() throws Exception {Class aClass = Class.forName("com.reflection.Cat"); //加载类,返回Class类型的对象aClassObject o = aClass.newInstance(); //通过aClass得到 com.reflection.Cat 类的对象实例Method method1 = aClass.getMethod("hi");//通过 aClass 得到 com.reflection.Cat 类的 methodName为“hi”的方法对象long start = System.currentTimeMillis();for (int i = 0; i < 900000000; i++) {method1.invoke(o);//反射调用方法}long end = System.currentTimeMillis();System.out.println("m2() 耗时:" + (end - start));}//反射机制优化调用方法hipublic static void m3() throws Exception {Class aClass = Class.forName("com.reflection.Cat"); //加载类,返回Class类型的对象aClassObject o = aClass.newInstance(); //通过aClass得到 com.reflection.Cat 类的对象实例Method method1 = aClass.getMethod("hi");//通过 aClass 得到 com.reflection.Cat 类的 methodName为“hi”的方法对象method1.setAccessible(true);long start = System.currentTimeMillis();for (int i = 0; i < 900000000; i++) {method1.invoke(o);//反射调用方法}long end = System.currentTimeMillis();System.out.println("m3() 耗时:" + (end - start));}
}=========执行结果============
m1() 耗时:4
m2() 耗时:1187
m3() 耗时:775
反射的简单优化——关闭访问检查:
1、Method 和 Field、Constructor对象都有setAccessible()方法
2、setAccessible作用是启动和禁用访问安全检查的开关
3、参数值为true表示,反射的对象在使用时取消访问检查,提高反射的效率。参数值为false,则表示反射的对象执行访问检查。