新特性
1. JDK5
1.1. 增强for循环(For-Each)
没有下标,适合打印输出。
for(int array : arrays){
}
1.2. 可变参数
在方法声明中,在指定参数类型后加一个省略号...
一个方法中只能指定一个可变参数,它必须是方法的最后一个参数。任何普通的参数必须在它之前声明。
可变参数的本质其实是数组
可变参数可匹配0个至多个参数
public void test(int a, int b, int ... i){
// 此方法至少需要两个参数分别给a,b i可匹配0个至多个参数
}
1.3. 自动装箱拆箱
自动装箱的过程实际上是调用了valueOf()方法。
int age = 30;
// 自动装箱
Integer integer = age;
// 自动拆箱
int age2 = integer;
1.4. 泛型
- Java泛型是JDK1.5中引入的一个新特性,其本质是参数化类型,把类型作为参数传递。
- 常见形式有泛型类、泛型接口、泛型方法。
- 语法:
<T,…> T
称为类型占位符,表示一种引用类型。
- 好处:
- 提高代码的重用性。
- 防止类型转换异常,提高代码的安全性。
2. JDK7
2.1. 数字之间可以用下划线分割。
int money = 100_000_000;
System.out.println(money); //100000000
2.2. switch支持String类型判断。
switch ("123"):
// ...
2.3. 实例化泛型类时,前面指定了泛型,后面可以省略,不再指定。
class MyGeneric<T> {
// ...
}
MyGeneric<String> myGeneric = new MyGeneric<String>();
// 以上是JDK1.5的写法
// 在JDK1.7后,上述可写成
MyGeneric<String> myGeneric = new MyGeneric<>();
3. JDK8
3.1. 函数式接口
Functional Interface(函数式接口)定义:
任何接口,如果只包含唯一一个抽象方法,那么它就是一个函数式接口。
@FunctionalInterface public interface Runnable { public abstract void run(); }
对于函数式接口,可以通过lambda表达式来创建该接口的对象。
四大函数式接口
Function 函数型接口
package com.gs.function;
import java.util.function.Function;
/**
* Function 函数型接口
* <p>
* 有一个输入参数,有一个输出
* 只要是函数型接口 可以用Lambda表达式简化
*
* @author admin
* @date 2021/8/26 8:14 下午
*/
public class Demo01 {
public static void main(String[] args) {
// 工具类:输出输入的值
// Function<String, String> function = new Function<String, String>() {
//
// @Override
// public String apply(String s) {
// return s;
// }
// };
// 上述代码可改写成
// Function function = (s) -> {
// return s;
// };
// 代码只有return语句的话,return也可以省略
Function function = s -> s;
System.out.println(function.apply("!23"));
}
}
Predicate 断定型接口
package com.gs.function;
import com.sun.org.apache.bcel.internal.generic.NEW;
import java.util.function.Predicate;
/**
* 断定型接口,有个输入参数,返回值只能是布尔值。
* @author admin
* @date 2021/8/26 8:25 下午
*/
public class Demo02 {
public static void main(String[] args) {
// 判断字符串是否为空
// Predicate<String> predicate = new Predicate<String>() {
// @Override
// public boolean test(String s) {
// return s.isEmpty();
// }
// };
Predicate<String> predicate = s -> s.isEmpty();
System.out.println(predicate.test("123"));
}
}
Consumer 消费型接口
package com.gs.function;
import java.util.function.Consumer;
/**
* Consumer 消费型接口
* 只有输入,没有返回值
*
* @author admin
* @date 2021/8/26 8:34 下午
*/
public class Demo03 {
public static void main(String[] args) {
// 打印字符串
// Consumer<String> consumer = new Consumer<String>() {
// @Override
// public void accept(String s) {
// System.out.println(s);
// }
// };
Consumer<String> consumer = s -> System.out.println(s);
consumer.accept("123");
}
}
Supplier 供给型接口
package com.gs.function;
import jdk.nashorn.internal.ir.ReturnNode;
import java.util.function.Supplier;
/**
* Supplier 供给型接口
* 没有参数,只有返回值
*
* @author admin
* @date 2021/8/26 8:38 下午
*/
public class Demo04 {
public static void main(String[] args) {
// Supplier<Integer> supplier = new Supplier<Integer>() {
// @Override
// public Integer get() {
// return 1024;
// }
// };
Supplier<Integer> supplier = () -> 1024;
System.out.println(supplier.get());
}
}
3.2. Lambda 表达式
- 允许把函数作为一个方法的参数(函数作为参数传递进方法中)。
- 使用 Lambda 表达式可以使代码变的更加简洁紧凑,避免匿名内部类定义过多,只留下核心的逻辑。
lambda 表达式的语法格式如下:
(parameters) -> expression
或
(parameters) ->{ statements; }
以下是lambda表达式的重要特征:
- 可选类型声明:不需要声明参数类型,编译器可以统一识别参数值。
- 可选的参数圆括号:一个参数无需定义圆括号,但多个参数需要定义圆括号。
- 可选的大括号:如果主体包含了一个语句,就不需要使用大括号。
- 可选的返回关键字:如果主体只有一个表达式返回值则编译器会自动返回值,大括号需要指定明表达式返回了一个数值。
如:
// 1. 不需要参数,返回值为 5
() -> 5
// 2. 接收一个参数(数字类型),返回其2倍的值
x -> 2 * x
// 3. 接受2个参数(数字),并返回他们的差值
(x, y) -> x – y
// 4. 接收2个int型整数,返回他们的和
(int x, int y) -> x + y
// 5. 接受一个 string 对象,并在控制台打印,不返回任何值(看起来像是返回void)
(String s) -> System.out.print(s)
以下二者效果等同:
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("hello1");
}
}).start();
new Thread(() -> System.out.println("hello2")).start();
简化过程
简化过程:外部类->静态内部类->局部内部类->匿名内部类->lambda
public class TestLambda {
// 3.静态内部类
static class Like2 implements ILike{
@Override
public void lambda(){
System.out.println("I like lambda2");
}
}
public static void main(String[] args) {
// 调用外部类创建对象
ILike like1 = new Like();
like1.lambda();
// 调用静态内部类创建对象
ILike like2 = new Like2();
like2.lambda();
// 4.局部内部类
class Like3 implements ILike{
@Override
public void lambda(){
System.out.println("I like lambda3");
}
}
ILike like3 = new Like3();
like3.lambda();
// 5.匿名内部类,没有类名字,必须借助接口或者父类
ILike like4 = new ILike() {
@Override
public void lambda() {
System.out.println("I like lambda4");
}
};
like4.lambda();
// 6.用lambda简化
ILike like5 = ()->{
System.out.println("I like lambda5");
};
like5.lambda();
}
}
// 1.定义一个函数接口
interface ILike{
void lambda();
}
// 2.实现外部类
class Likx
注意:
- lambda 表达式只能引用标记了 final 的外层局部变量,这就是说不能在 lambda 内部修改定义在域外的局部变量,否则会编译错误。
- 可以直接在 lambda 表达式中访问外层的局部变量。
- 局部变量可以不用声明为 final,但是必须不可被后面的代码修改(即隐性的具有 final 的语义)
- 在 Lambda 表达式当中不允许声明一个与局部变量同名的参数或者局部变量。
3.3. Stream流式计算
什么是流式计算?
大数据:存储 + 计算
集合、MySQL本质是存储东西的
计算都应该交给流来操作。
package com.gs.stream;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
/**
* 题目要求:一分钟内完成此题,只能用一行代码实现
* 现有5个用户,筛选:
* 1. ID必须是偶数
* 2. 年龄必须大于23岁
* 3. 用户名转为大写字母
* 4. 用户名字母倒着排序
* 5. 只输出一个用户
*
* @author admin
* @date 2021/8/26 8:49 下午
*/
public class Test {
public static void main(String[] args) {
User u1 = new User(1, "a", 21);
User u2 = new User(2, "b", 22);
User u3 = new User(3, "c", 23);
User u4 = new User(4, "d", 24);
User u5 = new User(6, "e", 25);
// 集合就是存储
List<User> list = Arrays.asList(u1, u2, u3, u4, u5);
// 计算交给Stream流
// lambda表达式、链式编程、函数式接口、Stream流式计算
list.stream().filter(user -> user.getId() % 2 == 0)
.filter(user -> user.getAge() > 23)
.map(user -> user.getName().toUpperCase())
.sorted(Comparator.reverseOrder())
.limit(1)
.forEach(System.out::println);
}
}
List<String> list = users.stream().map(User::getName).collect(Collectors.toList());
3.4. 隐藏final
局部内部类访问外部类当前方法中的局部变量时不需要再添加final关键字
class Outer{
public void method(){
// 在此处定义的变量为局部变量
String address = "gd";
class Inner {
// 如若在此处调用address,则局部变量需为常量,JDK1.8后可以不加final关键字
}
}
}
3.5. Optional类判空
解决java.lang.NullPointerException空指针异常的简单化。
JDK7前判空实现
User user = usersMapper.selectUserById(userId);
String userName = user.getUserName();
if(user!= null){
if(userName != null && !userName.isEmpty()){
.....
}
}
JDK8开始
User user = usersMapper.selectUserById(userId);
Optional.ofNullable(user)
.map(User::getUserName)
.ifPresent(userName->{
....
}
- ofNullable 方法 :将指定值用Optional封装之后返回,如果该值为null,则返回一个空的Optional对象
- empty 方法 :返回一个空的Optional实例
- filter 方法 :如果值存在并且满足提供的谓词,就返回包含该值的Optional对象;否则返回一个空的 Optional对象
- flatMap 方法 :如果值存在,就对该值执行提供的mapping函数调用,返回一个Optional类型的值,否则就返 回一个空的Optional对象
- get 方法 :如果该值存在,将该值用Optional封装返回,否则抛出一个NoSuchElementException异常
- ifPresent 方法 :如果值存在,就执行使用该值的方法调用,否则什么也不做
- isPresent 方法 :如果值存在就返回true,否则返回false
- map 方法 :如果值存在,就对该值执行提供的mapping函数调用
- of 方法 :将指定值用Optional封装之后返回,如果该值为null,则抛出一个NullPointerException异常
- orElse 方法:如果有值则将其返回,否则返回一个默认值
- orElseGet 方法 :如果有值则将其返回,否则返回一个由指定的Supplier接口生成的值
- orElseThrow 方法 :如果有值则将其返回,否则抛出一个由指定的Supplier接口生成的异常
3.6. 哈希表新增红黑树
JDK1.8+: 哈希表 = 数组 + 链表 + 红黑树。
4. JDK14
4.1. Instanceof模式匹配
新版判断后面可以做其他的事情,如跟上实例名和增加更多的判断。
// B extends A
// 判断B是否是A的子类
// 旧版的判断方法
if(B instanceof A){
// ...
}
// 新版的判断,其中a在B instanceof A结果为true后相当于直接进行类型转换,后面可以带一些判断
if(B isntanceof A a){
// ...
}
if(B isntanceof A a && a.getId() > 10){
// ...
}
5. 版本对照
Has been compiled by a more recent version of the Java Runtime (class file version 57.0)
解决:切换JDK版本到指定的版本。
对应版本表
49 = Java 5 50 = Java 6 51 = Java 7 52 = Java 8 53 = Java 9 54 = Java 10 55 = Java 11 56 = Java 12 57 = Java 13 58 = Java 14 59 = Java 15 60 = Java 16 61 = Java 17 62 = Java 18 63 = Java 19