- 函数式接口
- Lambda表达式
- 方法引用
- 接口增强
- Stream API
- Optional类
- 时间日期API
- 可重复注解
- Nashorn JavaScript引擎
- Base64
函数式接口
函数式接口:有且仅有一个抽象方法,但是可以有多个非抽象方法的接口。可以被隐式转换为lambda表达式。
Java8新增的函数式接口:
| 接口 | 方法 | 描述 |
|---|---|---|
| Consumer< T > | void accept(T t) | 消费型接口,对类型为T的对象应用操作 |
| Supplier < T > | T get() | 供给型接口,返回类型为T的对象 |
| Function < T > | R apply(T t) | 函数型接口,对类型为T的对象操作,返回类型为R的对象 |
| Predicate < T > | boolean test(T t) | 断定型接口,对类型为T的对象操作,返回boolean值 |
实例:
public class Test {
public static void main(String args[]){
predicate();
consumer();
}
public static void consumer(){
System.out.println();
Consumer consumer = n -> System.out.println("今天消费了"+ n + "元");
consumer.accept(120);
}
public static void predicate(){
List list = Arrays.asList(1, 2, 3, 4, 5);
Predicate predicate = n -> true;
System.out.println("输出所有数据:");
// 传递参数n
fore(list, predicate);
}
public static void fore(List list, Predicate predicate) {
for(Integer n: list) {
if(predicate.test(n)) {
System.out.print(n + ", ");
}
}
}
}
// 输出结果
输出所有数据:
1, 2, 3, 4, 5,
今天消费了120元
Lambda表达式
Lambda表达式:允许把函数作为一个方法的参数,即允许函数作为参数传递到方法中。
语法:(parameters) -> expression 或 (parameters) ->{statements; }。
特征:
- 可选类型声明:不需要声明参数类型,编译器可以统一识别参数值。
- 可选的参数圆括号:一个参数无需定义圆括号,但多个参数需要定义圆括号。
- 可选的大括号:如果主体包含了一个语句,就不需要使用大括号。
- 可选的返回关键字:如果主体只有一个表达式返回值则编译器会自动返回值,大括号需要指定明表达式返回了一个数值。
注意点:
- Lambda表达式主要用来定义行内执行的方法类型接口。
- Lambda表达式免去了使用匿名方法的麻烦,并且给予Java简单但是强大的函数化的编程能力。
- Lambda表达式中不允许声明一个与局部变量同名的参数或者局部变量。
- Lambda表达式的局部变量可以不用声明为final,但是不可被后面的代码修改。
变量作用域:lambda 表达式只能引用标记了 final 的外层局部变量,这就是说不能在lambda 内部修改定义在域外的局部变量,否则会编译错误。
实例:
public class Test02 {
public static void main(String[] args) {
test();
}
public static void test(){
// 入参为空
Ts1 ts1 = () -> System.out.println("无参");
ts1.t1();
// 单个参数
Ts2 ts2 = s -> System.out.println(s);
ts2.t2("单个参数");
// 多个参数
Ts3 ts3 = (s1, s2) -> System.out.println(s1+s2);
ts3.t3("多个", "参数");
}
interface Ts1{
void t1();
}
interface Ts2{
void t2(String str);
}
interface Ts3{
void t3(String str1, String str2);
}
}
// 输出结果
无参
单个参数
多个参数
方法引用
方法引用:通过方法的名字来指向一个方法。使用格式为一对冒号 ::。可以使语言的构造更紧凑简洁,减少冗余代码。
常用类型:
- 对象::实例方法。
- 类::静态方法。
- 类::实例方法。
实例:
public class Car {
// Supplier是jdk1.8的函数式供给型接口
public static Car create(final Supplier supplier) {
return supplier.get();
}
public static void collide(final Car car) {
System.out.println("Collided " + car.toString());
}
public void follow(final Car another) {
System.out.println("Following the " + another.toString());
}
public void repair() {
System.out.println("Repaired " + this.toString());
}
public static void main(String[] args) {
// 构造器引用:它的语法是Class::new,或者更一般的Class< T >::new实例如下:
Car car = Car.create(Car::new);
Car car1 = Car.create(Car::new);
Car car2 = Car.create(Car::new);
Car car3 = new Car();
List cars = Arrays.asList(car,car1,car2,car3);
// 静态方法引用:它的语法是Class::static_method,实例如下:
cars.forEach(Car::collide);
// 特定类的任意对象的方法引用:它的语法是Class::method实例如下:
cars.forEach(Car::repair);
// 特定对象的方法引用:它的语法是instance::method实例如下:
final Car police = Car.create(Car::new);
cars.forEach(police::follow);
}
}
// 输出结果
Collided com.outside.demo.methods.Car@214c265e
Collided com.outside.demo.methods.Car@448139f0
Collided com.outside.demo.methods.Car@7cca494b
Collided com.outside.demo.methods.Car@7ba4f24f
Repaired com.outside.demo.methods.Car@214c265e
Repaired com.outside.demo.methods.Car@448139f0
Repaired com.outside.demo.methods.Car@7cca494b
Repaired com.outside.demo.methods.Car@7ba4f24f
Following the com.outside.demo.methods.Car@214c265e
Following the com.outside.demo.methods.Car@448139f0
Following the com.outside.demo.methods.Car@7cca494b
Following the com.outside.demo.methods.Car@7ba4f24f
接口增强
接口默认方法:接口可以有实现方法,不需要实现类去实现其方法。只需在方法名前面加个default关键字即可实现默认方法。目的是为了解决接口的修改与现有的实现不兼容的问题。
接口静态方法:接口可以声明静态方法,并可提供具体实现。
实例:
public class Test {
public static void main(String args[]) {
Te te = new Te();
te.pr();
}
}
interface TestInterface {
default void mr() {
System.out.println("接口默认方法");
}
static void jt() {
System.out.println("接口静态方法");
}
}
class Te implements TestInterface {
public void pr() {
TestInterface.super.mr();
TestInterface.jt();
}
}
// 输出结果
接口默认方法
接口静态方法
Stream API
Stream API:流Stream,以声明的方式处理数据。Stream使用一种类似用SQL语句从数据库查询数据的直观方式来提供一种对Java集合运算和表达的高阶抽象。将要处理的元素集合看作一种流,流在管道中传输,并且可以在管道的节点上进行处理,比如筛选,排序,聚合等。流在管道中经过中间操作的处理,最后由最终操作得到前面处理的结果。
什么是 Stream?Stream(流)是一个来自数据源的元素队列并支持聚合操作:
- 元素:是特定类型的对象,形成一个队列。Java中的Stream并不会存储元素,而是按需计算。
- 数据源 :流的来源。可以是集合,数组,I/O channel,产生器generator等。
- 聚合操作: 类似SQL语句一样的操作,比如filter, map, reduce, find,match, sorted等。
基础特征:
- Pipelining::中间操作都会返回流对象本身。这样多个操作可以串联成一个管道,如同流式风格(fluent style)。这样做可以对操作进行优化,比如延迟执行(laziness)和短路( short-circuiting)。
- 内部迭代:以前对集合遍历都是通过Iterator或者For-Each的方式,显式的在集合外部进行迭代,这叫做外部迭代。Stream提供了内部迭代的方式,通过访问者模式(Visitor)实现。
生成流方法:
- stream() −为集合创建串行流。
- parallelStream() − 为集合创建并行流。
实例:
public class Test02 {
public static void main(String args[]) {
// 计算空字符串
List strings = Arrays.asList("abc", "", "bc", "efg", "abcd", "", "jkl");
long count = strings.stream().filter(string -> string.isEmpty()).count();
System.out.println("空字符串数量为: " + count);
count = strings.stream().filter(string -> string.length() == 3).count();
System.out.println("字符串长度为 3 的数量为: " + count);
List filtered = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.toList());
System.out.println("筛选后的列表: " + filtered);
String mergedString = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.joining(", "));
System.out.println("合并字符串: " + mergedString);
List numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
List squaresList = numbers.stream().map(i -> i * i).distinct().collect(Collectors.toList());
System.out.println("Squares List: " + squaresList);
List integers = Arrays.asList(1, 2, 13, 4, 15, 6, 17, 8, 19);
System.out.println("列表: " + integers);
IntSummaryStatistics stats = integers.stream().mapToInt((x) -> x).summaryStatistics();
System.out.println("列表中最大的数 : " + stats.getMax());
System.out.println("列表中最小的数 : " + stats.getMin());
System.out.println("所有数之和 : " + stats.getSum());
System.out.println("平均数 : " + stats.getAverage());
System.out.println("随机数2个: ");
Random random = new Random();
random.ints().limit(2).sorted().forEach(System.out::println);
// 并行处理
count = strings.parallelStream().filter(string -> string.isEmpty()).count();
System.out.println("空字符串的数量为: " + count);
}
}
// 输出结果
空字符串数量为: 2
字符串长度为 3 的数量为: 3
筛选后的列表: [abc, bc, efg, abcd, jkl]
合并字符串: abc, bc, efg, abcd, jkl
Squares List: [9, 4, 49, 25]
列表: [1, 2, 13, 4, 15, 6, 17, 8, 19]
列表中最大的数 : 19
列表中最小的数 : 1
所有数之和 : 85
平均数 : 9.444444444444445
随机数2个:
-1575488198
107330827
空字符串的数量为: 2
Optional类
Optional类:可以为null的容器对象。如果值存在则isPresent()方法会返回true,调用get()方法会返回该对象。可以保存类型T的值,或者仅仅保存null。主要解决空指针异常。
类声明:public final class Optional extends Object { }
类方法:
| 方法 | 说明 |
|---|---|
| static Optional empty() | 返回空的 Optional 实例 |
| boolean equals(Object obj) | 判断其他对象是否等于 Optional |
| Optional filter(Predicate super T> predicate) | 如果值存在,并且这个值匹配给定的 predicate,返回一个Optional用以描述这个值,否则返回一个空的Optional |
| Optional flatMap(Function super T, Optional> mapper) | 如果值存在,返回基于Optional包含的映射方法的值,否则返回一个空的Optional |
| T get() | 如果在这个Optional中包含这个值,返回值,否则抛出异常:NoSuchElementException |
| int hashCode() | 返回存在值的哈希码,如果值不存在 返回 0 |
| void ifPresent(Consumer super T> consumer) | 如果值存在则使用该值调用 consumer , 否则不做任何事情 |
| boolean isPresent() | 如果值存在则方法会返回true,否则返回 false |
| Optional map(Function super T,? extends U> mapper) | 如果存在该值,提供的映射方法,如果返回非null,返回一个Optional描述结果 |
| static Optional of(T value) | 返回一个指定非null值的Optional |
| static Optional ofNullable(T value) | 如果为非空,返回 Optional 描述的指定值,否则返回空的 Optional |
| T orElse(T other) | 如果存在该值,返回值, 否则返回 other |
| T orElseGet(Supplier extends T> other) | 如果存在该值,返回值, 否则触发 other,并返回 other 调用的结果 |
| T orElseThrow(Supplier extends X> exceptionSupplier) | 如果存在该值,返回包含的值,否则抛出由 Supplier 继承的异常 |
| String toString() | 返回一个Optional的非空字符串,用来调试 |
实例:
public class Test {
public static void main(String args[]) {
Test test = new Test();
Integer value1 = null;
Integer value2 = new Integer(10);
// Optional.ofNullable - 允许传递为 null 参数
Optional a = Optional.ofNullable(value1);
// Optional.of - 如果传递的参数是 null,抛出异常 NullPointerException
Optional b = Optional.of(value2);
System.out.println(test.sum(a, b));
}
public Integer sum(Optional a, Optional b) {
// Optional.isPresent - 判断值是否存在
System.out.println("第一个参数值存在: " + a.isPresent());
System.out.println("第二个参数值存在: " + b.isPresent());
// Optional.orElse - 如果值存在,返回它,否则返回默认值
Integer value1 = a.orElse(new Integer(0));
//Optional.get - 获取值,值需要存在
Integer value2 = b.get();
return value1 + value2;
}
}
// 输出结果
第一个参数值存在: false
第二个参数值存在: true
10
时间日期API
时间日期API:加强对日期与时间的处理。Local(本地) − 简化了日期时间的处理,没有时区的问题。Zoned(时区) − 通过制定的时区处理日期时间。新的java.time包涵盖了所有处理日期,时间,日期/时间,时区,时刻(instants),过程(during)与时钟(clock)的操作。
实例:
public class Test {
public static void main(String args[]) {
testLocalDateTime();
}
public static void testLocalDateTime() {
// 获取当前的日期时间
LocalDateTime currentTime = LocalDateTime.now();
System.out.println("当前时间: " + currentTime);
LocalDate date = currentTime.toLocalDate();
LocalDate date1 = LocalDate.now();
System.out.println("当前日期date: " + date);
System.out.println("当前日期date1: " + date1);
int year = currentTime.getYear();
Month month = currentTime.getMonth();
int day = currentTime.getDayOfMonth();
int hour = currentTime.getHour();
int minute = currentTime.getMinute();
int seconds = currentTime.getSecond();
System.out.println("年: " + year +",月: " + month + ", 日: " + day +", 时: " + hour+", 分: " + minute +", 秒: " + seconds);
// 获取具体某年某月某日日期
LocalDate date3 = LocalDate.of(2022, Month.JULY, 25);
System.out.println("date3: " + date3);
// 获取具体某时某分
LocalTime date4 = LocalTime.of(6, 0);
System.out.println("date4: " + date4);
// 解析字符串
LocalTime date5 = LocalTime.parse("06:00:00");
System.out.println("date5: " + date5);
}
}
// 输出结果
当前时间: 2022-06-24T11:53:27.111
当前日期date: 2022-06-24
当前日期date1: 2022-06-24
年: 2022,月: JUNE, 日: 24, 时: 11, 分: 53, 秒: 27
date3: 2022-07-25
date4: 06:00
date5: 06:00
可重复注解
可重复注解:允许在同一个地方多次使用同一个注解。使用@Repeatable注解定义重复注解。
类型注解:Java 8为@Target元注解新增了两种类型: TYPE_PARAMETER(表示该注解能写在类型参数的声明语句中。 类型参数声明如: ),TYPE_USE(表示注解可以再任何用到类型的地方使用) 。
可重复注解实例:
public class Test {
// 定义一个重复的注解容器
@Retention(RetentionPolicy.RUNTIME)
@interface MYTests{
MyTest[] value();
}
// 定义一个可以重复的注解
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(MYTests.class)
@interface MyTest{
String value();
}
// 配置重复的注解
@MyTest("test1")
@MyTest("test2")
public void test(){
}
public static void main(String[] args) {
// 获取方法上的注解
try {
MyTest[] tests = Test.class.getMethod("test").getAnnotationsByType(MyTest.class);
for (MyTest test : tests) {
System.out.println("test = " + test);
}
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
}
// 输出结果
test = @com.outside.demo.repeat.Test$Mytest(value=test1)
test = @com.outside.demo.repeat.Test$Mytest(value=test2)
Nashorn JavaScript引擎
Nashorn:JavaScript引擎。Nashorn取代Rhino(JDK 1.6, JDK1.7)成为Java的嵌入式JavaScript引擎。Nashorn完全支持ECMAScript 5.1规范以及一些扩展。它使用基于JSR292的新语言特性,其中包含在JDK 7中引入的invokedynamic,将JavaScript编译成Java字节码。与先前的Rhino实现相比,带来了2到10倍的性能提升。
jjs:基于Nashorn引擎的命令行工具。它接受一些JavaScript源代码为参数,并且执行这些源代码。
实例:
public class Test {
public static void main(String args[]){
ScriptEngine nashorn = new ScriptEngineManager().getEngineByName("nashorn");
String name = "outside";
Integer result = null;
try {
nashorn.eval("print('" + name + "')");
result = (Integer) nashorn.eval("2");
}catch(ScriptException e){
e.printStackTrace();
System.out.println(e.getMessage());
}
System.out.println(result);
}
}
// 输出结果
outside
2
Base64
Base64:Java 8 内置了 Base64 编码的编码器和解码器。
Base64工具类提供了一套静态方法获取下面三种BASE64编解码器:
- 基本:输出被映射到一组字符A-Za-z0-9+/,编码不添加任何行标,输出的解码仅支持A-Za-z0-9+/。
- URL:输出映射到一组字符A-Za-z0-9+_,输出是URL和文件。
- MIME:输出隐射到MIME友好格式。输出每行不超过76字符,并且使用’r’并跟随’n’作为分割。编码输出最后没有行分割。
内嵌类:
| 内嵌类 | 说明 |
|---|---|
| static class Base64.Decoder | 使用 Base64 编码来解码字节数据 |
| static class Base64.Encoder | 使用 Base64 编码来编码字节数据 |
方法:
| 方法 | 说明 |
|---|---|
| static Base64.Decoder getDecoder() | 解码使用基本型 base64 编码方案 |
| static Base64.Encoder getEncoder() | 编码使用基本型 base64 编码方案 |
| static Base64.Decoder getMimeDecoder() | 解码使用 MIME 型 base64 编码方案 |
| static Base64.Encoder getMimeEncoder() | 编码使用 MIME 型 base64 编码方案 |
| static Base64.Encoder getMimeEncoder(int lineLength, byte[] lineSeparator) | 编码使用 MIME 型 base64 编码方案,可以通过参数指定每行的长度及行的分隔符 |
| static Base64.Decoder getUrlDecoder() | 解码使用 URL 和文件名安全型 base64 编码方案 |
| static Base64.Encoder getUrlEncoder() | 编码使用 URL 和文件名安全型 base64 编码方案 |
实例:
public class test {
public static void main(String args[]) {
try {
// 使用基本编码
String base64encodedString = Base64.getEncoder().encodeToString("runoob?java8".getBytes("utf-8"));
System.out.println("Base64 编码字符串 (基本) :" + base64encodedString);
// 解码
byte[] base64decodedBytes = Base64.getDecoder().decode(base64encodedString);
System.out.println("原始字符串: " + new String(base64decodedBytes, "utf-8"));
base64encodedString = Base64.getUrlEncoder().encodeToString("TutorialsPoint?java8".getBytes("utf-8"));
System.out.println("Base64 编码字符串 (URL) :" + base64encodedString);
StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i < 10; ++i) {
stringBuilder.append(UUID.randomUUID().toString());
}
byte[] mimeBytes = stringBuilder.toString().getBytes("utf-8");
String mimeEncodedString = Base64.getMimeEncoder().encodeToString(mimeBytes);
System.out.println("Base64 编码字符串 (MIME) :" + mimeEncodedString);
} catch (UnsupportedEncodingException e) {
System.out.println("Error :" + e.getMessage());
}
}
}
// 输出结果
Base64 编码字符串 (基本) :cnVub29iP2phdmE4
原始字符串: runoob?java8
Base64 编码字符串 (URL) :VHV0b3JpYWxzUG9pbnQ_amF2YTg=
Base64 编码字符串 (MIME) :MjQ4NWQ4ZmEtMGM0ZC00MGZhLWFkYWQtM2RlNmUwNTlmNDMwMDdlOGExMGYtOTM1NC00OTk2LTk4
ZjItNzA4MTFkZWRjMjczN2JhOTlmZDQtZmFlMC00ZGMxLTlmMDktNjkyMDJkYmE4YzhiODQzOWM3
M2YtNTBiMi00ZjE3LWE5ZjMtYTY4Y2I5MzgxMDJjOTU2ZjU4NGMtOWY0YS00MGQwLThhZmItYmNj
MmNiZDc0Zjg4NTgyMDM5N2UtNGM4OC00MmQ0LTg5ZDAtNDQ4OGE1YWRiODc1ODMxYzk3M2ItZmZk
OS00OGQzLWI2NmMtNDkyZjc3MzNjZmI0ZjdlMTlkZDItNmYzNi00ZTM1LWI5ZjQtZTM3YzFjYTE4
MDdiMmZkMmUyYTEtODI4MC00MzY2LWEzNDAtZGRkZDdhZmZlNTEzZjNiNmI0YzMtMDg1Yi00Mzg3
LTgyMDAtYWYzNjA0OTM4YTkz



