java基础---JDK8探索

JDK8中部分新特性

  1. 对Lambda表达式的支持
  2. 方法引用
  3. 集合中加入了新的方法stream()
  4. 其他的还有接口加入了默认方法
  5. 解决空指针异常的Optional 类
  6. 新的日期时间工具类

其他的特性我这里基本不怎么用,所以也没有罗列出来,毕竟自己也没多少理解,下面详细说一下上面这几种特性

一、lambda表达式

要使用lambda,参数类型必须为函数式接口,所以从函数式接口说起

函数式接口

函数式接口要求接口中只有一个抽象方法,可以使用@FunctionalInterface注解表示。和@Override类似,可以不写,但建议标注上。
以List集合的foreach方法举例(不是增强for,是jdk8中新增的方法)

@FunctionalInterface
public interface Consumer<T> {
	void accept(T t);//这个就是唯一的抽象方法
	//这个方法为1.8中另一个新特性,默认方法
	default Consumer<T> andThen(Consumer<? super T> after) {
        Objects.requireNonNull(after);
        return (T t) -> { accept(t); after.accept(t); };
    }
}

javaAPI中包含很多的函数式接口,可以自行查看,另:感谢菜鸟教程,很好的新手入门网站

Lambda表达式

lambda表达式包含三个部分:

  1. 形式参数:用括号包裹,单个参数可以省略括号;参数类型可以省略
  2. 符号:->
  3. 方法体:多行用大括号包裹,单行可以省略
    举三个栗子:
    ()->System.out.println(“没有参数~~”);
    param-> System.out.println(param);
    (String param)->{
    String s = “两行代码了”;
    System.out.println(param);
    }
  • 用法一:替换匿名类,还是以经典的线程创建启动为例,new Thread()可以接收一个Runnable接口作为参数,先看Runnable接口的源码
@FunctionalInterface
public interface Runnable {
	public abstract void run();
}

很明显这是一个函数式接口,并且方法抽象方法是没有参数的,那么我们可以编写测试

@Test
public void test(){
	new Thread(new Runnable(){
        @Override
        public void run(){
            System.out.println("曾经我们这么写");
        }
    }).start();
    new Thread(() -> System.out.println("现在我们一行写完了,6不6")).start();
}

没有玩过的小朋友,可以尝试自己定义一个多参数的函数式接口,然后直接使用Lambda表达式写接口的实现

  • 用法二:最常使用的迭代集合(此处标记重点),用的非常多,先来个list集合的栗子
    先跟踪源码会发现List接口继承Collection接口,Collection接口又继承Iterable接口,我们看Iterable接口的源码:
public interface Iterable<T> {
	Iterator<T> iterator();
	default void forEach(Consumer<? super T> action) {
        Objects.requireNonNull(action);
        for (T t : this) {
            action.accept(t);
        }
    }
    default Spliterator<T> spliterator() {
        return Spliterators.spliteratorUnknownSize(iterator(), 0);
    }
}

OK,可以看到,源码中存在一个forEach方法,上面我们已经看过Consumer接口,为一个函数式接口,接下来我们就可以编写测试了

public void testList(){
    //先初始化一个list集合
    List<String> list = Arrays.asList("java好","java妙","java呱呱叫");
    //曾经我们使用增强for循环
    for(String s : list){
        System.out.println("java怎么样?回答:"+s);
    }
    //我们先不用Lambda的话foreach方法使用
    list.forEach(new Consumer<String>() {
        @Override
        public void accept(String s) {
            System.out.println("java怎么样?回答:"+s);
        }
    });
    //最后再来看,函数式接口用Lambda表达式实现
    list.forEach(s->System.out.println("java怎么样?回答:"+s));
}

啊,一行代码实现上面两种方式的,就是这么嗨皮,最后顺手来个Map的遍历看看

@Test
public void testMap(){
	//顺手写个Map的测试吧
	Map<String,String> m = new HashMap<>();
    m.put("1","java好");
    m.put("2","java妙");
    m.put("3","java呱呱叫");
    m.forEach((k,v)->System.out.println(k+v););
}

二、方法引用

直接上个栗子,简单明了

@Test
public void test(){
    //先初始化一个list集合
    List<String> list = Arrays.asList("java好","java妙","java呱呱叫");
    //正常的输出
    list.forEach(s-> System.out.println(s));
    //方法引用
    list.forEach(System.out::println);
    //应用到Stream的复杂一点栗子
    list.stream().map(s->s.toUpperCase()).forEach(s-> System.out.println(s));
    list.stream().map(String::toUpperCase).forEach(System.out::println);
}

没错,就是这么简单粗暴,不需要参数信息,Java编译器在为该方法引用生成实例时,会进行自动地将集合中的元素作为参数传入到该方法中

三、stream方法

到这里我们使用Lambda大部分还都是在集合的foreach方法中,那如果有一些复杂点的业务,比如需要先将结合中的元素排个序然后再一次输出,或者把集合中包含某个字母的元素全部都找出来,并且输出,那怎么办,这时候stream的强大之处就体现出来了。

集合接口有两个方法来生成流:stream() − 为集合创建串行流。parallelStream() − 为集合创建并行流,字面就可以理解,一个是顺序执行,一个是多线程并发执行,接下来的测试都采用stream() 即可

先来看一下Stream接口中提供的一些方法,已加入注释

public interface Stream<T> extends BaseStream<T, Stream<T>> {
	//通过设置的条件过滤出元素,只有符合条件的才会筛选出来
	Stream<T> filter(Predicate<? super T> predicate);
	//对元素做一些如求长度、转换大小写等操作后返回,下面是对应了一大堆数据类型
	<R> Stream<R> map(Function<? super T, ? extends R> mapper);
	IntStream mapToInt(ToIntFunction<? super T> mapper);
	LongStream mapToLong(ToLongFunction<? super T> mapper);
	DoubleStream mapToDouble(ToDoubleFunction<? super T> mapper);
	<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);
	IntStream flatMapToInt(Function<? super T, ? extends IntStream> mapper);
	LongStream flatMapToLong(Function<? super T, ? extends LongStream> mapper);
	DoubleStream flatMapToDouble(Function<? super T, ? extends DoubleStream> mapper);
	//很好理解,去重
	Stream<T> distinct();
	//排序方法
	Stream<T> sorted();
	Stream<T> sorted(Comparator<? super T> comparator);
	//监控方法
	Stream<T> peek(Consumer<? super T> action);
	//用于获取指定数量的流
	Stream<T> limit(long maxSize);
	//skip(n)为跳过前n个数据
	Stream<T> skip(long n);
	//这个我们很熟悉,遍历
	void forEach(Consumer<? super T> action);
	void forEachOrdered(Consumer<? super T> action);
	//转为数组
	Object[] toArray();
	<A> A[] toArray(IntFunction<A[]> generator);
	T reduce(T identity, BinaryOperator<T> accumulator);
	//进行归约操作
	Optional<T> reduce(BinaryOperator<T> accumulator);
	<U> U reduce(U identity,
		BiFunction<U, ? super T, U> accumulator,
		BinaryOperator<U> combiner);
	//Collector类提供了很多的归约方法
    <R, A> R collect(Collector<? super T, A, R> collector);
	<R> R collect(Supplier<R> supplier,
                  BiConsumer<R, ? super T> accumulator,
                  BiConsumer<R, R> combiner);
    //最大最小值
    Optional<T> min(Comparator<? super T> comparator);
    Optional<T> max(Comparator<? super T> comparator);
    //得到总数
    long count();
    //判断流中是否存在至少一个元素满足指定的条件
    boolean anyMatch(Predicate<? super T> predicate);
    //判断流中的所有元素是否都满足指定条件
    boolean allMatch(Predicate<? super T> predicate);
    //用于判断流中的所有元素是否都不满足指定条件
    boolean noneMatch(Predicate<? super T> predicate);
    //获取第一个元素findFirst
    Optional<T> findFirst();
    //从流中随便选一个元素出来
    Optional<T> findAny();
    ...
}

接下来使用代码来看下stream的强大之处

@Test
public void test(){
    //先初始化一个list集合
    List<String> list = Arrays.asList("java好","java妙","java呱呱叫","JDK8顶呱呱");
    //筛选出以java开头的第一个元素,Optional在后面有详细的解释
    System.out.println(list.stream().filter(s -> s.startsWith("JDK")).findFirst().get());
    System.out.println("------------------------------");
    //获取每个元素的长度
    list.stream().map(String::length).forEach(System.out::print);
    System.out.println("\r\n------------------------------");
    //获取每个元素的长度并取得总长度值,最大值
    System.out.println("总长度:"+list.stream().mapToInt(String::length).sum());
    System.out.println("最大值:"+list.stream().mapToInt(String::length).max().getAsInt());
    System.out.println("------------------------------");
    //查找打印集合中最长的字符串(相同取第一个)
    System.out.println("归约找最长:"+list.stream().reduce((x,y)->x.length()>=y.length()?x:y).get());
    System.out.println("归约找最长含有默认:"+list.stream().reduce("一只超级长的默认值",(x,y)->x.length()>=y.length()?x:y));
    System.out.println("------------------------------");
    //将集合中的元素以逗号分隔组合成一个串打印
    System.out.println("reduce逗号分隔结果:"+list.stream().reduce((x,y)-> x+","+y).get());
    //使用collect方法
    System.out.println("collect逗号分隔结果:"+list.stream().collect(Collectors.joining(",")));
    //String.join方法
    System.out.println("String.join:"+String.join(",",list));
    //全部都计算长度后用逗号分隔拼接
    System.out.println("计算长度后逗号分隔结果:"+list.stream().map(s->s.length()+"").reduce((x,y)->x+","+y).get());
    //计算长度并排序后逗号分隔拼接
    System.out.println("计算长度并排序后逗号分隔结果:"+list.stream().map(s->s.length()+"").sorted().reduce((x,y)->x+","+y).get());
}

这个栗子比较多,所以把执行结果一并放上来看下
Stream栗子的结果

四、默认方法

曾经我们的接口中只能有定义不能有实现,也就是说接口中的方法全部都是抽象方法,子类需要全部实现,而到了1.8这里,多出来了个默认方法,这样接口只要在方法上加上default关键字,那么接口也可以实现方法了,并且子类不需要实现这个方法。
很好理解,简单写个栗子吧

public interface DefaultInterFace{
	//这个还是普通的抽象方法,子类必须实现
	public void oldMethod();
	//这个就是默认方法了子类可以不实现,也可以重写
	public default void defaultMethod(){
		Sysout.out.println("我是在接口中的实现,6不6");
	}
}

五、Optional

这个怎么解释,看下API吧
public final class Optional
extends Object
A container object which may or may not contain a non-null value. If a value is present, isPresent() will return true and get() will return the value.
Additional methods that depend on the presence or absence of a contained value are provided, such as orElse() (return a default value if value not present) and ifPresent() (execute a block of code if the value is present).

This is a value-based class; use of identity-sensitive operations (including reference equality (==), identity hash code, or synchronization) on instances of Optional may have unpredictable results and should be avoided.

好吧,应该有人和我一样,英文渣渣,所以,google or baidu or 其他 翻译一下吧。

第一段就是说,这是一个可以包含null值得容器对象,若值存在, isPresent()将返回true,get()方法返回对应的值
第二段大致说,提供了其他的判断值存不存在的方法,如:orElse() (如果值不存在则返回默认值)和ifPresent() (如果值存在则执行代码块)。
第三段大致就是,Optional使用(==,标识哈希码,或同步)可能不可预测的实例结果,应该避免

使用Optional可以很好的避免空指针异常,下面看个栗子

@Test
@Test
public void test(){
    //ofNullable允许传入null
    Optional<Integer> x = Optional.ofNullable(null);
    Optional<Integer> y = Optional.ofNullable(2);
    //of方法如果传入null,会抛出空指针
    Optional<Integer> z = Optional.of(3);
    //做求和操作,因为x为null,所以需要处理成0
    Integer a = x.orElse(0);
    Integer b = y.orElse(0);
    Integer c = z.get();
    System.out.println("x存在值:"+x.isPresent()+";y存在值:"+y.isPresent()+";x+y+z求和:"+(a+b+c));
}

执行结果
在这里插入图片描述

六、新的日期时间

我们一直在使用的时间工具类有:
java.util包中的Date和Calendar等
java.text包中的SimpleDateFormat等
原来的时间工具类有很多弊端,缺乏年、月、日、时间、星期的单独抽象,线程不安全等等,这些大家可以查查或者在工作中体会。
JDK8中在java.time包下提供了很多新的时间处理方式
在这里插入图片描述
详细的可以查查API,简单写几个获取时间的栗子

@Test
public void test(){
    LocalDateTime localDateTime = LocalDateTime.now();
    String now = localDateTime.format(
            DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
    System.out.println(now);
    System.out.println(LocalDate.now());
    System.out.println(LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss")));
    System.out.println("年:"+localDateTime.getYear()+"月:"+localDateTime.getMonthValue()+
            "日:"+localDateTime.getDayOfMonth()+"时:"+localDateTime.getHour()+
            "分:"+localDateTime.getMinute()+"秒:"+localDateTime.getSecond());
    System.out.println(LocalDate.of(2008,Month.DECEMBER,21));
    System.out.println(LocalTime.parse("14:12:18"));
    System.out.println("巴黎时间:"+ZonedDateTime.now(ZoneId.of("Europe/Paris"))
            .format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
    System.out.println("当前时区:"+ZoneId.systemDefault());
}

看下执行结果:
在这里插入图片描述

总结

其实已经不新了,不过是当前我们刚更新上来不到一年,所以对这一顿时间看代码或者使用做一下总结,水平有限,路漫漫其修远啊~~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值