Java注解和JDK新特性

1. 注解

1.1. 认识注解

Annotation:JDK1.5新提供的技术

  1. 编译检查:比如@SuppressWarnings, @Deprecated和@Override都具有编译检查的作用
  2. 替代配置文件:使用反射来读取注解的信息

注解就是代码里的特殊标记,用于替代配置文件,开发人员可以通过注解告诉类如何运行
注解的应用:通过反射得到类中的注解,以决定类的运行。注解可以标记在包、类、属性、方法,方法参数以及局部变量上,且同一个地方可以同时标记多个注解。
注解可以在编译,类加载,运行时被读取,并执行相应的处理,以便于其他工具补充信息或部署。

1.2. 内置注解

  • @Override:检查该方法是否是重载方法,如发现其父类,或者引用的接口中并没有该方法,会报编译错误
  • @Deprecated:编辑过时的方法,使用该方法,会报编译警告
  • @SuppressWarnings:指示编译器忽略注解中注明的警告
    在这里插入图片描述
    从Java7开始,新增了3个注解:
  • @SafeVarags:忽略任何使用参数为泛型变量的方法或构造函数调用产生的警告
  • @FunctionalInterface:标识一个匿名函数或函数式接口
  • @Repeatable:标识某注解可以在同一个声明上使用多次
@SuppressWarnings(value = {"all"})
public class Student implements Comparable<Student>, Serializable {
    @Override
    public int compareTo(Student o) {
        return 0;
    }
    @Override
    public String toString() {
        return super.toString();
    }
    public static void main(String[] args) {
        Date date = new Date();
        System.out.println(date.toLocaleString());
        Student stu = new Student();
        stu.method1();
        List list = new ArrayList();
    }
    @Deprecated
    public void method1() {
        System.out.println("========");
    }
    public void method2() {
        Date date = new Date();
        System.out.println(date.toLocaleString());
    }
}
public class TestStudent {
    @SuppressWarnings(value = "deprecation")
    public static void main(String[] args) {
        Student stu = new Student();
        stu.method1();
    }
}

1.3. 元注解

元注解是指注解的注解

  1. @Retention:约束注解的生命周期:源码级别(SOURCE)、类文件级别(CLASS)或运行时级别(RUNTIME),若没有@Retention,则默认是RetentionPolicy.CLASS,含义如下:
    • SOURCE:注解将被编译器丢弃(就是不会保留在class文件中)
    • CLASS:注解在class文件中可用,但会被VM丢弃(不会加载到虚拟机中)
    • RUNTIME:注解在运行期间(JVM)也保留,因此可以通过反射机制读取注解的信息,如SpringMVC中的@Controller、@ Autowired、@RequestMappling等
  2. @Target:用来约束注解可以应用的地方(如方法、类或字段),其中ElementType是枚举类型。若没有@Target,则该Annotation可以用于任何地方
public enum ElementType {
    // 标明该注解可以用于类、接口(包括注解类型)或enum声明
    TYPE,
    // 标明该注解可以用于字段(域)声明,包括enum实例
    FIELD,
    // 标明该注解可以用于方法声明
    METHOD,
    // 标明该注解可以用于参数声明
    PARAMETER,
    // 标明注解可以用于构造函数声明
    CONSTRUCTOR,
    // 标明注解可以用于局部变量声明
    LOCAL_VARIABLE,
    // 标明注解可以用于注解声明(应用于另一个注解上)
    ANNOTATION_TYPE,
    // 标明注解可以用于包声明上
    PACKAGE,
    // 标明注解可以用于类型参数声明(1.8+)
    TYPE_PARAMETER,
    // 类型使用声明(1.8+)
    TYPE_USE
}
  1. @Documented:标记这些注解是否包含在用户文档中
  2. @Inherited:指示注解类型被自动继承。如果在注解类型声明中存在Inherited元注解,并且用户在某一类声明中查询该注解类型,同时该类声明中没有此类型的注解,则将在该类的超类中自动查询该注解类型

2. 注解

2.1. 自定义注解

@Retention(RetentionPolicy.RUNTIME)
@Target(value = {ElementType.METHOD, ElementType.TYPE})
public @interface MyAnnoation {
    int id() default 0;
    String name() default "";
    double[] scoreArr() default {};
}
public @interface  MyAnnoation2 {
    // 如果有只有一个配置参数,一般命名为value
    String value();
}
@MyAnnoation2("wyb")
@MyAnnoation
public class TestAnnotation {
    @MyAnnoation(id=5, name="wyb", scoreArr = {78,89,34})
    public static void main(String[] args) {
    }
    @MyAnnoation2(value="wyb")
    public void method1() {
    }
}

总结:

  • 定义注解的关键字是@interface
  • 自定义注解中可以定义多个配置参数,不是成员方法或成员变量;说明参数的名称,以及参数值的类型
  • 如果只有一个配置参数,一般命名为value
  • 如果配置参数是value,并且只有一个配置参数,value可省略
    注意:
  • 定义注解时,意味着他实现了java.lang.annotation.Anntotation接口,即该注解就是一个Anntotation
  • 和implements实现接口的方法不同。Anntotation接口的实现细节都由编译器来完成。通过@interface定义注解后,该注解不能继承其他注解或接口
  • 注解常见的API及其关系如下:
    在这里插入图片描述

2.2. 使用反射读取注解

在这里插入图片描述
模拟实现MyBatis的注解并使用反射读取

@Retention(value = RetentionPolicy.RUNTIME)
@Target(value = ElementType.TYPE)
public @interface Table {
    String value();
}
@Retention(value = RetentionPolicy.RUNTIME)
@Target(value = ElementType.FIELD)
public @interface Column {
    String columnName();
    String columnType();
    int length();
    int precision() default 0;
}
@Table(value = "t_student")
public class Student1 {
    @Column(columnName = "id", columnType = "int", length = 6)
    private int id;
    @Column(columnName = "sname", columnType = "varchar", length = 10)
    private String name;
    @Column(columnName = "score", columnType = "double", length = 4, precision = 1)
    private double score;
}
public class TestORM {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
        String className = "com.wyb.annotation.Student";
        Class clazz = Class.forName(className);
        // 获取类的所有注解
        Annotation[] annotations = clazz.getAnnotations();
        for (Annotation annotation : annotations) {
            System.out.println(annotation);
        }
        // 获取类的指定注解
        Table annotation = (Table) clazz.getAnnotation(Table.class);
        System.out.println(annotation);
        System.out.println(annotation.value());
        // 获取id属性的注解
        Field idField = clazz.getDeclaredField("id");
        Column idColumn = idField.getAnnotation(Column.class);
        System.out.println(idColumn.columnName());
        System.out.println(idColumn.columnType());
        System.out.println(idColumn.length());
        System.out.println(idColumn.precision());
    }
}

3. JDK新特性

3.1. JDK8新特性

3.1.1. Lamda表达式

Lamda表达式是JDK8的一个新特性,可以取代大部分的匿名内部类,写出更优雅的Java代码,尤其在集合的遍历和其他集合操作中,可以极大的优化代码结构

public class TestLamda {
    public static void main(String[] args) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("线程任务");
            }
        }).start();
        // Lamda使用1
        new Thread(() -> System.out.println("线程任务")).start();
        TreeSet<Integer> set = new TreeSet<Integer>(new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return -(o1.intValue() - o2.intValue());
            }
        });
        TreeSet<Integer> set2 = new TreeSet<>((o1, o2) -> (o1.intValue() - o2.intValue()));
        TreeSet<Integer> set3 = new TreeSet<>((o1, o2) -> {
            int v1 = o1.intValue();
            int v2 = o2.intValue();
            return -(v1 - v2);
        });
        Collections.addAll(set, 34,55,77,99,22);
        System.out.println(set);
    }
}

Lambda表达式是一种匿名函数(不是匿名内部类),他是没有声明的方法,也没有访问修饰符、返回值声明和名字。实质是属于函数式编程的概念。
Lambda表达式只能引用标记了final的外层局部变量。Lambda表达式的局部变量可以不用声明为final,但是必须不可被后面的代码修改
虽然Lambda表达式可以对某些接口进行简单的实现,但并不是所有接口都可以使用Lambda表达式来实现。Lambda规定接口中只能有一个需要被实现的抽象方法,不是规定接口中只能有一个方法,称为函数式接口。

3.1.2. 函数式接口

函数式接口:只能有一个抽象方法,其他的可以有default、static、Object里public方法等
JDK8专门提供了@FunctionalInterface注解,用来进行编译检查
作用:在Java中主要用在Lambda表达式和方法引用上

@FunctionalInterface
public interface FunInterface {
    // 只能有一个抽象方法
    void method1();
    default void method2() {}
    static void method3() {}
    // 从Object类继承的public方法不会计数
    public int hashCode();
    public boolean equals(Object obj);
}

JDK也提供了大量的函数式接口,使得Lambda表达式的运用更加方便、高效。这些内置的函数式接口可以解决开发中的大部分问题,只有小部分特殊情况需要自己去定义函数式接口

  • Consumer<T>:消费型接口(void accept(T t))。有参数,无返回值
  • Supplier<T>:供给型接口(T get())。只有返回值,没有入参
  • Function<T, R>:函数型接口(R apply(T t))。一个输入参数,一个输出参数
  • Predicate<T>:断言型接口(boolean test(T t))。输入一个参数,输出一个boolean类型的返回值
public class TestFunctionInterface {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        Collections.addAll(list, "Java", "JS", "MySQL", "Oracle");
        /*Consumer consumer = new Consumer<String>() {
            @Override
            public void accept(String s) {
                System.out.println(s);
            }
        };
        list.forEach(consumer);*/
        list.forEach((o) -> System.out.println(o));
        /*Predicate predicate = new Predicate<String>() {
            @Override
            public boolean test(String s) {
                return s.length() == 4;
            }
        };
        list.removeIf(predicate);*/
        list.removeIf((o) -> o.length() == 4);
        System.out.println(list);
    }
}

3.1.3. Stream API

Stream API与java.io包里的InputStream和OutputStream是完全不同的概念。他是对容器对象功能的增强,专注于对容器对象进行各种便利、高效的聚合操作或大批量数据操作
Stream API提供串行和并行两种模式进行汇聚操作,并发模式能够充分利用多核处理器的优势,使用fork/join并行方式来拆分任务和加速处理过程。
Stream有三个操作步骤:

  1. 创建Stream:从一个数据源,如集合、数组中获取流
  2. 中间操作:一个操作的中间链,对数据源的数据进行操作
  3. 终止操作:一个终止操作,执行中间操作链,并产生结果

在这里插入图片描述
在这里插入图片描述
当数据源的数据上了流水线之后,这个过程对数据进行的所有操作都称为中间操作
中间操作会返回一个流对象,因此多个中间操作可以串连起来形成一个流水线,比如map、filter、distinct、sorted、peek、limit等
当所有的中间操作完成后,若要执行终止操作。终止操作将返回一个执行结果,这就是最终的数据。
多个中间操作可以连接成一个流水线,除非流水线上触发终止操作,否则中间操作不会执行任何处理,而在终止操作全部处理,称为惰性求值

public class TestStream {
    public static void main(String[] args) {
        List<Integer> list = new ArrayList();
        Collections.addAll(list, 99,22,334,66,77);
        // list.stream().forEach((x) -> System.out.println(x));
        list.stream().sorted((o1, o2) -> o2 - o1).forEach(System.out::println);
        System.out.println("---------");
        list.stream().limit(2).forEach(System.out::println);
        System.out.println("---------");
        list.stream().filter((o) -> o >= 60).sorted((o1, o2) -> o2 - o1).forEach(System.out::println);
    }
}

3.1.4. 新的日期类

在这里插入图片描述

3.2. 其他版本新特性

3.2.1. JDK9新特性

模块化系统:允许开发者模块化开发的应用程序,同时也对JDK本身进行了模块化,因此一个应用程序就可以只包含他所需要的类库
模块化就是在package外面包裹一层,成员的作用范围在当前包和当前项目之间又增加了一个层次:模块
String类的底层由char数组变为byte数组,节省空间。
接口中可以定义private的非抽象方法,便于将多个方法中的冗余代码进行提取且不对外公开。

public interface interface9 {
    void method1();
    // JDK 8
    default void method2() {
        method5();
    };
    // JDK 8
    default void method3() {
        method5();
    }
    // JDK 8
    static void method4() {}
    // JDK 9
    private void method5() {}
}

3.2.2. JDK10新特性

局部变量类型推断:将前端思想var关键字引入,自动检测所属类型

/*
* JDK 10 之前的局部变量定义
* */
public class Test1 {
    public static void main(String[] args) {
        int num = 10;
        Scanner sc = new Scanner(System.in);
        List<String> list = new ArrayList<>();
        Map<Integer, String> map = new HashMap<>();
        map.put(1, "wyb");
        map.put(2, "xz");
        map.put(3, "bjy");
        Set<Map.Entry<Integer, String>> entries = map.entrySet();
        for (Map.Entry<Integer, String> e : entries) {
            System.out.println(e.getKey() + "\t" + e.getValue());
        }
        int[] arr = {1, 2, 3};
        for (int a : arr) {
            System.out.println(a);
        }
        for (int i = 0; i < arr.length; i++) {
            System.out.println(arr[i]);
        }
    }
}

其实:=左侧的类型,不用显示编写出来,完全可以由右侧推断出左侧的类型

/*
* JDK 10的局部变量类型推断
* */
public class Test2 {
    public static void main(String[] args) {
        var num = 10;
        var sc = new Scanner(System.in);
        var list = new ArrayList<String>();
        var map = new HashMap<>();
        map.put(1, "wyb");
        map.put(2, "xz");
        map.put(3, "bjy");
        var entries = map.entrySet();
        for (var e : entries) {
            System.out.println(e.getKey() + "\t" + e.getValue());
        }
        var arr = new int[]{1,2,3};
        for (var a : arr) {
            System.out.println(a);
        }
        for (var i = 0; i < arr.length; i++) {
            System.out.println(arr[i]);
        }
    }
}

局部变量的类型推断必须具类型推断的基础才行。并不是任何地方都可以进行类型推断,比如:方法的返回值、行参,构造方法的行参等
注意:类型推断只能发生在编译阶段,在编译后的class文件会转为推断后的具体类型

  • 10
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Qi妙代码

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值