总被神化的注解与反射,值得了解了解

注解篇

注解与注释

所谓注释,就是在Java代码编写中插入一段说明文字,记录参数、方法的定义与使用等等,在编译后的class文件中是不存在的。
所谓注解,又称作Java标注,是Java语言5.0版本开始引入的一种特殊语法元数据。可根据需要保存到class文件中,甚至运行期加载的Class对象中。它是交由Java编译器阅读的,其本身对程序的执行是没有影响的,其本质就是一个继承了 Annotation 接口的接口。了解注解,以下几点必不可少:

元注解

  • @Retention:定义注解的生命周期
  • @Documented:文档注释,会被Javadoc工具文档化
  • @Inherited:是否让子类继承该注解
  • @Target:描述了注解的应用范围

元注解的作用就是负责注解其他注解,以上四个,最常用的是@Retention和@Target,分别来说明以下:
@Retention:它描述了注解保留的时间范围,定义在RetentionPolicy枚举中,有以下三种,一般来说,我们自定义的注解希望在运行期间依旧生效,所以一般定义RUNTIME

  • SOURCE:源文件保留
  • CLASS: 编译期间保留(默认)
  • RUNTIME :运行期间保留

@Target:它描述了注解的应用范围,包括以下几种备选项:

  • TYPE, // 修饰类、接口(包括注释类型)、枚举类
  • FIELD, // 修饰成员变量(包括枚举常量)
  • METHOD, // 修饰方法
  • PARAMETER, // 修饰形式参数
  • CONSTRUCTOR, // 修饰构造器
  • LOCAL_VARIABLE, // 修饰局部变量
  • ANNOTATION_TYPE, // 修饰注解类型
  • PACKAGE, // 修饰包
  • TYPE_PARAMETER, // 泛型使用,JDK 1.8 新增
  • TYPE_USE // 在任何用到类型的地方使用,JDK 1.8 新增

创建注解

掌握了元注解之后,就可以创建自定义注解了,使用**public @interface AnnotationName{}**方式创建。注解支持添加信息,Java的基本数据结构都支持,可如下添加:

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD,ElementType.TYPE})
public @interface User{
    String name() default "Eric";
    String[] mores();
    int type();
}

Java常用注解

除了我们自定义注解,Java中也提供了一些常用的Annotation,比如

  • @Deprecated:所标注内容,不再被建议使用。
  • @Override:表示该方法覆盖父类中的方法,只能标注方法。
  • @SuppressWarnings:所标注内容产生的警告,编译器会对这些警告保持静默。

了解了注解的基本内容之后,就会开始考虑怎么让注解生效,开始让代码真正利用注解实现一些功能,这就需要应用反射的知识了。

反射篇

反射:在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任何一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能成为Java语言的反射。
掌握常见的反射api就可以使用反射来实现一些功能:
图片信息来自网易云课堂微专业课程

反射的优缺点

  • 通过反射可以使程序访问到装载在JVM中的类的内部信息,获取已装载类的属性信息、类的方法、构造方法信息等。
  • 反射的应用降低了耦合性,提高自适应能力。
  • 反射会对性能造成一定的影响(这个影响要看具体业务,可能会有几百毫秒的延迟,对于要求不高的程序其实影响不大),同时让代码的可读性变低(主要缺点)

这里给出一个反射应用的例子:

反射的应用

功能描述:获取一条查询sql语句,包括实体类使用注解的所有字段
具体实现
定义两个注解:@SqlState (获取表名),@Column (获取字段)

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface SqlState {
    String value();
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Column {

    String value() default "";
    String name() default "";
}

创建实体类Student

@SqlState("student_t")
public class Student {

    @Column
    private Long id;

    @Column("order_no")
    private String name;

    @Column("student_id")
    private int studentId;

    @Column
    private String grade;

    @Column("school_name")
    private String schoolName;

    public Long getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }

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

    public int getStudentId() {
        return studentId;
    }

    public void setStudentId(int studentId) {
        this.studentId = studentId;
    }

    public String getGrade() {
        return grade;
    }

    public void setGrade(String grade) {
        this.grade = grade;
    }

    public String getSchoolName() {
        return schoolName;
    }

    public void setSchoolName(String schoolName) {
        this.schoolName = schoolName;
    }
}

创建组装sql类

public class GenerateSqlUtil {

    public String query(Object obj) throws  Exception{
        StringBuffer sb = new StringBuffer();// 拼接sql主体
        sb.append("select");
        StringBuffer sb2 = new StringBuffer();// 拼接查询字段

        Class<?> clz = obj.getClass();
        SqlState annotation = clz.getAnnotation(SqlState.class);// 获取SqlState注解

        if (annotation==null)
            throw new RuntimeException("注解缺失!");// 必须有表名
        String tableName = annotation.value();// 拿到表名

        Field[] declaredFields = clz.getDeclaredFields();// 获取类中的属性
        for (Field field : declaredFields) {
            Column column = field.getAnnotation(Column.class);// Column
            if (column==null)
                continue; //没写不生成查询字段
            if (column.value().equals("")){ // 拿到默认值的column说明实体类字段名与数据库定义的名字一致
                String name = field.getName();
                field.setAccessible(true);// 设置属性可见
                Object o = field.get(obj);
                if (o != null){// 下面只粗浅的判断参数值的类型
                    if(o instanceof String){
                        sb2.append(" and " + name + "='" + o + "'");
                    } else if(o instanceof Integer && Integer.parseInt(o.toString()) != 0){
                        sb2.append(" and " + name + "=" + o);
                    }
                }
                sb.append(" " + name + ",");
            }else{ // 为column注解value赋值的获取对应的值
                sb.append(" " + column.value() + ",");
                field.setAccessible(true);
                Object o = field.get(obj);
                if (o !=null && Integer.parseInt(o.toString()) != 0){
                    sb2.append(" and " + column.value() + "=" + o);
                }
            }
        }

        sb.deleteCharAt(sb.length()-1);// 去掉多余的“,”
        sb.append(" from " + tableName + " where 1=1");
        sb.append(sb2);
        return sb.toString();
    }
}

编写测试类

public class Test {
    public static void main(String[] args) throws Exception {
        GenerateSqlUtil generateSqlUtil = new GenerateSqlUtil();
        Student student = new Student();
        student.setStudentId(1);
        student.setName("张三");
        String s1 = generateSqlUtil.query(student);
        System.out.println(s1);// 输出结果:select id, name, student_id, grade, school_name from student_t where 1=1 and name='张三' and student_id=1
    }
}

虽然使用这种暴力拆解的方式实现功能不便于阅读,但对于反射的学习还是很有帮助的。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值