Android面试

Java反射机制和动态代理

2019-07-23  本文已影响0人  小窦子

前言:

反射机制是java的高级特性之一,而且也是基础,那么反射有什么应用场景呢,当然平常写业务逻辑开发一般是用不到的,它的应用是在框架里面,所以看源码,学习框架不懂这个代码都看不懂,所以还是要掌握它。

反射是什么?

java反射是在运行状态下,对于任意一个类,都能知道这个类的所有属性和方法,对于任意一个对象,都能调用它的任意一个方法和属性,这种动态获取信息以及动态调用队形的功能成为java语言的反射机制。

Class

类的结构信息组成

反射的使用

获取到java中要反射类的字节码有三种方法
  1. 任意一个类都有一个隐士的静态成员变量class
    Class c=Statudent.class;
    
  2. 通过getClass方法获得
    Class c2=mStatudent.getClass();
    
  3. 通过Class.forname();
    Class c=Class.forName("包名+类名");
    
得到Class对象 我们就可以获取这个类中任意一个方法或者属性
  1. 获取该类中指定的属性
    Class<?> studentClass=student.getClass();
    Fiele ageField=studentClass.getDeclaredField("age);
    ageField.setAccessible(true);
    System.out.println("student的年龄"+student.getAge());                               
    
  2. 获取类中所有的字段包括父类的
    Class<?> studentClass=student.getClass();
    Field[] fieldArray=studentClass.getFields();
    for(int i=-;i<fieldArray.length();i++){
        System.out.println("当前方法名字是"+fieldArray[i]);
    }
    
  3. 获取该类所有的字段不包括父类的
    Class<?> studentClass=student.getClass();
    Field[] fieldArray=studentClass.getDeclaredFields();//主要是这个方法的区别
    for(int i=-;i<fieldArray.length();i++){
        System.out.println("当前方法名字是"+fieldArray[i]);
    }
    
  4. 修改非静态的方法和属性需要一个要修改的类的对象
    Student student=new Student(20);
    Class<?> studentClass=student.getClass();
    Field ageField=studentClass.getDeclaredField("age);
    ageField.setAccessible(trye);
    ageField.set(student,30);
    System.out.println("当前学生通过反射修改后的年龄是"+student.getAge());
    

    上面需要注意的是如果字段是非public的 需要在访问该字段之前设置setAccessible(true),那么对于一个final字段是否可以通过反射修改它的值呢,答案是肯定的,前提是在访问该字段之前要取消该字段的访问权限,但是如果该字段即被static修饰又被final修饰,那么是无法修改的。

  5. 修改静态的字段

    和修改非静态字段相比,修改类的静态字段就要轻松的多,因为静态字段是属于类所有的,所以在修改静态字段的时候就不需要在传递一个该类的实例对象了

    Field nameField=studentClass.getDeclaredField("name);
    nameField.setAccessible(true);
    nameField.set(null,"demo");
    System.out.println("反射修改静态字段  修改的名字是"+nameField.get(null));
    
通过反射获取并且调用类中的方法

Class类提供了4中方式获取类中的方法,其实和获取Field一样

  1. getMethod(String name,Class[] params) 使用特定的参数类型,获得name名字相同的公共方法
  2. Method[] getMethods() 获取类的所有公共方法 有父类的
  3. Method getDeclaredMethod(String name,Class[] params);可以获取私有的方法通过特定的参数类型。
  4. Method[] getDeclaredMethods() 获得类声明的所有方法 不包括父类的

和前面设置属性一样,method 有invoke(Object obj,Object ...args)这个方法,通过这个方法,可以调用任意一个类的任意一个方法,当然 如果要是调用的静态方法,那么第一个参数就直接填null。

反射机制的优缺点

代理

代理是一种常见的设计模式,其目的就是为其他对象提供一个代理以控制对某个对象的访问,代理类负责委托类预处理,以及进行被委托类执行后的处理。

如果需要委托类来处理某一个业务,那么我就可以先在代理类中统一处理然后在调用具体实现类。

分类:
  1. 静态代理:由开发人员创建代理类,在对其进行编译,代理类内部是持有真实类的对象的。在程序运行钱代理类的.class文件就已经存在了
  2. 动态代理:在程序运行时使用反射机制动态创建一个代理类。根据jdk的源码可以看到
动态代理:
//先定义接口
interface Subject{
    void say(String name);
}

//这是真实处理对象
class RealSubject implements Subject{
    
    public void say(String name){
        System.out.println("我的姓名是"+name);
    }
}
//下面是一个动态代理类,

class CustomInvocationHandler implements Invocationhandler{
    
    Object  subObj;
    public CustomInvocationHandler(Object obj){
        this.subObj=obj;
    }
    
    public Object getProxyInstance(){
        rerutn Proxy.newProxyInstance(subObj.getClass.getClassLoader(),subObj.getClass.getInterfaces,this);
    }
    
    public Object invoke(Object proxy,Method method,Object[] args){
       return method.invoke(subObj,args);
    }
}


//   下面开始使用动态代理类
RealSubject realObj=new RealSubject();
CustomInvocationHandler mInvocation=new CustomInvocationHandler(realObj);
Subject proxyObj=mInvocation.getProxyInstance();//得到代理对象 转成 
proxyObj.say();

jdk中生成动态代理的源码:

ProxyUtils.generateClassFile(aafactory.getClass(),employee1.getClass().getSimpleName();ProxyUtils.generateClassFile(bbToolsFactory.getClass(),employee2.getClass().getSimpleName());
Method[] methods = aafactory.getClass().getMethods();
for(Method method:methods) {
  System.out.println(method.getName());//打印方法名称
 }
public class ProxyUtils {

    public static void generateClassFile(Class clazz,String proxyName){
        /*ProxyGenerator.generateProxyClass(
                proxyName, interfaces, accessFlags);*/
        byte[] proxyClassFile =ProxyGenerator.generateProxyClass(
                proxyName, new Class[]{clazz});
        String paths = clazz.getResource(".").getPath();
        System.out.println(paths);
        FileOutputStream out = null;

        try {
            out = new FileOutputStream(paths+proxyName+".class");
            out.write(proxyClassFile);
            out.flush();
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            try {
                out.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

上一篇 下一篇

猜你喜欢

热点阅读