注解与反射

2022-10-03

什么是注解?

Java.Annotation

作用:

  • 不是程序本身,可以对程序作出一些解释,这一点和注释差不多
  • 可以被编译器读取

可以在各种东西上面添加

内置注解

  • @Override重写的注解
  • @Deprecated 不推荐使用
  • @SuppressWarnings 抑制编译时的warning信息

元注解

元注解的作用是负责解释其他的注解,java定义了四个meta-annotation

  • @Target 用来描述注解的适用范围
    • 需要传入参数,比如@Target(value= ElementType.Method)就表示该注解在方法上有效
  • @Retention 描述注解的生命周期
    • 表示我们的注解在什么地方还有效,比如@Retention(value=RetentionPolicy.RUNTIME)就是在所有地方有效
    • RUNTIME>class>source
  • @Document 表示注解将被包含在javadoc中
  • @Inherited 说明子类可以继承父类的该注解

自定义注解

@interface 加注解名,注意没有小括号!

例子:

//自定义注解
public class Demo01 {
    //注解可以显示赋值,如果没有默认值就一定要给其赋值
    @MyAnnotation1(name = "bo")
    public void test(){

    }
}

@Target(value = ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation1{
    //注意注解的参数:参数类型+参数名()
    String name() default "";//默认值为空
    int id() default -1;//默认为-1
    String[] schools() default {"uestc","qing hua"};
}

如果只有一个参数,建议使用value命名。

反射

Java.Reflection

反射是Java被视为动态语言的关键

加载完类之后,在堆内存的方法区中就产生了一个Class类型对象,一个类只有一个Class对象,这个对象就包含了完整的类的结构信息。我们可以通过这个对象看到完整的类的结构,这个对象就像一面镜子。于是我们形象地称之为:反射

  • 优点:可以动态实现创建对象和编译,体现出很大的灵活性
  • 缺点:对性能有影响,直接new出来的更快

image-20221004105024878

获取反射对象

//反射初学
public class Demo01 {
    public static void main(String[] args) throws ClassNotFoundException {
        //通过反射获取类的class对象
        Class c1 = Class.forName("com.bo.reflection.User");
        System.out.println(c1);
    }
}

//实体类
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;
    }

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

class com.bo.reflection.User

Process finished with exit code 0

得到Class类的几种方式

image-20221004152456979

方式:

//测试Class类的创建方式有哪些
public class Demo02 {
    public static void main(String[] args) throws ClassNotFoundException {
        Person person = new Student();
        System.out.println("这个人是"+person.name);
        //方式一,通过对象获得
        Class c1 = person.getClass();
        System.out.println(c1.hashCode());

        //方式二,forName
        Class c2 = Class.forName("com.bo.reflection.Student");
        System.out.println(c2.hashCode());
        //方式三:通过类名.class获得
        Class c3 = Student.class;
        System.out.println(c3.hashCode());
        //获得父类类型
        Class c4 = c1.getSuperclass();
        System.out.println(c4);
    }
}
class Person{
    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="老师";
    }

}

输出:

这个人是学生 460141958 460141958 460141958 class com.bo.reflection.Person

所有类型的Class对象

public class Demo03 {
    public static void main(String[] args) {
        Class c1 = Object.class;//类
        Class c2 = String.class;//字符串类
        Class c3 = Comparable.class;//接口
        Class c4 = String[].class;//一维数组
        Class c5 = int[][].class;//二维数组
        Class c6 = Override.class;//注解
        Class c7 = void.class;//void
        Class c8 = Integer.class;//基本类型
    }
}

一个类只有一个Class对象实例

动态创建对象执行方法

通过反射动态创建对象,调用Class对象的newInstance方法,条件:

  • 类必须有一个无参构造
  • 类的构造方法权限够

例子:

public class Demo04 {
    public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
        Class c1 = Class.forName("com.bo.reflection.User");
        User user = (User) c1.newInstance();//本质上是调用无参构造器
        System.out.println(user);

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

        //通过反射调用方法
        //通过反射获取一个方法
        Method setName = c1.getDeclaredMethod("setName", String.class);
        //invoke激活执行方法
        //参数:(对象,方法的参数的值)
        setName.invoke(user,"kun");
        System.out.println(user.getName());


        //通过反射操作属性
        User user3 = (User) c1.newInstance();
        Field name = c1.getDeclaredField("name");
        name.set(user3,"iho");
        System.out.println(user3.getName());
    }
}

解释一下为什么通过反射创建对象后,操作属性和方法传入的参数

第一个操作方法时,invoke,现在我们得到了一个方法,但是不知道这个方法是哪个对象去执行,所以需要传入一个对象

通过反射操作属性时的报错:

Class com.bo.reflection.Demo04 can not access a member of class com.bo.reflection.User with modifiers "private"

can not access,权限不够

这时需要改变user类的属性权限,或者关闭name的权限安全检测

name.setAccessible(true);

这样就成功了:

User{name='null', id=0, age=0}
User{name='bo', id=18, age=1}
kun
iho

Process finished with exit code 0

理解:通过注解和反射完成类和表结构的映射关系

假设数据库里有一张学生表,现在用对应的学生类去映射

public class Demo05 {

    public static void main(String[] args) throws ClassNotFoundException {
        Class c1 = Class.forName("com.bo.reflection.Student2");

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

        //获得注解的value的值
        TableBo annotation = (TableBo)c1.getAnnotation(TableBo.class);
        System.out.println(annotation.value());//打印注解TableBo的value
    }
}

@TableBo("db_student")
class Student2{
    @FieldBo(columnName = "db_id",type = "int",length = 10)
    private int id;
    @FieldBo(columnName = "db_age",type = "int",length = 10)
    private int age;
    @FieldBo(columnName = "db_name",type = "varchar",length = 3)
    private String name;

    public Student2() {
    }

    public Student2(int id, int age, String name) {
        this.id = id;
        this.age = age;
        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;
    }

    public String getName() {
        return name;
    }

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

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

//类名的注解
@Target(value = ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface TableBo{
    String value() default "";
}

//属性的注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface FieldBo{
    String columnName();//列名
    String type();//列的类型
    int length() default -1;//长度
}