一。反射入门案例
目录
提出一个问题,如何修改外部的配置文件,来操控java内部代码的执行逻辑。
反射类
ReflectionQuestion
package com.hspaidu.reflection.qusetion;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Properties;
public class ReflectionQuestion {
public static void main(String[] args) throws IOException, ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
// 使用properties类可以引入外部文件
Properties properties = new Properties();
properties.load(new FileInputStream("src\\re.properties"));
// 获得class的全部路径
String classfullpath = properties.get("classfullpath").toString();
// 得到对象的方法
String method = properties.get("method").toString();
System.out.println("method = "+method);
System.out.println("classfullpath = " + classfullpath);
// 返回了一个class类的类对象
Class cls = Class.forName(classfullpath);
// 通过cls得到对象的实例 可以不用强转也是cat类型
Object o = cls.newInstance();
System.out.println("o的运行类型 = "+o.getClass());
// 得到类当中cls的方法的对象 方法也是对象
Method method1 = cls.getMethod(method);
// 通过方法的对象来调用类实例当中的方法
method1.invoke(o); // 方法.invoke(对象)
}
}
运行结果
method = hi
classfullpath = com.hspaidu.Cat
o的运行类型 = class com.hspaidu.Cat
Hi 招财猫
被操作的类
Cat
package com.hspaidu;
public class Cat {
private String name = "招财猫";
public void hi(){
System.out.println("Hi "+name);
}
public void cry(){
System.out.println(name+" 在cry");
}
}
配置文件
re.properties
classfullpath = com.hspaidu.Cat
method = hi
修改外部文件
classfullpath = com.hspaidu.Cat
method = cry
反射类输出结果
method = cry
classfullpath = com.hspaidu.Cat
o的运行类型 = class com.hspaidu.Cat
招财猫 在cry
二。反射的机制
1.反射的原理
Class当中有一些api可以获取类相关的一些信息,比如实例,方法,全类名等。
所有的类在堆中都有一个复制体,我们可以通过操作这个复制体,来使用这个类。
原理图
描述:Java程序加载有三个阶段
编译阶段:类有属性和方法,构造器。Cat有hi方法,源代码当中会体现,经过Javac的编译,得到一个Cat.class字节码文件,会有属性,方法,构造器等等,
类的加载阶段:字节码文件会加载到加载区域。将class类对象放到堆当中。字节码文件到堆当中是使用了类加载器的classLoader的,类加载器就体现了反射机制。在底层,已经将放在堆当中对象的属性看成可以操作的对象了。类加载完之后生成对象。该对象知道他是属于那个class对象的。得到class对象之后,就可以对对象进行操作了。
运行阶段:调用Cat cat = new Cat(); cat.hi() 会导致类的加载
2.反射相关的类
代码演示
package com.hspaidu.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 {
Properties properties = new Properties();
properties.load(new FileInputStream("src\\re.properties"));
String classfullpath = properties.get("classfullpath").toString();
String method = properties.get("method").toString();
System.out.println("method = "+method);
System.out.println("classfullpath = " + classfullpath);
// 关于Class对象
Class cls = Class.forName(classfullpath);
//并没有进行类型的强转
Object o = cls.newInstance();
System.out.println(o.getClass());
// 关于方法
Method method1 = cls.getMethod(method);
method1.invoke(o);
// 关于属性
Field age = cls.getField("age"); //不能得到私有的属性
System.out.println("年龄"+age.get(o)); //反射反射,反过来的: 成员变量对象.get(对象)
// 关于构造器
Constructor constructor = cls.getConstructor(); //后边没有参数,返回的是无参的构造器
System.out.println(constructor); //没有形参的构造器
Constructor constructor1 = cls.getConstructor(String.class); //传入的是String的Class对象
System.out.println(constructor1); //有形参的构造器
}
}
结果
method = cry
classfullpath = com.hspaidu.Cat
class com.hspaidu.Cat
招财猫 在cry
年龄10
public com.hspaidu.Cat()
public com.hspaidu.Cat(java.lang.String)
3.反射的优点和缺点
实例
Reflection02
package com.hspaidu.reflection;
import com.hspaidu.Cat;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Reflection02 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
m1();
m2();
}
// 传统的方法来调用hi方法
public static void m1(){
Cat cat = new Cat();
long start = System.currentTimeMillis();
for (int i=0;i<9000000;i++){
cat.hi();
}
long end = System.currentTimeMillis();
System.out.println("传统方法调用 hi 耗时 = "+(end-start));
}
// 反射机制调用
public static void m2() throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException {
Class aClass = Class.forName("com.hspaidu.Cat");
Method hi = aClass.getMethod("hi");
Object o = aClass.newInstance();
long start = System.currentTimeMillis();
for (int i=0;i<9000000;i++){
hi.invoke(o);
}
long end = System.currentTimeMillis();
System.out.println("反射方法调用 hi 耗时 = "+(end-start));
}
}
记得把控制台输出语句关了,太耗时间。
结果
传统方法调用 hi 耗时 = 3
反射方法调用 hi 耗时 = 20
进行优化
Reflection02类
package com.hspaidu.reflection;
import com.hspaidu.Cat;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Reflection02 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
m1();
m2();
m3();
}
// 传统的方法来调用hi方法
public static void m1() {
Cat cat = new Cat();
long start = System.currentTimeMillis();
for (int i = 0; i < 9000000; i++) {
cat.hi();
}
long end = System.currentTimeMillis();
System.out.println("传统方法调用 hi 耗时 = " + (end - start));
}
// 反射机制调用
public static void m2() throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InstantiationException, In