(目录)
反射
一个需求 引出反射
需求如下:
根据配置文件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));
}
@
标签:反射,场景,Reflection,System,Cat,String,hi,public,name From: https://blog.51cto.com/panyujie/6101607