注解与反射
1.什么是注解
- Annontation是Java5开始引入的新特征,中文名称叫注解。它提供了一种安全的类似注释的机制,用来将任何的信息或元数据(metada)与程序元素(类、方法、成员变量)进行关联。为程序的元素(类、方法。成员变量)加上更直观更明了的说明,这些说明信息是与程序的业务逻辑无关,并且供指定的工具或框架使用。Annontation像一种修饰符一样,应用于包、类型、构造方法、方法、成员变量、参数及本地变量的声明语句中。
- Java注解是附加在代码中的一些元信息,用于一些工具在编译、运行时进行解析和使用,起到说明和配置的功能。注解不会影响代码的实际逻辑,仅仅起到辅助性的作用。包含在java.lang.annotation包中。
2.注解的用处
- 生产文档。这是最常见的,也是Java最早提供的注解,如@param@return等
- 跟踪代码依赖性,实现替代配置文件功能。比如Dragger 2依赖注入,未来Java开发,将大量注解配置,具有很大用处
- 在编译时进行格式检查。如@Override放在方法前,如果你这个方法并不是覆盖了超类的方法,则编译时就能检查出
3.内置注解
- @Override:定义在java.lang.Override中,此注释只用于修饰方法,表示一个方法申明打算重写超类中的另一个方法声明。
- @Deprecated:定义在java.lang.Deprcated中,此注释可以用于修饰方法、属性、类,表示不鼓励程序员使用这样的元素,通常是因为它很危险或者存在更好的选择。
- @SuppressWarnings:定义在java.lang.SuppressWarnings中,用来抑制编译时的警告。
java">package com.chen.AnnotationTest;
public class InnerAnnotation {
@Override
public String toString(){
return "override";
}
@Deprecated
public static void test()
{
System.out.println("deprecated");
}
@SuppressWarnings("All")
public void test1(int i)
{
int a;
}
public static void main(String[] args) {
test();
}
}
4.元注解
- 元注解的作用是负责注解其他注解,Java定义了4个标准的meta-annotation类型,他们被用来对其他annotation类型作说明
- 这些类型和它们所支持的类在java.lang.annotation包中可以找到(@Target,@Retention,@Documented,@Inherited)
- @Target:用于描述注解的使用范围(即:被描述的注解可以用在什么地方)
- @Retention:表示需要在什么级别保存该注释信息,用于描述注解的生命周期(SOURCE<CALSS<RUNTIME)
- @Documented:说明该注解被包含在javadoc中
- @Inherited:说明子类可以继承父类中的该注解
java">package com.chen.AnnotationTest;
import java.lang.annotation.Documented;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.SOURCE;
public class ElementTest {
@myAnnotation
public void test(){
}
}
//作用范围:方法
@Target(value =METHOD )
//声明周期:源代码
@Retention(value = SOURCE)
//注解被包含在文档中
@Documented
//子类可以继承该注解
@Inherited
@interface myAnnotation{
}
5.自定义注解
- 使用@interface自定义注解时,自动继承了java.lang.Annotation接口
- 分析:
- @interface用来声明一个注解,格式:public @interface 注解名{定义内容}
- 其中每一个方法实际上是生命了一个配置参数
- 方法的名称就是参数的名称
- 返回类型就是参数的类型(返回值只能是基本类型:Class,String,enum)
- 可以通过default来声明参数的默认值
- 如果只有一个参数成员,一般参数名为value
- 注解元素必须要有值,我们定义注解元素时,经常使用空字符串,0作为默认值
java">package Test.example1;
import java.lang.annotation.Target;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
public class Test1 {
@myAnnotation(name="xiaochen",id=1)
public void test(){
}
}
@Target(value= {ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@interface myAnnotation{
String name();
int id();
String[] school() default "";
}
6.理解反射
- Reflection(反射)是Java被视为动态语言的关键,反射机制允许程序在执行期间借助Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性和方法。
- 加载完类之后,在堆内存的方法区中就产生了一个Class类型的对象(一个类只有一个Class对象),这个类就包含了完整的类的结构信息。我们可以通过这个对象看到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构,所以,我们形象称之为“反射”。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CeOhLD1F-1605353778709)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20201029151444584.png)]
7.获得反射对象
java">package com.chen.AnnotationTest;
public class Test1 {
public static void main(String[] args) {
Class c1=null;
Class c2=null;
Class c3=null;
Class c4=null;
try
{
c1=Class.forName("com.chen.AnnotationTest.Demo");
c2=Class.forName("com.chen.AnnotationTest.Demo");
c3=Class.forName("com.chen.AnnotationTest.Demo");
c4=Class.forName("com.chen.AnnotationTest.Demo");
}
catch (Exception e)
{
e.printStackTrace();
}
System.out.println(c1);
System.out.println(c1.hashCode());
System.out.println(c2.hashCode());
System.out.println(c3.hashCode());
System.out.println(c4.hashCode());
}
}
class Demo{
private int id;
private String name;
}
8.获取Class类的几种方法
java">package com.chen.AnnotationTest;
//获取Class对象的多种方法
public class Test2 {
public static void main(String[] args) throws ClassNotFoundException {
Person p=new Student();
System.out.println(p);
//1.通过类名获取
Class c1=Student.class;
System.out.println(c1);
//2.通过对象调用getClass()方法获取
Class c2=p.getClass();
System.out.println(c2);
//3.通过Class.forName(类全路径名)获取
Class c3=Class.forName("com.chen.AnnotationTest.Student");
System.out.println(c3);
//4.基本内置类型包装类的TYPE属性
Class c4=Integer.TYPE;
System.out.println(c4);
//5.获取父类Class对象
Class c5=c1.getSuperclass();
System.out.println(c5);
}
}
class Person{
public String name;
public Person()
{
}
public Person(String name)
{
this.name=name;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
'}';
}
}
class Student extends Person{
public Student()
{
this.name="学生";
}
}
class Teacher extends Person{
public Teacher()
{
this.name="老师";
}
}
9.可以有Class对象的类型
- Class:外部类,成员(成员内部类、静态内部类、局部内部类、匿名内部类)
- interface:接口
- []:数组
- enum:枚举
- annotation:注解@interface
- primitive type:基本数据类型
- void
java">package com.chen.AnnotationTest;
public class Test3 {
public static void main(String[] args) {
Class c1=Class.class;
Class c2=String[].class;
Class c3=int[][].class;
Class c4=Comparable.class;
Class c5=Override.class;
Class c6=int.class;
Class c7=void.class;
Class c8=Enum.class;
System.out.println(c1);
System.out.println(c2);
System.out.println(c3);
System.out.println(c4);
System.out.println(c5);
System.out.println(c6);
System.out.println(c7);
System.out.println(c8);
}
}
10.类的加载与ClassLoader的理解
-
加载:将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后生成一个代表这个类的java.lang.Class对象
-
链接:将Java类的二进制代码合并到JVM的运行状态之中的过程
- 验证:确保加载的类信息符合JVM规范,没有安全方面的问题
- 准备:正式为类变量(static)分配内存并设置类变量默认初始值的阶段,这些内存都将在方法区中进行分配
- 解析:虚拟机常量池的符号引用(变量名)替换为直接引用(地址)的过程
-
初始化:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-upmtv94a-1605353778733)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20201029234310756.png)]
java">package com.chen.AnnotationTest;
public class Test4 {
public static void main(String[] args) {
A a=new A();
System.out.println(a.m);
}
}
class A{
static{
System.out.println("A的静态初始代码块初始化");
m=300;
}
static int m=100;
public A(){
System.out.println("A类无参构造器初始化");
}
}
11.类的初始化
-
类的主动引用(一定会发生类的初始化)
-
类的被动引用(不会发生类的初始化)
- 当访问一个静态域时,只有真正声明这个域的类才会被初始化。如:当通过子类引用父类的静态变量,不会导致子类初始化
- 通过数组定义类引用,不会触发此类的初始化
- 引用常量不会触发此类的初始化(常量在链接阶段就存入调用类的常量池中了)
例
java">package com.chen.AnnotationTest;
public class Test5 {
static {
System.out.println("加载main()方法类");
}
public static void main(String[] args) throws ClassNotFoundException {
//主动引用
//new 一个类对象
// Son son=new Son();
//调用静态成员
// System.out.println(Son.s);
//进行反射调用
// Class.forName("com.chen.AnnotationTest.Son");
//被动引用
//通过子类引用父类静态变量
// System.out.println(Son.f);
//通过数组定义类引用
// Son[] sonArry=new Son[10];
//引用常量
System.out.println(Son.m);
}
}
class Father{
static int f=9;
static{
System.out.println("加载父类");
}
}
class Son extends Father{
static{
System.out.println("加载子类");
}
static int s=8;
final static int m=1;
}
12.类加载器
- 引导类加载器:用C++编写的,是JVM自带的类加载器,负责Java平台核心库,用来装载核心类库。该加载器无法直接获取
- 拓展类加载器:负责jre/lib/ext目录下的jar包或-D java.ext.dirs指定目录下的jar包装入工作库
- 系统加载器:负责java -classpath或-D java.class.path所指的目录下的类与jar包装入工作,是最常用的加载类
例
java">package com.chen.AnnotationTest;
public class Test6 {
public static void main(String[] args) throws ClassNotFoundException {
//系统加载器
ClassLoader cl=ClassLoader.getSystemClassLoader();
System.out.println(cl);
//拓展类加载器
ClassLoader cl1=cl.getParent();
System.out.println(cl1);
//引导类加载器
ClassLoader cl3=cl1.getParent();
System.out.println(cl3);
//测试当前类是哪个加载器加载的
System.out.println(Class.forName("com.chen.AnnotationTest.Test6").getClassLoader());
System.out.println(Class.forName("java.lang.String").getClassLoader());
//获取系统类可以加载的路径
System.out.println(System.getProperty("java.class.path"));
}
}
13.获取类运行时的结构
- 实现的全部接口
- 所继承的父类
- 全部的构造器
- 全部的方法
- 全部的Field
- 注解
- …
例
- 带Declared ——public
- 不带Declared ——当前类全部
java">package com.chen.AnnotationTest;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class Test7 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
Class c1=Class.forName("com.chen.AnnotationTest.User");
//获取类名
System.out.println(c1.getName());
System.out.println("====================================");
//获取public 修饰的成员变量
Field[] fields=c1.getFields();
for(Field f : fields )
{
System.out.println(f);
}
//获取当前类中public 修饰的指定字段的值
Field field=c1.getField("name");
System.out.println(field);
System.out.println("====================================");
//获取全部成员变量
fields=c1.getDeclaredFields();
for(Field f : fields)
{
System.out.println(f);
}
//获取当前类指定字段值
field=c1.getDeclaredField("position");
System.out.println(field);
System.out.println("========================================");
//获取子类及其父类全部的 public 修饰的方法
Method[] methods=c1.getMethods();
for(Method m : methods)
{
System.out.println(m);
}
System.out.println("===========================================");
//获取当前类全部的方法
methods=c1.getDeclaredMethods();
for(Method m : methods)
{
System.out.println(m);
}
System.out.println("===========================================");
Constructor[] constructors=c1.getConstructors();
for(Constructor c : constructors)
{
System.out.println(c);
}
System.out.println("===========================================");
constructors=c1.getDeclaredConstructors();
for(Constructor c : constructors)
{
System.out.println(c);
}
}
}
14.动态创建对象、执行方法、访问字段
java">package com.chen.AnnotationTest;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Test8 {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
Class c1= Class.forName("com.chen.AnnotationTest.User");
//通过反射创建对象
User user=(User)c1.newInstance(); //实质上是调用无参构造器
System.out.println(user);
System.out.println("============================");
//通过获取构造器创建对象
Constructor constructor=c1.getConstructor(int.class,String.class);
user=(User)constructor.newInstance(1,"xiaoChen");
System.out.println(user);
System.out.println("============================");
//通过反射调用方法
Method method=c1.getDeclaredMethod("setId", int.class);
method.invoke(user,18);
System.out.println(user);
System.out.println("============================");
//通过反射调用字段
Field field=c1.getDeclaredField("id");
field.setAccessible(true); //无访问权限必须开放 访问权限(方法,构造器同)
field.set(user,22);
System.out.println(user);
}
}
15.性能对比分析
new 对象 —优于— 禁用安全访问开关的反射 —优于— 开启安全访问开关的反射
java">package com.chen.AnnotationTest;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Test9 {
private static void test01()
{
User user=new User(22,"xiaoChen");
long startTime=System.currentTimeMillis();
for (int i = 0; i < 1000000000; i++) {
user.getId();
}
long endTime=System.currentTimeMillis();
System.out.println("new 对象调用执行消耗时间:"+(endTime-startTime)+"ms");
}
private static void test02() throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
User user=new User();
Class c1=user.getClass();
Method method=c1.getDeclaredMethod("getId");
long startTime=System.currentTimeMillis();
for (int i = 0; i < 1000000000; i++) {
method.invoke(user);
}
long endTime=System.currentTimeMillis();
System.out.println("启用安全访问开关的 反射 调用执行消耗时间:"+(endTime-startTime)+"ms");
}
private static void test03() throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
User user=new User();
Class c1=user.getClass();
Method method=c1.getDeclaredMethod("getId");
method.setAccessible(true);
long startTime=System.currentTimeMillis();
for (int i = 0; i < 1000000000; i++) {
method.invoke(user);
}
long endTime=System.currentTimeMillis();
System.out.println("禁用安全访问开关的 反射 调用执行消耗时间:"+(endTime-startTime)+"ms");
}
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
test01();
test02();
test03();
}
}
16.获取泛型信息
-
ParameterizedType:表示一种参数化类型,比如Collection
-
GenericArrayType:表示一种元素类型是参数化类型或者类型变量的数组类型
-
TypeVariable:是各种类型变量的公共父接口
-
WildcardType:代表一种通配符类型表达式
java">package com.chen.AnnotationTest; import java.lang.reflect.Method; import java.lang.reflect.Parameter; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.HashMap; import java.util.List; import java.util.Map; public class Test10 { public void test01(Map<Integer,String> map, List<String> list) { } public Map<Integer,String> test02() { return new HashMap(); } public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException { Class c1 = Class.forName("com.chen.AnnotationTest.Test10"); //获取方法参数 泛型 Method method01 = c1.getMethod("test01", Map.class, List.class); Type[] types = method01.getGenericParameterTypes(); for (Type type : types) { System.out.println(type); if (type instanceof ParameterizedType) { Type[] types02 = ((ParameterizedType) type).getActualTypeArguments(); for (Type type02 : types02) { System.out.println(type02); } } } System.out.println("============================"); //获取方法返回值 泛型 Method method = c1.getDeclaredMethod("test02"); Type types02 = method.getGenericReturnType(); if(types02 instanceof ParameterizedType) { Type[] types002=((ParameterizedType) types02).getActualTypeArguments(); for(Type type : types002) { System.out.println(type); } } } }
获得注解信息
java">package com.chen.AnnotationTest; import java.lang.annotation.*; import java.lang.reflect.Field; public class Test11 { public static void main(String[] args) throws Exception{ Class c1=Class.forName("com.chen.AnnotationTest.Dog"); //获取注解 Annotation[] annotations=c1.getAnnotations(); for(Annotation a : annotations) { System.out.println(a); } //获取类注解值 Classchen a1=(Classchen)c1.getAnnotation(Classchen.class); System.out.println(a1.value()); //获取字段注解值 Field f=c1.getDeclaredField("id"); Fieldchen f1=f.getAnnotation(Fieldchen.class); System.out.println(f1.name()); System.out.println(f1.type()); System.out.println(f1.length()); } } @Classchen(value = "db_dog") class Dog{ @Fieldchen(name = "db_id",type="int",length=10) private int id; @Fieldchen(name="db_name",type="varchar",length=5) private String name; @Fieldchen(name="db_age",type="int",length=3) private int age; public Dog(int id,String name,int age) { this.id=id; this.name=name; this.age=age; } public void setId(int id) { this.id = id; } public void setName(String name) { this.name = name; } public void setAge(int age) { this.age = age; } public int getId() { return id; } public String getName() { return name; } public int getAge() { return age; } } @Target(value = ElementType.TYPE) @Retention(value = RetentionPolicy.RUNTIME) @interface Classchen{ String value(); } @Target(value = ElementType.FIELD) @Retention(value = RetentionPolicy.RUNTIME) @interface Fieldchen{ String name(); String type(); int length(); }