Java注解&反射

注解和反射

注解

内置注解

  • @Override:重写父类方法
  • @Deprecated:过时的方法,不鼓励程序猿使用,通常存在更好的替代方法
  • @SuppressWarnings:抑制编译时的警告信息

元注解

  • @Target:表示注解可以用在什么地方
@MyAnnotation
public class TestDemo1 {

    @MyAnnotation
    public void test() {
    }
}


@Target(value = {ElementType.METHOD, ElementType.TYPE})//可以用在方法和类上
@interface MyAnnotation {

}
  • @Retention:表示注解在什么时候还有效
    • RUNTIME > CLASS > RESOURCE

@Retention(value = RetentionPolicy.CLASS)表示在字节码的时候仍然有效

回忆一下java的编译和运行流程,先翻译成字节码.class,然后被C/C++解析成汇编,再被解析成二进制

  • @Document:表示是否将我们的注解生成再javadoc中
  • @Inherited:子类可以继承父类的注解

自定义注解

@MyAnnotation(name = "陈恒", value = -.5)
public class TestDemo1 {

    @MyAnnotation(name = "陈恒", id = 3, value = -.3)
    public void test() {
    }
}


@Target(value = {ElementType.METHOD, ElementType.TYPE})
@Retention(value = RetentionPolicy.RUNTIME)
@interface MyAnnotation {
    //参数=参数类型+参数名+()

    String name();

    int id() default -1;//默认值为-1

    double value();
}

反射

public final native Class<?> getClass();

new是从类实例化到对象,反射是从对象到类对象

创建Class类

public class TestDemo2 {
    public static void main(String[] args) throws ClassNotFoundException {
        Student student = new Student();
        Class c1 = Student.class;//通过类的.class
        Class c2 = student.getClass();//对象的getClass方法
        Class c3 = Class.forName("com.hznu.ch.annotation.Student");//forName获取Class类
        System.out.println(c3.getSuperclass().getName());
    }
}

class Person {

}

class Student extends Person {

}

类加载内存分析

基本过程

  1. 加载:将class文件加载到内存,并将这些静态数据转换成方法区的运行时数据结构,然后生成一个代表这个类的java.lang.Class对象
  2. 链接:将java类的二进制代码合并到JVM的运行状态之中的过程
    1. 验证:确保加载的类信息符合JVM规范,没有安全问题
    2. 正式为类变量(static)分配内存并初始化,这些内存在方法区中分配
    3. 解析:符号引用替换为直接饮用
  3. 初始化:
    1. 执行类构造器<clinit>()方法的过程,这个方法是由编译期自动收集类变量的赋值和静态代码块的语句合并产生的
    2. 初始化一个类,其父类没有初始化,先初始化父类
    3. 确保<clinit>()在多线程环境中被加锁和同步

类会在首次被“主动使用”时执行初始化,为类(静态)变量赋予正确的初始值。在Java代码中,一个正确的初始值是通过类变量初始化语句或者静态初始化块给出的。而我们这里所说的主动使用 包括:

  1. 创建类的实例
  2. 调用类的静态方法
  3. 使用类的非常量静态字段
  4. 调用Java API中的某些反射方法
  5. 初始化某个类的子类
  6. 含有main()方法的类启动时

关于静态变量/块的扩展

静态变量如何初始化
public class Test {
    public static int _i = 10;
}

public class Test {
    public static int _i;
    static {
        _i = 10;
    }
}

这两种本质上是没有区别的,第一个会在生成字节码的时候自动加上静态块

JDK如何处理多个static块
//1
public class Test {
    public static int _i;
    static {
        _i = 10;
    }
    
    public static void main(String[] args) {
    }
    
    static {
        _i = 20;
    }
}
//2
public class Test {
    public static int _i;
    
    public static void main(String[] args) {
    }
    
    static {
        _i = 10;
        _i = 20;
    }
}

会按照代码书写的顺序集中处理静态块,1会变成2

如何看待静态变量的声明

从上面类加载内存分析中我们可以知道,静态变量的声明和初始化是两个操作

静态变量的声明在编译时已经明确了内存的位置,而初始化则是在“主动使用”时才完成的

参考文献

Java静态变量的初始化(static块的本质)

动态创建对象执行方法

public class TestDemo3 {
    public static void main(String[] args) throws Exception {
        Class c1 = Class.forName("com.hznu.ch.annotation.User");
        User user = (User) c1.newInstance();//调用无参构造函数
        System.out.println(user);


        //通过构造器创建对象
        Constructor constructor = c1.getDeclaredConstructor(String.class, int.class, int.class);
        User user1 = (User) constructor.newInstance("陈恒", 1, 20);
        System.out.println(user1);


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

        //通过反射操作属性
        User user3 = (User) c1.newInstance();
        Field name = c1.getDeclaredField("name");
        name.setAccessible(true);//关闭安全检测才能访问私有属性
        name.set(user3, "ch");
        System.out.println(user3);
    }
}

class User {
    private String name;
    private int id;
    private int age;

    public User() {

    }

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

    public String getName() {
        return name;
    }

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

    public int getId() {
        return id;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", id=" + id +
                ", age=" + age +
                '}';
    }
}

性能对比分析

public class TestDemo4 {

    public void test1() {
        User user = new User();
        long start = System.currentTimeMillis();
        for (int i = 0; i < 1000000000; i++) {
            user.getName();
        }
        long end = System.currentTimeMillis();
        System.out.println(end - start + "ms");
    }

    public void test2() throws Exception {
        User user = new User();
        Class c1 = Class.forName("com.hznu.ch.annotation.User");
        Method method = c1.getDeclaredMethod("getName");
        long start = System.currentTimeMillis();
        for (int i = 0; i < 1000000000; i++) {
            method.invoke(user);
        }
        long end = System.currentTimeMillis();
        System.out.println(end - start + "ms");
    }

    public void test3() throws Exception {
        User user = new User();
        Class c1 = Class.forName("com.hznu.ch.annotation.User");
        Method method = c1.getDeclaredMethod("getName");
        method.setAccessible(true);
        long start = System.currentTimeMillis();
        for (int i = 0; i < 1000000000; i++) {
            method.invoke(user);
        }
        long end = System.currentTimeMillis();
        System.out.println(end - start + "ms");
    }

    public static void main(String[] args) throws Exception {
        TestDemo4 demo4 = new TestDemo4();
        demo4.test1();
        demo4.test2();
        demo4.test3();
    }
}	
/*
8ms
3422ms
1633ms
*/

通过反射操作泛型

public class TestDemo5 {
    public static void main(String[] args) throws Exception {
        Method method = Class.forName("com.hznu.ch.annotation.TestDemo5").getMethod("test1", Map.class, List.class);
        Type[] parameterTypes = method.getGenericParameterTypes();//获取泛型参数类型
        for (Type parameterType : parameterTypes) {
            System.out.println(parameterType);
            if (parameterType instanceof ParameterizedType) {
                Type[] arguments = ((ParameterizedType) parameterType).getActualTypeArguments();//获取真实的参数类型
                for (Type argument : arguments) {
                    System.out.println(argument);
                }
            }
        }
        System.out.println("-----------------");
        method = Class.forName("com.hznu.ch.annotation.TestDemo5").getMethod("test2");
        Type returnType = method.getGenericReturnType();//获取泛型返回类型
        if (returnType instanceof ParameterizedType) {
            Type[] arguments = ((ParameterizedType) returnType).getActualTypeArguments();
            for (Type argument : arguments) {
                System.out.println(argument);
            }
        }
    }

    public void test1(Map<String, Integer> mp, List<String> list) {

    }

    public Map<String, Double> test2() {
        return new HashMap<String, Double>();
    }
}

获取注解信息

public class TestDemo6 {
    public static void main(String[] args) throws Exception {
        Class c1 = Class.forName("com.hznu.ch.annotation.Student1");

        //获得类注解
        Annotation[] annotations = c1.getAnnotations();
        for (Annotation annotation : annotations) {
            System.out.println(annotation);
        }


        //获得类的注解值
        TableCH tableCH = (TableCH) c1.getAnnotation(TableCH.class);
        System.out.println(tableCH.value());


        //获得类属性的注解值
        Field field = c1.getDeclaredField("name");
        field.setAccessible(true);
        FieldCH fieldCH = field.getAnnotation(FieldCH.class);
        System.out.println(fieldCH.column_name());
        System.out.println(fieldCH.type());
        System.out.println(fieldCH.length());
    }
}

@TableCH("db_student")
class Student1 {

    @FieldCH(column_name = "Name", type = "varchar", length = 10)
    private String name;
    @FieldCH(column_name = "Age", type = "int", length = 10)
    private int age;
    @FieldCH(column_name = "Id", type = "int", length = 10)
    private int id;

    public Student1() {

    }

    public Student1(String name, int age, int id) {
        this.name = name;
        this.age = age;
        this.id = id;
    }

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public int getId() {
        return id;
    }

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

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×