Reflection反射机制原理、使用场景 及 缺陷

反射

一个需求 引出反射

需求如下:

根据配置文件re.properties中的指定信息,创建Cat对象并调用方法hi

在配置文件中代码:classfullpath=com.panyujie.reflection.Cat,method=hi

这样的需求在学习框架时非常多,通过外部文件配置,在不修改源码情况下控制程序 符合设计模式的ocp原则(开闭原则:不修改源码,扩容功能)

代码如下:

Cat 类:

public class Cat {

private String name;

public int age;

public String sex;

//无参构造器

public Cat() {}

private Cat(String name){

this.name = name;

}

public Cat(String name, int age, String sex) {

this.name = name;

this.age = age;

this.sex = sex;

}

public void hi() {

System.out.println("hi:" + name);

}

public void eat(String food) {

System.out.println(name+": 吃"+food);

}

public int sum(int x, int y) {

return x+y;

}

public void dosome(int x,float y, char z, boolean b, String... strs){

System.out.println(name+": "+x+" "+y+" "+z+" "+b);

System.out.println(name+": "+strs);

}

private void WC() {

System.out.println(name + ": 上厕所..");

}

@Override

public String toString() {

return "Cat{" +

"name='" + name + '\'' +

", age=" + age +

", sex='" + sex + '\'' +

'}';

}

}

实现需求

// 反射问题的引入
//根据 re.properties 配置文件中的信息,创建Cat对象并调用hi方法
public class ReflectionQuestion {
public static void main(String[] args) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
//传统方式:new 对象 --》 调用方法 Cat().hi();
// 1. 传统方法: 使用Properties类读写配置文件
Properties properties = new Properties();
properties.load(new FileInputStream("src/main/resources/re.properties"));
String classfullpath = properties.get("classfullpath").toString(); //com.panyujie.reflection.Cat
String methodName = properties.get("method").toString(); //hi
System.out.println("classfullpath=" + classfullpath);
System.out.println("methodName=" + methodName);
// 2. 创建对象
// 怎么创建对象???? 你拿到了类的全路径 你怎么 new?
// 只能使用 反射机制
// 2.1 加载类,返回Class类型的对象
Class<?> cls = Class.forName(classfullpath);
// 2.2 通过 cls对象 得到加载的类 com.panyujie.reflection.Cat 的对象
Object o = cls.newInstance();
// 2.3 通过 cls对象 得到加载的类 com.panyujie.reflection.Cat 的 methodName 方法对象 hi
// 即:在反射机制中,可以把方法视为对象(万物皆对象)
Method method1 = cls.getMethod(methodName);
// 2.4通过 method1 调用方法:即-》通过方法对象实现调用方法
// 反射机制:方法.invoke(对象)
method1.invoke(o);
}
}

效果: 调用成功,为null是因为我没赋值

此时我们发现,我们只需要将re.properties中的 method=hi 改成 method=eat,就会调用eat(),不需要修改源码,反射机制非常强大!

反射机制

Java Reflection

反射机制允许程序在执行期间借助于Reflection API取得任何类的内部信息(比如:成员变量,构造器,成员方法等),并能操作对象的属性及方法。反射在设计模式和框架的底层都会用到

加载完类之后,在堆内存中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象包含了类的完整结构信息。通过这个对象得到类的结构。这个Class对象就像一面镜子,透过这个镜子看到类的结构,所以形象称之为:反射

反射机制 原理图

反射机制可以完成的功能

在运行时,判断任意一个对象所属的类

在运行时,创建任意一个类的对象

在运行时,得到任意一个类所具有的成员变量和方法

在运行时,调用任意一个对象的成员变量和方法

生成 动态代理

反射相关的主要类

java.lang.Class:代表一个类,Class对象表示某个类加载后在堆中的对象

java.lang.reflect.Method:代表类的方法

java.lang.reflect.Field:代表类的成员变量

java.lang.reflect.Constructor:代表类的构造方法

java.lang.reflect.Array:创建和访问动态数组

测试Field和Constructor:

//java.lang.reflect.Field: 代表类的成员变量, Field对象表示某个类的成员变量
//得到name字段
//getField不能得到私有的属性
Field nameField = cls.getField("age"); //
System.out.println(nameField.get(o)); // 传统写法 对象.成员变量 , 反射 : 成员变量对象.get(对象)
//java.lang.reflect.Constructor: 代表类的构造方法, Constructor对象表示构造器
Constructor constructor = cls.getConstructor(); //()中可以指定构造器参数类型, 返回无参构造器
System.out.println(constructor);//Cat()

public class Cat {
private String name = "招财猫";
public int age = 10; //public的
public Cat() {} //无参构造器
public Cat(String name) {
this.name = name;
}
public void hi() { //常用方法
//System.out.println("hi " + name);
}
public void cry() { //常用方法
System.out.println(name + " 喵喵叫..");
}
}

测试Field和Constructor得到的结果:

获得有参数的构造器

//传入 String.class 就是String类的Class对象
Constructor constructor2 = cls.getConstructor(String.class);
System.out.println(constructor2);//Cat(String name)

反射优点 和 缺点

优点:可以动态的创建和使用对象(就是框架底层核心),使用灵活,没有反射机制,框架技术就失去底层支撑。

缺点:使用反射基本是解释执行,对执行速度有影响

反射调用 性能测试 及 关闭访问检测

测试代码:

普通调用、反射调用 5千万次 看耗时

/**
* Created with IntelliJ IDEA.
* Description:
* User: Submerge
* Date: 2022-10-23
* Time: 18:10
* 测试反射调用的性能
*/
public class Reflection2 {
public static void main(String[] args) throws Exception {
m1();
m2();
}
// 普通调用方法
public static void m1(){
Cat cat = new Cat();
long l = System.currentTimeMillis();
for(int i=0;i<50000000;i++){
cat.hi();
}
long r = System.currentTimeMillis();
System.out.println("普通调用耗时: " + (r-l));
}
// 反射调用方法
public static void m2() throws Exception {
Class<?> cls = Class.forName("com.panyujie.reflection.Cat");
Object o = cls.newInstance();
Method hi = cls.getMethod("hi");
long l = System.currentTimeMillis();
for(int i=0;i<50000000;i++){
hi.invoke(o);
}
long r = System.currentTimeMillis();
System.out.println("反射调用耗时: " + (r-l));
}
}

测试结果:反射对速度影响还是挺大的

关闭访问检查,提升的也不是很高哈,还有风险

//反射调用优化:关闭访问检测
public static void m3() throws Exception {
Class<?> cls = Class.forName("com.panyujie.reflection.Cat");
Object o = cls.newInstance();
Method hi = cls.getMethod("hi");
//在反射调用方法时,取消访问检查
hi.setAccessible(true);
long start = System.currentTimeMillis();
for (int i = 0; i < 50000000; i++) {
hi.invoke(o);
}
long end = System.currentTimeMillis();
System.out.println("关闭访问检测反射耗时:" + (end - start));
}

全部评论
这个难度是中等?
点赞 回复 分享
发布于 2023-03-31 22:48 山东
这是会出现在机试里面吗?
点赞 回复 分享
发布于 2023-03-31 22:15 河南

相关推荐

评论
点赞
1
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务