JAVA基础
版本特性
文章目录
- JAVA基础
- Java8
- Lambda表达式
- lambda表达式类型
- 函数式接口
- 接口默认方法与静态方法
- 接口默认方法
- 接口静态方法
- 方法引用
- Optional
- Stream流
- 生产Stream Source的方式
- 流的方法
- parallelStream流
- Date/Time API
- 其他特性
- 重复注解
- 扩展注解
- 参数名字保留在字节码中
- ReentrantLock
- ReentrantReadWriteLock
- StampedLock
- 并行数组
- CompletableFuture
- 虚拟机
Java8
Lambda表达式
JDK8之前,一个方法能接受的参数都是变量.如果需要传入一个动作如何做?
@FunctionalInterface
public interface PersonCallback {
void callback(Person person);
/**
* 声明覆盖java.lang.Object公共方法的抽象方法.
* 不计入接口的抽象方法计数,(接口的任何实现都将具有来自java.lang.Object的实现)
*/
boolean equals(Object obj);
}
public class Person {
private String id;
private String name;
public Person(String id, String name) {
this.id = id;
this.name = name;
}
// 创建一个Person之后,进行回调
public static void create(String id, String name, PersonCallback personCallback) {
Person person = new Person(id, name);
personCallback.callback(person);
}
}
public class Demo {
public static void main(String[] args) {
Person.create("1", "name", new PersonCallBack() {
@Override
public void callback(Person person) {
log.debug("person:{}", person);
}
});
Person.create("2", "name", (Person person) -> {
log.debug("person:{}", person);
});
//Java8的类型推导机制
Person.create("3", "name", (person) -> {
log.debug("person:{}", person);
});
}
}
Lambda允许把函数作为一个方法的参数,一个lambda由用逗号分隔的 参数列表、–>符号、函数体三部分表示.
当实现接口时,需要实现接口里面的方法,Lambda表达式也遵循基本准则.
一个Lambda表达式实现了接口里的有且仅有的唯一一个抽象方法.这种接口叫做接函数式接口.
可以认为Lambda表达式 代表一种动作,可以直接把这种特殊的动作动作进行传递.
lambda表达式类型
一个Lambda表达式 可以理解为一个函数式接口的实现者,作为表达式,写法是多种多样的.
- () -> {return 0;},无参数,有返回值
- (int i) -> {return 0;},一个参数,有返回值
- (int i) -> {System.out.println(i)},一个参数,无返回值
- (int i, int j) -> {System.out.println(i)},多参数,无返回值
- (int i, int j) -> {return i+j;},多参数,有int返回值
- (int i, int j) -> {return i>j;},多参数,有boolean返回值
- 其他
函数式接口
函数式接口是新增的一种接口定义.
用 @FunctionalInterface 修饰的接口叫做函数式接口,或者只具有一个抽象方法的普通接口,(@FunctionalInterface起到校验的作用)
有一个抽象方法能编译正确,具有多个抽象方法则无法编译.(除了覆盖java.lang.Object的方法)
@FunctionalInterface
public interface PersonCallback {
void callback(Person person);
/**
* 声明覆盖java.lang.Object公共方法的抽象方法.
* 不计入接口的抽象方法计数,(接口的任何实现都将具有来自java.lang.Object的实现)
*/
boolean equals(Object obj);
}
JDK7已有一些函数式接口,如Runnable、Callable、FileFilter等.
JDK8中也增加了很多函数式接口
接口 | 描述 |
---|---|
Supplier | 无参数,返回一个结果 |
Function | 接受一个输入参数,返回一个结果 |
Consumer | 接受一个输入参数,无返回结果 |
Predicate | 接受一个输入参数,返回一个布尔值结果 |
多函数式接口有什么作用?
一个Lambda表达式 可以理解为一个函数式接口的实现者,作为表达式,写法其实是多种多样的.
每种表达式的写法其实都应该是某个函数式接口的实现类,需要特定函数式接口进行对应函数式接口进行对应,
如lambda表达式类型对应如下函数式接口:
- Supplier
- Function<T,R>
- Consumer
- BiConsumer<T, U>
- BiFunction<T, U, R>
- BiPredicate<T, U>
为了写Lambda表达式更加方便.特殊情况还是需要自定义函数式接口.
接口默认方法与静态方法
JDK8前,想对接口新增一个方法,需要修改所有的实现类源码.
JDK8前,借助抽象类实现接口来解决: 当接口新增抽象方法时,只需要在抽象类中实现默认方法.
JDK8后,支持接口默认方法与静态方法.
使用默认方法和静态方法,不用修改实现类了,可以进行直接调用.
接口默认方法
在接口中用default修饰的方法称为默认方法默认方法.
接口中的默认方法必须有默认实现(方法体),接口实现者可以继承,也可以覆盖.
public interface TestInterface {
default void testDefault(){
System.out.println("default");
}
}
接口静态方法
在接口中用static修饰的方法称为静态方法静态方法.
public interface TestInterface {
static void testStatic() {
System.out.println("static");
}
}
//调用
TestInterface.testStatic();
方法引用
//lambda
Consumer<String> consumer = s -> System.out.println(s);
consumer.accept("test");
//方法引用
//PrintStream类(System.out的类型)的println方法已经实现
Consumer<String> consumer = System.out::println;
consumer.accept("test");
方法引用方法的参数列表必须与函数式接口的抽象方法的参数列表保持一致,返回值不作要求!
使用方式:
- 实例对象::实例方法名
//Consumer<String> consumer = s -> System.out.println(s); Consumer<String> consumer = System.out::println; consumer.accept("test");
- 类名::静态方法名
//Function<Long, Long> f = x -> Math.abs(x); Function<Long, Long> f = Math::abs; Long result = f.apply(-3L);
- 类名::实例方法名
//BiPredicate<String, String> b = (x,y) -> x.equals(y); BiPredicate<String, String> b = String::equals; b.test("a", "b");
- 引用构造器
//Function<Integer, StringBuffer> fun = n -> new StringBuffer(n); Function<Integer, StringBuffer> fun = StringBuffer::new; StringBuffer buffer = fun.apply(10);
- 引用数组
// Function<Integer, int[]> fun = n -> new int[n]; Function<Integer, int[]> fun = int[]::new; int[] arr = fun.apply(10); Function<Integer, Integer[]> fun2 = Integer[]::new; Integer[] arr2 = fun2.apply(10);
Optional
空指针异常是导致Java应用程序失败的最常见原因,为了解决空指针异常,
Google公司著名的Guava项目引入了Optional类,Guava通过使用检查空值的方式来防止代码污染.
受到Google Guava的启发,Optional类已经成为Java 8类库的一部分.
Optional实际上是个容器:保存类型T的值,或者null.Optional 提供很多方法,不用显式进行空值检测.
创建Optional对象的方法:
Optional.of(T value), 返回一个Optional对象,value不能为空,否则会出空指针异常
Optional.ofNullable(T value), 返回一个Optional对象,value可以为空
Optional.empty(),代表空
其他API:
//是否存在值
optional.isPresent()
//如果存在值则执行consumer
optional.ifPresent(Consumer<? super T> consumer)
//获取value
optional.get()
//如果value为null则返回other
optional.orElse(T other)
//如果value为null则执行other并返回
optional.orElseGet(Supplier<? extends T> other)
//如果value为null执行exceptionSupplier,并抛出异常
optional.orElseThrow(Supplier<? extends X> exceptionSupplier);
//映射,映射规则由function指定,返回映射值的Optional,所以可以继续使用Optional的API.
optional.map(Function<? super T, ? extends U> mapper);
//类似map,区别在于map中获取的返回值自动被Optional包装,flatMap中返回值保持不变,入参必须是Optional类型.
optional.flatMap(Function<? super T, Optional< U > > mapper);
//过滤,按predicate指定的规则进行过滤,不符合规则则返回empty,可以继续使用Optional的API.
optional.filter(Predicate<? super T> predicate);
Stream流
Java 8 中的 Stream 是对集合(Collection)对象功能的增强,
专注于对集合对象进行各种非常便利、高效的聚合操作(aggregate operation),或者大批量数据操作 (bulk data operation).
提供串行和并行两种模式进行汇聚操作,并发模式能够充分利用多核处理器的优势,使用 fork/join 并行方式来拆分任务和加速处理过程
有关算法和计算的,像一个高级版本的Iterator.
中间操作(Intermediate Operation):
一个流可以后面跟随零个或多个 intermediate 操作.其目的主要是打开流,做出某种程度的数据映射/过滤,然后返回一个新的流, 交给下一个操作使用.
这类操作都是惰性化的(lazy),调用到这类方法,并没有真正开始流的遍历.
终止操作(Terminal Operation):
一个流只能有一个 terminal 操作,当这个操作执行后,流就被使用“光”了,无法再被操作.必定是流的最后一个操作.
Terminal 操作的执行,才会真正开始流的遍历,并且会生成一个结果.
Intermediate Operation可以分为两种类型:
- 无状态操作(Stateless Operation):操作是无状态的,不需要知道集合中其他元素的状态,每个元素之间是相互独立的,比如map()、filter()等操作.
- 有状态操作(Stateful Operation):有状态操作,操作是需要知道集合中其他元素的状态才能进行的,比如sort()、distinct().
Terminal Operation从逻辑上可以分为两种:
- 短路操作(short-circuiting):不需要处理完所有元素即可结束整个过程.
- 非短路操作(non-short-circuiting):需要处理完所有元素之后才能结束整个过程.
流不是数据结构,没有内部存储,用操作管道从 source(数据结构、数组、generator function、IO channel)抓取数据.
不修改所封装的底层数据结构的数据.例如 Stream 的 filter 操作会产生一个不包含被过滤元素的新 Stream,而不是从 source 删除那些元素.
Stream 的操作必须以 lambda 表达式为参数.
惰性化,很多 Stream 操作是向后延迟的,Intermediate 操作永远是惰性化的.
当一个 Stream 是并行化的,所有对它的操作会自动并行进行的.
生产Stream Source的方式
从Collection 和数组生成:
- Collection.stream()
- Collection.parallelStream()
- Arrays.stream(T array)
- Stream.of(T t)
BufferReader:
- java.io.BufferedReader.lines()
静态工厂:
- java.util.stream.IntStream.range()
- java.nio.file.Files.walk()
其他:
- Random.ints()
- BitSet.stream()
- Pattern.splitAsStream(java.lang.CharSequence)
- JarFile.stream()
自定义:
- java.util.Spliterator
流的方法
- map/flatMap
- filter
- forEach
- peek
- reduce
- Limit/skip
- sorted/min/max/distinct
- allMatch
- anyMatch
- noneMatch
parallelStream流
通过默认的 ForkJoinPool,提高多线程任务的速度.
ForkJoinPool 默认线程数量等于运行计算机上的处理器数量,
java进程使用parallelStream的地方公用的同一个ForkJoinPool.
parallelStream提供了更简单的并发执行的实现,但并不意味着更高的性能.
如果cpu资源紧张 parallelStream不会带来性能提升;如果存在频繁的线程切换反而会降低性能.
stream.parallel()
Date/Time API
Java 8通过发布新的Date-Time API (JSR 310)来进一步加强对日期与时间的处理.
LocaleDate只持有ISO-8601格式且无时区信息的日期部分:
LocalDate date = LocalDate.now(); // 当前日期
date = date.plusDays(1); // 增加一天
date = date.plusMonths(1); // 增加一月
date = date.minusDays(1); // 减少一天
date = date.minusMonths(1); // 减少一月
LocalTime类只持有ISO-8601格式且无时区信息的时间部分:
LocalTime time = LocalTime.now(); // 当前时间
time = time.plusMinutes(1); // 增加一分钟
time = time.plusSeconds(1); // 增加一秒钟
time = time.minusMinutes(1); // 减少一分钟
time = time.minusSeconds(1); // 减少一秒钟
LocaleDateTime类和格式化:
LocaleDateTime把LocaleDate与LocaleTime的功能合并起来,持有ISO-8601格式无时区信息的日期与时间:
LocalDateTime localDateTime = LocalDateTime.now();
// 2023-06-21T08:57:07.465 UTC格式
localDateTime;
// 2023-06-21 08:57:07 自定义格式
localDateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
ZonedDateTime类:
需要特定时区的日期/时间,持有ISO-8601格式具具有时区信息的日期与时间:
//2023-06-21T08:57:07.465-08:00[America/Los_Angeles]
final ZonedDateTime zonedDatetimeFromZone = ZonedDateTime.now(ZoneId.of( "America/Los_Angeles" ));
Clock类:
通过指定一个时区,就可以获取到当前的时刻,日期与时间。
Clock可以替换System.currentTimeMillis()与TimeZone.getDefault().
final Clock utc = Clock.systemUTC(); // 协调世界时,又称为世界统一时间、世界标准时间、国际协调时间
final Clock shanghai = Clock.system(ZoneId.of("Asia/Shanghai")); // 上海
LocalDateTime.now(utc); // 2023-06-21T08:57:07.176
LocalDateTime.now(shanghai); // 22023-06-21T08:57:07.177
Duration类:
计算两个日期间的不同.
final LocalDateTime from = LocalDateTime.parse("2018-12-17 18:50:50", DateTimeFormatter.ofPattern("yyyyMM-dd HH:mm:ss"));
final LocalDateTime to = LocalDateTime.parse("2018-12-18 19:50:50", DateTimeFormatter.ofPattern("yyyyMM-dd HH:mm:ss"));
final Duration duration = Duration.between(from, to);
System.out.println("Duration in days: " + duration.toDays()); // 1
System.out.println("Duration in hours: " + duration.toHours()); // 25
其他特性
重复注解
java 8前不允许重复注解出现在同一个地方.
@Repeatable元注解
指定重复注解的存储注解(需要数组来存储重复注解).
反射相关的API提供了新的函数getAnnotationsByType()来返回重复注解的类型.
扩展注解
JDK8之前的注解只能加在:
public enum ElementType {
TYPE, // 类、枚举、接口
FIELD, // 类型变量
METHOD, // 方法
PARAMETER, // 方法参数
CONSTRUCTOR, // 构造方法
LOCAL_VARIABLE, // 局部变量
ANNOTATION_TYPE, // 注解类型
PACKAGE, // 包
//JDK8中新增了两种:
//类型变量的声明语句中。
TYPE_PARAMETER,
//能写在使用类型的任何语句中
TYPE_USE,
}
比如: @Nullable, @NonNull
参数名字保留在字节码中
Java8前没有能够获取到方法的参数名字列表!
Method.getParameterAnnotations() 获取方法参数上的注解
Method.getParameterTypes() 获取方法的参数类型列表
在JDK8中增加了两个方法:
Method.getParameters() 获取参数名字列表
Method.getParameterCount() 获取参数名字个数
ReentrantLock
ReentrantLock类,实现了Lock接口,是一种可重入的独占锁,它具有与使用 synchronized 相同的一些基本行为和语义,但功能更强大。ReentrantLock内部通过内部类实现了AQS框架(AbstractQueuedSynchronizer)的API来实现独占锁的功能。
ReentrantReadWriteLock
和ReentrantLock不同,ReentrantReadWriteLock实现的是ReadWriteLock接口。
在ReentrantReadWriteLock中,当读锁被使用时,如果有线程尝试获取写锁,写线程会阻塞。
在读线程非常多,写线程很少的情况下,很容易导致写线程“饥饿”.
虽然使用“公平”策略可以一定程度上缓解这个问题,但“公平”策略是以牺牲系统吞吐量为代价的。
StampedLock
对读写锁ReentrantReadWriteLock的增强,提供了一些功能,优化了读锁、写锁的访问,同时使读写锁之间可以互相转换,更细粒度控制并发.
并行数组
大量的新方法来对数组进行并行处理.
parallelSort()方法可以在多核机器上极大提高数组排序的速度。
long[] arrayOfLong = new long [ 20000 ];
Arrays.parallelSetAll( arrayOfLong, index -> ThreadLocalRandom.current().nextInt( 1000000 ) );
Arrays.parallelSort( arrayOfLong );
CompletableFuture
异步编排.
虚拟机
Metaspace元空间取代PermGen, 永久代空间被移除.
JVM选项-XX:PermSize与-XX:MaxPermSize 分别被-XX:MetaSpaceSize与-XX:MaxMetaspaceSize所代替.