JavaLearning

反射

java的反射机制的好处

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)出字节码中的构造函数、方法以及变量(字段)

反射的机制,无非就是先加载对应字节码中的类,然后,根据加载类的信息,一点点的去解剖其中的内容

Spring的IOC,即“控制反转”,大致实现过程
	//解析<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);  

java.lang.Class类的理解

1.类的加载过程:

程序经过javac.exe命令以后,会生成一个或多个字节码文件(.class结尾)。接着我们使用java.exe命令对某个字节码文件进行解释运行。相当于将某个字节码文件加载到内存中。此过程就称为类的加载

加载到内存中的类,我们就称为运行时类,此运行时类,就作为Class的一个实例。也就是说运行时类也是一个对象,它是CLass类的一个实例。

2.换句话说,==Class的实例就对应着一个运行时类==。一个用来描述类的类

3.加载到内存中的运行时类,会缓存一定的时间。在此时间之内,我们可以通过不同的方式来获取此运行时类。

获取Class的实例的方式

		// 方式一:调用.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

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("四川麻辣烫");
    }
}