JAVA实习找工作——反射机制和动态代理

引言

在反射出现之前,当我们有一个java类之后,我们在类的外部是不能使用类中的私有结构的,比如说不能调用private的构造器,private的方法等等。但是出现了反射之后,我们就可以调用这个类的任何结构,包括私有的。

import org.junit.Test;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class ReflectionTest {

    @Test
    public void test1(){
        Person p = new Person("tom",23);
        p.show();
    }


    @Test
    public void test2() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        //1.获取类对象
        Class<Person> personClass = Person.class;
        //2.获取类对象的构造器
        Constructor constructor = personClass.getConstructor(String.class, int.class);
        Object tom = constructor.newInstance("Jerry", 23);
        //3.获取类方法
        Method show = personClass.getDeclaredMethod("show");
        show.invoke(tom);

        //反射的特殊功能,可以调用私有的类结构
        Constructor<Person> cons1 = personClass.getDeclaredConstructor(String.class);
        cons1.setAccessible(true);
        Person wanglei = cons1.newInstance("wanglei");
        wanglei.show();


    }

}

反射和单例模式

单例模式私有化了构造器,希望这个类的实例只需要创建一个就可以了。到那时反射是解决能不能用的问题,单例模式是建议你的这张写法,他们是不冲突的。

关于java.lang.Class的理解

Class作为反射的源头

(1)类的加载过程:程序经过javac.exe编译之后会生成一个或多个字节码文件.class结尾,每一个java类对应一个字节码文件,通过java.exe解释运行某个包含有mian方法的字节码文件。相当于把这个字节码文件加载到内存中,此过程称为类的加载。记载到内存中的类称之为运行时类,这个运行时类作为Class的一个实例。换句话说Class的实例就对应着一个运行时类。

获取Class对象的四种方式

//方式一:调用运行时类的属性:.class
        Class clazz1 = Person.class;
        System.out.println(clazz1);
        //方式二:通过运行时类的对象,调用getClass()
        Person p1 = new Person();
        Class clazz2 = p1.getClass();
        System.out.println(clazz2);

        //方式三:调用Class的静态方法:forName(String classPath)
        Class clazz3 = Class.forName("com.atguigu.java.Person");
//      clazz3 = Class.forName("java.lang.String");
        System.out.println(clazz3);

        System.out.println(clazz1 == clazz2);
        System.out.println(clazz1 == clazz3);

        //方式四:使用类的加载器:ClassLoader  (了解)
        ClassLoader classLoader = ReflectionTest.class.getClassLoader();
        Class clazz4 = classLoader.loadClass("com.atguigu.java.Person");
        System.out.println(clazz4);

        System.out.println(clazz1 == clazz4);

三步:

加载:将类的class文件加入到内存,并创建Class对象,此过程由类加载器完成

链接:确保Class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。

初始化:在编译生成class文件时,编译器会产生两个方法加于class文件中,一个是类的初始化方法clinit, 另一个是实例的初始化方法init。clinit指的是类构造器,主要作用是在类加载过程中的初始化阶段进行执行,执行内容包括静态变量初始化和静态块的执行。

引言

代理模式是23种设计模式中的一种,是比较重要的知识,在Spring框架中比较重要的AOP(Aspect Oriented Programing)也是基于动态代理实现的。

代理的理解

关于代理的理解,我举个例子说明:20年前,我们想要购买一台联想电脑的方式是:我们去联想的工厂,工厂卖给我们电脑,并且提供售后的服务。

在这几十年的发展中,出现了一些经销商,他们从联想工厂进货,然后卖给我们,电脑坏了我们也先去经销商的店里找他,然后他再去找工厂,于是我们和工厂的联系其实就中断了。

2020年,我们想要买一台笔记本电脑,会直接去天猫、苏宁、京东等大型的经销商那里去买一台电脑,出现问题就7天无理由退货。

这里我们就是客户端Client,那些经销商也叫做代理商,联想工厂就叫做被代理对象。这样的过程就是代理。

动态代理

动态代理的特点:字节码随用随创建,随用随加载

动态代理的作用:在不修改源码的基础上增强方法

动态代理的分类:2类,一个是基于接口的动态代理,一个是基于子类的动态代理

基于接口的动态代理

由JDK提供的Proxy类实现,要求被代理类至少实现一个接口。实现过程如下

(1)首先创建Lenovo类并且实现一个接口

package com.alibaba200408.动态代理;

public class Lenovo implements ILenovo {
    @Override
    public void sale(Double money) {
        System.out.println("拿到"+money+"元,电脑发货");
    }

    @Override
    public void afterService(Double money) {
        System.out.println("拿到"+money+"元,售后服务开始");

    }
}

代码很简单,只涉及了2个方法,一个是销售电脑,一个是维修电脑。

接口的代码如下

package com.alibaba200408.动态代理;

public interface ILenovo {

    void sale(Double money);

    void afterService(Double money);
}

(2)实例化一个动态代理的对象,通过代理对象调用方法

package com.alibaba200408.动态代理;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class Client {
    public static void main(String[] args) {

        //联想厂家
        ILenovo lenovo = new Lenovo();


        //创建代理对象
        ILenovo proxyInstance = (ILenovo) Proxy.newProxyInstance(lenovo.getClass().getClassLoader(),
                lenovo.getClass().getInterfaces(),
                new InvocationHandler() {
                    /**
                     * invoke方法的作用是调用任何被代理对象的方法都会被这个invoke方法拦截
                     *  proxy:表示当前代理对象的引用
                     *  method:表示当前正在执行的方法
                     *  args:表示当前执行的方法的参数
                     *
                     *  返回值就是当前方法的返回值
                     */

                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        //提供增强的代码
                        Double money = (Double) args[0];
                        if ("sale".equals(method.getName())){
                            method.invoke(lenovo, money * 0.8);
                        }else {
                            method.invoke(lenovo,args);
                        }

                        return null;
                    }
                });

        proxyInstance.afterService(10000.0);

    }
}

基于子类的动态代理

这种实现方式要求被代理类不能是final的,因为final修饰的类不能有子类,其次这种方式需要第三方的cglib。这里不再赘述了。

发布了111 篇原创文章 · 获赞 60 · 访问量 7万+

猜你喜欢

转载自blog.csdn.net/Haidaiya/article/details/105512739