Java反射知识梳理

概念

能够分析类能力的程序称为反射。

用处

1、在运行时分析类的能力。
2、实现动态代理
3、读取配置

通过反射我们可以知道在运行时得到类的相关信息进行分析。

原理

Java反射原理
在这里插入图片描述

Class类

在程序运行期间,Java运行时系统始终为所有对象维护一个运行时类型标识。这个信息会跟踪每个对象所属的类。虚拟机利用运行时类型信息选择要执行的正确方法。不过,可以使用一个特殊的Java类访问这些信息。保存这些信息的类名为Class。

获取Class类的三种方法

1.Class.forName(全类名)
2.对象.getClass()
3.类名.class

API

static Class forName(String className)
返回一个Class对象,表示名为className的类。

Constructor getConstructor(Class … parameterTypes)
生成一个对象,描述有指定参数类型的构造器。

Field[] getFields()
Field[] getDeclaredFields()
getFields返回一个包含Field对象的数组,包含这个类或其超类的公共字段。getDeclaredFields返回这个类的全部字段。

Constructor [] getConstructors()
Constructor [] getDeclaredConstructors()
getConstructors返回一个包含Constructor 对象的数组,包含这个类或其超类的公共构造方法。getDeclaredConstructors返回这个类的全部构造方法。

Method[] getMethods()
Method[] getDeclaredMethods()
getMethods返回一个包含Method对象的数组,包含这个类或其超类的公共方法。getDeclaredMethods返回这个类的全部方法。

反射机制最重要的内容----检查类的结构。共有三个类,Field、Method、Constructor。

Constructor 类的构造方法
API

Object newInstance(Object… params)
将params传递到构造器,来构造这个构造器声明类的一个新实例

Constructor 类的构造方法、 Field 字段、Method 方法
API

Class getDeclaredClass()
返回一个Class对象,表示定义了这个构造器、方法或字段的类。

Class[] getExceptionTypes()(在Construcotr和Method classes类中)
返回一个Class对象数组,表示抛出异常的类型。

int getModifiers()
返回一个整数,描述这个构造器、方法和字段的修饰符。使用Modifier类中的方法分析这个返回值。

String getName()
返回名称

Class[] getParameterTypes()(在Construcotr和Method classes类中)
返回一个Class对象数组,其中各个对象表示参数的类型。

Class getReturnType() (在Method类中)
表示返回值的类型

Void setAccessible(boolean f)
设置访问权限

Java动态代理

静态代理需要为每个被代理类创建一个代理类,因此被代理类必须事先定义好。而动态代理可以代理任意实现了接口的类。

JDK动态代理只能对实现了接口的类进行代理。(为什么后文会讲)

动态代理过程

首先定义一个接口

public interface Calculator {
 
    int add(int a, int b);
    int sub(int a, int b);
    int mul(int a, int b);
    int div(int a, int b);
}
 

实现类

public class CalculatorImpl implements Calculator {
 
    @Override
    public int add(int a, int b) {
        System.out.println(a+b);
        return a+b;
    }
 
    @Override
    public int sub(int a, int b) {
        return a-b;
    }
 
    @Override
    public int mul(int a, int b) {
        return a*b;
    }
 
    @Override
    public int div(int a, int b) {
        return a/b;
    }
 
}
 

我们首先定义一个类实现InvocationHandler接口,将要代理的对象通过构造方法传入,并实现invoke方法。

public class MyProxyHandler implements InvocationHandler {
 
    //要代理的对象
    private Calculator target;
 
    public MyProxyHandler(Calculator h) {
        this.target = h;
    }
 
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //获取参数
        System.out.println("beginWith---方法的参数是--" + Arrays.asList(args));
 
        before();
        Object result = method.invoke(target,args);
        after();
        return result;
    }
 
    /**
     * 前置
     */
    public void before() {
        System.out.println("before---");
    }
 
    /**
     * 后置
     */
    public void after() {
        System.out.println("after---");
    }
}

test:

/**
 * jdk动态代理测试
 *
 */
public class Test {
 
    public static void main(String[] args) {
       Calculator target = new CalculatorImpl();
       Calculator proxy = (Calculator) Proxy.newProxyInstance(Calculator.class.getClassLoader(),
               new Class<?>[]{Calculator.class},
               new MyProxyHandler(target));
 
       proxy.add(1, 2);
    }
}

经过动态代理最终得到的代理类是下面这样的。

package com.sun.proxy;
 
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
import me.mingshan.dy.Calculator;
 
public final class $Proxy0 extends Proxy implements Calculator {
 
   private static Method m1;
   private static Method m2;
   private static Method m5;
   private static Method m3;
   private static Method m4;
   private static Method m6;
   private static Method m0;
 
 
   public $Proxy0(InvocationHandler var1) throws  {
      super(var1);
   }
 
   public final boolean equals(Object var1) throws  {
      try {
         return ((Boolean)super.h.invoke(this, m1, new Object[]{var1})).booleanValue();
      } catch (RuntimeException | Error var3) {
         throw var3;
      } catch (Throwable var4) {
         throw new UndeclaredThrowableException(var4);
      }
   }
 
   public final String toString() throws  {
      try {
         return (String)super.h.invoke(this, m2, (Object[])null);
      } catch (RuntimeException | Error var2) {
         throw var2;
      } catch (Throwable var3) {
         throw new UndeclaredThrowableException(var3);
      }
   }
 
   public final int mul(int var1, int var2) throws  {
      try {
         return ((Integer)super.h.invoke(this, m5, new Object[]{Integer.valueOf(var1), Integer.valueOf(var2)})).intValue();
      } catch (RuntimeException | Error var4) {
         throw var4;
      } catch (Throwable var5) {
         throw new UndeclaredThrowableException(var5);
      }
   }
 
   public final int add(int var1, int var2) throws  {
      try {
         return ((Integer)super.h.invoke(this, m3, new Object[]{Integer.valueOf(var1), Integer.valueOf(var2)})).intValue();
      } catch (RuntimeException | Error var4) {
         throw var4;
      } catch (Throwable var5) {
         throw new UndeclaredThrowableException(var5);
      }
   }
 
   public final int sub(int var1, int var2) throws  {
      try {
         return ((Integer)super.h.invoke(this, m4, new Object[]{Integer.valueOf(var1), Integer.valueOf(var2)})).intValue();
      } catch (RuntimeException | Error var4) {
         throw var4;
      } catch (Throwable var5) {
         throw new UndeclaredThrowableException(var5);
      }
   }
 
   public final int div(int var1, int var2) throws  {
      try {
         return ((Integer)super.h.invoke(this, m6, new Object[]{Integer.valueOf(var1), Integer.valueOf(var2)})).intValue();
      } catch (RuntimeException | Error var4) {
         throw var4;
      } catch (Throwable var5) {
         throw new UndeclaredThrowableException(var5);
      }
   }
 
   public final int hashCode() throws  {
      try {
         return ((Integer)super.h.invoke(this, m0, (Object[])null)).intValue();
      } catch (RuntimeException | Error var2) {
         throw var2;
      } catch (Throwable var3) {
         throw new UndeclaredThrowableException(var3);
      }
   }
 
   static {
      try {
         m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[]{Class.forName("java.lang.Object")});
         m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
         m5 = Class.forName("me.mingshan.dy.Calculator").getMethod("mul", new Class[]{Integer.TYPE, Integer.TYPE});
         m3 = Class.forName("me.mingshan.dy.Calculator").getMethod("add", new Class[]{Integer.TYPE, Integer.TYPE});
         m4 = Class.forName("me.mingshan.dy.Calculator").getMethod("sub", new Class[]{Integer.TYPE, Integer.TYPE});
         m6 = Class.forName("me.mingshan.dy.Calculator").getMethod("div", new Class[]{Integer.TYPE, Integer.TYPE});
         m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
      } catch (NoSuchMethodException var2) {
         throw new NoSuchMethodError(var2.getMessage());
      } catch (ClassNotFoundException var3) {
         throw new NoClassDefFoundError(var3.getMessage());
      }
   }
}

可以看到代理后生成的对象包含代理对象的方向,并使用自定义invoke方法进行了增强。原理就是使用反射获取代理对象的方法进行调用增强。那么为什么JDK动态代理必须要实现接口?
首先我们得知道为什么有动态代理?他和静态代理有什么区别?

动态代理和静态代理的区别

1.静态代理只能代理一个确定的类,动态代理可以代理一个接口下的多个类。
2.静态代理必须知道代理哪一个类,在程序运行前就已经完成代理。动态代理是在运行时才知道代理什么类。
所以动态代理的出现是因为静态代理每次只能代理一个类,在需要对多个类同时进行代理时就会出现很多重复开发,动态代理就是为了实现多个类的代理出现的。

JDK动态代理为什么只能代理实现了接口的类

1.动态代理是为了实现对多个类或者说多个方法的代理,且代理对象是运行时生成的,我们编写代码时并不知道代理对象是什么,所以我们只能用被代理对象来接收。
2.被代理对象能被接收,在Java中有两种方法实现,一种是继承,一种是实现接口。
3.Java中只能单继承。JDK动态 代理自动生成对的类$Proxy0继承了java.lang.reflect.Proxy类,由于java是单继承的,所以这里没有机会再去继承被代理类。
4.性能问题,反射的性能很差。那么假如java.lang.reflect.Proxy设计为接口,那么就能对类进行继承实现动态代理了。但是这样就得反射两次了,继承被代理对象后,首先要反射获得前置增强逻辑,然后super调用原逻辑,再反射获得后置增强逻辑。

  • 27
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值