注解
Java 注解(Annotation)又称 Java 标注,是 JDK5.0 引入的一种注释机制。Java 语言中的类、方法、变量、参数和包等都可以被标注。和 Javadoc 不同,Java标注可以通过反射获取标注内容。在编译器生成类文件时,标注可以被嵌入到字节码中。Java 虚拟机可以保留标注内容,在运行时可以获取到标注内容, 当然它也支持自定义 Java 标注。
反射
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;通过获得Class对形成的对象去调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。
java提供了7个注解,分为内置注解与元注解
内置注解
内置注解:
- Override—重写的注解
- Deprecated—不推荐程序员使用,但是可用,或者存在更合适的方式
- SuppressWarnings(String[] value)—镇压警告
实例
@Override
public String toString() {
return super.toString();
}
@Deprecated
public static void test(){
System.out.println("Deprecated");
}
@SuppressWarnings("all")
public static void demo(){
ArrayList array = new ArrayList();
}
元注解
负责注解其他注解,可以让你自定义注解
- @Targe:用于描述注解的使用范围(即被描述的注解可以用在什么地方)
- @Retention:表示需要在什么级别保存该注释信息,用于描述注解的生命周期(SOURCE<CLASS<RUNTIME)
- @Document:说明该注解将被包含在javadoc中
- @Inherited:说明子类可以继承父类中的该注解
实例
@MyAnnotation
public class MetaAnnotationDemo {
@MyAnnotation
public void test(){
}
}
@Inherited
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD,ElementType.TYPE})
@interface MyAnnotation{
}
反射:为java提供了动态性,通过获得Class对形成的对象及其进行操作。
反射的核心就是获取Class方法。因此我们先了解一下什么是类加载。
- 加载:将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,会产生一个类对应Class对象
- 链接:将java类的二进制代码合并到JVM的运行状态之中的过程
- 验证:确保加载的类信息符合JVM的规范,无安全问题
- 准备:正式为类变量(static)分配内存并 设置类变量默认初始值 的阶段,这些内存都将在方法区中进行分配。
- 解析:虚拟机常量池内的符号引用(常量名)替换为直接引用(地址)的过程
- 初始化:
- 执行类构造器()方法的过程。类构造器是由编译期自动收集类中的 所有类变量 的赋值动作和静态代码块中的语句合并产生的
- 当初始化一个类的时候,如果法相其父类还没有进行初始化,则需要先触发其父类的初始化
- 虚拟机会保证一个类的()方法在对线程环境中被正确加锁和同步
类的主动引用(一定是类的初始化)
当虚拟机启动,先初始化main所在的类
new一个类的对象
调用类的静态成员(除了final常量)和静态方法
使用java.lang.reflect包的方法对其类进行反射调用
当初始化一个类,如果其父类没有被初始化,则会初始化它的父类
类的被动引用(不会发生类的初始化)
当访问一个静态域时,只有真声明这个域的类才会被初始化。如:当通过子类引用父类的静态变量,不会导致子类初始化
通过数组定义类引用,不会触发此类的初始化
引用常量不会触发此类的初始化(常量在链接阶段就存入调用类的常量池中了)
反射有三种获取方式,还有一种是获取基本内置类型包装类的Class:
//获得Class的方式一:通过对象获得
Person person = new Student();
Class<? extends Person> aClass1 = person.getClass();
//获得Class的方式二:通过forName获得
Class<?> aClass2 = Class.forName("pizssn.Reflection.Student");
//获得Class的方式三:通过类名.Class获得
Class<Student> aClass3 = Student.class;
//获得Class的方式四:基本内置类型的包装类都有一个Type属性
Class<Integer> aClass4 = Integer.TYPE;
在获取到Class对象之后,我们就可以通过反射的一些方法获取到类的一些信息。
获取类的名字:
public String getName():返回由类对象表示的实体的名称(类,接口,数组类,原始类型或void),作为String。
public String getSimpleName():返回源代码中给出的基础类的简单名称。如果基础类是匿名的,则返回一个空字符串。
获取类的属性:
public Field getField(String name):返回一个Field对象,它反映此表示的类或接口的指定公共成员字段类对象。
public Field[] getFields():返回包含一个数组Field对象反射由此表示的类或接口的所有可访问的公共字段类对象。
public Field getDeclaredField(String name):返回一个Field对象,它反映此表示的类或接口的指定已声明字段类对象。
获取类的方法:
public Method getMethod(String name,Method<?>... parameterTypes):返回一个方法对象,它反映此表示的类或接口的指定公共成员方法类对象。
public Method[] getMethods():返回包含一个数组方法对象反射由此表示的类或接口的所有公共方法类对象,包括那些由类或接口和那些从超类和超接口继承的声明。
public Method[] getDeclaredMethods():返回包含一个数组方法对象反射的类或接口的所有声明的方法,通过此表示类对象,包括公共,保护,默认(包)访问和私有方法,但不包括继承的方法。
获取类的构造器:
public Constructor<T> getConstructor(Method<?>... parameterTypes):返回一个Constructor对象,该对象反映Constructor对象表示的类的指定的公共类函数。
public Constructor<?>[] getConstructors():返回一个包含Constructor对象的数组,反映由此Constructor对象表示的类的所有公共类函数。
public Constructor<?>[] getDeclaredConstructors():返回反映Constructor对象表示的类声明的所有Constructor对象的数组类。
通过反射动态创建对象与方法
public T newInstance():创建由此类对象表示的类的新实例。该类被实例化为一个具有空参数列表的new表达式。
如果类尚未初始化,则初始化该类。
public Object invoke(Object obj,Object... args):在具有指定参数的方法对象上调用此方法对象表示的基础方法。
public void setAccessible(boolean flag):将此对象的accessible标志设置为指示的布尔值。
true的值表示反射对象应该在使用时抑制Java语言访问检查。
false的值表示反映的对象应该强制执行Java语言访问检查。
之所以将注解和反射一起放在这总结,是因为我们可以通过反射获取注解信息。
//通过反射获得注解
Annotation[] annotations = aClass.getAnnotations();
//获得注解的value值
tablePizssn annotation = (tablePizssn) aClass.getAnnotation(tablePizssn.class);
当然,反射也是动态代理的一种实现方式。
这种实现方式需要使用到一个类Proxy和一个接口InvocationHandler。Proxy是所有动态类的父类,它提供一个静态方法来创建动态代理的class对象和实例,这个方法就是newProxyInstance()。
反射实现动态代理的逻辑:代理类调用接口方法→调用invocationHandler.invoke方法→反射调用指定被代理类方法→Proxy.newProxyInstance 生成代理类