Java面试题集锦------反射机制和代理模式

1. forName方法的作用是返回一个class类型的对象,一般与newInstance方法配套使用;

    newInstance的作用是加载类,通过Java虚拟机的类加载机制把指定的类加载到内存中;

2. 当一个类或接口被装入Java虚拟机时,便会产生一个与它相关联的java.lang.Class对象,通过Class.forName方法,我们能得到一个指定类的Class对象,其中包含了该类的属性和方法等原信息,通过newInstance方法,可以加载指定的类;

          链接:Java创建对象的四种方式:

(1)使用new创建对象;

(2)使用反射的机制创建对象:

         《1》使用Class类的newInstance方法:

           Class heroClass = Class.forName("yunche.test.Hello");
           Hello h =(Hello) heroClass.newInstance();

         《2》使用Constructor类的newInstance方法:

           //获取类对象
           Class heroClass = Class.forName("yunche.test.Hello");
           //获取构造器
           Constructor constructor = heroClass.getConstructor();
           Hello h =(Hello) constructor.newInstance();

(3)采用clone:

         clone时,需要已经有一个分配了内存的源对象,创建新对象时,首先应该分配一个和源对象一样大的内存空间。

  要调用clone方法需要实现Cloneable接口,由于clone方法是protected的,所以修改原类;

(4)采用序列化机制:

         使用序列化时,要实现实现Serializable接口,将一个对象序列化到磁盘上,而采用反序列化可以将磁盘上的对象信息转化           到内存中。

            Hello h = new Hello();            
            //准备一个文件用于存储该对象的信息
            File f = new File("hello.obj");
            FileOutputStream fos = new FileOutputStream(f);
            ObjectOutputStream oos = new ObjectOutputStream(fos);
            FileInputStream fis = new FileInputStream(f);
            ObjectInputStream ois = new ObjectInputStream(fis);
            //序列化对象,写入到磁盘中
            oos.writeObject(h);
            //反序列化对象
            Hello newHello = (Hello)ois.readObject();

3. 反射的常用类及他们的用法: 通过Field类,能得到类中的属性(class.getDeclaredFields),通过Method类,能得到并调用类中的方法(class.getDeclaredMethods);通过Constructor类,能得到类的构造函数(class.getDeclaredConstructors)。

4. 反射机制和代理模式(动态代理)的关系:

    代理模式--使用场景:如果处于性能或成本的考虑,我们无法直接调用某个服务时。不能为了“降低耦合度(只是附带的一个优         点)” 而引入代理模式。

     (1)静态的代理:继承同一个接口,代理对象在重写的方法前后加逻辑。

            缺点:每个“代理角色”代理了一个“真实角色”,如果需要被代理的“真实角色”很多,就必须写多个“代理角色”,代码难             以维护;

      (2)动态代理:实现InvocationHandler接口并重写其中invoke方法;

              和Spring中面向切面编程的关联:通过代理对象调用服务方法时,方法最终是在invoke中被调用的。这样可以在invoke方法中调用服务方法前后加上所需的关联方法,这就是面向切面编程的做法。            

5. 代码示例:

(1)静态代理

接口:

public interface HelloInterface {
    void sayHello();
}

真实对象:

public class Hello implements HelloInterface{
    @Override
    public void sayHello() {
        System.out.println("Hello zhanghao!");
    }
}

代理对象:

public class HelloProxy implements HelloInterface{
    private HelloInterface helloInterface = new Hello();
    @Override
    public void sayHello() {
        System.out.println("Before invoke sayHello" );
        helloInterface.sayHello();
        System.out.println("After invoke sayHello");
    }
}

JDK动态代理:(基于接口实现)

接口:

public interface Service {
    String selllCar(String carName);
}

真实对象:

public class ServiceImpl implements Service {
    @Override
    public String selllCar(String carName) {
        return carName + "is ready!";
    }
}

代理对象:

public class MyInvocationHandler implements InvocationHandler {
    private Object target;
    //初始化target对象
    public MyInvocationHandler(Object target){
        this.target=target;
    }
    //通过invoke方法,调用target类中的方法
    @Override
    public Object invoke(Object o, Method method, Object[] args) throws Throwable {
        
        System.out.println("Call:"+method.getName());
        //之前可加入其他方法(AOP)        //通过method的invoke方法调用target类中的方法
        Object result=method.invoke(target,args);
        //之后也可加入其他方法
        return result;
    }
}

测试类:通过Proxy类的静态方法newProxyInstance返回一个接口的代理实例。针对不同的代理类,传入相应的代理程序控制器InvocationHandler。

public class TestProxy {
    public static void main(String[] args) {
        CarFactory carFactory=new CarProxy();
        carFactory.sellCar();

        //动态代理
        //代理的真实对象
        Service service=new ServiceImpl();
        InvocationHandler invocationHandler=new MyInvocationHandler(service);
        //三个参数的含义:1》指定加载代理对象的方法;2》为代理对象提供服务的接口;3》把代理对象关联到invocationHandler对象上
        Service serviceProxy=(Service) Proxy.newProxyInstance(service.getClass().getClassLoader(),service.getClass().getInterfaces(),invocationHandler);
        System.out.println(serviceProxy.selllCar("Aston Martin"));
    }
}

JDK动态代理底层实现

动态代理具体步骤:

  1. 通过实现 InvocationHandler 接口创建自己的调用处理器;
  2. 通过为 Proxy 类指定 ClassLoader 对象和一组 interface 来创建动态代理类;
  3. 通过反射机制获得动态代理类的构造函数,其唯一参数类型是调用处理器接口类型;
  4. 通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数被传入。

JDK动态代理不足

JDK动态代理的代理类字节码在创建时,需要实现业务实现类所实现的接口作为参数。如果业务实现类是没有实现接口而是直接定义业务方法的话,就无法使用JDK动态代理了。(JDK动态代理重要特点是代理接口) 并且,如果业务实现类中新增了接口中没有的方法,这些方法是无法被代理的(因为无法被调用),动态代理只能对接口产生代理,不能对类产生代理

CGlib 动态代理代理类实现 (基于继承)

Cglib是针对类来实现代理的,他的原理是对代理的目标类生成一个子类,并覆盖其中方法实现增强,因为底层是基于创建被代理类的一个子类,所以它避免了JDK动态代理类的缺陷。但因为采用的是继承,所以不能对final修饰的类进行代理。final修饰的类不可继承。

目标类(一个公开方法,另外一个用final修饰):

public class Dog{
    
    final public void run(String name) {
        System.out.println("狗"+name+"----run");
    }
    
    public void eat() {
        System.out.println("狗----eat");
    }
}

方法拦截器:

public class MyMethodInterceptor implements MethodInterceptor{    

    //可写在test里面也可提成方法
    /*public Object CglibProxyGeneratory(Class target) {     
       Enhancer enhancer = new Enhancer();        // 为代理类指定需要代理的类,也即是父类
       enhancer.setSuperclass(target);       
       enhancer.setCallback(this);        // 获取动态代理类对象并返回
       return enhancer.create();        /** 创建cglib 代理类 end */
   } */ 
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("这里是对目标类进行增强!!!");
        //注意这里的方法调用,不是用反射哦!!!
        Object object = proxy.invokeSuper(obj, args);
        return object;
    }  
}

测试类:

public class CglibTest {    
     public static void main(String[] args) {
        //在指定目录下生成动态代理类,我们可以反编译看一下里面到底是一些什么东西
        System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\java\\java_workapace");
        
        //创建Enhancer对象,类似于JDK动态代理的Proxy类,下一步就是设置几个参数
        Enhancer enhancer = new Enhancer();
        //设置目标类的字节码文件
        enhancer.setSuperclass(Dog.class);
        //设置回调函数
        enhancer.setCallback(new MyMethodInterceptor());
        //这里的creat方法就是正式创建代理类
        Dog proxyDog = (Dog)enhancer.create();
        //调用代理类的eat方法
        proxyDog.eat();       
    }
}

Cglib 总结

  • CGlib可以传入接口也可以传入普通的类,接口使用实现的方式,普通类使用会使用继承的方式生成代理类.
  • 由于是继承方式,如果是 static方法,private方法,final方法等描述的方法是不能被代理的
  • 做了方法访问优化,使用建立方法索引的方式避免了传统JDK动态代理需要通过Method方法反射调用.
  • 提供callback 和filter设计,可以灵活地给不同的方法绑定不同的callback。编码更方便灵活。
  • CGLIB会默认代理Object中equals,toString,hashCode,clone等方法。比JDK代理多了clone。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值