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 extends Annotation> T getAnnotation(Class<T> 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 被忽略,可以为 null ;args 可以为 null ,也可以为空数组
-
int getModifiers()
返回方法的修饰符 -
Class<?>[] getParameterTypes()
返回方法的参数类型 -
Class<?> getReturnType()
返回方法的返回值类型 -
Class<?>[] getExceptionTypes()
返回方法声明抛出的异常类型 -
Annotation[] getDeclaredAnnotations()
获取方法的注解信息 -
<T extends Annotation> T getAnnotation(Class<T> annotationClass)
获取方法指定类型的注解信息 -
Annotation[][] getParameterAnnotations()
获取方法参数的注解信息
创建对象
使用Class
的T newInstance()
方法创建对象
该方法会调用类的默认构造方法(即无参公共构造方法),如果类没有该构造方法,会抛异常
获取构造方法的方法
-
Constructor<?>[] getConstructors()
返回所有公共的构造方法 -
Constructor<?>[] getDeclaredConstructors()
返回所有构造方法,包括非公共的 -
Constructor<T> getConstructor(Class<?>... parameterTypes)
返回指定参数类型的公共构造方法 -
Constructor<T> 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<? super T> 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<Class<T>>[] 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 extends Annotation> T getAnnotation(Class<T> annotationClass)
返回特定类型的注解,没有返回 null -
boolean isAnnotationPresent(Class<? extends Annotation> annotationClass)
判断是否有指定类型的注解
Annotation
注解本身是一个接口
所有的注解类型,内部实现都是扩展Annotation
动态代理
Java SDK动态代理
使用Proxy
的newProxyInstance
方法创建代理对象
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动态代理
基本原理
动态生成一个类,继承被代理的类,代理类重写了父类的所有 public 非 final 的方法
对比
Java SDK动态代理面向的是一组 接口 ,只能为接口创建代理,返回的代理对象也只能转换到某个接口类型,无法代理一个没有接口的类,也无法代理非接口中定义的方法;而cglib则是面向一个具体的 类 ,动态创建新类,继承被代理类,重写其方法
类加载机制
类加载的基本机制和过程
Java运行时,会根据类的完全限定名寻找并加载类,寻找的方式基本就是在系统类和指定的类路径中寻找
- 如果是 class 文件的根目录,则直接查看是否有对应的子目录及文件
- 如果是 jar 文件,则首先在内存中解压文件,然后再查看是否有对应的类
类加载器
负责加载类的类,输入完全限定的类名,输出Class
对象
一般程序运行时,都会有三个
-
Bootstrap ClassLoader
启动类加载器这个加载器是Java虚拟机实现的一部分,不是Java语言实现的,一般是C++实现,它负责加载Java的基础类,主要是 <JAVA_HOME>/lib/rt.jar
-
Extension ClassLoader
扩展类加载器这个加载器的实现类是
sun.misc.Launcher$ExtClassLoader
,它负责加载Java的一些扩展类,一般是 <JAVA_HOME>/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)
用于加载类
-
对比
使用ClassLoader
的loadClass
方法和Class
的forName
方法都可以加载类,但ClassLoader
的loadClass
方法不会执行类的初始化代码
应用
- 实现隔离
- 实现热部署