Jdk1.8新特性
新增的类以及常用的方法
在Java的java.util.concurrent包中,除了之前提到的并发工具类外,还有一些新增的类以及它们常用的方法。以下是一些例子:
新增的类
CompletableFuture:
CompletableFuture是Java 8中引入的一个类,用于表示异步计算的结果。它提供了多种方法来处理异步任务的结果,包括thenApply, thenAccept, thenCompose, exceptionally等。
常用方法:
complete(T value): 完成计算,并设置结果。
completeExceptionally(Throwable ex): 以异常完成计算。
thenApply(Function<? super T, ? extends U> fn): 当计算完成时,应用一个函数到结果上,并返回一个新的CompletableFuture。
thenAccept(Consumer<? super T> action): 当计算完成时,执行一个动作。
exceptionally(Function<Throwable, ? extends T> fn): 当计算以异常完成时,应用一个函数到异常上,并返回一个新的CompletableFuture。
ForkJoinPool:
ForkJoinPool是Java 7中引入的一个类,用于执行可以并行化的任务(通常是递归分治算法)。它使用工作窃取(work-stealing)算法来优化线程之间的任务分配。
常用方法:
submit(ForkJoinTask<T> task): 提交一个任务到池中。
invoke(ForkJoinTask<T> task): 提交一个任务并等待其完成。
shutdown(): 关闭池。
awaitTermination(long timeout, TimeUnit unit): 等待池中的所有任务完成,或者等待超时。
StampedLock:
StampedLock是Java 8中引入的一种锁,它支持三种锁模式:写锁、悲观读锁和乐观读锁。乐观读锁是一种非阻塞锁,它允许线程在读取数据时不必阻塞其他读线程或写线程。
常用方法:
writeLock(): 获取写锁。
readLock(): 获取悲观读锁。
tryOptimisticRead(): 尝试获取乐观读锁。
unlockWrite(): 释放写锁。
unlockRead(): 释放读锁。
常用的方法(在其他类中)
ExecutorService:
submit(Callable<T> task): 提交一个Callable任务并返回一个Future。
submit(Runnable task, T result): 提交一个Runnable任务和一个结果,并返回一个Future。
shutdown(): 关闭线程池。
awaitTermination(long timeout, TimeUnit unit): 等待线程池中的所有任务完成,或者等待超时。
ScheduledExecutorService:
schedule(Runnable command, long delay, TimeUnit unit): 在给定的延迟后执行一个命令。
scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit): 以固定的频率执行一个命令。
scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit): 在每次执行之间以固定的延迟执行一个命令。
BlockingQueue(及其实现类如ArrayBlockingQueue, LinkedBlockingQueue等):
put(E e): 将一个元素添加到队列中,如果队列满了则阻塞。
take(): 从队列中取出一个元素,如果队列空了则阻塞。
offer(E e, long timeout, TimeUnit unit): 尝试将一个元素添加到队列中,如果队列满了则等待指定的时间。
poll(long timeout, TimeUnit unit): 尝试从队列中取出一个元素,如果队列空了则等待指定的时间。
ConcurrentMap(及其实现类如ConcurrentHashMap等):
putIfAbsent(K key, V value): 如果键不存在,则将一个键值对添加到映射中。
computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction): 如果键不存在,则使用给定的映射函数来计算其值,并将其添加到映射中。
computeIfPresent(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction): 如果键存在,则使用给定的重新映射函数来计算其新值,并更新映射。
merge(K key, V value, BiFunction<? super V, ? super V, ? extends V> mergeFunction): 如果键存在,则使用给定的合并函数来合并其值和给定的值;如果键不存在,则将给定的值添加到映射中。
这些新增的类和常用的方法提供了更丰富的并发编程工具,使得开发者能够更容易地处理复杂的并发问题。在使用这些工具时,需要仔细考虑线程安全性、性能以及代码的可读性和可维护性。
接口中新增属性
在Java编程语言中,“接口”(Interface)这一概念主要用于定义一个可以被多个类共同实现的契约或规范。传统上,接口主要包含方法签名,而不涉及具体的实现细节或属性(字段)。然而,从Java 8开始,接口得到了显著的增强,引入了若干新特性,包括默认方法(default methods)和静态方法(static methods),使得接口更加灵活和强大。
尽管Java接口的主要用途并非定义属性,但从Java 8起,由于默认方法和静态方法的加入,接口内实际上可以包含一些与这些方法紧密相关的属性。具体来说:
默认方法(Default Methods):Java 8允许在接口内定义默认方法,即带有实现体的方法。这些方法不是抽象的,因此可以在接口中直接定义其行为。由于默认方法可以包含实现,它们自然也可能需要访问某些数据。虽然直接在接口中定义实例变量(字段)仍然是不被允许的,但接口可以包含与默认方法相关的局部变量、常量(即用static final修饰的字段)或是类变量(即用static修饰的字段,无论是否final)。
静态方法(Static Methods):Java 8还引入了静态方法,使得接口能够像类一样拥有静态成员。静态方法可以直接在接口中实现,并且只能访问接口中的静态变量。这意味着,与静态方法相关的属性(即静态字段)现在可以被定义在接口中。
私有方法(Private Methods, Java 9+):从Java 9开始,接口中可以定义私有方法。这些私有方法既可以是静态的,也可以是非静态的(但在接口中,非静态私有方法通常与默认方法或静态方法结合使用,以提供辅助或重用功能)。私有方法同样可以访问接口中的私有静态字段(如果定义了的话)。
需要强调的是,尽管Java 8及之后的版本放宽了接口的定义规则,允许包含更多种类的成员(如默认方法、静态方法和私有方法),但接口的主要目的和设计初衷仍然是定义类应实现的契约或行为规范。因此,直接在接口中定义大量属性(尤其是实例变量)仍然是不推荐的做法。属性和状态通常应该由实现接口的类来维护和管理。
Lambda的表达式
Lambda表达式是Java 8中引入的一种紧凑型的表达式,它提供了一种简洁的方式来表示单方法接口(functional interface)的实例。Lambda表达式主要用于简化代码,特别是在使用诸如集合、线程或者任何需要单方法接口的地方。
Lambda表达式的基本语法如下:
text
Copy Code
(parameters) -> expression
或
(parameters) -> { statements; }
parameters:这是Lambda表达式的参数列表。它与常规方法的参数列表非常相似,但没有类型声明(类型推断由编译器完成)。如果只有一个参数,可以省略括号。
->:这是Lambda操作符,它将参数列表与Lambda体分隔开。
expression或statements:这是Lambda体,它包含了要执行的代码。如果Lambda体只包含一个表达式,那么可以省略大括号和return关键字(该表达式的结果会自动返回)。如果包含多条语句,则需要用大括号括起来,并可能需要使用return关键字来返回值。
例如,以下是一个使用Lambda表达式的简单示例,它创建了一个接受两个整数并返回它们之和的BinaryOperator:
java
Copy Code
BinaryOperator<Integer> adder = (a, b) -> a + b;
int sum = adder.apply(10, 20); // sum = 30
在这个例子中,BinaryOperator<Integer>是一个函数式接口,它接受两个相同类型的参数并返回一个相同类型的结果。Lambda表达式(a, b) -> a + b简洁地实现了这个接口,而不需要编写一个完整的类。
Lambda表达式在Java中广泛应用于集合的流式操作、线程编程、事件监听器以及任何需要函数式接口的地方。它们使得代码更加简洁、易读,并提高了开发效率。
方法引用
方法引用(Method Reference)是Java 8中引入的一项特性,它是lambda表达式的一种简写形式。当lambda表达式仅仅是调用一个已经存在的方法时,可以使用方法引用来简化代码。方法引用通过::操作符来引用现有的方法或构造函数。
方法引用可以理解为一种更简洁、更易读的lambda表达式。它避免了在lambda表达式中重复编写方法名、参数列表和返回类型等冗余信息,直接通过引用已有的方法来表示lambda表达式的行为。
方法引用大致可以分为以下几类:
对象::实例方法:
当lambda表达式调用的是一个对象的实例方法时,可以使用对象名加双冒号加方法名的方式来表示方法引用。
例如:System.out::println表示的是x -> System.out.println(x)。
类::静态方法:
当lambda表达式调用的是一个类的静态方法时,可以使用类名加双冒号加方法名的方式来表示方法引用。
例如:Math::abs表示的是x -> Math.abs(x)。
类::实例方法:
当lambda表达式的第一个参数是方法的调用者,并且第二个参数是方法的参数时,可以使用类名加双冒号加实例方法名的方式来表示方法引用。这种引用方式通常用于需要传递函数式接口的场景,如Comparator、Function等。
例如:String::compareTo表示的是(x, y) -> x.compareTo(y)。
类::new(构造函数引用):
当lambda表达式需要创建一个类的对象时,可以使用类名加双冒号加new的方式来表示构造函数引用。
例如:ArrayList::new表示的是() -> new ArrayList<>(),它创建了一个空的ArrayList对象;而HashMap<String, String>::new则表示的是() -> new HashMap<String, String>(),它创建了一个空的HashMap对象(键和值都是String类型)。
方法引用的使用可以大大简化代码,提高代码的可读性和维护性。但是,它只能用于那些可以直接映射到lambda表达式的场景,对于那些需要更复杂逻辑处理的场景,仍然需要使用完整的lambda表达式或匿名类来实现。