疯狂-反射
2017-08-14 22:34:43 59 举报
AI智能生成
疯狂java讲义,反射这一章内容,看开源框架,反射肯定有,需要系统的学习
作者其他创作
大纲/内容
类的加载、连接和初始化
JVM和类
当系统出现以下情况JVM将终止:<br>1,程序运行到最后正常结束<br>2,System.exit()或Runtime.getRuntime().exit()<br>3,程序执行中,遇到未捕获的异常或错误<br>4,程序所在平台强制结束JVM进程
类的加载
系统会通过加载、连接、初始化三个步骤对类进行初始化。<br>类加载: 将类的class文件读入内存,并创建一个java.lang.Class对象。
类的加载是由类加载器完成的,JVM提供的这些类加载器被叫做类加载器。开发者也可以继承ClassLoader基类来创建自己的类加载器。<br><br>可以从以下来源加载类的二进制数据:<br>1,从本地文件系统加载class文件。<br>2,从JAR包加载class文件<br>3,通过网络加载class文件<br>4,将java源文件动态编译,并执行加载<br><br>java虚拟机规范允许系统与现在加某些类,而并不一定等到“首次使用”时才加载
类的连接
当加载完类后,系统生成一个对应的Class文件,接着进入连接阶段,负责把类的二进制数据合并到JRE中。类连接分为如下三个阶段:<br>1,验证:校验加载的类是否有正确的结构,并和其他类协调一致<br>2,准备:负责为类的类变量分配内存,并设置默认初始值<br>3,解析:将类的二进制数据中的符号应用替换成直接引用
类的初始化
虚拟机负责对类进行初始化,主要就是对类变量进行初始化。在java类中对类变量指定初始值有两种方式:<br>1,申明类变量时指定初始值 <br>2,使用静态初始化块为类变量指定初始化值
JVM初始化一个类包含如下几个步骤<br>1,如果还没加载该类,则程序先加载并连接该类<br>2,如果该类的直接父类还没有被初始化,则先初始化其直接父类<br>3,如果该类中有初始化语句,则系统依次执行这些初始化语句
类初始化的时机
当首次通过下面的方式来使用某个类或接口时,系统就会初始化该类或接口<br>1,创建实例:new类、通过反射来创建、反序列化创建。<br>2,调用某个类的类方法(静态方法)<br>3,访问某个类或接口的类变量,或为该类变量赋值。<br>4,通过反射方式创建类或接口的java.lang.Class对象。<br>5,初始化某个类的子类<br>6,用java.exe命令运行某个主类
ClassLoader#loadClass()只是加载某个类,并不会初始化该类<br>Class#forName()会初始化该类。
类加载器
类加载器简介
在JVM中,用类的全包名作为唯一标识
在JVM启动时,会有三个类加载器:<br>1,Bootstrap ClassLoader : 根类加载器<br>2,Extension ClassLoader : 扩展类加载器<br>3,System ClassLoader : 系统类加载器
Extension Classloader 被称为扩展类加载器,会加载扩展目录<br>(%JAVA_HOME%/jre/lib/ext 或由 java.ext.dirs系统属性指定的目录)<br><br>System Classloader系统类加载器,加载 java命令的 -classpath选项、<br>java.class.path系统属性或CLASSPATH环境变量所指定的JAR包和类路径
类加载机制
1,全盘负责:当加载某个class时,该class所依赖的和引用的其他class也将由该类加载器负责载入,除非显示指定另外一个类加载器来载入。<br>2,父类委托:先让parent类加载器试图加载该class,只有父加载器无法加载该类时,才尝试从自己的类路径中加载该类。<br>3,缓存机制:加载过的class都会被缓存,当需要使用某个class时,先从缓存中搜寻该class,只有当缓存不命中时,才会读取二进制数据。
类加载器加载class8个步骤:<br>1,检测该类是否加载过<br>2,<br>3,<br>4,<br>5,<br>6,<br>7,<br>8,
创建并使用自定义的类加载器
ClassLoader包含两个关键方法:<br>loadClass(String name, boolean resolve):根据指定名称加载类<br>findClass(String name):根据指定名称来查找类<br>
loadClass执行步骤:<br>1,用findLoadedClass()检查是否已经加载类,如果已经加载则直接返回<br>2,调用父加载器的loadClass()方法,如果父类加载器为null,则直接用bootStarp加载器加载<br>3,调用findClass()方法
Class defineClass(String name, byte[] b, int off, int len) : <br>该方法负责将指定的class文件读入到字节数组byte[] b内,并转换成class对象。该字节码可以是网络文件,本地文件。
findSystemClass(String name):从本地文件系统装入文件,如果存在,就使用defineClass()<br>方法转换成Class对象。<br>static getSystemClassLoader(): 返回系统类加载器<br>getParent():获取该类加载的父类加载器<br>resolveClass(Class<?> c):链接指定的类<br>findLoadedClass(String name):返回已经加载的类
URLClassLoader类
URLClassLoader(URL[] urls) : 使用默认的父类加载器创建一个ClassLoader对象<br>URLClassLoader(URL[] urls, ClassLoader parent) : 使用指定的父类加载器创建一个<br>ClassLoader对象
loadClass执行步骤:<br>1,用findLoadedClass()检查是否已经加载类,如果已经加载则直接返回<br>2,调用父加载器的loadClass()方法,如果父类加载器为null,则直接用bootStarp加载器加载<br>3,调用findClass()方法
Class defineClass(String name, byte[] b, int off, int len) : <br>该方法负责将指定的class文件读入到字节数组byte[] b内,并转换成class对象。该字节码可以是网络文件,本地文件。
通过反射查看类信息
java许多对象都有两种类型:编译时类型和运行时类型<br>例如:<br> Person p = new Student();<br>p的编译时类型是Person,运行时类型是Student<br><br>程序需要运行时发现对象和类的真实信息。一般有一下两种做法:<br>1,如果在编译和运行时都知道类型的具体信息,可以先使用instanceof判断下,在强转<br>2,编译时不知道该对象和类可能属于哪些类,只能依靠运行时信息来发现该对象和类的真实信息,这就需要反射
获得Class对象
获得Class对象的三种方法:<br>1,Class.forName( String clazzName) (必须是完整的包名)<br>2,类.class<br>3,对象.getClass()
从Class中获取信息
获取构造函数:<br><br>Constructor<T> getConstructor(Class<?>...parameterTypes):<br>返回参数对应的public构造函数<br><br>Constructor<?>[] getConstructors():获得所有的public构造器<br><br>Constructor<T> getDeclaredConstructor(Class<?>...parameterTypes):<br>返回参数对应的构造函数<br><br>Constructor<?>getDeclaredConstructors():返回所有的构造函数
获取函数:<br>Method getMethod(String name, Class<?>...parameterTypes):返回指定参数的public方法<br>Method[] getMethods(): 返回所有的public方法<br>Method getDeclaredMethod(String name, Class<?>...parameterTypes) : 获取指定参数的方法<br>Method[] getDeclaredMethods():获取所有的方法
获取成员变量:<br>Field getField(String name) : 获取指定名称的public字段<br>Field[] getFields(): 返回所有的public字段<br>Field getDeclaredField(String name) : 返回指定名称字段<br>Field[] getDeclaredFields() : 返回所有的字段
获取注解:<br><A extends Annotation> A getAnnotation(Class<A> annotationClass) : <br>获取指定的Annotation; 如果不存在,则返回null<br><br><A extends Annotation> A getDeclaredAnnotation(Class<A> annotationClass) : <br>获取指定的类Annotation<br><br>Annotation[] getAnnotations() : 返回修饰该Class对象对应类上存在的所有Annotations<br><br>Annotation[] getDeclaredAnnotations() : 返回所有的类Annotation<br><br><A extends Annotation> A[] getAnnotationsByType(Class<A> annotationClass) : <br>获取指定的类Annotation<br><br><A extends Annotation> A[] getDeclaredAnnotationByType(Class<A> annotationClass) : <br>获取指定类型的多个类Annotation<br>
获取内部类:<br>Class<?>[] getDeclaredClasses() : 返回包含的额全部内部类<br><br>获取外部类:<br>Class<?> getDeclaringClass() : 返回外部类<br>
获取类实现的接口:<br>Class<?>[] getInterfaces() : 返回该类实现的所有接口<br><br>获取类所继承的父类<br>Class<? super T> getSuperclass() : 返回父类的Class对象 <br>
获取类的修饰符、所在包、类名等基本信息<br>int getModifiers():返回修饰符<br>Package getPackage() : 获取此类的包名<br>String getName() : 返回class的类名<br>String getSimpleName() : 返回class简称的类名<br><br>boolean isAnnotation():该class对象是否是注解(@interface)<br>boolean isAnnotationPresent(Class<? extends Annotation> annotationClass):该class对象是否使用了Annotation修饰<br>boolean isAnonymousClass():该class对象是否是一个匿名类<br>boolean isArray():该class对象是否表示一个数组类<br>boolean isEnum(): 该class对象是否表示一个枚举类<br>boolean isInterface():该class对象是否表示一个接口<br>boolean isInstance(Object obj): 判断obj是否是此Class对象的实例
8新增的方法参数反射
Executable 派生了 Constructor、method方法<br><br>int getParameterCount() : 获取构造器或方法的形参个数<br>Parameter[] getParameters() : 获取构造器或方法的所有形参
Parameter是8新增的,代表方法或构造器的一个参数<br>getModifiers() : 获取修饰符<br>String getName() : 获取形参名<br>Type getParameterizedType() : 获取带泛型的形参类型<br>Class<?> getType() : 获取形参类型<br>boolean isNamePresent() : class文件是否包含了方法的形参名信息<br>boolean isVarArgs() : 返回该参数是否是个数可变的形参<br>ps : 使用javac命令编译的java源文件,生成的Class文件不包含方法的形参名信息,<br> javac -parameters 源文件名 : 生成的Class文件则包含方法的形参信息
使用反射生成并操作对象
创建对象
通过反射创建对象的两种方法:<br>1, Class.newInstance()<br>2, 获取Constructor对象,constructor.newInstance();
调用方法
Object invoke(Object obj, Object... args) : obj形参是执行该方法的主调,args是执行该方法时传入该方法的实参
访问成员变量值
getXxx(Object obj) : 获取obj对象的该成员变量的值。Xxx对应8种基本类型,如果该成员变量的类型是引用类型,则不需要Xxx。<br><br>setXxx(Object obj, Xxx val) : 将obj对象的该成员变量设置为val的值。Xxx对应8种基本类型,如果该成员变量的类型是引用类型,则不需要Xxx。<br><br>访问private的成员变量需要设置field.setAccessible(true);
操作数组
Array类提供了如下几个方法:<br>static Object newInstance(Class<?> componentType, int ... length) :<br> 创建一个指定元素类型、指定维度的新数组<br><br>static xxx getXxx(Object array, int index):<br>返回array数组中的第index个元素,xxx为基本类型,如果数组元素是引用类型,则方法变为<br>get(Object array, int index)<br><br>static void setXxx(Object array, int index, xxx val):<br>设置array数组的第index个元素值为value
使用反射生成JDK动态代理
Proxy和InvocationHandler创建动态代理
Proxy提供两个方法:<br>static Class<?> getProxyClass(ClassLoader loader, Class<?>...interfaces) :<br> 创建动态代理类所对应的Class对象,该类会实现interfaces的多个接口,第一个参数生成动态代理类的类加载器<br><br>static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) : 创建动态代理对象,执行代理对象的每个方法,都是在实现InvocationHandler对象的invoke方法。
动态代理和AOP
泛型和反射
泛型和Class类
public static <T> T getInstance(Class<T> cls)
使用反射来获取泛型信息
获取成员变量f的类型:<br>Class<?> a = f.getType();<br><br>获取成员变量f的泛型类型<br>Type gType = f.getGenericType();<br>ParameterizedType pType = (ParameterizedType) gType;<br>Type rType = pType.getRawType(); (原始类型)<br>Type[] tArgs = pType.getActualTypeArguments(); (泛型参数信息)
0 条评论
下一页