java中反射是在运行中动态的加载进入。有个很大的好处就是可以节省很多资源。为什么这么说呢,首先我们来说一下jvm,jvm是java的虚拟机,java之所以支持跨平台就是因为java虚拟机的存在。程序你有如下语句 Object o=new Object();运行起来的时候,java的虚拟机会首先启动。将你的java文件编译成 .class文件。加载进入你jvm的内存之中。你的类Object会加载进入方法区,这时候会生成个类的类型对象(即class类的对象)加载到堆中,作为方法区类的数据结构的接口。jvm创建对象时会看你的类是否加载好,加载好了便生成你的类的对象。然而在大型工程项目中,有很多可能暂时用不到,所以我们没必要把每一个类都生成对象。而反射机制正是解决这了这个问题。
举个例子我们的项目底层有时是用mysql,有时用oracle,需要动态地根据实际情况加载驱动类,这个时候反射就有用了,假设 com.java.dbtest.myqlConnection,com.java.dbtest.oracleConnection这两个类我们要用,这时候我们的程序就写得比较动态化,通过Class tc =Class.forName(“com.java.dbtest.TestConnection”);通过类的全类名让jvm在服务器中找到并加载这个类,而如果是oracle则传入的参数就变成另一个了。这时候就可以看到反射的好处了,这个动态性就体现出java的特性了!举多个例子,大家如果接触过spring,会发现当你配置各种各样的bean时,是以配置文件的形式配置的,你需要用到哪些bean就配哪些,spring容器就会根据你的需求去动态加载,你的程序就能健壮地运行。 ———————————————— 原文链接:https://blog.csdn.net/weixin_54963682/article/details/113913964
“程序运行时,允许改变程序结构或变量类型,这种语言称为动态语言”。从这个观点看,Perl、Python(看过我写的Python3学习系列的博文,不止一次突出Python动态语言的特点)、Ruby是动态语言,C++、Java、C#不是动态语言。但是JAVA有着一个非常突出的动态相关机制——Reflection(反射),用在Java身上指的是可以于运行时加载、探知、使用编译期间完全未知的classes。换句话说,Java程序可以加载一个运行时才得知名称的class,获悉其完整构造(但不包括methods定义),并生成其对象实体(newInstance)或对其fields设值,或唤起(invoke)其methods方法。
Java的反射机制,操作的就是这个.class文件,首先加载相应类的字节码(运行eclipse的时候,.class文件的字节码会加载到内存中),随后解剖(反射 reflect)出字节码中的构造函数、方法以及变量(字段)
反射的机制,无非就是先加载对应字节码中的类,然后,根据加载类的信息,一点点的去解剖其中的内容
//解析<property .../>元素的name属性得到该字符串值为“dataSource”
String nameStr = "dataSource";
//解析<property .../>元素的ref属性得到该字符串值为“dataSource”
String refStr = "dataSource";
//生成将要调用setter方法名
String setterName = "set" + nameStr.substring(0, 1).toUpperCase()
+ nameStr.substring(1);
//获取spring容器中名为refStr的Bean,该Bean将会作为传入参数
Object paramBean = container.get(refStr);
//获取setter方法的Method类,此处的cls是刚才反射代码得到的Class对象
Method setter = cls.getMethod(setterName, paramBean.getClass());
//调用invoke()方法,此处的obj是刚才反射代码得到的Object对象
setter.invoke(obj, paramBean);
1.类的加载过程:
程序经过javac.exe命令以后,会生成一个或多个字节码文件(.class结尾)。接着我们使用java.exe命令对某个字节码文件进行解释运行。相当于将某个字节码文件加载到内存中。此过程就称为类的加载。
加载到内存中的类,我们就称为运行时类,此运行时类,就作为Class的一个实例。也就是说运行时类也是一个对象,它是CLass类的一个实例。
2.换句话说,==Class的实例就对应着一个运行时类==。一个用来描述类的类
3.加载到内存中的运行时类,会缓存一定的时间。在此时间之内,我们可以通过不同的方式来获取此运行时类。
// 方式一:调用.class
Class clazz1 = Person.class;
System.out.println(clazz1);
// 方式二:调用getClass()
Person p1 = new Person("Bob", 20);
Class clazz2 = p1.getClass();
System.out.println(clazz2);
// (非常推荐!!!) 方式三:forName(String classPath)
Class clazz3 = Class.forName("Person");
// clazz3 = Class.forName("java.lang.String");
System.out.println(clazz3);
类加载器作用是用来把类(class)装载进内存的。
public class Test {
public static void main(String[] args) throws Exception {
// 获取运行时类的实例后,可以进行如下操作
// 获取运行时类的实例
Class clazz = Person.class;
// 1.创建Person类的对象
// 1.1 创建无参的对象
Constructor con1 = clazz.getConstructor();
Object obj1 = con1.newInstance();
Person p1 = (Person) obj1;
System.out.println(p1.toString());
// 1.2 创建带参的对象
// 3 获取构造器
Constructor con2 = clazz.getConstructor(String.class, int.class);
Object obj2 = con2.newInstance("Bob", 19);
Person p2 = (Person) obj2;
System.out.println(p2.toString());
// 2. 调用对象指定属性、方法
// 2.1 获取属性
Field age = clazz.getDeclaredField("age");
// 设置权限
age.setAccessible(true);
// 对p2的age进行设置
age.set(p2, 30);
System.out.println(p2.toString());
// 2.2 获取方法
Method showNation = clazz.getDeclaredMethod("showNation", String.class);
showNation.setAccessible(true);
String nation = (String) showNation.invoke(p2, "中国");
System.out.println(nation);
}
}
Properties pros = new Properties();
// 读取配置文件
// 方式一:IO流 , 配置文件在当前module下
FileInputStream fis = new FileInputStream("src\\jdbc.properties");
pros.load(fis)
// 方式二:使用ClassLoader , 配置文件在当前module的src下
ClassLoader classLoader = ClassLoader.class.getClassLoader();
InputStream is = classLoader.getResourceAsStream("jdbc.properties");
pros.load(is);
String user = pros.getProperty("user");
System.out.println(user);
反射的基本步骤:首先获得Class对象,然后实例化对象,获得类的属性、方法或者构造函数,最后访问属性、调用方法、调用构造函数创建对象。invoke()方法就是用来执行指定对象的方法。
interface ClothFactory{
void produceCloth();
}
//代理类
class ProxyClothFactory implements ClothFactory{
private ClothFactory factory;//用被代理类对象进行实例化
public ProxyClothFactory(ClothFactory factory){
this.factory = factory;
}
@Override
public void produceCloth() {
System.out.println("代理工厂做一些准备工作");
factory.produceCloth();
System.out.println("代理工厂做一些后续的收尾工作");
}
}
//被代理类
class NikeClothFactory implements ClothFactory{
@Override
public void produceCloth() {
System.out.println("Nike工厂生产一批运动服");
}
}
public class StaticProxyTest {
public static void main(String[] args) {
//创建被代理类的对象
ClothFactory nike = new NikeClothFactory();
//创建代理类的对象
ClothFactory proxyClothFactory = new ProxyClothFactory(nike);
proxyClothFactory.produceCloth();
}
}
要想实现动态代理,需要解决的问题?
问题一:如何根据加载到内存中的被代理类,动态的创建一个代理类及其对象。————(根据被代理类 动态创建 代理类)
问题二:当通过代理类的对象调用方法a时,如何动态的去调用被代理类中的同名方法a。———— (通过在代理类中使用invoke()调用方法a,来动态调用被代理类的同名方法a)
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
interface Human{
String getBelief();
void eat(String food);
}
/**
* 被代理类
*/
class SuperMan implements Human{
@Override
public String getBelief() {
return "I believe I can fly!";
}
@Override
public void eat(String food) {
System.out.println("我喜欢吃" + food);
}
}
class ProxyFactory{
public static Object getProxyInstance(Object obj){
/**
* 调用此方法,返回一个代理类的对象。解决问题一
* obj:被代理类的对象
* return:代理类对象
*/
// 实现InvocationHandler
MyInvocationHandler handler = new MyInvocationHandler();
// 将被代理类进行绑定,就是将对象传入
handler.bind(obj);
// 传入被代理类的加载器、实现的接口、handler
return Proxy.newProxyInstance(obj.getClass().getClassLoader(),obj.getClass().getInterfaces(),handler);
}
}
class MyInvocationHandler implements InvocationHandler{
/**
* 实现InvocationHandler,获取handler
*/
// 需要使用被代理类的对象进行赋值
private Object obj;
public void bind(Object obj){
this.obj = obj;
}
// 当我们通过代理类的对象,调用方法a时,就会自动的调用如下的方法:invoke()
// 将被代理类要执行的方法a的功能就声明在invoke()中
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//method:即为代理类对象调用的方法,此方法也就作为了被代理类对象要调用的方法
//obj:被代理类的对象
Object returnValue = method.invoke(obj,args);
//上述方法的返回值就作为当前类中的invoke()的返回值。
return returnValue;
}
}
public class Test {
public static void main(String[] args) {
// 创建一个被代理类
SuperMan superMan = new SuperMan();
// 传入被代理类对象,获取一个代理类对象
Human proxyInstance = (Human) ProxyFactory.getProxyInstance(superMan);
// 当通过代理类对象调用方法时,会自动的调用被代理类中同名的方法
proxyInstance.eat("四川麻辣烫");
}
}