注解和反射(全方面解析)

「这是我参与11月更文挑战的第7天,活动详情查看:2021最后一次更文挑战

前言:注解和反射是一切框架的底层 本文很多是学习狂神视频时做的笔记,感谢狂神,也有加入了自己的理解和思考,希望作为一个整理,也能对大家有所帮助。

注解(java.Annotation)

什么是注解

1.不是程序本身,可以对程序作出解释
2.可以被其他程序(如编译器等)读取

内置注解

@Override 重写
@Deprecated 指这段代码过时了
@SuppressWarning("all") 抑制编译时候的警告信息,需要一个参数

元注解

image.png @Target

class learn extends Object{
    @Override
    public String toString() {
        return "learn{}";
    }
    @MyAnnotation()
    public void say(){
        
    }
}

@Target(value = {ElementType.METHOD})
@interface MyAnnotation{
    
}
复制代码

例如lombol的源码中标记了运行时才有效果。
Retention 美[rɪˈtenʃn] n.保持 维持;
Retention默认RUNTIME

image.png

自定义注解

image.png

class Person{
    @MyAnno(name="lcg",id = 1,schools = {"bjtu","cn"})
    public void say(){
    }
}

@Target(value = {ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnno{
    String name() default "xixi";
    int id() default -1;
    String[] schools();
}
复制代码

反射机制(java.Reflection)

Java是静态语言,因为有了反射机制变成了动态语言
反射:即在运行状态中,对于任意一个类,都能够知道这个类的所以属性和方法;对于任意一个对象,都能调用它的任意一个方法和属性。这种动态获取信息及动态调用对象方法的功能叫Java的反射机制。

静态语言和动态语言

  1. 动态语言

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

  1. 静态语言

与动态语言相对应的,运行时结构不可变的语言就是静态语言。如Java、C、C++、C#。
PS:C#不是动态语言,但是MS有将.NET支持动态语言的趋势,3.0吸收了一定动态特征,比如 匿名函数,临时类型,临时变量等

动态语言示例:

function Person(name){
    this.name=name;
}
Person.prototype.getName=function(){
    return this.name;
}
var person=new Person("okok");
alert(person.getName());
person.getName=function(){return "nono"};
alert(person.getName());
复制代码

Java反射机制概述

image.png

image.png

image.png

image.png

image.png

Demo

package com.company;

public class learn1 {
    public static void main(String[] args) throws ClassNotFoundException {
        Class c1=Class.forName("com.company.Person");
        System.out.println(c1);
        Class c2=Class.forName("com.company.Person");
        /**
         * 一个类只有一个class对象
         * 一个类被加载后,类的整个结构多会被封装在class对象中
         */
        System.out.println("c1.hashCode"+c1.hashCode());
        System.out.println("c2.hashCode"+c2.hashCode());
        System.out.println("c1==c2:  "+(c2.hashCode()==c1.hashCode()));//true
    }
}
class User{
    String username;
    String password;
    int number;

    public void setUsername(String username) {
        this.username = username;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public void setNumber(int number) {
        this.number = number;
    }

    public String getUsername() {
        return username;
    }

    public String getPassword() {
        return password;
    }

    public int getNumber() {
        return number;
    }

    public User() {
    }

    public User(String username, String password, int number) {
        this.username = username;
        this.password = password;
        this.number = number;
    }
}
复制代码

输出

class com.company.Person
c1.hashCode692404036
c2.hashCode692404036
c1==c2:  true

Process finished with exit code 0
复制代码

理解Class类并获取Class实例

image.png

image.png

image.png

package com.company;

public class learn1 {
    public static void main(String[] args) throws ClassNotFoundException {
        Class c1=Class.forName("com.company.Person1");
        System.out.println(c1);
        Class c2=Class.forName("com.company.Person1");
        /**
         * 一个类只有一个class对象
         * 一个类被加载后,类的整个结构多会被封装在class对象中
         */
        System.out.println("c1.hashCode"+c1.hashCode());
        System.out.println("c2.hashCode"+c2.hashCode());
        System.out.println("c1==c2:  "+(c2.hashCode()==c1.hashCode()));//true
        User user=new User();
        //方式一,通过对象获得
        Class aClass1=user.getClass();
        System.out.println(aClass1.hashCode());
        //方式二,通过forname获得
        Class aClass2=Class.forName("com.company.User");
        System.out.println(aClass2.hashCode());
        //方式三,通过类型.class
        Class<User> aClass3 = User.class;
        System.out.println(aClass3.hashCode());
        //方法四:基本内置类型的包装类都有一个Type的属性
        Class<Integer> integerClass = Integer.TYPE;
        System.out.println(integerClass);
        //方式五,获取父类类型
        Class<AdminUser> adminUserClass = AdminUser.class;
        Class aClass = adminUserClass.getSuperclass();
        System.out.println(adminUserClass+" "+adminUserClass.hashCode());
        System.out.println(aClass+" "+aClass.hashCode());
    }
}
class AdminUser extends User{
    @Override
    public void login() {
        System.out.println("Admin login...");

    }
}

class User{
    String username;
    String password;
    int number;
    public void login(){
        System.out.println("User login...");
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public void setNumber(int number) {
        this.number = number;
    }

    public String getUsername() {
        return username;
    }

    public String getPassword() {
        return password;
    }

    public int getNumber() {
        return number;
    }

    public User() {
    }

    public User(String username, String password, int number) {
        this.username = username;
        this.password = password;
        this.number = number;
    }
}
复制代码

image.png

image.png

image.png

image.png

image.png

类的加载原理与ClassLoader

image.png

image.png

image.png

public class learn3 {
    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;//如果m在静态代码块前面的话,m就是300
    public A(){
        System.out.println("A类的无参构造初始化");
    }
}

output:
A类静态代码块初始化
A类的无参构造初始化
100
Process finished with exit code 0
复制代码

image.png

  1. 加载到内存,会产生一个类对应Class对象
  2. 链接,链接结束后m=0
  3. 初始化
    <clinit>(){
        System.out.println("A类静态代码块初始化");
        m=300;
        m=100;
    }
    复制代码

分析类初始化

image.png 子类没有被加载

image.png

image.png

image.png

image.png

因为在链接阶段就已经设置类变量默认初始值了

常量在链接阶段就存入调用类的常量池中了

类加载器的作用(了解)

image.png

image.png

image.png

获取运行时类的结构

image.png

image.png

image.png

image.png

image.png

image.png

有了Class对象,能做什么

image.png

动态创建对象
  1. 直接用字节码文件获取对应实例
// 调用无参构造器 ,若是没有,则会报异常
Object o = clazz.newInstance();
复制代码
  1. 有带参数的构造函数的类,先获取到其构造对象,再通过该构造方法类获取实例:
//获取构造函数类的对象
Constroctor constroctor = clazz.getConstructor(String.class,Integer.class); 

![image.png](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/275ddf5d2a8f40968a0c56805934c39e~tplv-k3u1fbpfcp-watermark.image?)
// 使用构造器对象的newInstance方法初始化对象
Object obj = constroctor.newInstance("龙哥", 29); 
复制代码

image.png

image.png

image.png

调用指定方法

image.png

image.png

image.png Demo

public class Calculator{ 
    
    public double add(double score1,double score2){
        return score1 + score2;
    }
     
    public void print(){
        System.out.println("OK");
    }
     
    public static double mul(double score1,double score2){
        return score1 * score2;
    }
}


public class CalculatorTest {
 
    public static void main(String[] args) throws Exception {
        //通过类的.class属性获取
        Class<Calculator> clz = Calculator.class;
        //或者通过类的完整路径获取,这个方法由于不能确定传入的路径是否正确,这个方法会抛ClassNotFoundException
//      Class<Calculator> clz = Class.forName("test.Calculator");
        //或者new一个实例,然后通过实例的getClass()方法获取
//      Calculator s = new Calculator();
//      Class<Calculator> clz = s.getClass();
        //1. 获取类中带有方法签名的mul方法,getMethod第一个参数为方法名,第二个参数为mul的参数类型数组
        Method method = clz.getMethod("mul", new Class[]{double.class,double.class});
        //invoke 方法的第一个参数是被调用的对象,这里是静态方法故为null,第二个参数为给将被调用的方法传入的参数
        Object result = method.invoke(null, new Object[]{2.0,2.5});
        //如果方法mul是私有的private方法,按照上面的方法去调用则会产生异常NoSuchMethodException,这时必须改变其访问属性
        //method.setAccessible(true);//私有的方法通过发射可以修改其访问权限
        System.out.println(result);//结果为5.0
        //2. 获取类中的非静态方法
        Method method_2 = clz.getMethod("add", new Class[]{double.class,double.class});
        //这是实例方法必须在一个对象上执行
        Object result_2 = method_2.invoke(new Calculator(), new Object[]{2.0,2.5});
        System.out.println(result_2);//4.5
        //3. 获取没有方法签名的方法print
        Method method_3 = clz.getMethod("print", new Class[]{});
        Object result_3 = method_3.invoke(new Calculator(), null);//result_3为null,该方法不返回结果    
    }
}
复制代码
获取属性值
package com.company.myp1;

import java.lang.reflect.Field;

public class learn1 {
    public static void main(String[] args) throws IllegalAccessException {
        Dog d1=new Dog("xixi",11);
        Dog d2=new Dog("hehe",22);
        //获取某属性
        System.out.println(ClassUtil.getField(d1.getClass(),"name"));
        //获取某属性值
        Object o1=ClassUtil.getPropertyValue(d1,"name");
        System.out.println(o1);
        //获取全部属性值
        Field[] declaredFields = d1.getClass().getDeclaredFields();
        for (int i = 0; i < declaredFields.length; i++) {
            System.out.println(declaredFields[i].getName()+"  "+declaredFields[i].get(d2));
        }
    }

}
class Dog{
    String name;
    int age;

    public Dog() {
    }

    public Dog(String name, int age) {
        this.name = name;
        this.age = age;
    }
}
class ClassUtil {

    public static Object getPropertyValue(Object obj, String propertyName) throws IllegalAccessException {
        Class<?> Clazz = obj.getClass();
        Field field;
        if ((field = getField(Clazz, propertyName)) == null)
            return null;
        field.setAccessible(true);
        return field.get(obj);
    }

    public static Field getField(Class<?> clazz, String propertyName) {
        if (clazz == null)
            return null;
        try {
            return clazz.getDeclaredField(propertyName);//getDeclaredFiled 仅能获取类本身的属性成员(包括私有、共有、保护)
        } catch (NoSuchFieldException e) {
            return getField(clazz.getSuperclass(), propertyName);//getField 仅能获取类(及其父类可以自己测试) public属性成员
        }
    }
}
复制代码

性能对比分析

image.png

image.png

image.png

image.png

反射操作范型

image.png

image.png

image.png

image.png

反射操作注解

image.png

练习Demo,生成sql语句

image.png

package com.company.com.learn1;

import java.lang.annotation.*;
import java.lang.reflect.Field;

public class Demo {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
        Class c1 = Class.forName("com.company.com.learn1.Student"); //根据包名获取Class
        Annotation[] annotations=c1.getAnnotations();//方法返回此元素上存在的所有注解
        for (int i = 0; i < annotations.length; i++) {
            System.out.println(annotations[i]);//只获得了注解,想要注解里面的值
        }
        //获得注解value的值
        System.out.println("===============");
        TableName tableName = (TableName) c1.getAnnotation(TableName.class);
        System.out.println(tableName.value());

        //获得类指定的注解
        System.out.println("===============");
        Field f=c1.getDeclaredField("name");
        FieldName fieldName=f.getAnnotation(FieldName.class);
        System.out.println(fieldName.type());
        System.out.println(fieldName.lengtn());
        System.out.println(fieldName.columnName());
        //获得所有字段的列名,类型和长度
        System.out.println("===============");
        Field[] fieldNames = c1.getDeclaredFields();
        for (int i = 0; i < fieldNames.length; i++) {
            FieldName annotation = fieldNames[i].getAnnotation(FieldName.class);
            System.out.println(annotation.columnName()+"  "+annotation.type()+"  "+annotation.lengtn());
        }
        //获得所有字段的属性名,属性值和注解
        System.out.println("===============");
        Student s=new Student(1001,18,"小明");
        //s.getClass().getAnnotation();
        TableName annotation = s.getClass().getAnnotation(TableName.class);
        String mytableName=annotation.value();
        Field[] declaredFields = s.getClass().getDeclaredFields();
        StringBuffer value=new StringBuffer();
        StringBuffer column=new StringBuffer();
        for (int i = 0; i < declaredFields.length; i++) {
            declaredFields[i].setAccessible(true);
            System.out.println("属性名:" + declaredFields[i].getName() + " 属性值:" + declaredFields[i].get(s)+" 注解columnname "+ declaredFields[i].getAnnotation(FieldName.class).columnName());
            value.append(declaredFields[i].get(s));
            column.append(declaredFields[i].getAnnotation(FieldName.class).columnName());
            if(i!=declaredFields.length-1){
                value.append(" , ");
                column.append(" , ");
            }
        }
        //模仿自己生成sql
        System.out.println("===============");
        System.out.println("利用反射和注解生成sql语句:");
        String sql="INSERT INTO "+mytableName+" ( "+column+" ) VALUES "+" ( "+value+" )";
        System.out.println(sql);

    }
}

@TableName("db_student")
class Student{
    @FieldName(columnName = "db_id",type = "int",lengtn = 10)
    private int id;
    @FieldName(columnName = "db_age",type = "int",lengtn = 10)
    private int age;
    @FieldName(columnName = "db_name",type = "varchar",lengtn = 30)
    private String 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;
    }

    public String getName() {
        return name;
    }

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

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

    public Student() {
    }
}

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface TableName{
    String value();
}

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface FieldName{
    String columnName();
    String type();
    int lengtn();
}
复制代码

输出:

@com.company.com.learn1.TableName(value=db_student)
===============
db_student
===============
varchar
30
db_name
===============
db_id  int  10
db_age  int  10
db_name  varchar  30
===============
属性名:id 属性值:1001 注解columnname db_id
属性名:age 属性值:18 注解columnname db_age
属性名:name 属性值:小明 注解columnname db_name
===============
利用反射和注解生成sql语句:
INSERT INTO db_student ( db_id , db_age , db_name ) VALUES  ( 1001 , 18 , 小明 )

Process finished with exit code 0
复制代码

Guess you like

Origin juejin.im/post/7034760945498849293