一个需求,引出反射 | 完整的反射使用流程:
在不修改源码的情况下,来控制程序,也符合设计模式中的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类型的对象aClass
Object 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类型的对象aClass
Object 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();
}
//传统方法调用hi
public 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));
}
//反射机制调用方法hi
public static void m2() throws Exception {
Class aClass = Class.forName("com.reflection.Cat"); //加载类,返回Class类型的对象aClass
Object 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));
}
//反射机制优化调用方法hi
public static void m3() throws Exception {
Class aClass = Class.forName("com.reflection.Cat"); //加载类,返回Class类型的对象aClass
Object 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,则表示反射的对象执行访问检查。