JAVA 8-13 高级特性
二 JDK8新特性之接口和日期处理
1 default关键字
-- 1.8以前接口只能有抽象方法,不能有任何方法实现
-- default修饰方法,可以定义具体的方法实现
-- 实现类可以直接调用
2 Base64
public static void oldBase64() throws IOException {
BASE64Encoder encoder = new BASE64Encoder();
String encodesString = encoder.encode("3322".getBytes());
BASE64Decoder decoder = new BASE64Decoder();
String decodesString = new String(decoder.decodeBuffer(encodesString));
System.out.println(encodesString + " -------- " + decodesString);
}
public static void newBase64(){
Base64.Encoder encoder = Base64.getEncoder();
String encodesString = encoder.encodeToString("234234".getBytes());
Base64.Decoder decoder = Base64.getDecoder();
String decodesString = new String(decoder.decode(encodesString));
System.out.println(encodesString + " -------- " + decodesString);
}
3 时间日期处理
1 时间处理API
-- SimpleDateFormat,Calendar等缺点:
java.util.Date 非线程安全
日期/时间对象比较、加减比较麻烦
-- 新版提供Date-Time API(JSR 310)
包:java.time
核心类:
LocalDate(不包含具体时间的日期)
LocalTime 不含日期的时间
LocalDateTime 包含日期和时间
2 时间日期格式化
-- 引入线程安全的DateTimeFormatter
-- 日期差 Duration
4 Optional
-- 解决空指针异常
本质是一个有可选值得包装类
-- 创建Optional类
ofNullable()
get() 调用前判断是否有值
isPresent() 值存在则isPresent返回true
Optional<Dog> optional1Not = Optional.ofNullable(dog);
System.out.println(optional1Not.isPresent());
orElse() 如果有值返回,否则返回参数值
Dog dog2 = new Dog(44);
dog = Optional.ofNullable(dog).orElse(dog2);
System.out.println(dog.getAge());
Dog dog3 = null;
int result = Optional.ofNullable(dog3).map(obj -> obj.getAge()).orElse(7);
System.out.println(result);
三 JDK8 Lambda表达式
1 lambda表达式
–函数式编程
将函数(行为)作为一个参数进行传递。面向对象编程 是对数据的抽象,函数式编程则是对行为的抽象
--创建线程
jdk8之前
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("old thread create");
}
}).start();
jdk8之后
new Thread(()->System.out.println("new Thread create")).start();
--集合容器
使用前
List<String> list = Arrays.asList("111","000","aaa");
Collections.sort(list, new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o2.compareTo(o1);
}
});
for(String s : list){
System.out.println(s);
}
使用后
List<String> list = Arrays.asList("111","000","aaa");
Collections.sort(list,(a,b)->b.compareTo(a));
for(String s : list){
System.out.println(s);
}
--使用场景
一个接口中只包含一个方法,语法:(params)->expression
参数列表:
括号中参数列表的数据类型可省略
括号中的参数只有一个,参数类型和()都可以省了
方法体:
如果{}中的代码只有1行,无论有无返回值,可省略{},return,分号要一起省略
--好处:Lambda表达式的实现方式本质是以匿名内部类的方式进行实现
重构有臃肿代码,更高效的开发效率,尤其集合Collection操作的时候
2 自定义Lambda接口编程
-- 自定义Lambda接口编程
定义一个函数式接口,@FunctionalInterface
编写一个方法,输入需要的数据和接口
在调用方法时传入数据和lambda表达式
@FunctionalInterface
public interface OperFunc<R,T> {
R operator(T t1,T t2);
}
public static Integer operator(Integer t1,Integer t2,OperFunc<Integer,Integer> of){
return of.operator(t1,t2);
}
public static void testOper(){
System.out.println(operator(2,3,(Integer x,Integer y)-> {
return x+y;
}));
System.out.println(operator(33,2,(x,y)->x+y));
System.out.println(operator(42,23,(x,y)->x-y));
}
四 函数式编程Function
1 Function接口的使用
Lambda表达式必须先定义接口,创建后才可以用;JDK内置了许多接口
JAVA8 内置的四大核心函数式接口
Consumer:消费型接口:有入参,无返回
void accept(T t);
Supplier:共给型接口:无入参,有返回
T get();
Function<T,R>:函数型
R apply(T t);
Predicate:断言型接口,有入参,有返回
boolean testOper(T t);
– 例子
public class FunctionUtils implements Function {
@Override
public Object apply(Object o) {
return o+"apply ....";
}
public static void testFunc(){
Function functionUtils = new FunctionUtils();
functionUtils.apply("op---");
//常规使用
Function<Integer,Integer> function = p->p*10;
System.out.println(function.apply(11));
}
}
2 BitFunction接口
BitFunction<T,U,R> 接收2个参数
BiFunction<Integer,Integer,Integer> function = (x,y)->x+y;
System.out.println(function.apply(20,33));
3 Consumer接口
无输入,无返回,常用于打印和发短信
public static void testConsumer(){
Consumer<String> consumer = phone ->{
System.out.println("手机" +phone+"已发送");
};
sendMsg("111111111",consumer);
//集合应用
List<String> list = Arrays.asList("sdf","ddd");
list.forEach(str->{
System.out.println(str);
});
}
public static void sendMsg(String phone, Consumer<String> consumer){
consumer.accept(phone);
}
4 Supplier接口
无入参,有返回,常用于:无参的工厂方法
public static void testSupplier(){
Dog dog = newDog();
System.out.println(dog.getAge());
}
public static Dog newDog(){
Supplier<Dog> supplier = ()->{
Dog dog=new Dog();
dog.setAge(2);
return dog;
};
return supplier.get();
}
5 Predicate接口
接收参数,用于判断是否满足一定的条件,过滤数据
public static void testPredicate(){
List<String> list = Arrays.asList("322","abc","jone","jane");
list = filter(list, str -> str.startsWith("a") );
System.out.println(list.toString());
}
public static List<String>filter(List<String> list , Predicate<String>predicate){
List<String> result = Lists.newArrayList();
list.forEach(str->{
if(predicate.test(str)){
result.add(str);
}
});
return result;
}
6 方法引用与构造函数引用
方法引用是一种更简洁易懂的Lambda表达式,操作符 :: ,用来直接类或者实例已经存在的方法或者构造方法
静态方法, ClassName::methodName
实例方法,Instance::methodName
构造函数, 类名::new
public static void test1(){
// 静态函数引用
Function<String,Integer> function = Integer::parseInt;
Integer t1 = function.apply("22");
System.out.println(t1);
//非构造函数引用
String content = "JAVA";
Function<Integer,String> subFun = content::substring;
System.out.println(subFun.apply(1));
//多个参数构造函数引用
BiFunction<String,Integer, User> biFunction = User::new;
User u1 = biFunction.apply("jone",11);
System.out.println(u1.getAge());
//函数引用
sayHi(String::toUpperCase,"abcd");
}
public static void sayHi(Function<String,String> function,String msg){
System.out.println(function.apply(msg));
}
五 流Stream
1 stream使用
– 将集合转换为一种叫做"流"的元素队列,通过声明性方式能够对集合中的
每个元素进行一系列并行或者串行的流水线操作
– 元素是特定类型的对象,元素集合看成流。流在管道中传输,且可以在管道的节点上进行处理,比如:排序,聚合,过滤等
– 操作;
数据元素:List、Set、Map等
生成流,串行流stream()并行流parallelStream()
中间操作,排序,聚合,转换等
终端操作,很多流操作本身就会返回一个流,多个操作可以直接连接起来,最后统一进行收集
– 例子
public static void test1(){
List<String> list = Arrays.asList("111","333","4343");
List<String> result = list.stream().map(str->"add"+str).collect(Collectors.toList());
result.forEach(str->{
System.out.println(str);
});
}
2 map函数和filter函数
– map
将流中的每个元素T映射为R
场景:转换对象
List<User> users = Arrays.asList(new User("JONE",22),new User("jane",33));
List<UserDO> results = users.stream().map(obj -> {
UserDO userDO = new UserDO(obj.getName(),obj.getAge());
return userDO;
}).collect(Collectors.toList());
results.forEach(obj ->{
System.out.println(obj.getAge());
});
– filter
场景:过滤条件
public static void filterTest(){
List<String> list = Arrays.asList("dsf","sfddsssss","2342342343");
list = list.stream().filter(str -> str.length()>5).collect(Collectors.toList());
System.out.println(list);
}
3 sorted函数和limit函数
– sorted
List<String>result1 = list.stream().sorted().collect(Collectors.toList());
//按长度排序
List<String> result2 = list.stream().sorted(Comparator.comparing(obj->obj.length())).collect(Collectors.toList());
//按长度反序
List<String> result3 = list.stream().sorted(Comparator.comparing(obj->obj.length(),Comparator.reverseOrder())).collect(Collectors.toList());
List<String> result4 = list.stream().sorted(Comparator.comparing(String::length).reversed()).collect(Collectors.toList());
– limit
//排序后取前3数据
List<String> result5 = list.stream().sorted(Comparator.comparing(String::length).reversed()).limit(3).collect(Collectors.toList());
4 match函数
//数组中字符串是否都全部长度》5
boolean flag = list.stream().allMatch(obj -> obj.length()>5);
//数组中字符串是否存在长度》8
flag = list.stream().anyMatch(obj -> obj.length() >8);
5 min和max
//返回长度最长的字符串
String s1 = list.stream().max(Comparator.comparingInt(obj ->obj.length())).get();
//返回年龄最小的用户
List<User> users = Arrays.asList(new User("JONE",22),new User("jane",33));
User user = users.stream().min(Comparator.comparingInt(User::getAge)).get();
六 JDK8并行流parallelStream
1 parallelStream
–为什么用
集合做重复操作,如果使用串行会相当耗时,一般采用多线程来加快
parallelStream用fork/join框架提供了并发执行能力
–底层原理
线程池(ForkjoinPool)维护一个线程队列
分割任务,将父任务分成子任务,完全贴合分治思想
–区别
parallelStream输出已排序的数组时,会乱序
–parallelStream是否一定比stream串行快
错误、数量少的情况,可能串行更快,Forkjoin会更耗性能
–多数情况并行比串行快,是否都可以用并行
不行,部分情况有线程安全问题,parallelStream里面使用的外部变量;
如:多个线程往list里加数据,可以使用CopyOnWriteArrayList线程安全的集合
for(int i=0;i<10;i++) {
List list = new ArrayList();
//List list = new CopyOnWriteArrayList();
IntStream.range(0, 100).parallel().forEach(list::add);
System.out.println(list.size());
}
2 reduce操作
-- 聚合操作
-- 根据一定规则将stream中的元素进行计算后返回唯一的值
-- 常用方法
-- Optional<T> reduce(BinaryOperator<T> accumulator);
//accumulator累加计数器
//累加
int value = Stream.of(0,1,4,32,454).reduce((obj1,obj2)->obj1+obj2).get();
System.out.println(value);
value = Stream.of(0,1,4,32,454).reduce(new BinaryOperator<Integer>() {
@Override
public Integer apply(Integer integer, Integer integer2) {
return integer+integer2;
}
}).get();
//identity初始值
-- T reduce(T identity,BinaryOperator<T> accumulator)
value = Stream.of(0,1,4,32,454).reduce(100,(sum,item)->sum+item);
//求最大值
value = Stream.of(1645, 234345, 32,
44434,564534,435,34343542,212).max(Comparator.comparingInt(Integer::shortValue)).get();
System.out.println(value);
value = Stream.of(1645, 234345, 32,
44434,564534,435,34343542,212).reduce((o1,o2) -> o1>o2?o1:o2).get();
3 forEach
– 不能修改包含本地变量的值
– 不能用break或者return或者continue等关键字结束
七 jdk8收集器和集合统计
1 collect收集器
– 方法作用
对流中的数据进行归集操作
有两个重载方法,在Stream接口
R collect(Supplier supplier,BiConsumer<R,? super T>accumulator,BiConsumer<R,R> combiner);
<R,A> collect(Collector<? super T,A,R> collector);
– Collector作用
提供很多工厂方法
– Collectors
工具类,提供常见的收集器实现
– 例子
List<String> list = Arrays.asList("sdfsf","dd","234234");
List<String> list1 = list.stream().collect(Collectors.toList());
Map<String,String> map = list.stream().collect(Collectors.toMap(obj ->obj,obj->obj));
LinkedList<String> list2 = list.stream().collect(Collectors.toCollection(LinkedList::new));
Set<String> set = list.stream().collect(Collectors.toCollection(TreeSet::new));
2 collectors.join 拼接
– join实现
public static Collector<CharSequence, ?, String> joining() {
return new CollectorImpl<CharSequence, StringBuilder, String>(
StringBuilder::new, StringBuilder::append,
(r1, r2) -> { r1.append(r2); return r1; },
StringBuilder::toString, CH_NOID);
}
-- 例子
List<String> list = Arrays.asList("sdfsf","dd","234234");
String ss = Stream.of("sdfsf","dd","234234").collect(Collectors.joining());
String ss2 = list.stream().collect(Collectors.joining(";"));
//连接符,前缀,后缀
String ss3 = list.stream().collect(Collectors.joining(",","[","]"));
3 partitioningBy 分组
-- Collectors.partitioningBy
public static <T> Collector<T, ?, Map<Boolean, List<T>>> partitioningBy(Predicate<? super
T> predicate) {
return partitioningBy(predicate, toList());
}
-- 对list分组,字符串长度大于4,其他为另一组
List<String> list = Arrays.asList("java", "springboot",
"HTML5","nodejs","CSS3");
Map<Boolean,List<String>> map = list.stream().collect(Collectors.partitioningBy(obj -> obj.length()>4));
System.out.println(map);
{false=[java, CSS3], true=[springboot, HTML5, nodejs]}
4 group by 分组
-- Collections.groupingBy
public static <T, K> Collector<T, ?, Map<K, List<T>>>
groupingBy(Function<? super T, ? extends K> classifier) {
return groupingBy(classifier, toList());
}
-- 根据学生所在省份,进行分组
List<User> users = Arrays.asList(new User("⼴东", 23), new
User("⼴东", 24), new User("⼴东", 23),new User("北京", 22), new
User("北京", 20), new User("北京", 20),new User("海南", 25));
Map<String,List<User>> map = users.stream().collect(Collectors.groupingBy(User::getName));
5 分组统计
-- 聚合函数进行统计查询,分组后统计个数
-- Collectors.counting()
-- 统计各个省份的人数
Map<String,Long> map2 = users.stream().collect(Collectors.groupingBy(User::getName,Collectors.counting()));
6 统计相关
-- summarizing
public static <T>Collector<T, ?, DoubleSummaryStatistics> summarizingDouble(ToDoubleFunction<? super T> mapper) {
return new CollectorImpl<T, DoubleSummaryStatistics, DoubleSummaryStatistics>(
DoubleSummaryStatistics::new,
(r, t) -> r.accept(mapper.applyAsDouble(t)),
(l, r) -> { l.combine(r); return l; }, CH_ID);
}
-- 统计学生的各个年龄信息
IntSummaryStatistics intSummaryStatistics = users.stream().collect(Collectors.summarizingInt(User::getAge));
System.out.println("max ="+intSummaryStatistics.getMax());
System.out.println("avg ="+intSummaryStatistics.getAverage());
System.out.println("count="+intSummaryStatistics.getCount());
八 Collection实战
1 需求描述:电商订单数据处理,根据下面的list1和list2各10个订单
– 统计出同时被两个人购买的商品列表(交集)
– 统计出两个人购买商品的差集
– 统计出全部被购商品的去重并集
– 统计两个人的分别购买订单的平均价格
– 统计两个人的分别购买订单的总价格
//统计出同时被两个人购买的商品列表
//重写equals方法和hashCode方法
List<VideoOrder> list = videoOrders1.stream().
filter(videoOrders2::contains).collect(Collectors.toList());
list.forEach(obj -> System.out.println(obj.getVideoTitle()));
//统计出两个人购买商品的差集
List<VideoOrder> list1 = videoOrders1.stream().filter(obj ->!videoOrders2.contains(obj)).collect(Collectors.toList());
List<VideoOrder> list2 = videoOrders2.stream().filter(obj ->!videoOrders1.contains(obj)).collect(Collectors.toList());
System.out.println(list1.toString());
System.out.println(list2.toString());
//统计出全部被购商品的去重并集
List<VideoOrder> all = videoOrders1.parallelStream().collect(Collectors.toList());
all.addAll(videoOrders2);
//必须重写equals和hashCode
List<VideoOrder> allDistinct = all.stream().distinct().collect(Collectors.toList());
allDistinct.forEach(obj ->
System.out.println(obj.getVideoTitle()));
//统计两个人的分别购买订单的平均价格
IntSummaryStatistics intSummaryStatistics1 = videoOrders1.stream().collect(Collectors.summarizingInt(VideoOrder::getTotalFee));
System.out.println(intSummaryStatistics1.getAverage());
Double d2 = videoOrders2.stream().collect(Collectors.averagingDouble(VideoOrder::getTotalFee));
System.out.println(d2);
//统计两个人的分别购买订单的总价格
System.out.println(intSummaryStatistics1.getSum());
int sum2 = videoOrders2.stream().collect(Collectors.summingInt(VideoOrder::getTotalFee));
System.out.println(sum2);
九 JDK8新的内存空间和异常处理
1 新内存空间Matespace
--JVM内存知识
## 永久代(permanent) ,通过命令行设置参数--XX:MaxPermSize来设置永久代最大可分配内存空间
## JDK8设置PermSize和MaxPermSize会被忽略
## 永久代作用:存放class和mate信息,当class被加载loader时会被存储到该区域,如方法的编译信息
及字节码、常量池和符号解析、类的层级信息、字段、名字等
## java.lang.OutOfMemoryError:PermGen space,原因:永久代空间不够,类太多导致
-- jdk8的修改
jdk8修改HotSpot JVM使用本地内存来存储元数据信息,即 元空间
常用参数:-XX:MetaspaceSize=N 扩容时触发FullGC的初始化阈值
-XX:MaxMetaspaceSize=N 限制Metaspace增长的上线,防止因为某些情况导致其无限的使用本地内存
查看大小:
jstat -gc pid MC:current metaspace capacity MU:metaspace utilization 单位:KB
2 JDK7特性try-with-resources
-- try-with-resources
public static void testNewTry(){
try(OutputStream out = new FileOutputStream(filePath)){
out.write("abcsss".getBytes());
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
public static void testOldTry() throws FileNotFoundException {
OutputStream out = new FileOutputStream(filePath);
try{
out.write("abc".getBytes());
}catch (Exception e){
e.printStackTrace();
}finally {
try{
out.close();
}catch (IOException e){
e.printStackTrace();
}
}
}
## 实现AutoCloseable接口的类,在try()里声明该类实例的时候,try结束自动调用close方法,会早于finally调用的方法
## 不管是否出现异常,try()里的实例都会被调用close
## try里面可以声明多个自动关闭的对象、越早声明的对象、会越晚被close
十 JDK9的常用特性
1 JDK9私有方法
– JDK8新增静态方法和默认方法,不支持私有方法
– JDK9新增私有方法
public interface OrderPay {
void pay();
default void defaultPay(){
privateMethod();
}
private void privateMethod(){
System.out.println("私有方法");
}
}
-- 注意点
## 接口中的静态方法不能被实现类继承和子接口继承,但是接口中的非静态的默认方法可以被实现类继承
如:List.of(),但是ArrayList不能继承
## 类的静态方法可以被继承
2 JDK9的增强try-with-resources
public static void testNewJdk9Try() throws FileNotFoundException {
OutputStream out = new FileOutputStream(filePath);
try(out){
out.write("dsds".getBytes());
}catch (IOException e){
e.printStackTrace();
}
}
-- JDK9中,在try外进行初始化,括号内引用,既可以实现资源自动关闭,多个变量则分号分隔
十一 Stream和集合API
1 只读集合
– jdk9前
public static void testBeforeJdk9(){
List list = Arrays.asList(“sfdsdf”,“323”,“sdf”);
//只读,不可修改
list = Collections.unmodifiableList(list);
Map map = new HashMap<String,String>();
map.put("age","18");
map.put("name","join");
map = Collections.unmodifiableMap(map);
}
-- jdk9后
public static void testAfterJdk9(){
List<String> list = List.of("sfdsdf","323","sdf");
Map map = Map.of("age","18","name","join");
Set set = Set.of("1323","423","234");
}
2 JDK9新增的Stream API
-- takeWhile
有序集合:从Stream中获取一部分数据,返回从头开始的尽可能多的元素,直到遇到第一个false结果,如果第一个值不满足断言条件,将返回一个空的Stream
List<String> list = List.of("git","html","","dd").stream()
.takeWhile(obj ->!obj.isEmpty()).collect(Collectors.toList());
-- dropWhile
与takeWhile相反,返回剩余元素
list = List.of("git","html","","dd").stream()
.dropWhile(obj ->!obj.isEmpty()).collect(Collectors.toList());
十二 JDK10和JDK11常见特性
1 JDK10之局部变量类型var
-- Local-Variable Type Inference(局部变量类型推断)
仅适用于局部变量
不能使用于方法形参、构造函数形参、方法返回类型或任何其他类型的变量声明
标识符var不是关键字,而是一个保留类型名称,而且不支持类或接口
2 JDK11新增Http客户端
JDK9引入在JDK10更新
– 常用类和接口
HttpClient.Builder 构建工具类
HttpRequest.Builder
HttpRequest.BodyPublisher
将java对象转换为可发送的HTTP body字节流,如form表单提交
HttpResponse.BodyHandler
处理收到的Response Body