小白学JAVA之二十六——Java新特性
一. Java8的新特性
1.1 函数式接口
- 函数式接口主要指只包含一个抽象方法的接口,如:java.lang.Runnable、java.util.Comparator接口等。
- Java8提供@FunctionalInterface注解来定义函数式接口,若定义的接口不符合函数式的规范便会报错。
- Java8中增加了java.util.function包,该包包含了常用的函数式接口,具体如下:
1.2 Lambda 表达式
- Lambda 表达式是实例化函数式接口的重要方式,使用 Lambda 表达式可以使代码变的更加简洁紧凑。
- lambda表达式:参数列表、箭头符号->和方法体组成,而方法体中可以是表达式,也可以是语句块。
- 语法格式:(参数列表) -> { 方法体; } - 其中()、参数类型、{} 以及return关键字 可以省略。
import java.util.Comparator;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
public class FunctionalInterfaceTest {
public static void main(String[] args) {
// 1.匿名内部类的语法格式: 父类/接口类型 引用变量名 = new 父类/接口类型(){ 方法的重写 };
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println("我是既没有参数又没有返回值的方法!");
}
};
runnable.run(); // 我是既没有参数又没有返回值的方法!
// 使用lambda表达式实现函数式接口对象的创建: (参数列表)->{方法体;}
//Runnable runnable1 = () -> { System.out.println("我是既没有参数又没有返回值的方法!"); };
Runnable runnable1 = () -> System.out.println("我是既没有参数又没有返回值的方法!");
runnable1.run();
System.out.println("----------------------------------------------------------------------");
Consumer consumer = new Consumer() {
@Override
public void accept(Object o) {
System.out.println(o + "有参但没有返回值的方法就是我!");
}
};
consumer.accept("友情提示:"); // 友情提示:有参但没有返回值的方法就是我!
//Consumer consumer1 = (Object o) -> {System.out.println(o + "有参但没有返回值的方法就是我!");};
//Consumer consumer1 = (o) -> System.out.println(o + "有参但没有返回值的方法就是我!");
// 省略了()、参数类型、{}, 自动类型推断
Consumer consumer1 = o -> System.out.println(o + "有参但没有返回值的方法就是我!");
consumer1.accept("友情提示:");
System.out.println("----------------------------------------------------------------------");
Supplier supplier = new Supplier() {
@Override
public Object get() {
return "无参有返回值!";
}
};
System.out.println(supplier.get()); // 无参有返回值
//Supplier supplier1 = () -> {return "无参有返回值!";};
Supplier supplier1 = () -> "无参有返回值!";
System.out.println(supplier1.get());
System.out.println("----------------------------------------------------------------------");
Function function = new Function() {
@Override
public Object apply(Object o) {
return o;
}
};
System.out.println(function.apply("有参有返回值的方法")); // 有参有返回值的方法
// return 和 {} 都可以省略
Function function1 = o -> o;
System.out.println(function1.apply("有参有返回值的方法"));
System.out.println("----------------------------------------------------------------------");
Comparator comparator = new Comparator() {
@Override
public int compare(Object o1, Object o2) {
return 0;
}
};
System.out.println(comparator.compare(10, 20)); // 0
Comparator comparator1 = (o1, o2) -> 0;
System.out.println(comparator1.compare(10, 20));
System.out.println("----------------------------------------------------------------------");
Predicate predicate = new Predicate() {
@Override
public boolean test(Object o) {
return false;
}
};
System.out.println(predicate.test("hello")); // false
Predicate predicate1 = o -> false;
System.out.println(predicate1.test("hello"));
}
}
1.3 方法引用
- 方法引用主要指通过方法的名字来指向一个方法而不需要为方法引用提供方法体,该方法的调用交给函数式接口执行。
- 方法引用使用一对冒号 :: 将类或对象与方法名进行连接,通常使用方式如下:
- 对象的非静态方法引用 ObjectName :: MethodName
- 类的静态方法引用 ClassName :: StaticMethodName
- 类的非静态方法引用 ClassName :: MethodName
- 构造器的引用 ClassName :: new
- 数组的引用 TypeName[] :: new
- 方法引用是在特定场景下lambda表达式的一种简化表示,可以进一步简化代码的编写使代码更加紧凑简洁,从而减少冗余代码。
//自定义一个person类
public class Person implements Comparable<Person> {
private String name;
private int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public void show() {
System.out.println("没事出来秀一下哦");
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public int compareTo(Person o) {
//return getName().compareTo(o.getName());
return getAge() - o.getAge();
}
}
import java.util.Arrays;
import java.util.Comparator;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
public class MethodReferenceTest {
public static void main(String[] args) {
// 1.使用匿名内部类的方式通过函数式接口Runnable中的方法实现对Person类中show方法的调用
Person person = new Person("zhangfei", 30);
Runnable runnable = new Runnable() {
@Override
public void run() {
person.show();
}
};
runnable.run(); // 没事出来秀一下哦
System.out.println("-------------------------------------------------------------");
// 2.使用lambda表达式的方式实现Person类中show方法的调用
Runnable runnable1 = () -> person.show();
runnable1.run(); // 没事出来秀一下哦
System.out.println("-------------------------------------------------------------");
// 3.使用方法引用的方式实现Person类中show方法的调用
Runnable runnable2 = person::show;
runnable2.run();
System.out.println("-------------------------------------------------------------");
// 4.使用匿名内部类的方式通过函数式接口Consumer中的方法来实现Person类中setName方法的调用
Consumer<String> consumer = new Consumer<String>() {
@Override
public void accept(String s) {
person.setName(s);
}
};
consumer.accept("guanyu");
System.out.println("person = " + person); // guanyu 30
System.out.println("-------------------------------------------------------------");
// 5.使用lambda表达式的方式实现Person类中setName方法的调用
Consumer<String> consumer1 = s -> person.setName(s);
consumer1.accept("liubei");
System.out.println("person = " + person); // liubei 30
System.out.println("-------------------------------------------------------------");
// 6.使用方法引用的方式实现Person类中setName方法的调用
Consumer<String> consumer2 = person::setName;
consumer2.accept("zhangfei");
System.out.println("person = " + person); // zhangfei 30
System.out.println("-------------------------------------------------------------");
// 7.使用匿名内部类的方式通过函数式接口Supplier中的方法来实现Person类中getName方法的调用
Supplier<String> supplier = new Supplier<String>() {
@Override
public String get() {
return person.getName();
}
};
System.out.println(supplier.get()); // zhangfei
Supplier<String> supplier1 = () -> person.getName();
System.out.println(supplier1.get()); // zhangfei
Supplier<String> supplier2 = person::getName;
System.out.println(supplier2.get()); // zhangfei
System.out.println("-------------------------------------------------------------");
// 8.使用匿名内部类的方式通过函数式接口Function中的方法实现Integer类中parseInt方法的调用
Function<String, Integer> function = new Function<String, Integer>() {
@Override
public Integer apply(String s) {
return Integer.parseInt(s);
}
};
System.out.println(function.apply("12345")); // 12345
Function<String, Integer> function1 = s -> Integer.parseInt(s);
System.out.println(function1.apply("12345")); // 12345
Function<String, Integer> function2 = Integer::parseInt;
System.out.println(function2.apply("12345")); // 12345
System.out.println("-------------------------------------------------------------");
// 9.使用匿名内部类的方式通过函数式接口Comparator中的方法实现Integer类中compare方法的调用
Comparator<Integer> comparator = new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return Integer.compare(o1, o2);
}
};
System.out.println(comparator.compare(10, 20)); // -1
Comparator<Integer> comparator1 = (o1, o2) -> Integer.compare(o1, o2);
System.out.println(comparator1.compare(10, 20)); // -1
Comparator<Integer> comparator2 = Integer::compare;
System.out.println(comparator2.compare(10, 20)); // -1
System.out.println("-------------------------------------------------------------");
// 10.使用匿名内部类的方式通过类名来调用非静态方法
// 其中一个参数对象作为调用对象来调用方法时,可以使用上述方式 更抽象
Comparator<Integer> comparator3 = new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o1.compareTo(o2);
}
};
System.out.println(comparator3.compare(10, 20)); // -1
Comparator<Integer> comparator4 = (o1, o2) -> o1.compareTo(o2);
System.out.println(comparator4.compare(10, 20)); // -1
Comparator<Integer> comparator5 = Integer::compareTo;
System.out.println(comparator5.compare(10, 20)); // -1
System.out.println("-------------------------------------------------------------");
// 11.使用匿名内部类的方式通过Supplier函数式接口创建Person类型的对象并返回
Supplier<Person> supplier3 = new Supplier<Person>() {
@Override
public Person get() {
return new Person();
}
};
System.out.println(supplier3.get()); // null 0
Supplier<Person> supplier4 = () -> new Person();
System.out.println(supplier4.get()); // null 0
Supplier<Person> supplier5 = Person::new;
System.out.println(supplier5.get()); // null 0
System.out.println("-------------------------------------------------------------");
// 12.使用匿名内部类的方式通过BiFunction函数式接口采用有参方式创建Person类型的对象并返回
BiFunction<String, Integer, Person> biFunction = new BiFunction<String, Integer, Person>() {
@Override
public Person apply(String s, Integer integer) {
return new Person(s, integer);
}
};
System.out.println(biFunction.apply("zhangfei", 30)); // zhangfei 30
BiFunction<String, Integer, Person> biFunction1 = (s, integer) -> new Person(s, integer);
System.out.println(biFunction1.apply("zhangfei", 30)); // zhangfei 30
BiFunction<String, Integer, Person> biFunction2 = Person::new;
System.out.println(biFunction2.apply("zhangfei", 30)); // zhangfei 30
System.out.println("-------------------------------------------------------------");
// 12.使用匿名内部类的方式通过Function函数式接口创建指定数量的Person类型的对象数组并返回
Function<Integer, Person[]> function3 = new Function<Integer, Person[]>() {
@Override
public Person[] apply(Integer integer) {
return new Person[integer];
}
};
Person[] pArr = function3.apply(3);
System.out.println(Arrays.toString(pArr));
Function<Integer, Person[]> function4 = integer -> new Person[integer];
System.out.println(Arrays.toString(function4.apply(4)));
Function<Integer, Person[]> function5 = Person[]::new;
System.out.println(Arrays.toString(function5.apply(5)));
}
}
1.4 Stream接口
1.4.1 基本概念
- java.util.stream.Stream接口是对集合功能的增强,可以对集合元素进行复杂的查找、过滤、筛选等操作。
- Stream接口借助于Lambda 表达式极大的提高编程效率和程序可读性,同时它提供串行和并行两种模式进行汇聚操作,并发模式能够充分利用多核处理器的优势。
1.4.2 使用步骤
- 创建Stream,通过一个数据源来获取一个流。
- 转换Stream,每次转换返回一个新的Stream对象。
- 对Stream进行聚合操作并产生结果。
1.4.3 创建方式
- 方式一:通过调用集合的默认方法来获取流,如:default Stream stream()
- 方式二:通过数组工具类中的静态方法来获取流,如:static IntStream stream(int[] array)
- 方式三:通过Stream接口的静态方法来获取流,如:static Stream of(T… values)
- 方式四:通过Stream接口的静态方法来获取流,static Stream generate(Supplier<? extends T>s)
1.4.4 常见的中间操作
筛选与切片的常用方法如下:
映射的常用方法如下:
排序的常用方法如下:
1.4.5 终止操作
匹配与查找的常用方法如下:
规约的常用方法如下:
收集的常用方法如下:
1.4.6 案例
准备一个List集合并放入Person类型的对象,将集合中所有成年人过滤出来放到另外一个集合并打印出来。
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;
import java.util.Optional;
import java.util.function.BinaryOperator;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
public class ListPersonTest {
public static void main(String[] args) {
// 1.准备一个List集合并放入Person类型的对象后打印
List<Person> list = new LinkedList<>();
list.add(new Person("zhangfei", 30));
list.add(new Person("xiaoqiao", 17));
list.add(new Person("zhouyu", 20));
list.add(new Person("zhangfei", 30));
list.add(new Person("guanyu", 35));
list.add(new Person("liubei", 40));
for (Person tp: list) {
System.out.println(tp);
}
System.out.println("-------------------------------------------------------");
// 2.使用forEach方法将List集合中所有成年人过滤出来并放入另外一个集合中打印
List<Person> list1 = new LinkedList<>();
for (Person tp : list) {
if (tp.getAge() >= 18) {
list1.add(tp);
}
}
for (Person tp : list1) {
System.out.println(tp);
}
System.out.println("-------------------------------------------------------");
// 3.使用Stream接口实现上述功能
list.stream().filter(new Predicate<Person>() {
@Override
public boolean test(Person person) {
return person.getAge() >= 18;
}
}).forEach(new Consumer<Person>() {
@Override
public void accept(Person person) {
System.out.println(person);
}
});
System.out.println("-------------------------------------------------------");
// 4.使用lambda表达式对上述代码进行优化
//list.stream().filter(person -> person.getAge() >= 18).forEach(person -> System.out.println(person));
list.stream().filter(person -> person.getAge() >= 18).forEach(System.out::println);
System.out.println("-------------------------------------------------------");
// 5.实现对集合中元素通过流跳过2个元素后再取3个元素后打印
list.stream().skip(2).limit(3).forEach(System.out::println);
System.out.println("-------------------------------------------------------");
// 6.实现集合中所有元素中的年龄获取出来并打印
list.stream().map(new Function<Person, Integer>() {
@Override
public Integer apply(Person person) {
return person.getAge();
}
}).forEach(System.out::println);
//list.stream().map(person -> person.getAge()).forEach(System.out::println);
list.stream().map(Person::getAge).forEach(System.out::println);
System.out.println("-------------------------------------------------------");
// 7.实现集合中所有元素的自然排序并打印
list.stream().sorted().forEach(System.out::println);
System.out.println("-------------------------------------------------------");
// 8.判断集合中是否没有元素的年龄是大于45岁的
boolean b1 = list.stream().noneMatch(new Predicate<Person>() {
@Override
public boolean test(Person person) {
return person.getAge() > 45;
}
});
System.out.println("b1 = " + b1); // true
b1 = list1.stream().noneMatch(person -> person.getAge() > 45);
System.out.println("b1 = " + b1); // true
System.out.println("-------------------------------------------------------");
// 9.按照指定的比较器规则获取集合所有元素中的最大值
Optional<Person> max = list.stream().max(new Comparator<Person>() {
@Override
public int compare(Person o1, Person o2) {
return o1.getAge() - o2.getAge();
}
});
System.out.println("按照年龄排序后的最大值是:" + max);
max = list.stream().max((o1, o2) -> o1.getAge() - o2.getAge());
System.out.println("按照年龄排序后的最大值是:" + max);
System.out.println("-------------------------------------------------------");
// 10.实现将集合中所有元素的年龄映射出来并进行累加后打印
Optional<Integer> reduce = list.stream().map(Person::getAge).reduce(new BinaryOperator<Integer>() {
@Override
public Integer apply(Integer integer, Integer integer2) {
return integer + integer2;
}
});
System.out.println("最终所有年龄的累加和是:" + reduce); // 172
//reduce = list.stream().map(Person::getAge).reduce(((integer, integer2) -> integer + integer2));
reduce = list.stream().map(Person::getAge).reduce((Integer::sum));
System.out.println("最终所有年龄的累加和是:" + reduce); // 172
System.out.println("-------------------------------------------------------");
// 11.实现将集合中所有元素的姓名映射出来并收集到集合中打印
list.stream().map(Person::getName).collect(Collectors.toList()).forEach(System.out::println);
}
}
1.5 Optional类
1.5.1 基本概念
java.util.Optional类可以理解为一个简单的容器,其值可能是null或者不是null,代表一个值存在或不存在。该类的引入很好的解决空指针异常,不用显式进行空值检测。
1.5.2 常用方法
1.5.3 案例
判断字符串是否为空,若不为空则打印字符串的长度,否则打印0。
import java.util.Optional;
import java.util.function.Function;
public class OptionalTest {
public static void main(String[] args) {
//普通方法判断字符串是否为空:
//String str1 = "hello";
String str1 = null;
if (null != str1) {
System.out.println("字符串的长度是:" + str1.length()); // 5 空指针异常
} else {
System.out.println("字符串为空,因此长度为0!");
}
System.out.println("----------------------------------------------------");
// Java8中使用Optional类实现空值的处理
// 1.将数据str1装到Optional对象代表的容器中
Optional<String> optional = Optional.ofNullable(str1);
// 2.建立映射关系 使用字符串的长度与字符串建立映射关系
/*Optional<Integer> integer = optional.map(new Function<String, Integer>() {
@Override
public Integer apply(String s) {
return s.length();
}
});*/
//Optional<Integer> integer = optional.map(s -> s.length());
Optional<Integer> integer = optional.map(String::length);
// 3.若字符串为空则打印0,否则打印字符串的数值
System.out.println("integer = " + integer); // Optional.empty
System.out.println(integer.orElse(0)); // 0
}
}
二. Java9的新特性
2.1 模块化的使用
2.1.1 语法格式
在 module-info.java 文件中,我们可以用新的关键词module来声明一个模块,具体如下:
module 模块名称 {
}
2.1.2 模块化的优势
- 减少内存的开销。
- 可简化各种类库和大型应用的 开发和维护。
- 安全性,可维护性,提高性能。
2.2 钻石操作符的使用升级
在Java9中允许在匿名内部类的使用中使用钻石操作符。
import java.util.Comparator;
public class DiamondTest {
public static void main(String[] args) {
// 实现匿名内部类和钻石操作符的搭配使用
//Comparator<Integer> comparator = new Comparator<Integer>() {
Comparator<Integer> comparator = new Comparator<>() {
@Override
public int compare(Integer o1, Integer o2) {
return 0;
}
};
}
}
2.3 集合工厂方法
2.3.1 基本概念
- Java9的List、Set和Map集合中增加了静态工厂方法of实现不可变实例的创建。
- 不可变体现在无法添加、修改和删除它们的元素。
- 不允许添加null元素对象。
import java.util.List;
import java.util.Map;
import java.util.Set;
public class CollectionTest {
public static void main(String[] args) {
// 创建List类型的不可变实例
List<Integer> list = List.of(1, 2, 3, 4, 5);
//list.add(6); // 编译ok,运行发生UnsupportedOperationException不支持此操作的异常
System.out.println(list); // [1, 2, 3, 4, 5]
Set<Integer> set = Set.of(6, 7, 8);
//set.add(null);// 编译ok,运行发生UnsupportedOperationException不支持此操作的异常
Map<Integer, String> map = Map.of(1, "one", 2, "two");
//map.put(3, "three");// 编译ok,运行发生UnsupportedOperationException不支持此操作的异常
}
}
2.3.2 实际意义
- 保证线程安全:在并发程序中既保证线程安全性,也大大增强了并发时的效率。
- 被不可信的类库使用时会很安全。
- 如果一个对象不需要支持修改操作,将会节省空间和时间的开销。
- 可以当作一个常量来对待,并且这个对象在以后也不会被改变。
2.4 InputStream的增强
InputStream类中提供了transferTo方法实现将数据直接传输到OutputStream中。
import java.io.*;
public class InputStreamTest {
public static void main(String[] args) {
InputStream inputStream = null;
OutputStream outputStream = null;
try {
inputStream = new FileInputStream("d:/a.txt");
outputStream = new FileOutputStream("d:/b.txt");
inputStream.transferTo(outputStream); // 实现数据的复制,底层是read和write方法的调用
} catch (IOException e) {
e.printStackTrace();
} finally {
if (null != outputStream) {
try {
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (null != inputStream) {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
三. Java10的新特性
3.1 局部变量类型推断
3.1.1 基本概念
- Java10可以使用var作为局部变量类型推断标识符,此符号仅适用于局部变量,增强for循环的索引,以及传统for循环的本地变量。
- 它不能使用于方法形式参数,构造函数形式参数,方法返回类型,字段,catch形式参数或任何其他类型的变量声明。
import java.util.LinkedList;
import java.util.List;
public class VarTest {
public static void main(String[] args) {
// 由初始值可以推断出变量的类型,因此可以使用var取代
//int num = 10;
var num = 10;
//List<Integer> list = new LinkedList<>();
var list = new LinkedList<Integer>();
list.add(10);
for (var v : list) {
System.out.println(v);
}
for (var i = 0; i < 10; i++) {}
}
}
3.1.2 实际意义
- 标识符var不是关键字,只是一个保留的类型名称。这意味着var用作变量,方法名或包名的代码不会受到影响,但var不能作为类或则接口的名字。
- 避免了信息冗余。
- 对齐了变量名。
- 更容易阅读。
四. Java11的新特性
4.1 简化的编译运行操作
- 在Java11中可以使用java命令一次性进行编译和运行操作。
- 执行源文件中的第一个类必须包含主方法。
- 不可以使用其它源文件中自定义的类。