Java笔记之动态特性

本笔记来自 计算机程序的思维逻辑 系列文章

反射

Class

Class是一个泛型类,有一个类型参数

  • 所有类的根父类Object有一个 Class getClass()方法,可以获取对象的Class对象
  • 如果类名已知,可以使用<类名>.class获取Class对象
  • 可以根据类名直接加载Class,获取Class对象:Class.forName(xxx)

名称信息

  • String getName() 返回Java内部使用的真正的名字,带包信息
  • String getSimpleName() 返回不带包的名字
  • String getCanonicalName() 返回标准名字
  • Package getPackage() 返回包信息
数组元素类型对应表
元素类型 字符表示
类或接口 L
byte B
boolean Z
char C
int I
float F
long J
double D
short S

字段信息

类中定义的静态变量和实例变量都被称为 字段 ,用类Field表示

获取字段信息的方法
  • Field[] getFields() 返回所有的公共字段,包括其父类的
  • Field[] getDeclaredFields() 返回本类声明的所有字段,包括非公共的,但不包括父类的
  • Field getField(String name) 返回本类或父类中指定名称的公共字段
  • Field getDeclaredField(String name) 返回本类中声明的指定名称的字段
Field
  • String getName() 返回字段名称
  • boolean isAccessible() 判断当前程序是否有该字段的访问权限
  • void setAccessible(boolean flag) 修改访问权限,忽略Java的访问检查机制,以允许读写非公共的字段
  • Object get(Object obj) 获取指定对象中该字段的值
  • void set(Object obj, Object value) 设置指定对象中该字段的值
  • int getModifiers() 返回字段的修饰符
  • Class getType() 返回字段的类型
  • T getAnnotation(Class annotationClass) 返回字段指定类型的注解信息
  • Annotation[] getDeclaredAnnotations() 获取字段的注解信息

方法信息

类中定义的静态方法和实例方法都称为 方法 ,用类Method表示

获取方法信息的方法
  • Method[] getMethods() 返回所有的公共方法,包括其父类的
  • Method[] getDeclaredMethods() 返回本类声明的所有方法,包括非公共的,但不包括父类的
  • Method getMethod(String name, Class... parameterTypes) 返回本类或父类中指定名称和参数类型的公共方法
  • Method getDeclaredMethod(String name, Class... parameterTypes) 返回本类中声明的指定名称和参数类型的方法
Method
  • String getName() 返回方法名称

  • boolean isAccessible() 判断当前程序是否有该方法的访问权限

  • void setAccessible(boolean flag) 修改访问权限,忽略Java的访问检查机制,以允许调用非公共的方法

  • Object invoke(Object obj, Object... args) 调用指定对象中该方法

    如果是静态方法,obj 被忽略,可以为 nullargs 可以为 null ,也可以为空数组

  • int getModifiers() 返回方法的修饰符

  • Class[] getParameterTypes() 返回方法的参数类型

  • Class getReturnType() 返回方法的返回值类型

  • Class[] getExceptionTypes() 返回方法声明抛出的异常类型

  • Annotation[] getDeclaredAnnotations() 获取方法的注解信息

  • T getAnnotation(Class annotationClass) 获取方法指定类型的注解信息

  • Annotation[][] getParameterAnnotations() 获取方法参数的注解信息

创建对象

使用ClassT newInstance()方法创建对象

该方法会调用类的默认构造方法(即无参公共构造方法),如果类没有该构造方法,会抛异常

获取构造方法的方法
  • Constructor[] getConstructors() 返回所有公共的构造方法
  • Constructor[] getDeclaredConstructors() 返回所有构造方法,包括非公共的
  • Constructor getConstructor(Class... parameterTypes) 返回指定参数类型的公共构造方法
  • Constructor getDeclaredConstructor(Class... parameterTypes) 返回指定参数类型的构造方法,包括非公共的
Constructor
  • T newInstance(Object ... initargs) 创建对象
  • Class[] getParameterTypes() 返回参数类型
  • int getModifiers() 返回修饰符
  • Annotation[][] getParameterAnnotations() 返回参数的注解信息

类型检查和转换

  • boolean isInstance(Object obj) 判断类型
  • T cast(Object obj) 转换类型
  • boolean isAssignableFrom(Class cls) 检查参数类型能否赋给当前对象

类型信息

  • boolean isPrimitive() 是否基本类型
  • boolean isArray() 是否数组
  • boolean isInterface() 是否接口
  • boolean isEnum() 是否枚举
  • boolean isAnnotation() 是否注解
  • boolean isAnonymousClass() 是否匿名内部类
  • boolean isLocalClass() 是否本地类,即定义在方法内的类
  • boolean isMemberClass() 是否成员类

声明信息

  • int getModifiers() 返回类的修饰符
  • Class getSuperclass() 返回父类
  • Class[] getInterfaces() 返回类所声明实现的所有接口,或接口所直接扩展的接口

内部类

  • Class[] getClasses() 返回所有公共的内部类和接口,包括从父类继承的
  • Class[] getDeclaredClasses() 返回本类声明的所有内部类和接口
  • Class getDeclaringClass() 如果当前类为内部类,返回声明该类的最外部的类
  • Class getEnclosingClass() 如果当前类为内部类,返回直接包含该类的类
  • Method getEnclosingMethod() 如果当前类为本地类或匿名内部类,返回包含它的方法

数组

Class getComponentType() 获取数组元素类型

Array
  • Object newInstance(Class componentType, int length) 创建指定元素类型,指定长度的数组
  • Object newInstance(Class componentType, int... dimensions) 创建多维数组
  • int getLength(Object array) 返回数组长度
  • Object get(Object array, int index) 返回指定数组指定索引位置的值
  • void set(Object array, int index, Object value) 设置指定数组指定索引位置的值

数组使用Object而非Object[]表示,是为了方便处理多种类型的数组

枚举

T[] getEnumConstants() 返回枚举常量

泛型

Class

TypeVariable>[] getTypeParameters() 返回类的泛型信息

Field

Type getGenericType() 返回字段的泛型信息

Method
  • Type getGenericReturnType() 返回方法返回值的泛型信息
  • Type[] getGenericParameterTypes() 返回方法参数的泛型信息
  • Type[] getGenericExceptionTypes() 返回方法声明异常的泛型信息
Constructor

Type[] getGenericParameterTypes() 返回构造方法参数的泛型信息

Type

接口

子接口有

  • TypeVariable

    类型参数,可以有上界

  • ParameterizedType

    参数化的类型

    • Type getRawType() 返回原始类型
    • Type[] getActualTypeArguments() 返回具体的类型参数
  • WildcardType

    通配符类型

注解

创建

@interface

定义注解

@Target

元注解,表示注解的目标;目标可以有多个,用 {} 表示

类型为ElementType,可选值有:

  • TYPE 表示类、接口(包括注解),或者枚举声明
  • FIELD 字段,包括枚举常量
  • METHOD 方法
  • PARAMETER 方法中的参数
  • CONSTRUCTOR 构造方法
  • LOCAL_VARIABLE 本地变量
  • ANNOTATION_TYPE 注解类型
  • PACKAGE
@Retention

表示注解信息保留到什么时候,取值只能有一个

类型为RetentionPolicy,可选值有:

  • SOURCE 只在源代码保留,编译器将代码编译为字节码文件后就会丢失
  • CLASS 保留到字节码文件中,但Java虚拟机将 class 文件加载到内存时不一定会在内存中保留
  • RUNTIME 一直保留到运行时
参数

定义的方式是在注解内定义一些方法

参数定义时可以使用 default 指定一个默认值

参数的值不能为 null

当只有一个参数,且名称为 value 时,提供参数值时可以省略 value=

注解内合法的参数类型有

  • 基本类型
  • 字符串
  • 枚举
  • 注解
  • 以上类型的数组
@Inherited

注解不能继承

在注解上声明该元注解,当父类声明了该注解,子类自动拥有该注解

查看注解信息

  • Annotation[] getAnnotations() 返回所有注解
  • Annotation[] getDeclaredAnnotations() 返回本元素所有声明的注解
  • T getAnnotation(Class annotationClass) 返回特定类型的注解,没有返回 null
  • boolean isAnnotationPresent(Class annotationClass) 判断是否有指定类型的注解

Annotation

注解本身是一个接口

所有的注解类型,内部实现都是扩展Annotation

动态代理

Java SDK动态代理

使用ProxynewProxyInstance方法创建代理对象

Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)

  • loader 类加载器

  • interfaces 代理类要实现的接口列表,数组

    元素类型只能是接口,不能是普通的类

  • h 接口,对代理接口所有方法的调用都会转发给其invoke方法

InvocationHandler

处理代理类和被代理对象之间的关系和调用实现

Object invoke(Object proxy, Method method, Object[] args)

  • proxy 代理对象本身,用处不大
  • method 被调用的方法
  • args 方法参数
基本原理

动态生成代理类,继承Proxy,动态创建每个接口的实现代码,即转发给InvocationHandler处理

查看动态生成的代理类文件
java -Dsun.misc.ProxyGenerator.saveGeneratedFiles=true <包含main方法的完整类名>

cglib动态代理

基本原理

动态生成一个类,继承被代理的类,代理类重写了父类的所有 publicfinal 的方法

对比

Java SDK动态代理面向的是一组 接口 ,只能为接口创建代理,返回的代理对象也只能转换到某个接口类型,无法代理一个没有接口的类,也无法代理非接口中定义的方法;而cglib则是面向一个具体的 ,动态创建新类,继承被代理类,重写其方法

类加载机制

类加载的基本机制和过程

Java运行时,会根据类的完全限定名寻找并加载类,寻找的方式基本就是在系统类和指定的类路径中寻找

  • 如果是 class 文件的根目录,则直接查看是否有对应的子目录及文件
  • 如果是 jar 文件,则首先在内存中解压文件,然后再查看是否有对应的类
类加载器

负责加载类的类,输入完全限定的类名,输出Class对象

一般程序运行时,都会有三个

  • Bootstrap ClassLoader 启动类加载器

    这个加载器是Java虚拟机实现的一部分,不是Java语言实现的,一般是C++实现,它负责加载Java的基础类,主要是 /lib/rt.jar

  • Extension ClassLoader 扩展类加载器

    这个加载器的实现类是sun.misc.Launcher$ExtClassLoader,它负责加载Java的一些扩展类,一般是 /lib/ext 目录中的jar包

  • Application ClassLoader 应用程序类加载器

    这个加载器的实现类是sun.misc.Launcher$AppClassLoader,它负责加载应用程序的类,包括自己写的和引入的第三方类库,即所有在类路径中指定的类

基本过程

这三个类加载器有父子委派关系,子加载器有一个变量 parent 指向父加载器,在子加载器加载时,一般会首先通过父加载器加载

加载一个类时,基本过程:

  • 判断是否已经加载过了,加载过了,直接返回Class对象,一个类只会被一个ClassLoader加载一次
  • 如果没有被加载,先让父加载器去加载,如果加载成功,返回得到的Class对象
  • 在父加载器没有加载成功的前提下,自己尝试加载类

这个过程一般被称为 双亲委派 模型,即优先让父加载器去加载

一个程序运行时,会创建一个Application ClassLoader,在程序中用到ClassLoader的地方,如果没有指定,一般用的都是它,所以,它也被称为 系统类加载器

ClassLoader

方法
  • Class
    • ClassLoader getClassLoader() 返回实际加载它的加载器
  • ClassLoader
    • ClassLoader getParent() 返回它的父加载器
    • ClassLoader getSystemClassLoader() 返回默认的系统类加载器
    • Class loadClass(String name) 用于加载类
对比

使用ClassLoaderloadClass方法和ClassforName方法都可以加载类,但ClassLoaderloadClass方法不会执行类的初始化代码

应用

  • 实现隔离
  • 实现热部署

你可能感兴趣的:(Java笔记之动态特性)