Java注解和反射机制深层剖析

本章主要仔细讲讲注解和反射机制

注解

内置注解

@Override

  • 定义在 java.lang.Override 中,只适用于修辞方法
  • 表示一个方法声明打算重写超类中的另一个方法声明

@Deprecated

  • 定义在java.lang.Deprecated中,此注释可以用于修辞方法、属性、类
  • 表示不鼓励程序员使用这样的元素,通常是因为它很危险或者存在更好的选择

@SuppressWarnings

  • 定义在java.lang.SuppressWarnings中,用来抑制编译时的警告信息,此注释可以用于修辞方法、属性、类

  • 与前两个注释有所不同,你需要添加一个参数才能正确使用,这些参数都是已经定义好了的,我们选择性的使用就好

public class AnnotationTest extends Object{
    @Override
    public String toString() {
        return super.toString();
    }

    @Deprecated
    public void stop(){
        System.out.println("如果加了@Deprecated注解,该方法过时,可能存在问题,并不是不能使用");
        System.out.println("在IDEA中,调用该方法时会被划线");
    }

    @SuppressWarnings("all")
    public void testSupWarn(){
        System.out.println("在IDEA中,testSupWarn灰色会消失");
    }
}

元注解

  • 元注解的作用就是负责注解其他注解
  • 这些注解都可以在java.lang.annotation包中可以找到

@Target

  • 用于描述注解的使用范围,即被描述的注解可以用在什么地方

@Retention

  • 表示需要在什么级别保存该注释信息,用于描述注解的生命周期 (SOURCE < CLASS < RUNTIME),比如定义了RUNTIME,那么这个注解在SOURCE和CLASS中也有效

@Document

  • 说明该注解将被包含在JavaDoc中

@Inherited

  • 说明子类可以继承父类中的该注解
public class AnnotationTest extends Object{
    @MyAnnotation
    public void test(){
    }
}

//定义一个注解
@Target(value = {ElementType.METHOD,ElementType.TYPE})
@Retention(value = RetentionPolicy.RUNTIME)
@Inherited
@Documented
@interface MyAnnotation{}

自定义注解

public class AnnoSelf {
    @MyAnno(id = 1)
    public void test(){

    }
    @MyAnno(name = "Jasper",age = 18,id = 1,area = {"杭州"})
    public void test2(){

    }
    @MyAnno2("Jasper")
    public void test3(){

    }
}
@Target(value = {ElementType.METHOD})
@Retention(value = RetentionPolicy.RUNTIME)
@interface MyAnno{
    String name() default "";
    int age() default 0;
    int id(); //如果没有默认值那么必须要在注解括号内给该属性赋值
    String[] area() default {"北京","上海"};
}
@Target(value = {ElementType.METHOD})
@Retention(value = RetentionPolicy.RUNTIME)
@interface MyAnno2{
    String value(); //如果注解只有一个属性并且为value,那么使用注解赋值时不需要写属性名称
}

引入反射概念之前

静态语言 vs 动态语言

动态语言

  • 是一类在运行时可以改变其结构的语言:例如新的函数、对象、甚至代码可以被引进,已有的函数可以被删除或是其他结构上的变化。通俗点说就是在运行时代码可以根据某些条件改变自身结构
  • 主要动态语言:Object-C、C#、JavaScript、PHP、Python等

静态语言

  • 与动态语言相对应的,运行时结构不可变的语言就是静态语言,如Java、C、C++

  • Java不是动态语言,但Java可以称之为“准动态语言”。即Java有一定的动态性,我们可以利用反射机制获得类似动态语言的特性。Java的动态性让编程的时候更加灵活

Java文件如何运行

javaprocess

类加载

classload
  • 这张图表现了一个类的生命周期,而类加载只包括加载、连接和初始化这三个过程
  • 解析部分是灵活的,它可以在初始化环节之后再进行,实现所谓的“后期绑定”,其他环节顺序不可以改变

加载

  • 加载时一个读取Class文件,将其转化为某种静态数据结构存储在方法区内,并在堆中生成便于用户调用的java.lang.class类型的对象的过程

校验

  • 确保加载的类信息符合JVM规范,没有安全方面的问题
  • 校验其实包含了很多个步骤,分散在各个不同的阶段内
  • 校验的内容是会不断发展的

准备

  • 正式为静态变量分配内存并设置默认初始值的阶段,这些内存都将在方法区中进行分配
  • 在JDK8之前,类的元信息、常量池、静态变量等都存储在永久代这种具体实现中;在JDK8及以后,常量池(字符串常量池被拿到堆中,运行时常量池还在方法区)、静态变量被移除了方法区,转移到了堆中,元信息仍然保留在方法区内,但是存储方式改成了元空间
store

解析

  • 虚拟机常量池内的符号引用(常量名)替换为直接引用(地址)的过程

    • 当一个Java类被编译成Class之后,假如这个类为A,并且A中引用了B,在编译阶段A是不知道B有没有被编译,而且此时B也一定没有被加载,所以A肯定不知道B的实际地址,此时在A的Class文件中,将会使用一个字符串S来代替B的地址,S就被称为符号引用
    • 在运行时如果A发生了类加载,在解析阶段发现B没有被加载,那么将会触发B的类加载,将B加载到虚拟机中,此时A中B的符号引用将会被替换成B的实际地址,这被称为直接引用

    静态解析 vs 动态解析

    • 如果A调用的B是一个具体的实现类,那么就称为静态解析
    • 假如上层Java代码使用了多态,这里的B可能是抽象类或者是接口,那么B可能有两个具体的实现类C和D,此时B的具体实现并不明确,就不知道使用哪个具体实现的直接引用来替换
    • 直到运行阶段发生了调用,这时虚拟机调用栈中将会得到具体的类型信息,这时候再进行解析就能用明确的直接引用来替换符号引用,这就是为什么解析阶段有时会发生在初始化阶段之后,这就是动态解析,用它来实现后期绑定

类加载器

  • 类加载器作用是用来把类(class)装载进内存的

classloadmachine

public class ClassLoaderTest {
    public static void main(String[] args) {
        //获取应用程序类加载器
        ClassLoader classLoader=ClassLoader.getSystemClassLoader();
        System.out.println(classLoader);

        //获取系统类加载器的父类加载器,即拓展类加载器
        ClassLoader parent=classLoader.getParent();
        System.out.println(parent);

        //获得拓展类加载器的父类加载器,即启动类加载器,该加载器无法直接获取
        ClassLoader parent2=parent.getParent();
        System.out.println(parent2);
    }
}
//输出结果:
sun.misc.Launcher$AppClassLoader@18b4aac2
sun.misc.Launcher$ExtClassLoader@511d50c0
null

反射

  • Reflection(反射)是Java被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法
  • 加载完类之后,在堆内存的方法区中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息。我们可以通过这个对象看到类的结构。所以,我们形象的称之为反射
reflection

得到Class几种方式

public class GetClassTest {
    public static void main(String[] args) throws ClassNotFoundException {
        Person person=new Teacher();
        //通过对象获得
        Class c1 = person.getClass();
        System.out.println(c1.hashCode());
        //forName获得
        c1=Class.forName("javabase.javaannoandref.Teacher");
        System.out.println(c1.hashCode());
        //通过类名.class获得
        Class c2 = Teacher.class;
        System.out.println(c2.hashCode());
        //基本内置类型的包装类都有一个Type
        Class c3 = Integer.TYPE;
        System.out.println(c3);
        //获得父类类型
        Class superclass = c1.getSuperclass();
        System.out.println(superclass);
    }
}
class Person{ }
class Teacher extends Person{ }
//输出结果:
1360875712
1360875712
1360875712
int
class javabase.javaannoandref.Person

获得类的信息

public class ClassInfo {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        Class c1 = Class.forName("javabase.javaannoandref.User");
        //获得类的名字
        System.out.println(c1.getName());//获得包名+类名
        System.out.println(c1.getSimpleName());//获得包名
        //获得类的属性
        System.out.println("------获得类的属性------");
        Field[] fields = c1.getFields(); //只能获得public属性
        for (Field field : fields) {
            System.out.println(field);
        }
        System.out.println("-------------------");
        Field[] declaredFields = c1.getDeclaredFields(); //获得所有属性
        for (Field declaredField : declaredFields) {
            System.out.println(declaredField);
        }
        System.out.println("-------------------");
        //获得指定属性的值
        Field name = c1.getDeclaredField("name");
        System.out.println(name);

        //获得类的方法
        System.out.println("------获得类的方法------");
        Method[] methods = c1.getMethods(); //获得本类及父类的全部public方法
        for (Method method : methods) {
            System.out.println(method);
        }
        System.out.println("-------------------");
        Method[] declaredMethods = c1.getDeclaredMethods(); //获取本类的全部方法
        for (Method declaredMethod : declaredMethods) {
            System.out.println(declaredMethod);
        }
        System.out.println("-------------------");
        //获得指定方法
        Method setName = c1.getDeclaredMethod("setName", String.class);
        System.out.println(setName);
        Method getName = c1.getDeclaredMethod("getName");
        System.out.println(getName);

        //获得类的构造器
        System.out.println("------获得类的构造器------");
        Constructor[] constructors = c1.getConstructors(); //获得public的构造器
        for (Constructor constructor : constructors) {
            System.out.println(constructor);
        }
        System.out.println("-------------------");
        Constructor[] declaredConstructors = c1.getDeclaredConstructors(); //获得所有的构造器
        for (Constructor declaredConstructor : declaredConstructors) {
            System.out.println(declaredConstructor);
        }
        System.out.println("-------------------");
        //获得指定的构造器
        Constructor declaredConstructor = c1.getDeclaredConstructor(String.class, int.class, int.class, boolean.class);
        System.out.println(declaredConstructor);
    }
}
class User{
    private String name;
    private int age;
    public int id;
    protected boolean sex;

    public User() {
    }

    private User(String name) {
        this.name = name;
    }

    public User(String name, int age, int id, boolean sex) {
        this.name = name;
        this.age = age;
        this.id = id;
        this.sex = sex;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    private void method(){}
    protected void method2(){}
}
//输出结果:
javabase.javaannoandref.User
User
------获得类的属性------
public int javabase.javaannoandref.User.id
-------------------
private java.lang.String javabase.javaannoandref.User.name
private int javabase.javaannoandref.User.age
public int javabase.javaannoandref.User.id
protected boolean javabase.javaannoandref.User.sex
-------------------
private java.lang.String javabase.javaannoandref.User.name
------获得类的方法------
public java.lang.String javabase.javaannoandref.User.getName()
public void javabase.javaannoandref.User.setName(java.lang.String)
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
public final void java.lang.Object.wait() throws java.lang.InterruptedException
public boolean java.lang.Object.equals(java.lang.Object)
public java.lang.String java.lang.Object.toString()
public native int java.lang.Object.hashCode()
public final native java.lang.Class java.lang.Object.getClass()
public final native void java.lang.Object.notify()
public final native void java.lang.Object.notifyAll()
-------------------
protected void javabase.javaannoandref.User.method2()
private void javabase.javaannoandref.User.method()
public java.lang.String javabase.javaannoandref.User.getName()
public void javabase.javaannoandref.User.setName(java.lang.String)
-------------------
public void javabase.javaannoandref.User.setName(java.lang.String)
public java.lang.String javabase.javaannoandref.User.getName()
------获得类的构造器------
public javabase.javaannoandref.User()
public javabase.javaannoandref.User(java.lang.String,int,int,boolean)
-------------------
public javabase.javaannoandref.User()
private javabase.javaannoandref.User(java.lang.String)
public javabase.javaannoandref.User(java.lang.String,int,int,boolean)
-------------------
public javabase.javaannoandref.User(java.lang.String,int,int,boolean)

通过反射,动态创建对象

class User{
    private String name;
    private int age;
    public int id;
    protected boolean sex;

    public User() {
    }

    private User(String name) {
        this.name = name;
    }

    public User(String name, int age, int id, boolean sex) {
        this.name = name;
        this.age = age;
        this.id = id;
        this.sex = sex;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    private void method(){}
    protected void method2(){}
}
public class CreateObj {
    public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
        Class c1 = Class.forName("javabase.javaannoandref.User");
        //构造一个对象
        User user =(User) c1.newInstance();  //本质上调用的是无参构造public User() {}
        System.out.println(user);

        //通过构造器创建对象
        Constructor declaredConstructor = c1.getDeclaredConstructor(String.class,int.class,int.class,boolean.class);
        User userByCs =(User) declaredConstructor.newInstance("Jasper1",18,001,true);
        System.out.println(userByCs.getName());

        //通过反射调用普通方法
        User user2 =(User) c1.newInstance();
        Method method = c1.getDeclaredMethod("setName", String.class);
        method.invoke(user2,"Jasper2");
        System.out.println(user2.getName());

        //通过反射操作属性
        User user3 =(User) c1.newInstance();
        Field name = c1.getDeclaredField("name");
        //启动和关闭访问安全检查的开关 true=关闭
        name.setAccessible(true);
        name.set(user3,"Jasper3");
        System.out.println(user3.getName());
    }
}
//输出结果:
javabase.javaannoandref.User@511d50c0
Jasper1
Jasper2
Jasper3

性能检测

//User类同上
public class TimeTest {
    public static void test(){
        User user=new User();
        long startTime=System.currentTimeMillis();
        for (int i = 0; i < 1000000000; i++) {
            user.getName();
        }
        long endTime=System.currentTimeMillis();
        System.out.println("普通方法:"+(endTime-startTime)+"s");
    }
    public static void test2() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        User user=new User();
        Class c = Class.forName("javabase.javaannoandref.User");
        Method method = c.getDeclaredMethod("getName");
        long startTime=System.currentTimeMillis();
        for (int i = 0; i < 1000000000; i++) {
            method.invoke(user);
        }
        long endTime=System.currentTimeMillis();
        System.out.println("未关检测,反射方法:"+(endTime-startTime)+"s");
    }
    public static void test3() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        User user=new User();
        Class c = Class.forName("javabase.javaannoandref.User");
        Method method = c.getDeclaredMethod("getName");
        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)+"s");
    }

    public static void main(String[] args) throws ClassNotFoundException, InvocationTargetException, NoSuchMethodException, IllegalAccessException {
        test();
        test2();
        test3();
    }
}
//输出结果:
普通方法:16s
未关检测,反射方法:2182s
关闭检测,反射方法:1065s

反射操作范型

//User类同上
public class GetGeneric {
    public void test(Map<String,User> map, List<User> list){

    }
    public Map<String,User> test02(){
        return null;
    }
    public static void main(String[] args) throws NoSuchMethodException {
        //反射操作范型参数信息
        Method method = GetGeneric.class.getDeclaredMethod("test", Map.class, List.class);
        Type[] genericParameterTypes = method.getGenericParameterTypes();
        for (Type genericParameterType : genericParameterTypes) {
            if (genericParameterType instanceof ParameterizedType){
                Type[] actualTypeArguments = ((ParameterizedType) genericParameterType).getActualTypeArguments();
                for (Type actualTypeArgument : actualTypeArguments) {
                    System.out.println(actualTypeArgument);
                }
            }
        }
        System.out.println("----------");
        //反射操作范型返回值
        Method method2 = GetGeneric.class.getDeclaredMethod("test02");
        Type genericReturnType = method2.getGenericReturnType();
        if (genericReturnType instanceof ParameterizedType){
            Type[] actualTypeArguments = ((ParameterizedType) genericReturnType).getActualTypeArguments();
            for (Type actualTypeArgument : actualTypeArguments) {
                System.out.println(actualTypeArgument);
            }
        }
    }
}
//输出结果:
class java.lang.String
class javabase.javaannoandref.User
class javabase.javaannoandref.User
----------
class java.lang.String
class javabase.javaannoandref.User

反射操作注解

public class GetAnnotation {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
        Class c = Class.forName("javabase.javaannoandref.Worker");
        //通过反射获得类上面注解
        Annotation[] annotations = c.getAnnotations();
        for (Annotation annotation : annotations) {
            System.out.println(annotation);
        }
        //获得类上面指定注解的value值
        WorkerType workerType =(WorkerType) c.getAnnotation(WorkerType.class);
        System.out.println(workerType.value());

        //获得类字段的注解值
        Field workerName = c.getDeclaredField("workerName");
        WorkerField annotation = workerName.getAnnotation(WorkerField.class);
        System.out.println(annotation.columName());
        System.out.println(annotation.type());
        System.out.println(annotation.length());

    }
}
@WorkerType("db_student")
class Worker{
    @WorkerField(columName = "db_id",type = "int",length = 10)
    private int id;
    @WorkerField(columName = "db_workerAge",type = "int",length = 10)
    private int workerAge;
    @WorkerField(columName = "db_workerName",type = "varchar",length = 3)
    private String workerName;

    public Worker() {
    }

    public Worker(int id, int workerAge, String workerName) {
        this.id = id;
        this.workerAge = workerAge;
        this.workerName = workerName;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public int getWorkerAge() {
        return workerAge;
    }

    public void setWorkerAge(int workerAge) {
        this.workerAge = workerAge;
    }

    public String getWorkerName() {
        return workerName;
    }

    public void setWorkerName(String workerName) {
        this.workerName = workerName;
    }

    @Override
    public String toString() {
        return "Worker{" +
                "id=" + id +
                ", workerAge=" + workerAge +
                ", workerName='" + workerName + '\'' +
                '}';
    }

}
@Target(value = {ElementType.TYPE})
@Retention(value = RetentionPolicy.RUNTIME)
@interface WorkerType {
    String value();
}
@Target(value = {ElementType.FIELD})
@Retention(value = RetentionPolicy.RUNTIME)
@interface WorkerField{
    String columName();
    String type();
    int length();
}
//输出结果:
@javabase.javaannoandref.WorkerType(value=db_student)
db_student
db_workerName
varchar
3

Hi, welcome to JasperのBlog

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值