Java基础之反射、注解、代理

反射

笔者对反射的理解就是解剖class文件,来进行一系列操作。

Class类

获取Class类实例的三种方式:

  1. 类名.class
  2. 对象.getClass()
  3. static Class forName(String className)根据类的名称获取类的Class对象(这里的className必须是类的全名)

一般使用第三种方式获取Class对象。

常用成员方法:

  • Field getField(String name)获取类中的成员变量的对象
  • String getName() 获取类的全名:包名.类名
  • String getSimpleName() 获取类的简称:类名
  • String getPackageName() 获取包名
  • Field getField(String name)获取公共成员变量对象
  • Method getMethod(String name, Class<?>... parameterTypes)获取类中的公共成员函数的对象
  • Constructor<T> getConstructor(Class<?>... parameterTypes)获取类中的公共构造函数的对象
  • newInstance()获取该类的实例(JDK 9已过期,使用clazz.getDeclaredConstructor().newInstance() 实例化)

静态:

  • forName(String className)加载类

其中成员变量、成员函数、构造函数要素过多,使用了独立的类来描述:

描述
Field 成员变量
Method 成员方法
Constructor 构造方法

预定义对象:八中基本类型以及void,其class对象就是其本身

Constructor、Method、Field

Constructor

  • newInstance(Object... initargs)获取实例
  • setAccessible(boolean)是否可以暴力访问,即越过私有权限的检验

setAccessible(boolean)是其父类的方法,Field、Method、Constructor都可以设置暴力访问私有方法/变量。

Method

  • Object invoke(Object obj, Object... args)调用方法,返回值为该方法的返回值(void方法返回null),第一个参数为调用的对象,第二个参数为方法的参数

类加载器

常见的类加载器有三种,每个类加载器负责加载不同位置的类:
加载器 | 说明
| - | - |
Bootstrap 根类加载器 | Bootstrap是最顶级的类加载器。它加载类文件不是我们自己书写的,负责Java核心类的,比如System,String等。只有所有类加载到内存中,我们才可以使用。(Bootstrap自动加载)
ExtClassLoader 扩展类加载器 | ExtClassLoader 扩展类加载器,加载的是扩展类的,我们是用不到的,都是jdk内部自己使用的。(ExtClassLoader类被BootStrap加载)
AppClassLoader 系统/应用类加载器 | AppClassLoader 系统/应用类加载器,是用来加载ClassPath 指定的所有jar或目录,ClassPath表示存放类路径的,我们如果不配置ClassPath,那么就表示当前文件夹,在idea环境下的ClassPath是out目录。在out目录存放的都是我们书写好的class文件,也就是说 AppClassLoader 类加载器是用来加载我们书写的out目录下的class文件。(AppClassLoader类被ExtClassLoader加载)

默认相对目录(文件路径)为相对项目目录,加载器相对目录为src

注解

JDK 常用注解:

注解 用途
@Override 重写
@SuppressWarnings() 抑制警告
@FunctionalInterface 函数式接口

@SuppressWarnings()参数关键字列表:

参数 描述
all to suppress all warnings (抑制所有警告)
boxing to suppress warnings relative to boxing/unboxing operations(抑制装箱、拆箱操作时候的警告)
cast to suppress warnings relative to cast operations (抑制映射相关的警告)
dep-ann to suppress warnings relative to deprecated annotation(抑制启用注释的警告)
deprecation to suppress warnings relative to deprecation(抑制过期方法警告)
fallthrough to suppress warnings relative to missing breaks in switch statements(抑制确在switch中缺失breaks的警告)
finally to suppress warnings relative to finally block that don’t return (抑制finally模块没有返回的警告)
hiding to suppress warnings relative to locals that hide variable()
incomplete-switch to suppress warnings relative to missing entries in a switch statement (enum case)(忽略没有完整的switch语句)
nls to suppress warnings relative to non-nls string literals(忽略非nls格式的字符)
null to suppress warnings relative to null analysis(忽略对null的操作)
rawtypes to suppress warnings relative to un-specific types when using generics on class params(使用generics时忽略没有指定相应的类型)
restriction to suppress warnings relative to usage of discouraged or forbidden references
serial to suppress warnings relative to missing serialVersionUID field for a serializable class(忽略在serializable类中没有声明serialVersionUID变量)
static-access to suppress warnings relative to incorrect static access(抑制不正确的静态访问方式警告)
synthetic-access to suppress warnings relative to unoptimized access from inner classes(抑制子类没有按最优方法访问内部类的警告)
unchecked to suppress warnings relative to unchecked operations(抑制没有进行类型检查操作的警告)
unqualified-field-access to suppress warnings relative to field access unqualified (抑制没有权限访问的域的警告)
unused to suppress warnings relative to unused code (抑制没被使用过的代码的警告)

自定义注解

声明注解
自定义注解使用@interface声明
其内可以有属性:public abstract 属性类型 属性名() default 属性的默认值;
注解中public abstract可以省略,并且属性类型只能是是:基本类型StringClass注解,以及以上类型的一维数组,属性可以没有默认值。
使用注解
注解可以没有属性,如果有属性需要在括号内赋值,有默认值可以不赋值。
相同注解在同一方法只能使用一次。

解析自定义注解

元注解
元注解是用来标记注解的注解。
一共有四种:

  1. @Target 用于确定被修饰的注解使用的位置(主要)
  2. @Retention 用于确定被修饰的自定义注解生命周期(主要)
  3. @Documented 将此注解包含在Javadoc中
  4. @Inherited 允许子类继承父类中的注解
  • @Target:表示该注解可以用于什么地方。可能的ElementType参数包括:
    1. CONSTRUCTOR:构造器的生命
    2. FIELD:域声明(包括enum实例)
    3. LOCAL_VARIABLE:局部变量声明
    4. METHOD:方法声明
    5. PACKAGE:包声明
    6. PARAMETER:参数声明
    7. TYPE:类、接口(包括注解类型)和enum声明
    8. ANNOTATION_TYPE:注解声明(与TYPE的区别?专门用在注解上的TYPE)
    9. TYPE_PARAMETER:Java8
    10. TYPE_USE:Java8
  • @Retention:
    表示需要在什么级别保存该注解信息。可选的RetentionPolicy参数包括:
    1. SOURCE:注解将在编译器丢弃
    2. CLASS:注解在class文件中可用,但会被VM丢弃
    3. RUNTIME:VM将在运行期也保留注解,因此可以通过反射机制读取注解的信息

可以通过反射拿到方法,然后拿到注解,通过拿到的注解便可以拿到注解中的值。

代理

静态代理

代理:代理对象主要用来拦截目标业务对象的访问

  1. 代理对象要持有真实业务对象。(在代理类中创建被代理类的对象。
  2. 代理对象要和真实业务对象,具备相同的行为方法。实现同一个接口。
  3. 代理对象拦截对真实对象的访问,可以修改访问的参数、返回值,甚至拦截访问。

静态代理使用较少。

动态代理

动态代理:就是在程序运行的过程中,动态的生成一个类,这个类要代理目标业务对象,并且可以动态生成这个代理类的对象。
在Java中当某个类需要被代理的时候,要求这个类中的被代理的方法必须抽取到一个接口中,然后这个类需要实现那个接口,只有在这个接口中的方法,代理类才能代理它。

Proxy类
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
获得代理类实例,参数:
ClassLoader loader类加载器,一般给被代理对象的类加载器即可
Class<?>[] interfaces被代理对象的所有的接口数组
InvocationHandler h调用处理器,是一个接口。

InvocationHandler接口有一个静态方法:public Object invoke(Object proxy, Method method, Object[] args)
第一个参数是代理本身,第二个参数是调用的方法,第三个参数是调用方法的参数(在方法中this是这个接口的实现类,proxy才是代理本身,通过返回代理本身可以实现链式调用,参考这里

上个栗子:

@Test
public void proxyTest() throws Exception {
    //获取自定义类的Class对象
    Class clazz = Class.forName("org.junit.sh.n1.Person");
    //获取该类的类加载器
    ClassLoader loader = clazz.getClassLoader();
    //获取接口数组
    Class[] interfaces = clazz.getInterfaces();
//    @SuppressWarnings("all")//clazz.newInstance()方法在JDK9中过期了,懒得改了,注解一下
    //实例化一个对象作为调用者
    Person p = (Person) clazz.newInstance();
    //使用Lambda实现InvocationHandler
    InvocationHandler h = (proxy, method, args) -> {
        if ("show".equals(method.getName())) {
            System.out.println("修改方法");
            return method.invoke(p, args);
        }
        return null;
    };
    //自定义类实现的接口
    Showable theProxy = (Showable) Proxy.newProxyInstance(loader, interfaces, h);
    //调用方法
    theProxy.show();
}

真正理解反射和注解还是得结合框架(。・∀・)ノ

猜你喜欢

转载自www.cnblogs.com/lixin-link/p/11116294.html