注解和反射
注解
注解和反射是所有框架的基础
注解关键字:
Annotation
概述
历史:JDK5.0引入作用:不是程序本身,可以对程序作出解释(注解给程序看的,注释是给程序员看的)。可以被程序读取格式:以"@注解名"在代码中存在,还可以添加一些参数值使用:可以在Package,Class,Method,Field等上面,相当于给他们添加了额外的辅助信息,可以通过反射机制编程实现对这些元数据的访问
元注解
元注解的作用就是负责注解其他注解
@Target:用于描述注解的使用范围,即注解可以用在什么地方@Retention:表示需要在什么级别保存该注解信息,用于描述注解的生命周期(SOURCE < CLASS < RUNTIME)- @Document:说明该注解将被包含在javadoc中
- @Inherited:说明子类可以继承父类中的该注解
自定义注解
使用
@Interface自定义注解是,自动继承了java.lang.Annotation接口
- @Interface用来声明一个注解,格式:public @Interface 注解名 {内容}
- 其中的每个方法实际上是声明了一个配置参数
- 方法的名称就是参数的名称
- 返回类型就是参数的类型(返回类型只能是Class,String,enum,基本数据类型,数组)
- 可以通过default来说声明参数的默认值
- 如果只有一个参数成员,一般参数名为value
- 注解元素必须要有值,定义注解元素时,经常使用空字符串或0作为默认值
示例
({ElementType.TYPE,ElementType.METHOD})
(RetentionPolicy.RUNTIME) //元注解
(value="wu",type=ElementType.TYPE,classes=ElementType.class) //使用
public @interface MyAnnotation {
//注解的参数:参数类型 + 参数名
String value() default ""; //String类型
ElementType[] type() default ElementType.TYPE ; //枚举类型
Class<?>[] classes() default {}; //Class类型
int num() default 0;
}
反射
类加载概述
当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,连接,初始化三步来实现对类的初始化
类加载过程
- 加载 指将class文件读入内存,并为之创建一个Class对象 任何类被使用时系统都会建立一个Class对象
- 链接
验证是否有正确的内存结构,并和其它类协调一致准备负责为类的静态成员分配内存,并设置默认初始值解析将类的二进制数据中的符号引用替换为直接引用- 初始化 通过类构造器,对数据进行初始化

类的初始化
类的主动引用(一定会发生类的初始化)
- 当JVM启动,先初始化main方法所在的类
- new一个类的对象
- 调用类的静态成员(出来final常量)和静态方法
- 使用java.lang.reflect包的方法对类进行反射调用
- 当初始化一个类,如果器父类没有被初始化,则向初始化他的父类
类的被动引用(不会发生类的初始化)
- 当访问一个静态域时,只有真正声明这个域的类才会被初始化。如:当通过子类引用父类的静态变量,不会导致子类初始化。
- 通过数组定义类引用,不会触发此类的初始化
- 引用常量不会触发此类的初始化(常量在链接阶段就调入类的常量池中了)
类加载器
类加载的作用:将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后再堆中生成一个代表这个类的java.lang.Class对象,作为方法区中类数据的访问入口。
类缓存:标准的JavaSE加载器可以按要求查找类,一旦某个类被加载到类加载器中,它将维持加载(缓存)一段时间。不过JVM垃圾回收机制可以回收这些Class对象。

分类
Bootstrap ClassLoader 根类加载器(引导类加载器)
用C++编写,是JVM自带的类加载器
负责Java核心类的加载 比如System类,String类,在JDK中JRE的lib目录下rt.jar文件中。该加载无法直接获取。
Extension ClassLoader 扩展类加载器
负责JRE的扩展目录中jar包的加载 在JDK中JRE的lib目录下ext目录
System ClassLoader 系统类加载器
负责在JVM启动时加载来自java命令的class文件以及 classpath环境变量所指定的jar包和类路径
是最常用的加载器。

xxxxxxxxxx
public class Load {
public static void main(String[] args) throws ClassNotFoundException {
//系统加载器
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
System.out.println(systemClassLoader);
//扩展类加载器
ClassLoader ext = systemClassLoader.getParent();
System.out.println(ext);
//根类加载器
ClassLoader parent = ext.getParent();
System.out.println(parent);
//查看某个类是用那个加载器加载的
ClassLoader classLoader = Class.forName("java.lang.String").getClassLoader();
System.out.println(classLoader);
//查看系统加载器可以加载的路径
String property = System.getProperty("java.class.path");
System.out.println(property);
}
}
动态代理
实质:动态代理实质是通过反射来生成一个代理对象
理解:本来应该自己做的事情,却请了别人来做,被请的人就是代理对象
代理方式
JDK提供的代理只能针对接口代理
Proxy类和InvoactionHandler接口 通过使用这个类和接口就可以生成动态代理对象
框架中的动态代理
cglib
动态代理类的使用
xxxxxxxxxx
//Proxy类
public static Object newProxyInstance(ClassLoader
loader,Class<?>[] interfaces,InvocationHandler h
)
//返回一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序
//InvoactionHandler接口
Object invoke(Object proxy,Method method,Object[] args)
//在代理实例上处理方法调用并返回结果
反射
动态语言VS静态语言
- 动态语言
- 是一类在运行时可以改变器结构的语言,例如新的函数,对象甚至代码可以被引进,已有函数可以被删除或是其他结构上的变化。即在运行时代码可以根据某些条件改变自身结构
- 动态语言:Object-C,C#,JavaScript,PHP,Python等
- 静态语言
- 与动态语言相对应,运行时结构不可变得语言。如Java,C,C++
- Java不是动态语言,但是Java可以称为"准动态语言"。即Java有一定的动态性。可以利用反射机制获得类似动态语言的特性。Java的动态性让编程更加灵活。
定义
反射:Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个类对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称之为Java语言的反射机制。
为什么称为反射:在加载完类之后,在堆内存的方法区中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象就包含了完整的类结构信息。可通过这个对象看到类的结构,这个对象就像一面镜子,透过这个镜子看到类的结构,所以形象的称之为:反射
优点:可以实现动态创建对象和编译,体现出很大的灵活
缺点:对性能有影响。使用反射基本上是一种解释操作。我们可以告诉JVM,我们希望做什么并且它满足我们的要求。这类操作总是慢与直接执行相同的操作。

Java内存分析

Class类
必须先要获取 到该类的字节码文件对象,使用的就是Class类中的方法
获取class文件对象的方式
Object类的getClass()方法
数据类型的静态属性class(如:int.class,String.class)
Class类中的静态方法
public static Class forName(String className) className 必须是类的全路径,带包名的
基本类型的包装类都有一个Type属性(如:Integer.Type)
开发中一般使用第三种 Why? 第三种是一个字符串,而不是一个具体的类名 这样我们就可以吧这样的字符串串配置到配置文件中
那些对象可以有Class对象(几乎所有的对象都有Class对象)
- 外部类
- interface 接口
- 数组
- enum
- annotation
- primitive type
- void
...................
使用步骤
- 获取对象的字节码文件对象
- 通过字节码文件对象获取对相应的构造方法对象,成员变量对象或成员方法对象
- 成员变量对象(set)调用类对象设置值 成员方法对象(invoke)调用对象,调用方法传递参数,如果有返回值,需要接收
应用场景:
创建类的对象,调用Class对象的newInstance()方法
前提:类必须有一个无参构造器,类的构造器的访问权限足够
调用指定的方法

反射的基本使用
获取构造方法
xxxxxxxxxx
public Constructor<?>[] getConstructors()
//返回一个包含某些Constructor对象的数组
//返回的公共的构造方法
public Constructor<?>[] getDeclaredConstructors()
//返回Constructor对象的一个数组,
//返回的事所有的构造方法
public Constructor<T> getConstructor(Class<?>... parameterTypes)
//返回一个Constructor对象(公共的)
//传递的参数是数据类型对应的class(字节码文件对象)
//参数为需要传递的参数的字节码对象形式
public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
//返回一个Constructor对象(包含私有的)
//必须配合
void setAccessible(true)使用
//将次对的accessible标志设置为指示的布尔值
//值为true则指示反射的对象在使用时应该取消java
//语言访问检查
//使用构造方法新建实例:
public newInstance(Object...initargs)
//使用此Constructor对象表示的构造方法来创建该构造方法的声明类的新实例,并用指定的初始化参数初始化该实例。
//(实际是new了个对象)
// 参数为传递的参数
获取成员变量
xxxxxxxxxx
Field[] getFields()
//获取所有的公共成员变量
Field[] getDeclareFields()
//获取所有的成员变量
Field getField()
//获取单个成员变量(受访问权限限制)
Field getDeclareFields()
//获取单个成员变量(不受访问权限限制)
//必须配合setAccessible(true)使用
void set(Object obj,Object value)
//将指定对象变量上此Field对象表示的字段设置为指定的新值
获取成员方法
xxxxxxxxxx
Method[] getMethods()
//获取所有的公共成员方法(自己的包括父类的公共方法)
Method[] getDeclaredMethods()
//获取所有的成员方法 (获取自己的所有的方法)
Method getMethod(String name,Class<?>...parameterTypes)
//获取单个方法(受访问权限限制)
//参数一:方法名称
//参数二:方法参数的class类型
Method getDeclaredMethod(
String name,Class<?>...parameterTypes)
//获取单个方法(不受访问权限限制)
//配合setAccessible(true)使用
Object invoke(Object obj,Object... args)
//参数一:表示对象是谁
//参数二:表示调用该方法的实际参数
反射操作泛型
- Java采用泛型擦除的机制来引入泛型,Java中的泛型仅仅是给编译器javac使用的,确保数据的安全性和免去强制类型转换问题。但是一旦编译完成,所有和泛型有关的类型全部擦除。
- 为了通过反射来操作这些类型,Java新增了ParameterizedType,GenericArrayType,TypeVariable和WildcardType几种类型来代表不能被归一到Class类中的类型,但是有和原始类型齐名的类型。
ParameterizedType:表示一种参数化类型,比如CollectionGenericArrayType:表示一种元素类型是参数化类型或者类型变量的数组类型TypeVariable:是各种类型变量的公共父接口WildcardType:代表一种通配符类型表达式
xxxxxxxxxx
public class Genericity {
public void start(Map<String,Boolean> map,List<Integer> list){
}
public static void main(String[] args) throws Exception {
Method method = Genericity.class.getMethod("start", Map.class,List.class);
Type[] type = method.getGenericParameterTypes();
for(Type t: type){
System.out.println(t);
if(t instanceof ParameterizedType){
Type[] actual = ((ParameterizedType) t).getActualTypeArguments();
for(Type ty: actual){
System.out.println(ty);
}
}
}
}
}
反射与注解
核心方法:
getAnnotation()
getAnnotations()
示例
xxxxxxxxxx
public class Synthesize {
public static void main(String[] args) throws Exception{
Table table = UserTable.class.getAnnotation(Table.class);
System.out.println(table);
Field field = UserTable.class.getDeclaredField("id");
Annotation[] annotations = field.getAnnotations();
for(Annotation one : annotations){
System.out.println(one);
}
}
}
("userTable")
class UserTable{
(columnName = "user_id",type="varchar",size=32)
private String id;
(columnName = "user_name",type="varchar",size=32)
private String userName;
(columnName = "pass_word",type="varchar",size=32)
private String password;
}
(ElementType.TYPE)
(RetentionPolicy.RUNTIME)
@interface Table{
String value();
}
(ElementType.FIELD)
(RetentionPolicy.RUNTIME)
@interface Column{
String columnName();
String type();
int size();
}
(ElementType.FIELD)
(RetentionPolicy.RUNTIME)
@interface Verify{
boolean value() default true;
}

浙公网安备 33010602011771号