Java8新特性学习

news2024/11/25 20:00:54

文章目录

    • Lambda表达式
      • 为什么使用Lambda表达式
      • Lambda表达式语法
        • 语法格式一:无参数,无返回值
        • 语法格式二:有一个参数,并且无返回值
        • 语法格式三:若只有一个参数,小括号可以省略不写
        • 语法格式四:有两个以上的参数,有返回值,并且 Lambda体中有多条语句
        • 语法格式五:若 Lambda体中只有一条语句, return和大括号都可以省略不写
        • 语法格式六:参数列表的数据类型可以省略不写
    • 函数式接口
      • 什么是函数式接口
      • Java内置函数式接口
        • 四大核心接口
        • 其他接口
    • 方法引用、构造器引用、数组引用
      • 方法引用
      • 构造器引用
      • 数组引用
    • 强大的Stream API
      • 什么是Stream
      • Stream操作的三个步骤
      • 创建Stream
      • 中间操作
        • 筛选与切片
        • 映射
        • 排序
      • 终止操作(终端操作)
        • 查找与匹配
        • 内部迭代
        • 最值、数量
        • 归约
        • 收集
    • 接口中的默认方法与静态方法
      • 默认方法
      • 静态方法
    • 新时间日期API
      • 简介
      • Instant时间戳
      • Duration和Period
      • 时间校正器
      • 解析和格式化日期或时间
      • 带时区的时间或日期
      • 与传统日期处理的转换
    • Optional类
    • JVM的新特性
    • 优化HashMap
    • 重复注解

Lambda表达式

为什么使用Lambda表达式

  • Lambda是一个匿名函数,相当于匿名内部类
  • 只有函数式接口(可以用@FunctionalInterface注解修饰) 可以使用Lambda表达式
  • 使用 Lambda省略了写实现类的繁琐步骤,可以写出更简洁、更灵活的代码,使Java的语言表达能力得到了提升。

Lambda表达式语法

Lambda 表达式在Java 语言中引入了一个新的操作符, “->”, 它将 Lambda 分为两个部分:
左侧:指定了 Lambda表达式的参数列表
右侧:指定了 Lambda体,即 Lambda表达式要执行的功能。

语法格式一:无参数,无返回值

() -> System.out.println("Hello Lambda!");

语法格式二:有一个参数,并且无返回值

(x) -> System.out.println(x);

语法格式三:若只有一个参数,小括号可以省略不写

x -> System.out.println(x)

语法格式四:有两个以上的参数,有返回值,并且 Lambda体中有多条语句

Comparator<Integer> com = (x, y) -> {
	System.out.println("函数式接口");
	return Integer.compare(x, y);
};

语法格式五:若 Lambda体中只有一条语句, return和大括号都可以省略不写

Comparator<Integer> com = (x, y) -> Integer.compare(x, y);

语法格式六:参数列表的数据类型可以省略不写

JVM编译器可以通过上下文推断出参数列表对应的数据类型,Lambda 表达式中无需指定类型,程序依然可以编译,即**“类型推断”**。

// Integer推荐省略
(Integer x, Integer y) -> Integer.compare(x, y);

语法速记口诀:
上联:左右遇一括号省
下联:左侧推断类型省
横批:能省则省

函数式接口

什么是函数式接口

  • 只包含一个抽象方法的接口,称为函数式接口。
  • 可以通过 Lambda 表达式来创建该接口的实现类。

函数式接口只会有一个抽象方法,default方法不属于抽象方法,接口重写了Object的公共方法也不算入内,所以Comparator是函数式接口。

Java内置函数式接口

四大核心接口

接口名参数类型返回类型用途
Consumer<T> 消费型接口Tvoidvoid accept(T t);
适合“传递参数没有返回值”的场景
Supplier<T> 供给型接口TT get();
适合“没有参数有返回值”的场景
Function<T,R> 函数型接口TRR apply(T t);
适合“传递参数有返回值”的场景
Predicate<T> 断定型接口Tbooleanboolean test(T t);
适合“传递参数返回boolean值”的场景

其他接口

接口名参数类型返回类型用途
BiFunction<T, U, R>T, URR apply(T t, U u);
适合“传递两个参数有返回值”的场景
UnaryOperator<T> (Function子接口)TTT apply(T t);
进行一元运算,适合“传递一个参数有返回值”的场景
BinaryOperator<T>(BiFunction 子接口)T, TTT apply(T t1, T t2);
进行二元运算,适合“传递两个参数有返回值”的场景
BiConsumer<T, U>Tvoidvoid accept(T t, U u);
适合“传递两个参数没有返回值”的场景

方法引用、构造器引用、数组引用

方法引用

若Lambda 体中的功能,已经有方法提供了实现,可以使用方法引用。
可以将方法引用理解 Lambda 表达式的另外一种表现形式
使用操作符 “::” 将方法名和对象或类的名字分隔开来。
主要有下面三种使用情况:

  • 对象::实例方法
// 示例1
PrintStream ps = System.out;
Consumer<String> con = (str) -> ps.println(str);
con.accept("Hello World!");

Consumer<String> con2 = ps::println;
con2.accept("Hello Java8!");
// 示例2
Employee emp = new Employee(101, "张三", 18, 9999.99);
Supplier<String> sup = () -> emp.getName();
System.out.println(sup.get());

Supplier<String> sup2 = emp::getName;
System.out.println(sup2.get());
  • 类::静态方法
// 示例1
BiFunction<Double, Double, Double> fun = (x, y) -> Math.max(x, y);
System.out.println(fun.apply(1.5, 22.2));
BiFunction<Double, Double, Double> fun2 = Math::max;
System.out.println(fun2.apply(1.2, 1.5));
// 示例2
Comparator<Integer> com = (x, y) -> Integer.compare(x, y);
Comparator<Integer> com2 = Integer::compare;
  • 类::实例方法
// 示例1
BiPredicate<String, String> bp = (x, y) -> x.equals(y);
System.out.println(bp.test("abcde", "abcde"));
BiPredicate<String, String> bp2 = String::equals;
System.out.println(bp2.test("abc", "abc"));
// 示例2
Function<Employee, String> fun = (e) -> e.show();
System.out.println(fun.apply(new Employee()));
Function<Employee, String> fun2 = Employee::show;
System.out.println(fun2.apply(new Employee()));

注意:

  • 方法引用所引用的方法的参数列表与返回值类型,需要与函数式接口中抽象方法的参数列表和返回值类型保持一致。
  • 若Lambda 的参数列表的第一个参数,是实例方法的调用者,第二个参数(或无参)是实例方法的参数时,格式: ClassName::MethodName

构造器引用

构造器的参数列表,需要与函数式接口中参数列表保持一致。
语法格式:
类名 :: new
示例:

Function<String, Employee> fun = Employee::new;

数组引用

语法格式:
类型[] :: new
示例:

Function<Integer, String[]> fun = (length) -> new String[length];
String[] strs = fun.apply(10);
System.out.println(strs.length);
Function<Integer, String[]> fun2 = String[]::new;
String[] strs2 = fun2.apply(15);
System.out.println(strs2.length);

强大的Stream API

Stream 是 Java8 中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。使用Stream API 对集合数据进行操作,就类似于使用 SQL 执行的数据库查询。也可以使用 Stream API 来并行执行操作。简而言之,Stream API 提供了一种高效且易于使用的处理数据的方式。

什么是Stream

Stream是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列。
集合讲的是数据,流讲的是计算!

注意:

  • Stream 自己不会存储元素。
  • Stream 不会改变源对象。相反,他们会返回一个持有结果的新Stream。
  • Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。

Stream操作的三个步骤

在这里插入图片描述
Stream操作的三个步骤如下:

  • 创建 Stream
    一个数据源(如:集合、数组),获取一个流
  • 中间操作
    一个中间操作链,对数据源的数据进行处理
  • 终止操作(终端操作)
    一个终止操作,执行中间操作链,并产生结果

示例如下:

//所有的中间操作不会做任何的处理
Stream<Employee> stream = emps.stream()
	.filter((e) -> {
		System.out.println("测试中间操作");
		return e.getAge() <= 35;
	});
//只有当做终止操作时,所有的中间操作会一次性的全部执行,称为“惰性求值”
stream.forEach(System.out::println);

创建Stream

一、Java8 中的Collection接口被扩展,提供了两个获取流的方法:

  • default Stream stream() : 返回一个顺序流
  • default Stream parallelStream() : 返回一个并行流

示例代码如下:

//1、 Collection提供了两个方法  stream() 与 parallelStream()
List<String> list = new ArrayList<>();
Stream<String> stream = list.stream(); //获取一个顺序流
Stream<String> parallelStream = list.parallelStream(); //获取一个并行流

二、由数组创建流
Java8 中的 Arrays 的静态方法 stream() 可以获取数组流:
static Stream stream(T[] array): 返回一个流。

示例代码如下:

//2、 通过 Arrays 中的 stream() 获取一个数组流
Integer[] nums = new Integer[10];
Stream<Integer> stream1 = Arrays.stream(nums);

三、用静态方法Stream.of()
可以使用静态方法 Stream.of(), 创建一个流。它可以接收任意数量的参数。
public static Stream of(T… values) : 返回一个流。
示例代码如下:

//3、通过 Stream 类中静态方法 of()
Stream<Integer> stream2 = Stream.of(1,2,3,4,5,6);

四、创建无限流
示例代码:

//迭代
Stream<Integer> stream3 = Stream.iterate(0, (x) -> x + 2).limit(10);
stream3.forEach(System.out::println);
//生成
Stream<Double> stream4 = Stream.generate(Math::random).limit(2);
stream4.forEach(System.out::println);

中间操作

多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作,否则中间操作不会执行任何的处理!而在终止操作时一次性全部处理,称为**“惰性求值”**。

筛选与切片

常用方法如下:

方法描述
filter(Predicate p)过滤: 接收 Lambda , 从流中排除某些元素。
limit(long maxSize)截断流: 使其元素不超过给定数量。
skip(long n)跳过元素: 返回一个扔掉了前 n 个元素的流。若流中元素不足 n 个,则返回一个空流。
limit(n) 互补
distinct()去重: 通过流所生成元素的hashCode()和equals()去除重复元素

示例代码如下:

// 1、filter过滤
//所有的中间操作不会做任何的处理
Stream<Employee> stream = emps.stream()
	.filter((e) -> {
		System.out.println("测试中间操作");
		return e.getAge() <= 35;
	});
//只有当做终止操作时,所有的中间操作会一次性的全部执行,称为“惰性求值”
stream.forEach(System.out::println);
// 2、limit截断流
emps.stream()
	.filter((e) -> {
		System.out.println("短路!"); // &&  ||
		return e.getSalary() >= 5000;
	})
	.limit(3)
	.forEach(System.out::println);
// 3、skip跳过元素
emps.parallelStream()
	.filter((e) -> e.getSalary() >= 5000)
	.skip(2)
	.forEach(System.out::println);
// 4、distinct去重
emps.stream()
	.distinct()
	.forEach(System.out::println);

映射

常用方法如下:

方法描述
map(Function f)接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
mapToDouble(ToDoubleFunction f)接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的DoubleStream。
mapToInt(ToIntFunction f)接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的IntStream。
mapToLong(ToLongFunction f)接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的LongStream。
flatMap(Function f)接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流

示例代码如下:

public static void main(String[] args) {
     List<String> strList = Arrays.asList("aaa", "bbb", "ccc", "ddd", "eee");
     // 1、map简单使用
     Stream<String> stream = strList.stream()
             .map(String::toUpperCase);
     stream.forEach(System.out::println);

     Stream<Stream<Character>> stream2 = strList.stream()
             .map(Test::filterCharacter);
     // 输出aaabbbcccdddeeeaaabbbcccdddeee
     stream2.forEach((sm) -> {
         sm.forEach(System.out::print);
     });

     // 2、flatMap使用:相当于List的addAll()
     Stream<Character> stream3 = strList.stream()
             .flatMap(Test::filterCharacter);
     // 输出aaabbbcccdddeeeaaabbbcccdddeee
     stream3.forEach(System.out::print);
 }

// 把字符串中的每一个字符加入到list,返回流
 public static Stream<Character> filterCharacter(String str){
     List<Character> list = new ArrayList<>();
     for (Character ch : str.toCharArray()) {
         list.add(ch);
     }
     return list.stream();
 }

排序

常用方法如下:

方法描述
sorted()产生一个新流,按自然顺序排序。
sorted(Comparator comp)产生一个新流,按比较器顺序排序。

示例代码如下:

// 自然排序
emps.stream()
	.map(Employee::getName)
	.sorted()
	.forEach(System.out::println);
// 定制排序
emps.stream()
	.sorted((x, y) -> {
		if(x.getAge() == y.getAge()){
			return x.getName().compareTo(y.getName());
		}else{
			return Integer.compare(x.getAge(), y.getAge());
		}
	}).forEach(System.out::println);

终止操作(终端操作)

注意:
流进行了终止操作后,不能再次使用。

查找与匹配

常用方法如下:

方法描述
allMatch(Predicate p)检查是否匹配所有元素
anyMatch(Predicate p)检查是否至少匹配一个元素
noneMatch(Predicate p)检查是否没有匹配所有元素
findFirst()返回第一个元素
findAny()返回当前流中的任意元素

示例代码如下:

boolean bl = emps.stream()
	.allMatch((e) -> e.getStatus().equals(Status.BUSY));

boolean bl1 = emps.stream()
	.anyMatch((e) -> e.getStatus().equals(Status.BUSY));

boolean bl2 = emps.stream()
	.noneMatch((e) -> e.getStatus().equals(Status.BUSY));

Optional<Employee> op = emps.stream()
	.sorted((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()))
	.findFirst();
System.out.println(op.get());

Optional<Employee> op2 = emps.parallelStream()
	.filter((e) -> e.getStatus().equals(Status.FREE))
	.findAny();
System.out.println(op2.get());

内部迭代

方法描述
forEach(Consumer c)内部迭代

最值、数量

方法描述
count()返回流中元素总数
max(Comparator c)返回流中最大值
min(Comparator c)返回流中最小值

示例代码如下:

long count = emps.stream()
				 .filter((e) -> e.getStatus().equals(Status.FREE))
				 .count();

Optional<Double> op = emps.stream()
	.map(Employee::getSalary)
	.max(Double::compare);

Optional<Employee> op2 = emps.stream()
	.min((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()));

归约

方法描述
reduce(T iden, BinaryOperator b)可以将流中元素反复结合起来,得到一个值。
返回 T。
reduce(BinaryOperator b)可以将流中元素反复结合起来,得到一个值。
返回Optional

示例代码如下:

List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
Integer sum = list.stream()
	.reduce(0, (x, y) -> x + y);
System.out.println(sum);

Optional<Double> op = emps.stream()
	.map(Employee::getSalary)
	.reduce(Double::sum);
System.out.println(op.get());

// 搜索名字中 “六” 出现的次数
Optional<Integer> sum = emps.stream()
	.map(Employee::getName)
	.flatMap(Test::filterCharacter)
	.map((ch) -> {
		if(ch.equals('六'))
			return 1;
		else 
			return 0;
	}).reduce(Integer::sum);
System.out.println(sum.get());

收集

方法描述
collect(Collector c)将流转换为其他形式。接收一个Collector接口的实现,用于给Stream中元素做汇总的方法

Collector 接口中方法的实现决定了如何对流执行收集操作(如收集到 List、Set、Map)。但是Collectors 实用类提供了很多静态方法,可以方便地创建常见收集器实例,具体方法与实例如下表:
在这里插入图片描述
在这里插入图片描述

示例代码如下:

List<DeptInfoVo> deptInfoVoList = getDeptInfoVoData();
Set<String> set = deptInfoVoList.stream()
        .map(DeptInfoVo::getDeptName)
        .collect(Collectors.toSet());

HashSet<String> hashSet = deptInfoVoList.stream()
        .map(DeptInfoVo::getDeptName)
        .collect(Collectors.toCollection(HashSet::new));

// 分组
Map<Status, List<Employee>> map = emps.stream()
.collect(Collectors.groupingBy(Employee::getStatus));

// 多级分组
Map<Status, Map<String, List<Employee>>> map = emps.stream()
			.collect(Collectors.groupingBy(Employee::getStatus, Collectors.groupingBy((e) -> {
				if(e.getAge() >= 60)
					return "老年";
				else if(e.getAge() >= 35)
					return "中年";
				else
					return "成年";
			})));

接口中的默认方法与静态方法

默认方法

Java 8中允许接口中包含具有具体实现的方法,该方法称为“默认方法”,默认方法使用 default关键字修饰。

接口默认方法的类优先原则:
一个子类继承了一个父类,同时实现了一个接口。这个父类和接口有同名(默认)的方法,则字类调用方法会有些调用父类的方法。
示例代码如下:

public class ParentClass {
    public String getName(){
        return "嘿嘿嘿";
    }
}

public interface MyInterface {
    default String getName(){
        return "哈哈哈哈";
    }
}

public class Test {
    public static void main(String[] args) {
        SubClass subClass = new SubClass();
        // 输出:嘿嘿嘿
        System.out.println(subClass.getName());
    }
 }

当然如果子类再重写方法的话,以子类的为准。

接口默认方法冲突的解决方法:
如果一个父接口提供一个方法,而另一个父接口接口也提供了一个具有相同名称和参数列表的方法(不管方法是否是默认方法),那么必须重写该方法来解决冲突
示例代码如下:

public interface MyInterface {
    default String getName(){
        return "嘿嘿嘿嘿";
    }
}
public interface MyInterfaceTwo {
    default String getName(){
        return "哈哈哈哈";
    }
}
public class SubClass implements MyInterface,MyInterfaceTwo {
    // 必须重写该方法
    @Override
    public String getName() {
        // return MyInterface.super.getName();
        return "我是子类重写的方法";
    }
}
public class Test {
    public static void main(String[] args) {
        SubClass subClass = new SubClass();
        // 输出:我是子类重写的方法
        System.out.println(subClass.getName());
    }
 }

Java中Super的用法

静态方法

Java8接口中允许添加静态方法。
示例代码如下:

public interface MyInterface {
    static String getName(){
        return "我是静态方法";
    }
}
public class Test {
    public static void main(String[] args) {
    	//输出:我是静态方法
        System.out.println(MyInterface.getName());
    }
  }

新时间日期API

简介

SimpleDateFormat有线程安全问题,且Date类不好用。
Java8提供了LocalDate、LocalTime和LocalDateTime 类,它们的实例是不可变的对象做任何操作都会新生成一个对象,分别表示使用 ISO-8601日历系统的日期、时间、日期和时间。它们提供了简单的日期或时间,并不包含当前的时间信息。也不包含与时区相关的信息。

注:ISO-8601日历系统是国际标准化组织制定的现代公民的日期和时间的表示法
相关方法如下:
在这里插入图片描述
示例代码如下:

LocalDateTime ldt = LocalDateTime.now();
System.out.println(ldt);
System.out.println(ldt.getYear());
System.out.println(ldt.getMonthValue());
System.out.println(ldt.getDayOfMonth());
System.out.println(ldt.getHour());
System.out.println(ldt.getMinute());
System.out.println(ldt.getSecond());

LocalDateTime ld2 = LocalDateTime.of(2016, 11, 21, 10, 10, 10);
System.out.println(ld2);

LocalDateTime ldt3 = ld2.plusYears(20);
System.out.println(ldt3);

LocalDateTime ldt4 = ld2.minusMonths(2);
System.out.println(ldt4);

Instant时间戳

Instant用于“时间戳”的运算。它是以Unix元年(传统的设定为UTC时区1970年1月1日午夜时分)开始所经历的毫秒数进行运算。
示例代码如下:

// 默认使用 UTC 时区
Instant ins = Instant.now();
System.out.println(ins);

// 使用东8区
OffsetDateTime odt = ins.atOffset(ZoneOffset.ofHours(8));
System.out.println(odt);
// 获取毫秒数
System.out.println(ins.toEpochMilli());

// 1970元年之后5秒
Instant ins2 = Instant.ofEpochSecond(5);
System.out.println(ins2);

Duration和Period

1、Duration: 用于计算两个“时间”间隔
2、Period:用于计算两个“日期”间隔

示例代码如下:

Instant ins1 = Instant.now();
try {
	Thread.sleep(1000);
} catch (InterruptedException e) {
}
Instant ins2 = Instant.now();
System.out.println("所耗费时间为:" + Duration.between(ins1, ins2));

LocalDate ld1 = LocalDate.now();
LocalDate ld2 = LocalDate.of(2011, 1, 1);
Period pe = Period.between(ld2, ld1);
System.out.println(pe.getYears());
System.out.println(pe.getMonths());
System.out.println(pe.getDays());

时间校正器

  • TemporalAdjuster : 时间校正器。有时我们可能需要获
    取例如:将日期调整到“下个周日”等操作。
  • TemporalAdjusters: 该类通过静态方法提供了大量的常
    用 TemporalAdjuster 的实现。

示例代码如下:

LocalDateTime ldt = LocalDateTime.now();
System.out.println(ldt);
// 获取下一个星期天
LocalDateTime ldt3 = ldt.with(TemporalAdjusters.next(DayOfWeek.SUNDAY));
System.out.println(ldt3);

//自定义:下一个工作日
LocalDateTime ldt5 = ldt.with((l) -> {
	LocalDateTime ldt4 = (LocalDateTime) l;
	DayOfWeek dow = ldt4.getDayOfWeek();
	if(dow.equals(DayOfWeek.FRIDAY)){
		return ldt4.plusDays(3);
	}else if(dow.equals(DayOfWeek.SATURDAY)){
		return ldt4.plusDays(2);
	}else{
		return ldt4.plusDays(1);
	}
});
System.out.println(ldt5);

解析和格式化日期或时间

示例代码如下:

//DateTimeFormatter dtf = DateTimeFormatter.ISO_LOCAL_DATE;
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss E");
LocalDateTime ldt = LocalDateTime.now();
String strDate = ldt.format(dtf);
System.out.println(strDate);
LocalDateTime newLdt = ldt.parse(strDate, dtf);
System.out.println(newLdt);

带时区的时间或日期

Java8 中加入了对时区的支持,带时区的时间为分别为:ZonedDate、ZonedTime、ZonedDateTime。其中每个时区都对应着 ID,地区ID都为 “{区域}/{城市}”的格式,
例如 :Asia/Shanghai。

示例代码如下:

LocalDateTime ldt = LocalDateTime.now(ZoneId.of("Asia/Shanghai"));
System.out.println(ldt);

ZonedDateTime zdt = ZonedDateTime.now(ZoneId.of("US/Pacific"));
System.out.println(zdt);

与传统日期处理的转换

相关方法如下:
在这里插入图片描述

Optional类

Optional 类(java.util.Optional) 是一个容器类,代表一个值存在或不存在,原来用 null 表示一个值不存在,现在 Optional 可以更好的表达这个概念。并且可以避免空指针异常。
常用方法如下:

方法描述
Optional.of(T t)创建一个 Optional实例
Optional.empty()创建一个空的 Optional实例
Optional.ofNullable(T t)若 t 不为 null,创建 Optional 实例,否则创建空实例。相当于Optional.of(T t)+Optional.empty()
isPresent()判断是否包含值
orElse(T t)如果调用对象包含值,返回该值,否则返回t
orElseGet(Supplier s)如果调用对象包含值,返回该值,否则返回 s 获取的值
map(Function f)如果有值对其处理,并返回处理后的Optional,否则返回 Optional.empty()
flatMap(Function mapper)与 map 类似,要求返回值必须是Optional

JVM的新特性

使用元空间Metaspace代替持久代PermGen space。
在JVM参数方面,使用-XX:MetaSpaceSize和-XX:MaxMetaspaceSize代替原来的-XX:PermSize和-XX:MaxPermSize。

优化HashMap

“数组+链表” 数据结构改为了 “数组+(链表/红黑树)”
当链表长度大于8,且HashMap总容量大于64,会将链表自动转为红黑树。
添加元素比链表慢,其他的都比链表更快速。

重复注解

Java8允许在同一申明类型(类,属性,或方法)的多次使用同一个注解,这就是重复注解。Java8开始注解可以应用在任何地方。
Java8之前对重复注解的支持
示例代码:

public @interface Authority {
     String role();
}
public @interface Authorities {
    Authority[] value();
}

public class RepeatAnnotationUseOldVersion {

    @Authorities({@Authority(role="Admin"),@Authority(role="Manager")})
    public void doSomeThing(){
    }
}

Java8之前,重复注解实现是由另一个注解来存储重复注解
在使用时候,用存储注解Authorities来扩展重复注解。
很明显,这种方式可读性不是很好

Java8对重复注解的支持
示例代码:

@Repeatable(Authorities.class)
public @interface Authority {
    String role();
}
public @interface Authorities {
   Authority[] value();
}

public class RepeatAnnotationUseNewVersion {
   @Authority(role="Admin")
   @Authority(role="Manager")
   public void doSomeThing(){ }
}

不同的地方是,创建重复注解Authority时,加上@Repeatable,指向存储注解Authorities
在使用时候,直接可以重复使用Authority注解。
从上面例子看出,java 8里面做法更适合常规的思维,可读性强一点

说明:本笔记整理自尚硅谷Java8新特性课程以及互联网,仅供学习使用。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/101719.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Docker容器数据卷

是什么 卷就是目录或文件&#xff0c;存在于一个或多个容器中&#xff0c;由docker挂载到容器&#xff0c;但不属于联合文件系统&#xff0c;因此能够绕过Union File System提供一些用于持续存储或共享数据的特性&#xff1a;卷的设计目的就是数据的持久化&#xff0c;完全独立…

LSTM(Long Short-Term Memory)

长短期记忆&#xff08;long short-term memory&#xff0c;LSTM&#xff09;&#xff0c;LSTM 中引入了3个门&#xff0c;即输入门&#xff08;input gate&#xff09;、遗忘门&#xff08;forget gate&#xff09;和输出门&#xff08;output gate&#xff09;&#xff0c;以…

华为时习知,让企业培训更简单!

在数字经济的发展过程中&#xff0c;人才始终是不容忽视的关键因素&#xff0c;企业对数字化人才培养的需求也愈加迫切。然而企业培训说起来简单&#xff0c;要做好却绝非易事。企业可能会面临员工分散各地、流动性大、关键岗位人才培训等复杂培训场景问题&#xff0c;无法高效…

为什么我们说NFT拥有无限潜力?

欢迎来到Hubbleverse &#x1f30d; 关注我们 关注宇宙新鲜事 &#x1f4cc; 预计阅读时长&#xff1a;8分钟 本文仅代表作者个人观点&#xff0c;不代表平台意见&#xff0c;不构成投资建议。 2021年底&#xff0c;NFT就已经发展得炙手可热了&#xff0c;热门到410亿美元投…

YOLO-V5 算法和代码解析系列(一)—— 快速开始

文章目录运行环境配置Demo重新训练 YOLO-V5s运行环境配置 环境配置的官方教程如下&#xff0c;如果一些库安装失败&#xff0c;导致安装中断&#xff0c;可以单独安装一些库&#xff0c;比如 Pytorch&#xff0c;然后再执行下列安装步骤&#xff0c;具体如下&#xff1a; 个人建…

国内食糖行业数据浅析

大家好&#xff0c;这里是小安说网控。 食糖行业是国民消费不可或缺的产业之一。2022年9月份国内成品糖产量当期值为40.4万吨&#xff0c;同比增长30.7%&#xff1b;10月份当期值为63.7万吨&#xff0c;同比下滑2%。今年1-10月份&#xff0c;国内成品糖产量累计值为1089.4万吨&…

艾美捷细胞糖酵解分析试剂盒基本参数和相关研究

艾美捷基于糖酵解细胞的测定试剂盒提供了一种比色法&#xff0c;用于检测培养细胞产生和分泌的糖酵解最终产物L-乳酸。在该测定中&#xff0c;乳酸脱氢酶催化NAD和乳酸之间的反应&#xff0c;产生丙酮酸和NADH。NADH直接将四氮唑盐&#xff08;INT&#xff09;还原为吸收490至5…

【High 翻天】Higer-order Networks with Battiston Federico (3)

目录模型&#xff08;1&#xff09;Equilibrium modelsBipartite modelsMotifs modelsStochastic set modelsHypergraphs modelsSimplicial complexes models模型的目的是再现、解释和预测系统的结构&#xff0c;最好用涉及系统两个或多个元素的交互来描述。为了考虑其输出的可…

【1971. 寻找图中是否存在路径】

来源&#xff1a;力扣&#xff08;LeetCode&#xff09; 描述&#xff1a; 有一个具有 n 个顶点的 双向 图&#xff0c;其中每个顶点标记从 0 到 n - 1&#xff08;包含 0 和 n - 1&#xff09;。图中的边用一个二维整数数组 edges 表示&#xff0c;其中 edges[i] [ui, vi] …

计算机毕设Python+Vue学衡国学堂围棋社管理系统(程序+LW+部署)

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

nvidia 使用

watch -n 0.5 nvidia-smi ./build/examples/openpose/openpose.bin --video examples/media/video.avi Linux CPU&GPU烤机&#xff08;压力测试&#xff09; 盛夏捷关注IP属地: 青海 0.1342021.04.14 09:50:16字数 152阅读 6,307 GPU-burn工具进行GPU烤机 下载Multi-G…

基于MATLAB的车牌识别系统设计(Matlab代码实现)

&#x1f468;‍&#x1f393;个人主页&#xff1a;研学社的博客 &#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜…

vue之非父子组件通信实现方式

在开发中&#xff0c;我们构建了组件树之后&#xff0c;除了父子组件之间的通信之外&#xff0c;还会有非父子组件之间的通信。这里主要讲两种方式&#xff1a; Provide/InjectMitt全局事件总线 1、Provide和Inject 应用场景 比如有一些深度嵌套的组件&#xff0c;子组件想要…

SVG 在前端的7种使用方法,你还知道哪几种?

本文简介 点赞 关注 收藏 学会了 技术一直在演变&#xff0c;在网页中使用 SVG 的方法也层出不穷。每个时期都有对应的最优解。 所以我打算把我知道的 7种 SVG 的使用方法列举出来&#xff0c;有备无患~ 如果你还知道其他方法&#xff0c;可以在评论区补充~ 1. 在浏览器直…

PMO(项目管理办公室)的未来趋势

PMO&#xff08;项目管理办公室&#xff09;是在组织内部将实践、过程、运作形式化和标准化的部门&#xff0c;也是提高组织管理成熟度的核心部门。现在&#xff0c;让我们把目光投向当前PMO的典型职责之外&#xff0c;思考一下&#xff1a;PMO的未来是什么&#xff1f; 如今&a…

Java:进一步理解多态性

Java&#xff1a;进一步理解多态性 每博一文案 有人说我心里有事&#xff0c;但我谁也不想说&#xff0c;沉默不是没有情绪&#xff0c;而是我明白了&#xff0c;说了又没有意义&#xff0c; 比起诉说的委屈和不甘&#xff0c;沉默或许更好。当我们经历越多真实与虚假&#xf…

Docker安装(centos 7)

安装 以在centos安装为例&#xff0c;主要有以下几个步骤 1、确定你是CentOS7及以上版本 2、卸载旧版本 3、yum安装gcc相关 yum -y install gccyum -y install gcc-c 4、安装需要的软件包 执行如下命令 yum -y install gcc-c 5、设置stable镜像仓库 由于docker外网镜像…

Docker安装canal-admin以及canal-server

一、安装canal-admin可视化管理工具 此处的数据库已经进行了相应的配置&#xff0c;望周知 docker run -it --name canal-admin \ -e spring.datasource.addressxxx:3306 \ -e spring.datasource.databasecanal_manager \ -e spring.datasource.usernameroot \ -e spring.da…

minicom发送AT指令

参考&#xff1a;使用minicom发AT指令&#xff0c;和外设传感器通信 地址&#xff1a;https://blog.csdn.net/hannibaychty/article/details/125463268 目录1、Linux minicom 和 windows串口调试助手的区别2、使用的基本流程3、使用 minicom 需要注意的几点ARM板子外接传感器&a…

从国企到进大厂,全靠阿里、腾讯内网22版Java核心手册合集

记得19年初的时候&#xff0c;我通过一整天的笔试及面试加入一家(某一线城市国资委全资控股)某集团的研究机构(中央研究院)&#xff0c;任职高级软件工程师(中级职称);在这边工作了整整一年&#xff0c;目前已经跳槽到一家互联网公司&#xff0c;在回头看看这一整年&#xff0c…