java8的特性

参考菜鸟教程,此篇博客的目的是学习java8 的新特性,示例来源与菜鸟教程,学习的过程中加入了自己的感悟,java8 已经出现好久了,现在才耐心的学习下,惭愧惭愧


1.lambda

函数编程 《=》 lambda 表达式,函数式变成强调结果,强调利用若干简单的执行单元让计算结果不断渐进,而不是设计一个复杂的执行过程
_维基百科
逻辑 作为参数
返回一个新的值,尤其是不得修改外部变量的值

//lambda 表达式语法格式
(parameters) -> expression
或
(parameters) ->{ statements; }

lambda 表达式的特点:

可选类型声明: 不需要声明参数类型,编译器可以统一识别参数值
可选的参数圆括号: 一个参数无需定义圆括号,但是多个参数需要定义圆括号
可选大括号: 如果主体包含了一个语句,就不需要使用大括号
可选的返回关键字: 如果主体只有一个表达式返回值则编译器自动返回值,大括号需要指定表达式返回了一个数值

lambda 表达式实例:

()-> 5 //1.不需要参数,返回值为5
x->x*2 //2.接受一个参数(数字类型),返回其2倍的值
(x,y)-> x-y //3.接受2个参数(数字),并返回他们的差值
(int x, int y) -> x + y  // 4. 接收2个int型整数,返回他们的和  
(String s) -> System.out.print(s)// 5. 接受一个 string 对象,并在控制台打印,不返回任何值(看起来像是返回void)  

java 使用 lambda 表达式

package com.study.java8;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

public class Lambda {
    public static void main(String[] args) {
        Lambda tester = new Lambda();

        // 类型声明
        MathOperation addition = (int a, int b) -> a + b;

        // 不用类型声明
        MathOperation subtraction = (a, b) -> a - b;

        // 大括号中的返回语句
        MathOperation multiplication = (int a, int b) -> { return a * b; };

        // 没有大括号及返回语句
        MathOperation division = (int a, int b) -> a / b;

        System.out.println("10 + 5 = " + tester.operate(10, 5, addition));
        System.out.println("10 - 5 = " + tester.operate(10, 5, subtraction));
        System.out.println("10 x 5 = " + tester.operate(10, 5, multiplication));
        System.out.println("10 / 5 = " + tester.operate(10, 5, division));

        // 不用括号
        GreetingService greetService1 = message ->
                System.out.println("Hello " + message);

        // 用括号
        GreetingService greetService2 = (message) ->
                System.out.println("Hello " + message);

        greetService1.sayMessage("Runoob");
        greetService2.sayMessage("Google");

        //集合类的lambda 表达式
        collectTest();
    }

    private static  void   collectTest(){
        List<String> names1 = new ArrayList<String>();
        names1.add("Google ");
        names1.add("Runoob ");
        names1.add("Taobao ");
        names1.add("Baidu ");
        names1.add("Sina ");

        List<String> names2 = new ArrayList<String>();
        names2.add("Google ");
        names2.add("Runoob ");
        names2.add("Taobao ");
        names2.add("Baidu ");
        names2.add("Sina ");

        Lambda tester = new Lambda();
        System.out.println("使用 Java 7 语法: ");

        tester.sortUsingJava7(names1);
        System.out.println(names1);
        System.out.println("使用 Java 8 语法: ");

        tester.sortUsingJava8(names2);
        System.out.println(names2);
    }

    interface MathOperation {
        int operation(int a, int b);
    }

    interface GreetingService {
        void sayMessage(String message);
    }

    private int operate(int a, int b, MathOperation mathOperation){
        return mathOperation.operation(a, b);
    }

    // 使用 java 7 排序
    private void sortUsingJava7(List<String> names){
        Collections.sort(names, new Comparator<String>() {
            @Override
            public int compare(String s1, String s2) {
                return s1.compareTo(s2);
            }
        });
    }

    // 使用 java 8 排序
    private void sortUsingJava8(List<String> names){
        Collections.sort(names, (s1, s2) -> s1.compareTo(s2));
    }
}


总结: 使用lambda 表达式,就像使用使用就是想参数一样使用逻辑,将逻辑带入引用的地方。如上程序,将逻辑带入算数,将逻辑带入输出,相当与使用了实现类,但没有实际的实现类。
画重点:lamdb 表达式,放在使用接口的地方,程序就会将lambda 表达式的逻辑,认为成对应接口抽象方法的逻辑,就相当实现了这个接口的抽象方法,只不过没有对应的实现类,对应的实现方法罢了。


2.java 8 方法的引用

概念: 通过方法的名字来指向一个方法,使语言构造更紧凑简介,使用 ::

下面是四个方法区分,java 中四个不同方法的引用


package com.runoob.main;
 
@FunctionalInterface
public interface Supplier<T> {
    T get();
}
 
class Car {
    //Supplier是jdk1.8的接口,这里和lamda一起使用了
    public static Car create(final Supplier<Car> 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());
    }
}

构造器的引用:它的语法是Class::new ,或者更一般的Class::new

final Car car = Car.create(Car::new);
final List<Car> cars = Arrays.asList(car)

静态方法的引用:他的语法是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 );

画中点: 方法引用 :: , 就是用方法名称使用方法, 点击源码forEach 接收的参数类型式java8函数接口编程中java.util.function 中的 Consumer 接口,即这个接口式lambda 表达式中的 代表了接受一个输入参数并且无返回的操作(参考函数式编程)。 方法引用应该也是一种lambda 表达式的形式,即可理解成 方法引用,像是将方法伪装成类似lambda,或者说方法引用就是一种lambda 表达式,而lambda 表达式的逻辑就相当于 方法的实现逻辑。


3.函数式接口

函数式接口(functional interface),就是一个有且但有一个抽象方法,但是可以有多个非抽象方法的接口
函数式接口可以被隐式转换为lambda 表达式
Lambda 表达式和方法引用(实际上也可认为是Lambda表达式)上。
如定义了一个函数式接口如下:

@FunctionalInterface
interface GreetingService 
{
    void sayMessage(String message);
}

这就可以使用Lambda 表达式来表示的一个实现(注:Java 8 之前一般是用匿名类实现的)

GreetingService greetService1  = message -> System.out.println("Hello" + message)

函数式接口可以对现有的函数友好地支持lambda
java.lang.Runnable
java.util.concurrent.Callable
java.security.PrivilegedAction
java.util.Comparator
java.io.FileFilter
java.nio.file.PathMatcher
java.lang.reflect.InvocationHandler
java.beans.PropertyChangeListener
java.awt.event.ActionListener
javax.swing.event.ChangeListener

JDK 1.8 新增加的函数接口

java.util.function

java.util.function 它包括了很多类,用来支持java 的函数式编程,该包中的函数式接口有:

java.util.function函数式编程函数list参考

貌似要常用的接口:

BiConsumer<T,U> 代表了一个接受两个输入参数的操作,并且不返回任何结果

BiFunction<T,U,R> 代表了一个接受两个输入参数的方法,并且返回一个结果

BinaryOperator 代表了一个作用于于两个同类型操作符的操作,并且返回了操作符同类型的结果

Consumer 代表了接受一个输入参数并且无返回的操作

Function<T,R> 接受一个输入参数,返回一个结果。

ObjDoubleConsumer 接受一个object类型和一个double类型的输入参数,无返回值。

ObjIntConsumer 接受一个object类型和一个int类型的输入参数,无返回值。

ObjLongConsumer 接受一个object类型和一个long类型的输入参数,无返回值。

Predicate 接受一个输入参数,返回一个布尔值结果。

UnaryOperator 接受一个参数为类型T,返回值类型也为T。

代码示例:

package com.study.java8;

import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;

/**
 * 函数式编程
 */
public class FunctionFormula {
    public static void main(String[] args) {
        List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
        System.out.println("输出所有数据");
        // 传递参数 n
        // Predicate 接受一个输入参数,返回一个boolean结果
        eval(list,n->true);
        System.out.println("输出所有的偶数");
        eval(list,n->n%2 == 0);
        //
        System.out.println("输出大于3的所有数字");
        eval(list,n -> n > 3);

    }
    public static void eval(List<Integer>list, Predicate<Integer> predicate){
        for (Integer n :list){
            if (predicate.test(n)){
                System.out.println(n + " ");
            }
        }
    }
}


java8 默认方法 default

java8 接口可以有实现方法,而且不需要实现类去实现这个方法。默认的方法的好处是,在不改变接口现有实现的基础上,可以对接口作出一些修改。

一个接口有默认方法,一个类实现了多个接口,且这些接口有相同的默认方法,那么这个类选择那个默认方法应用那
方法:
(1)类实现自己的同名默认方法
(2)用super 关键字指定接口的默认方法


public class Car implements Vehicle, FourWheeler {
   public void print(){
      Vehicle.super.print();
   }
}


4.静态默认方法

public interface Vehicle {
   default void print(){
      System.out.println("我是一辆车!");
   }
    // 静态方法
   static void blowHorn(){
      System.out.println("按喇叭!!!");
   }
}

默认方法实例


public class Java8Tester {
   public static void main(String args[]){
      Vehicle vehicle = new Car();
      vehicle.print();
   }
}
 
interface Vehicle {
   default void print(){
      System.out.println("我是一辆车!");
   }
    
   static void blowHorn(){
      System.out.println("按喇叭!!!");
   }
}
 
interface FourWheeler {
   default void print(){
      System.out.println("我是一辆四轮车!");
   }
}
 
class Car implements Vehicle, FourWheeler {
   public void print(){
      Vehicle.super.print();
      FourWheeler.super.print();
      Vehicle.blowHorn();
      System.out.println("我是一辆汽车!");
   }
}

输出

我是一辆车!
我是一辆四轮车!
按喇叭!!!
我是一辆汽车!

5. Stream

概念: java 8 api 添加了一个新的抽象称为流Stream,可以以一种声明的方法处理数据
Stream 使用一种类似用SQL 语句从数据库查询数据的直观方式来提供一种对Java 集合运算和表达的高阶抽象。Stream API 可以极大提高Java 程序员的生产力,让程序员写出高效,干净,简洁的代码(喜欢这几个字),这种风格将要处理的元素集合看做一种流,流在管道中传输,并且可以在管道的节点上进行处理,比如筛选,排序,聚合等。元素流在管道中经过中间操作(intermediate operation)的处理,最后由最终操作(terminal operation) 得到前面处理的结果。

如下图:

+--------------------+       +------+   +------+   +---+   +-------+
| stream of elements +-----> |filter+-> |sorted+-> |map+-> |collect|
+--------------------+       +------+   +------+   +---+   +-------+

以上的流程转换为 Java 代码为:

List<Integer> transactionsIds = 
widgets.stream()
             .filter(b -> b.getColor() == RED)
             .sorted((x,y) -> x.getWeight() - y.getWeight())
             .mapToInt(Widget::getWeight)
             .sum();

什么是Stream

Stream (流) 是一个来自数据源的元素队列并支持聚合操作

数据源: 流的来源。可以是集合,数组,I/O channel , 产生器 generator 等。
聚合操作: 类似SQL语句一样的操作,比如filter,map,reduce,match,sorted 等

和以前Collection 操作不同,Stream 操作还有两个基础得特征
**Pipelining:**中间操作都会返回对象本身。这样多个操作可以串联成一个管道,如同流式风格(flent style).这样做可以对操作进行优化,比如延迟执行(laziness) 和短路(short-circuiting).

内部迭代: 以前对集合遍历都是通过Iterator 或者 For-each 得方式,显示的在集合外部进行迭代,这叫做外迭代,Stream 提供了内部迭代的方式,通过访问者模式(Vistor)实现。

生成流

集合接口有两个方法来生成流:
串行流: stream()
并行流: parallelStream()


List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
List<String> filtered = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.toList());

画重点

forEach : Stream 提供新的方法forEach 来迭代流中的每个数据

Random random = new Random();
random.ints().limit(10).forEach(System.out::printLn)

map: 用于映射每个元素对应的结果

List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
// 获取对应的平方数
List<Integer> squaresList = numbers.stream().map( i -> i*i).distinct().collect(Collectors.toList());

filter: 方法用于通过设置的条件过滤元素


List<String>strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
// 获取空字符串的数量
long count = strings.stream().filter(string -> string.isEmpty()).count();

limit: 方法用于获取指定数量的流。

Random random = new Random();
random.ints().limit(10).forEach(System.out::println);

sorted: 方法用于对流进行排序


Random random = new Random();
random.ints().limit(10).sorted().forEach(System.out::println);

并行(parallel) 程序
parallelStream 是流并行处理程序的代替方法。


List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
// 获取空字符串的数量
int count = strings.parallelStream().filter(string -> string.isEmpty()).count();

Collectors: 类实现了很多归约操作,例如将流转换成集合和聚合元素。Collectors 可用于返回列表或字符串


List<String>strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
List<String> 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);


统计:另外,一些产生统计结果的收集器也非常有用。它们主要用于int、double、long等基本类型上,它们可以用来产生类似如下的统计结果。


List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
 
IntSummaryStatistics stats = numbers.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());


6.Optional 类

Optional 类是一个可以为null的容器对象。如果值存在则isPresent() 方法会返回true,调用get() 方法会返回给对象。Optional是个容器:可以保存类型T的值,或者仅仅保存null.Optional 提供很多有用的方法,这样我们就不用显式进行控制检测。Optional 类的引入很好的解决空指针异常。

类声明
以下是一个java.util.Optional 类的声明:

public final class Optional<T>  extends Object

类方法:参考菜鸟教程


7、Nashorn , JavaScript 引擎


8、 新的日期时间API

Java 8通过发布新的Date-Time API (JSR 310)来进一步加强对日期与时间的处理。

在旧版的 Java 中,日期时间 API 存在诸多问题,其中有:

非线程安全 − java.util.Date 是非线程安全的,所有的日期类都是可变的,这是Java日期类最大的问题之一。

设计很差 − Java的日期/时间类的定义并不一致,在java.util和java.sql的包中都有日期类,此外用于格式化和解析的类在java.text包中定义。java.util.Date同时包含日期和时间,而java.sql.Date仅包含日期,将其纳入java.sql包并不合理。另外这两个类都有相同的名字,这本身就是一个非常糟糕的设计。

时区处理麻烦 − 日期类并不提供国际化,没有时区支持,因此Java引入了java.util.Calendar和java.util.TimeZone类,但他们同样存在上述所有的问题。

Java 8 在 java.time 包下提供了很多新的 API。以下为两个比较重要的 API:

Local(本地) − 简化了日期时间的处理,没有时区的问题。

Zoned(时区) − 通过制定的时区处理日期时间。

新的java.time包涵盖了所有处理日期,时间,日期/时间,时区,时刻(instants),过程(during)与时钟(clock)的操作。

java 用例

package com.study.java8;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.Month;

public class TimeExample {
    public static void main(String[] args) {
        TimeExample timeExample = new TimeExample();
        timeExample.testLocalDateTime();
    }

    private void testLocalDateTime() {
        //获取当前的日期时间
        LocalDateTime now = LocalDateTime.now();
        System.out.println("当前时间:" + now);

        LocalDate localDate = now.toLocalDate();
        System.out.println("date1:" + localDate);

        Month month = now.getMonth();
        int dayOfMonth = now.getDayOfMonth();
        int second = now.getSecond();
        System.out.println("月:"+ month+",日:" + dayOfMonth + ", 秒" +second);

        // 12 december 2014
        LocalDate of = LocalDate.of(2014, Month.DECEMBER, 12);
        System.out.println("date3:"+ of);

        //12 小时 15 分钟
        LocalTime date4 = LocalTime.of(12, 15);
        System.out.println("date4:"+date4);

        //解析字符串
        LocalTime date5 = LocalTime.parse("20:15:30");
        System.out.println("date5: " + date5);

    }
}


9、Base64

java 8 中,Base64 编程已经称为Java 类库的标准
Java8 内置Base64 编码的编码器和解析器

基本:输出被映射到一组字符A-Za-z0-9+/,编码不添加任何行标,输出的解码仅支持A-Za-z0-9+/。
URL:输出映射到一组字符A-Za-z0-9+_,输出是URL和文件。
MIME:输出隐射到MIME友好格式。输出每行不超过76字符,并且使用'\r'并跟随'\n'作为分割。编码输出最后没有行分割。

要克服生活的焦虑和沮丧,得先学会做自己的主人,有问题留言,没问题留下你的赞
博客声明:
1.博客内容全是对工作学习的总结
2.知识点都经过测试和推敲,如有疑问请留言,一定及时解决

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值