三十八、Java基础之注解

一、自定义注解

1、创建自定义注解含义

/**
 * 创建自定义注解:
 * 1、新建一个普通的Java类
 * 2、将class关键字替换成@interface
 * 3、类型的作用:a、声明变量,b、声明方法参数,c、声明方法的返回类型,d、强制类型转换
 *
 * 我们声明的自定义注解实际上是一个接口,这个接口默认的继承自java.lang.annotation.Annotation接口
 * 声明自定义注解时,不需要指明继承自java.lang.annotation.Annotation接口,只要使用了@interface,编译器就会自动完成响应操作
 *
 *
 * 注解默认使用位置:Annotation和修饰符一样,应用于包,类型,构造方法,方法,成员变量,参数,本地变量的声明中
 */

2、实例注解1

public @interface MyAnnotation {
    /**
     * 在java中声明属性,又声明了方法
     * 注解中声明顺序的语法:属性类型 属性名称();
     * 属性命名类型,名词或名词性短语,第一个单词手字符小写,其余单词首字符大写
     * @return
     */

    String name();

    int age();

    String[] arr();

    String spaceName() default "石油大学";
}

3、实例注解2

@Target({ElementType.TYPE,ElementType.METHOD})//注解可以同时选择多个使用范围,规定自定义注解在类型上使用,在方法上使用
@Retention(RetentionPolicy.RUNTIME)//规定自定义注解在运行时保留,只有注解在运行时保留,我们才能在运行时通过反射获取注解信息
@Inherited//父类中使用的注解也可以被子类继承,子类也可以使用这个注解
import java.lang.annotation.*;

/**
 *
 *1、 @Target说明了Annotation所修饰的对象范围:
 * 即注解的作用域,用于说明注解的使用范围(即注解可以用在什么地方,比如类的注解,方法注解,成员变量注解等等)

 * 注意:如果Target元注解没有出现,那么定义的注解可以应用于程序的任何元素。
 * 取值是在java.lang.annotation.ElementType这个枚举中规定的:
 * 1.CONSTRUCTOR:用于描述构造器
 * 
2.FIELD:用于描述域

 * 3.LOCAL_VARIABLE:用于描述局部变量

 * 4.METHOD:用于描述方法

 * 5.PACKAGE:用于描述包

 * 6.PARAMETER:用于描述参数
 * 
7.TYPE:用于描述类、接口(包括注解类型) 或enum声明
 *
 *
 * 注解可以同时选择多个使用范围
 *
 * 2、@Retention定义了该Annotation被保留的时间长短:

 * (1)某些Annotation仅出现在源代码中,而被编译器丢弃;
---------------------------------注解信息只能在源代码中出现
 * (2)而另一些却被编译在class文件中;编译在class文件中的Annotation可能会被虚拟机忽略,----注解信息只在字节码中出现
 * 
(3)而另一些在class被装载时将被读取(请注意并不影响class的执行,因为Annotation与class在使用上是被分离的)。----也就是注解信息在JVM运行注解的时候依旧保留
 *
 * 注意:只有注解信息在运行时出现,我们才能在运行时使用反射操作获取注解信息
 *
 * 
使用这个meta-Annotation可以对 Annotation的“生命周期”限制。
 * @Retention的取值是在RetentionPoicy这个枚举中规定的
 * 1.SOURCE:在源文件中有效(即源文件保留)

 * 2.CLASS:在class文件中有效(即class保留)
 * 
3.RUNTIME:在运行时有效(即运行时保留)
 */


@Target({ElementType.TYPE,ElementType.METHOD})//注解可以同时选择多个使用范围,规定自定义注解在类型上使用,在方法上使用
@Retention(RetentionPolicy.RUNTIME)//规定自定义注解在运行时保留,只有注解在运行时保留,我们才能在运行时通过反射获取注解信息
@Inherited//父类中使用的注解也可以被子类继承,子类也可以使用这个注解
public @interface MyAnn {




}

二、@Override注解:方法执行覆盖

 /**
     * 方法重写也称为方法覆盖,当子类中从父类继承的方法不再合适子类的需要的时候,需要在子类中对该方法重新定义,即为方法覆盖(重写)
     * 方法覆盖的要求:
     * 1、 访问权限不能降低(低--->高:private----default---protected---public)
     * 2、返回类型必须一致
     * 3、方法名必须一致
     * 4、参数列表必须一致
     * 5、抛出的异常不能扩大(高--->低:Exception--IOException--FileOutFountException)
     */

    //@Override    /就是一个注解,这个注解的作用告诉编译器我们就是要执行方法覆盖,则编译器会按照方法覆盖的要求进行检测
public class Person {

    private String name;
    private int age;


    /**
     * 方法重写也称为方法覆盖,当子类中从父类继承的方法不再合适子类的需要的时候,需要在子类中对该方法重新定义,即为方法覆盖(重写)
     * 方法覆盖的要求:
     * 1、 访问权限不能降低(低--->高:private----default---protected---public)
     * 2、返回类型必须一致
     * 3、方法名必须一致
     * 4、参数列表必须一致
     * 5、抛出的异常不能扩大(高--->低:Exception--IOException--FileOutFountException)
     */

    //@Override    /就是一个注解,这个注解的作用告诉编译器我们就是要执行方法覆盖,则编译器会按照方法覆盖的要求进行检测


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

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

    public Person() {
        super();
    }

    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;
    }


}

三、@Deprecated注解:在一个类中由于某种原因某个方法已经不推荐再使用了,可以使用Deprecated注解

public class Test01 {

    public static void main(String[] args){

        Person per = new Person("张三",18);
        System.out.println(per);
        System.out.println(per.toString());

        test1();
    }

    /**
     * 在一个类中由于某种原因某个方法已经不推荐再使用了,可以使用Deprecated注解
     */
    @Deprecated
    public static void test1(){
        System.out.println("废弃");
    }
}

四、@SuppressWarnings({"rawtypes","unused"})注解:让编译器忽略多个警告

/**
         * 使用集合的时候,应该使用泛型,如果没有使用泛型,编译器就会出现警告信息
         * 警告信息不影响代码的执行,但是影响代码的美观
         * 可使用SuppressWarnings注解,忽略警告,格式:属性=属性值
         */
        //@SuppressWarnings(value={"rawtypes"})//让编译器忽略警告
        //@SuppressWarnings(value={"rawtypes","unused"})//让编译器忽略多个警告
import java.util.ArrayList;
import java.util.Collection;

public class Test02 {

    public static void main(String[] args){

        /**
         * 使用集合的时候,应该使用泛型,如果没有使用泛型,编译器就会出现警告信息
         * 警告信息不影响代码的执行,但是影响代码的美观
         * 可使用SuppressWarnings注解,忽略警告,格式:属性=属性值
         */
        //@SuppressWarnings(value={"rawtypes"})//让编译器忽略警告
        //@SuppressWarnings(value={"rawtypes","unused"})//让编译器忽略多个警告

        @SuppressWarnings({"rawtypes","unused"})//直接写属性值
        Collection coll = new ArrayList();



    }
}

五、注解的作用

/**
 *注解默认使用位置:Annotation和修饰符一样,应用于包,类型,构造方法,方法,成员变量,参数,本地变量的声明中
 *
 * 注解中属性没有顺序,多个属性之间用逗号隔开
 *
 *
 * 自定义注解中如果只有一个属性,就可以直接使用属性值
 *
 */
@MyAnnotation(name="李四",age=18 , arr={"hh","ww"})
public class Test03 {


    @MyAnnotation(age=18 , arr={"hh","ww"},name="李四",spaceName ="北京大学")//成员变量
    private String name;


    //方法
    @MyAnnotation(name="李四",age=18 , arr={"hh","ww"})
    public static void main(String[] args){

        //局部变量
        @MyAnnotation(name="李四",age=18 , arr={"hh","ww"})
        String str = "北京";

    }
}

六、元注解

   就是注解的注解,就是给你自己定义的注解添加注解,你自己定义了一个注解,但你想要你的注解有什么样的功能,此时就需要用元注解对你的注解进行说明

七、反射

/**
 * class class{
 *
 *
 * }
 * Class类描述类对象的信息:
 * 类对象信息包括:
 * 1、类对象基本信息:包名,修饰符,类名,基类,实现的接口
 * 2、类属性的信息:修饰符,属性类型,属性名称,属性值
 * 3、类的方法信息:修饰符,返回方法,方法名称,参数列表,抛出的异常
 * 4、类构造方法的信息:修饰符,类名,参数列表,抛出的异常
 * 5、类中携带的注解信息
 * 因为:类对象信息保存在class类中,所以Class类中会提供获取这些信息的方法,java中每个类型都有对应的class
 *
 *
 * 要想执行反射操作,必须要获取指定类型的Class
 * 获取Class的方式:
 * 1、获取基本类型的Class:
 *      1,基本类型.class,例如int.class,获取的就是int类型的Class对象
 *      2,包装器类型.TYPE,例如Integer.TYPE,基本类型.class,例如int.class,
 * 2、获取引用类型的Class:
 *      1,引用类型.class,例如String.class,获取的是String类型的Class对象
 *      2,通过对象获取:
 *          Object obj = "Hello";
 *          Class cls=obj。getClass();获取的是String类型的Class对象
 *3、CLass.forName("java.lang.String"),获取的是String类型的Class对象
 */

八、@Inherited//父类中使用的注解也可以被子类继承,子类也可以使用这个注解;@Retention(RetentionPolicy.RUNTIME)//规定自定义注解在运行时保留,只有注解在运行时保留,我们才能在运行时通过反射获取注解信息

1、注解1

import java.lang.annotation.*;

@Target({ElementType.TYPE,ElementType.METHOD})//注解可以同时选择多个使用范围,规定自定义注解在类型上使用,在方法上使用
@Retention(RetentionPolicy.RUNTIME)//规定自定义注解在运行时保留,只有注解在运行时保留,我们才能在运行时通过反射获取注解信息
@Inherited//父类中使用的注解也可以被子类继承,子类也可以使用这个注解


public @interface MyAnn1 {

}

2、注解2

import java.lang.annotation.*;

@Target({ElementType.TYPE})//注解可以同时选择多个使用范围,规定自定义注解在类型上使用,在方法上使用
@Retention(RetentionPolicy.RUNTIME)//规定自定义注解在运行时保留,只有注解在运行时保留,我们才能在运行时通过反射获取注解信息
public @interface MyAnn2 {
    
}

3、父子类使用注解1

@MyAnn2
public class SubClass extends SuperClass{


}
@MyAnn1
public class SuperClass {

}

4、测试类

import java.lang.annotation.Annotation;

public class Test {

    public static void main(String[] args){

        getAnnotations();
    }



    //获取子类中使用的所有的注解
    @SuppressWarnings("all")
    public static void getAnnotations(){

        //获取子类的类对象
        Class cls=SubClass.class;

        //在一个类中可以使用多个注解,所以cls.getAnnotations();返回的是一个注解的数组
        Annotation[] annos = cls.getAnnotations();


        System.out.println("数组的长度为:"+annos.length);

        for (Annotation anno: annos
             ) {
            System.out.println(anno);
        }


    }


}

输出:

数组的长度为:2
@com.csjin.edu.TestAnnotations.other1.MyAnn1()
@com.csjin.edu.TestAnnotations.other1.MyAnn2()

 九、@Inherited//父类中使用的注解也可以被子类继承,子类也可以使用这个注解;@Retention(RetentionPolicy.CLASS)//规定自定义注解在字节码中保留

1、注解1

import java.lang.annotation.*;

@Target({ElementType.TYPE,ElementType.METHOD})//注解可以同时选择多个使用范围,规定自定义注解在类型上使用,在方法上使用
@Retention(RetentionPolicy.CLASS)//规定自定义注解在字节码中保留
@Inherited//父类中使用的注解也可以被子类继承,子类也可以使用这个注解


public @interface MyAnn1 {




}

2、注解2

import java.lang.annotation.*;

@Target({ElementType.TYPE})//注解可以同时选择多个使用范围,规定自定义注解在类型上使用,在方法上使用
@Retention(RetentionPolicy.RUNTIME)//规定自定义注解在运行时保留,只有注解在运行时保留,我们才能在运行时通过反射获取注解信息
public @interface MyAnn2 {




}

3、父子类

@MyAnn2
public class SubClass extends SuperClass {


}
@MyAnn1
public class SuperClass {


}

4、测试类

import java.lang.annotation.Annotation;

public class Test{

    public static void main(String[] args){

        getAnnotations();
    }



    //获取子类中使用的所有的注解
    @SuppressWarnings("all")
    public static void getAnnotations(){

        //获取子类的类对象
        Class cls= SubClass.class;

        //在一个类中可以使用多个注解,所以cls.getAnnotations();返回的是一个注解的数组
        Annotation[] annos = cls.getAnnotations();


        System.out.println("数组的长度为:"+annos.length);

        for (Annotation anno: annos
             ) {
            System.out.println(anno);
        }


    }


}

输出:

数组的长度为:1
@com.csjin.edu.TestAnnotations.other1.other.MyAnn2()

 九、使用注解,返回一个创建数据库的SQL

1、创建注解Column

import java.lang.annotation.*;
/**
 * 用来携带字段信息的Column注解,再类的属性中使用
 * 字段信息包括:字段名称,字段类型,字段长度,字段约束
 */
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Column {

    String columnName();  //字段名称

    String columnType();  //字段类型

    String columnLength() default "10";  //字段长度

    String columnConstraint() default "";   //字段约束

}

2、创建一个实体类,JavaBean类

JavaBean的规范:
* 1、类中声明私有属性,并且提供公开的getter和setter方法
* 2、类中有公开的无参构造方法
* 3、类中要覆盖toString()方法
* 4、类中要覆盖hashCode()/equals(),为把JavaBean对象放到HashSet/HashMap中做好了准备
* 5、类中要实现Serializable接口,为序列化做准备
import java.io.Serializable;
import java.lang.annotation.Target;
import java.sql.Date;
import java.util.Objects;

/**
 * 一个实体对应一个数据库的表,实体类中的属性对应数据库中的字段
 * 一个数据库中的表对应一个Java类,表中的字段对应类中的属性和数据库表对应的Java类称为JavaBean
 *
 * JavaBean的规范:
 * 1、类中声明私有属性,并且提供公开的getter和setter方法
 * 2、类中有公开的无参构造方法
 * 3、类中要覆盖toString()方法
 * 4、类中要覆盖hashCode()/equals(),为把JavaBean对象放到HashSet/HashMap中做好了准备
 * 5、类中要实现Serializable接口,为序列化做准备
 *
 *
 * 根据一个已有的JavaBean类,自动生成数据库的建表语句DDL
 * 数据库建表需要以下信息
 * 1、数据库表名称
 * 2、字段相关信息:字段名称,字段类型,字段长度,字段约束
 *
 * 这些信息如何携带?
 * 注解在实际开发中的作用,通过注解在类中携带信息,然后通过反射操作来获取信息
 */

//携带表名信息的@Table注解,在类型上面使用,需要携带表明信息,在注解中通过属性来携带信息
//
@Table(tableName = "EMP1")
public class Emp implements Serializable{

    @Column(columnName = "emp_no",columnType = "INT",columnLength = "4",columnConstraint = "PRIMARY KEY")
    private Integer empno;

    @Column(columnName = "e_name",columnType = "VARCHAR",columnLength = "64")
    private String ename;

    @Column(columnName = "hire_date",columnType = "DATE")
    private Date hiredate;

    @Column(columnName = "e_job",columnType = "VARCHAR",columnLength = "20")
    private String job;

    @Column(columnName = "e_sal",columnType = "DOUBLE",columnLength = "10,2")
    private double sal;

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Emp emp = (Emp) o;
        return Double.compare(emp.sal, sal) == 0 &&
                empno.equals(emp.empno) &&
                ename.equals(emp.ename) &&
                hiredate.equals(emp.hiredate) &&
                job.equals(emp.job);
    }

    @Override
    public int hashCode() {
        return Objects.hash(empno, ename, hiredate, job, sal);
    }

    @Override
    public String toString() {
        return "Emp{" +
                "empno=" + empno +
                ", ename='" + ename + '\'' +
                ", hiredate=" + hiredate +
                ", job='" + job + '\'' +
                ", sal='" + sal + '\'' +
                '}';
    }

    public Emp() {
    }

    public Integer getEmpno() {
        return empno;
    }

    public void setEmpno(Integer empno) {
        this.empno = empno;
    }

    public String getEname() {
        return ename;
    }

    public void setEname(String ename) {
        this.ename = ename;
    }

    public Date getHiredate() {
        return hiredate;
    }

    public void setHiredate(Date hiredate) {
        this.hiredate = hiredate;
    }

    public String getJob() {
        return job;
    }

    public void setJob(String job) {
        this.job = job;
    }

    public Double getSal() {
        return sal;
    }

    public void setSal(Double sal) {
        this.sal = sal;
    }
}

3、创建一个表名称的注解Table

import java.lang.annotation.*;

/**
 * 携带表名信息的@Table注解,在类型上面使用,需要携带表明信息,在注解中通过属性来携带信息
 *
 */
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Table {

    String tableName();
}

4、测试类

代码实现顺序:
* 1、按照规范声明一个JavaBean类型
* 2、为了携带表名信息,创建一个Table注解,然后在EMP类中使用,Table注解
* 3、为了携带字段信息,创建一个Column注解,然后在EMP表中的属性使用Column注解
* 4、使用反射读取注解信息,生成数据库建表语句
* 5、连接JDBC,在数据库中生成表

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

/**
 *
 * Java中携带信息的方式:
 * 1、通过注解来携带信息,然后通过反射来获取信息
 * 2、通过文件来携带信息,然后通过IO流来获取信息
 *
 *
 * 代码实现顺序:
 * 1、按照规范声明一个JavaBean类型
 * 2、为了携带表名信息,创建一个Table注解,然后在EMP类中使用,Table注解
 * 3、为了携带字段信息,创建一个Column注解,然后在EMP表中的属性使用Column注解
 * 4、使用反射读取注解信息,生成数据库建表语句
 * 5、连接JDBC,在数据库中生成表
 */

public class Test {

    public static void main(String[] args){

        String s = buildSql();
        System.out.println(s);

    }

    private static String buildSql() {


        StringBuilder str = new StringBuilder("CREATE TABLE ");

        //1、获取Emp对应的Class
        Class cls = Emp.class;
        //2、通过Class获取Emp类中携带的表名信息
        //(1)获取Emp表中携带的注解
        Table table = (Table) cls.getAnnotation(Table.class);
        //(2)获取注解中的表明信息
        String tableName = table.tableName();
        //(3)将表名拼接到sql中
        str.append(tableName + "(");
        //3、通过Class获取Emp表中字段的信息
        //(1)获取类中所有的声明的所有属性,包括公开的,私有的,缺省的,保护的属性,但是没有继承的属性
        Field[] fields  = cls.getDeclaredFields();

        //4、遍历数据
        for (Field field :
               fields ) {
            //判断属性上是否携带注解
            if (field.isAnnotationPresent(Column.class)){
                //获取注解中的信息
                Column column = field.getDeclaredAnnotation(Column.class);

                String columnName = column.columnName();
                String columnType = column.columnType();
                String columnLength = column.columnLength();
                String columnConstraint = column.columnConstraint();

                //拼接到sql
                if ("hire_date".equalsIgnoreCase(columnName)){

                    str.append(columnName + " " + columnType + " " + columnConstraint + ",");

                }else {
                    str.append(columnName + " " + columnType + "(" + columnLength + ") " + columnConstraint + ",");
                }

            }
        }

        //5、去掉最后一个逗号,并且加上最后一个)
        String sql = str.substring(0, str.length() - 1)+")";


        return sql;
    }
}

 

posted @ 2019-10-22 15:51  旅行没有终点  阅读(249)  评论(0编辑  收藏  举报