jdk8新功能

一、接口

之前的接口是个双刃剑,好处是面向抽象而不是面向具体编程,缺陷是,当需要修改接口时候,需要修改全部实现该接口的类,目前的java 8之前的集合框架没有foreach方法,通常能想到的解决办法是在JDK里给相关的接口添加新的方法及实现。然而,对于已经发布的版本,是没法在给接口添加新方法的同时不影响已有的实现。所以引进的默认方法。他们的目的是为了解决接口的修改与现有的实现不兼容的问题。

public static void main(String[] args) throws Exception {
    A.show("静态方法");
    B b = new B();
    b.print();
}

interface A{
    //默认方法
    default void print(){
        System.out.println("this is A");
    }
    //静态方法
    static void show(String str){
        System.out.println(str);
    }
}
class B implements A{
    //可以重写也可以不重写
    public void print(){
        System.out.println("this is B");
    }
}

二、base64

Java 8 内置了 Base64 编码的编码器和解码器。Base64工具类提供了一套静态方法获取下面三种BASE64编解码器:

  • 基本:输出被映射到一组字符A-Za-z0-9+/,编码不添加任何行标,输出的解码仅支持A-Za-z0-9+/。
  • URL:输出映射到一组字符A-Za-z0-9+_,输出是URL和文件。
  • MIME:输出隐射到MIME友好格式。输出每行不超过76字符,并且使用’\r’并跟随’\n’作为分割。编码输出最后没有行分割。
String base64encodedString = Base64.getEncoder().encodeToString("jdk8".getBytes("utf-8"));
System.out.println(base64encodedString);//打印:amRrOA==
byte[] bs = Base64.getDecoder().decode(base64encodedString);
System.out.println(new String(bs, "utf-8"));//打印:jdk8

三、时间api

在旧版的 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,涵盖了所有处理日期,时间,日期/时间,时区,时刻(instants),过程(during)与时钟(clock)的操作。以下为两个比较重要的 API:

  • Local(本地) − 简化了日期时间的处理,没有时区的问题。
  • Zoned(时区) − 通过制定的时区处理日期时间。
//获取当前日期
LocalDate date = LocalDate.now();
System.out.println(date);//打印:2018-04-20
//获取当前时间
LocalTime time = LocalTime.now();//打印:14:39:31.839
System.out.println(time);
//获取当前日期和时间
LocalDateTime dateTime = LocalDateTime.now();
System.out.println(dateTime);//打印:2018-04-20T14:39:31.839

//操作时间对象
int year = dateTime.getYear();
//month使用比较特殊,但是会相对方便,不需要在月份上加1
Month month = dateTime.getMonth();
int monthInt = month.getValue();
int day = dateTime.getDayOfMonth();
int minute = dateTime.getMinute();

//传入参数,获得实践对象
Month inMonth = Month.of(1);//或者Month.JANUARY
LocalDateTime inDateTime = LocalDateTime.of(1992, inMonth, 1, 12, 12, 12);
System.out.println(inDateTime);//打印:1992-01-01T12:12:12
LocalDateTime inDateTime2 = LocalDateTime.parse("1992-01-01T12:12:12");
//本机时间
LocalDateTime localnow = LocalDateTime.now();
//将本地时间的时区设置为哈尔滨时区
ZonedDateTime localHarbin = ZonedDateTime.parse(localnow + "+08:00[Asia/Harbin]");
//将本地时间的时区设置为洛杉矶时区
ZonedDateTime localLos = ZonedDateTime.parse(localnow + "-07:00[America/Los_Angeles]");
System.out.println(localHarbin);//打印:2018-04-20T13:56:41.227+08:00[Asia/Harbin]
System.out.println(localLos);//打印:2018-04-20T13:56:41.227-07:00[America/Los_Angeles]
//获取洛杉矶时区id
ZoneId id = ZoneId.of("America/Los_Angeles");
//获取当前时区的本地时间
ZonedDateTime losnow = ZonedDateTime.now(id);
System.out.println(losnow);//打印:2018-04-19T22:31:49.882-07:00[America/Los_Angeles]
//比较
System.out.println(localHarbin.compareTo(losnow));//打印:-18000000
System.out.println(localHarbin.compareTo(localLos));//打印:-18000000
System.out.println(localLos.compareTo(losnow));//打印:1
//localHarbin的时间是哈尔滨时间,与losnow的洛杉矶时间只是时区差别,时间其实是一样的,所以输出的结果只是程序执行所花费的时间18毫秒
//localLos的时间由哈尔滨,也就是本地时间转为洛杉矶时间的,与losnow的洛杉矶时间就会相差7+8两个时区的时间

注意:Local和Zoned获取当前时间都是获取本机的系统时间,如果电脑的系统时间改变,获得的时间对象也会改变。

四、Optional

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

Integer value1 = null;
Integer value2 = new Integer(10);
//允许传递为 null 参数
Optional<Integer> optional1 = Optional.ofNullable(value1);
boolean b = optional1.isPresent();//false
//如果传递的参数是 null,抛出异常 NullPointerException
Optional<Integer> optional2 = Optional.of(value2);
Integer integer = optional2.get();//10

五、Lambda表达式

public static void main(String[] args) throws Exception {
    //有参数、有返回值
    MathOperation1 m1 = (a,b) -> a < b ? a : b;
    int min = m1.getMin(20, 30);
    System.out.println(min);

    //有参数、无返回值
    MathOperation2 m2 = (a, b) -> System.out.println(a < b ? a : b);
    m2.showMin(20, 30);

    //无参数、无返回值
    String str = "hello";
    MathOperation3 m3 = () ->  System.out.println(str);//不能在 lambda 内部修改定义在域外的局部变量,否则会编译错误。
    m3.printHello();
}
interface MathOperation1{
    int getMin(int a, int b);
}
interface MathOperation2{
    void showMin(int a, int b);
}
interface MathOperation3{
    void printHello();
}

要使用lambda表达式实现接口,要求只能有一个抽象方法,否则会报错。但java8的新特性允许实现如下写法:

interface MathOperation1{
    int getMin(int a, int b);
    default int getSum(int a, int b){
        return a + b;
    }
    static int getMax(int a, int b){
        return a < b ? b : a;
    }
}

六、方法引用

以下demo都使用的实体类:

public static void main(String[] args) throws Exception {
    Student[] stus = new Student[3];
    stus[0] = new Student(20, "A");
    stus[1] = new Student(40, "B");
    stus[2] = new Student(30, "C");
}
class Student{
    private int age;
    private String name;
    public Student() {
        super();
    }
    public Student(int age, String name) {
        super();
        this.age = age;
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public String getName() {
        return name;
    }
    public String toString() {
        return "Student [age=" + age + ", name=" + name + "]";
    }
    //静态比较方法
    public static int staticMethodCompareByAge(Student s1, Student s2){
        return s1.age - s2.age;
    }
    //实例比较方法,不需要放在本类中
    public int instanceMethodCompareByAge(Student s1,Student s2){
        return s1.getAge() - s2.getAge();
    }
    //实例比较方法
    public int methodCompareByAge(Student s){
        return this.age - s.age;
    }
    //创建本类的实体对象
    public static Student getInstance(Supplier<Student> supplier){
        return supplier.get();
    }
}

1、内部类

//使用匿名内部类
Arrays.sort(stus, new Comparator<Student>() {
    @Override
    public int compare(Student s1, Student s2) {
        return s1.getAge() - s2.getAge();
    }
});
//使用lambda表达式
Arrays.sort(stus, (s1, s2) -> s1.getAge() - s2.getAge());

2、引用静态方法
ContainingClass::staticMethodName

//使用lambda表达式
Arrays.sort(stus, (s1, s2) -> Student.staticMethodCompareByAge(s1, s2));
//引用静态方法
Arrays.sort(stus, Student::staticMethodCompareByAge);

3、引用某个对象的实例方法
containingObject::instanceMethodName

//创建Student只是为了使用这个类里面的比较方法,也可以是别的类,里面有比较两个Student类的方法也可以,这里只是为了书写方便。
Student s = new Student();
//使用lambda表达式
Arrays.sort(stus, (s1, s2) -> s.instanceMethodCompareByAge(s1, s2));
//引用对象的实例方法
Arrays.sort(stus, s::instanceMethodCompareByAge);

4、引用某个类型的任意对象的实例方法
ContainingType::methodName

//使用lambda表达式
Arrays.sort(stus, (s1, s2) -> s1.methodCompareByAge(s2));
//引用类型对象的实例方法
Arrays.sort(stus, Student::methodCompareByAge);

5、引用构造方法
ClassName::new

Student student = Student.getInstance(Student::new);

七、Stream

java 8 API添加了一个新的抽象称为流Stream,可以让你以一种声明的方式处理数据。它有以下几个特点:

  • stream不存储数据
  • stream不改变源数据
  • stream的延迟执行特性

通常我们在数组或集合的基础上创建stream,stream不会专门存储数据,对stream的操作也不会影响到创建它的数组和集合,对于stream的聚合、消费或收集操作只能进行一次。

List<String> list = Arrays.asList("a", "b", "c");

1、生成流

Stream<String> stream = list.stream();
Stream<String> stream2 = list.parallelStream();

2、forEach,迭代流中的每个数据

list.stream().forEach(System.out::println);

3、map,映射每个元素到对应的结果

list.stream().map((str) -> "这是" + str).forEach(System.out::println);

4、filter,通过设置的条件过滤出元素

list.stream().filter((str) -> str.equals("c")).forEach(System.out::println);

5、limit,获取指定n个数据;skip,跳过前n个数据

list.stream().limit(2).forEach(System.out::println);
list.stream().skip(1).forEach(System.out::println);

6、sorted,对流进行排序

list.stream().sorted((s1, s2) -> s1.hashCode() - s2.hashCode()).forEach(System.out::println);

7、去重

list.stream().distinct().forEach(System.out::println);

8、合并流

Stream<List<String>> stream2 = Stream.of(list, list);
Stream<List<String>> stream3 = Stream.of(list, list);
Stream.concat(stream2,stream3).distinct().forEach(System.out::print);

9、聚合操作

//最小值,如果有值
list.stream().min((s1, s2) -> s1.hashCode() - s2.hashCode()).ifPresent(System.out::println);//a
//个数
System.out.println(list.stream().count());//3

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

Set<String> set = list.stream().collect(Collectors.toSet());//[a, b, c]
String string = list.stream().collect(Collectors.joining("-"));//b-c-a

11、将普通流转换成数值流
StreamAPI提供了三种数值流:IntStream、DoubleStream、LongStream,也提供了将普通流转换成数值流的三种方法:mapToInt、mapToDouble、mapToLong。

IntSummaryStatistics stats = list.stream().mapToInt(x -> x.hashCode()).summaryStatistics();
int max = stats.getMax();//99
int min = stats.getMin();//97

八、function包的使用

这几个接口属于java.util.function包使用方法,使用方法大同小异,后面都是跟着Lambda表达式或者方法引用。

1、Predicate
判断使用,返回的都是boolean类型数据。

//Lambda表达式
Predicate<String> predicate1 = (str) -> str.length() > 0;
boolean b1 = predicate1.test("");//false

//方法引用
Predicate<String> predicate2 = Objects::nonNull;
boolean b2 = predicate2.test(null);//false

//negate获得与传入的Predicate相反的Predicate,即(t) -> !test(t)
boolean b3 = predicate1.negate().test("");

//orand方法是把两个测试条件组合在一起,or的结果是test(t) || other.test(t),and结果是test(t) && other.test(t);
Predicate<String> predicate3 = predicate1.or(predicate2);
Predicate<String> predicate4 = predicate1.and(predicate2);
boolean b4 = predicate3.test("");//true
boolean b5 = predicate4.test("");//false

2、Function
执行特定的函数,Function【T, R】传入的是T类型,输出的是R类型。

//返回值是Integer
Function<String, Integer> function1 = Integer::parseInt;
Integer i1 = function1.apply("10");//10
System.out.println(i1);

//返回值是String
Function<Integer, String> function2 = (i) -> (i+20) + "";
String s1 = function2.apply(10);//30

//先执行function1,String转为Integer,再执行function2,转回String
Function<String, String> function3 = function1.andThen(function2);
String s2 = function3.apply("10");//30

//先执行function2,Integer转为String,再执行function1,转回Integer
Function<Integer, Integer> function5 = function1.compose(function2);
Integer i2 = function5.apply(10);//30

3、Consumer
表示一个接受单个输入参数并且没有返回值的操作。不像其他函数式接口,Consumer接口期望执行带有副作用的操作(译者注:Consumer的操作可能会更改输入参数的内部状态)。

Consumer<List<String>> consumer = (list) -> list.forEach(System.out::println);
List<String> list = new ArrayList<>();
list.add("a");
list.add("b");
consumer.accept(list);

4、Supplier
与方法引用一起使用创建一个新的空对象。

Supplier<Student> supplier = Student::new;
Student student = supplier.get();
Supplier<ArrayList<String>> supplier2 = ArrayList<String>::new;
ArrayList<String> list = supplier2.get();

九、杂项改进

    String string = String.join("-", "first","second");

通过一个分隔符将字符串连接起来,相当于split的相反方法。

    int i = Integer.sum(1, 2);
    long l = Long.max(20L, 30L);

Short、Integer、Long、Float、Double五种包装类型数据提供了sum、max、min方法;同样Boolean也提供了logicalXor、logicalOr、logicalAnd方法。

    long l = Math.multiplyExact(200000000L, 20000000L);

准确的计算出200000000*200000000的值。

    int i = Math.toIntExact(20000L);

将long类型数值转化为相应的int类型数值,不超过int取值范围。

double d = Math.nextDown(2F);

取到无线接近于2的浮点数字。

猜你喜欢

转载自blog.csdn.net/zajiayouzai/article/details/78983419