Java基础之java8 新特性

一、Lambda 表达式

  Lambda 表达式是在 Java8 中引入的,并号称是 Java8 的最大的特点。Lambda 表达式有利于函数式编程,简化了开发了很多。

  语法:parameter -> expression body

  操作符 “->” 称为箭头操作符或 Lambda 操作符

  箭头操作符将 Lambda 表达式拆分成两部分:

  左侧:Lambda 表达式的参数列表

  右侧:Lambda 表达式中所需执行的功能, 即 Lambda 体

  案例:集合排序

public class Demo {
    public static void main(String[] args) {
        List<Entity> list = new ArrayList<>();
        list.add(new Entity("张一", 18));
        list.add(new Entity("张二", 25));
        list.add(new Entity("张三", 14));
        list.add(new Entity("张四", 19));
        list.add(new Entity("张五", 23));
        list.sort(new Comparator<Entity>() {
            @Override
            public int compare(Entity o1, Entity o2) {
                return o1.age > o2.age ? 1 : o1.age < o2.age ? -1 : 0;
            }
        });
        System.out.println(list);
    }
}
class Entity {
    String name;
    Integer age;

    public Entity(String name, Integer age) {
        this.age = age;
        this.name = name;
    }
}

  转化后的排序变成一行代码:两个参数一条语句(大括号和 return 可以不写),Lambda 表达式的参数列表的数据类型可以省略不写,因为 JVM 编译器通过上下文推断出,数据类型,即“类型推断”。

list.sort((o1, o2) -> o1.age > o2.age ? 1 : o1.age < o2.age ? -1 : 0);

  idea 一般都会自动提示普通写法和 lambda 相互转化,(alt + 回车)一般按照提示转化即可。
在这里插入图片描述
在这里插入图片描述
  案例:Runnable :lambda 表达式一般可以操作加 @FunctionalInterface 注解的接口(函数式接口)

Runnable r = new Runnable() {
    @Override
    public void run() {
        System.out.println("Hello lambda!");
    }
};		
r.run();

//变成
		
Runnable r1 = () -> System.out.println("Hello Lambda!");
r1.run();

  案例:forEach 遍历 list

List<Entity> list = new ArrayList<>();
list.add(new Entity("张一", 18));
list.add(new Entity("张二", 25));
list.add(new Entity("张三", 14));
list.add(new Entity("张四", 19));
list.add(new Entity("张五", 23));
list.forEach(new Consumer<Entity>() {
    @Override
    public void accept(Entity entity) {
        System.out.println(entity);
    }
});

  转化为 Lambda 后,当方法的参数只有一个时前面的括号可以省略。

list.forEach(entity -> System.out.println(entity));

  案例:forEach 遍历 map

Map<String, Object> map = new HashMap<>();
map.put("id", 1);
map.put("name", "张三");
map.put("password", "123456");
map.forEach((k, v) -> {
    System.out.println(k);
    System.out.println(v);
});

二、Stream 初体验

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

  流程:

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

map

  map 方法用于映射每个元素到对应的结果。

  案例:使用 map 输出了元素对应的平方数:

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

filter

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

  案例:使用 filter 方法过滤出空字符串:

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

limit

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

  案例:使用 limit 方法获取前 3 条不为空的数组数据:

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

sorted

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

  案例:使用 sorted 方法对集合数据进行排序:

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

distinct

  distinct 方法用于对流进行去重。

  案例:

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

count

  count 方法用于对流进行计数。

List<String>strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
long count = strings.stream().filter(string -> string.isEmpty()).count();

  扩展案例:

Random random = new Random();
//生成 10 个[0,100] 的随机数
List<Integer> collect = random.ints(0, 101).boxed().limit(10).collect(Collectors.toList());
System.out.println(collect);

//生成一个从0开始到10的集合
List<Integer> list = IntStream.range(1, 11).boxed().collect(Collectors.toList());
System.out.println(list);

//计算上面集合的平均值
Double avarage = list.stream().collect(Collectors.averagingInt(item -> item));
System.out.println(avarage);

//对列表元素进行统计
IntSummaryStatistics iss = list.stream().collect(Collectors.summarizingInt(value -> value));
System.out.println(iss);
//{count=10, sum=55, min=1, average=5.500000, max=10}

//根据 List 创建 Map
Map<Integer, Integer> map = list.stream().collect(Collectors.toMap(p -> p, q->q*3));
System.out.println(map);

//获取列表的最大值
Optional<Integer> max = list.stream().reduce(Math::max);
max.ifPresent(value -> System.out.println(value));

注意:

  1.boxed 用于将 IntStream 转化成 Stream。

  2.Math::max 称为方法引用。

三、方法引用

  在 Java8 中,你可以使用 class::methodName 语法引用类或对象的方法。让我们来学习一下 Java 8 中不同类型的方法引用。Java 8 中包含了四种类型的方法引用。

方法引用                	描述	                                    例子
静态方法引用            	用于引用类的静态方法	                    Math::max 相当于 Math.max(x,y)
从对象中引用实例方法    	使用对象的引用来调用实例方法	            System.out::println 相当于 System.out.println(x)
从类中引用实例方法        	在上下文提供的对象的引用上调用实例方法	    String::length 相当于 str.length()
引用构造函数            	引用构造函数	                            ArrayList::new```相当于new ArrayList()`

  引用静态方法 - Class::staticMethodName

  一个使用 Math.max() 静态方法的例子。

List<Integer> integers = Arrays.asList(1,12,433,5);
Optional<Integer> max = integers.stream().reduce( Math::max ); 
max.ifPresent(value -> System.out.println(value));

//433

  从对象中引用实例方法 - ClassInstance::instanceMethodName

  在上面的例子中,我们使用了 System.out.println(value) 打印集合中的最大值,我们可以使用 System.out::println 打印这个值。

List<Integer> integers = Arrays.asList(1,12,433,5);        
Optional<Integer> max = integers.stream().reduce( Math::max ); 
max.ifPresent( System.out::println );

//433

  引用特定类型的实例方法 - Class::instanceMethodName

  在这个例子中 s1.compareTo(s2) 被简写为 String::compareTo。

List<String> strings = Arrays
        .asList("how", "to", "do", "in", "java", "dot", "com");
List<String> sortedAlt = strings
        .stream()
        .sorted(String::compareTo)
        .collect(Collectors.toList());
System.out.println(sortedAlt);

引用构造函数 - Class::new

  使用 lambda 表达式修改第一个例子中的方法,可以非常简单的创建一个从1到100的集合(不包含100)。创建一个新的 ArrayList 实例,我们可以使用 ArrayList::new。

List<Integer> integers = IntStream
                .range(1, 100)
                .boxed()
                .collect(Collectors.toCollection( ArrayList::new ));
Optional<Integer> max = integers.stream().reduce(Math::max); 
max.ifPresent(System.out::println);

//99

  案例:

public class User {
    private String username;
    private Integer age;

    public User() {
    }

    public User(String username, Integer age) {
        this.username = username;
        this.age = age;
    }

    @Override
    public String toString() {
        return "User{" +
                "username='" + username + '\'' +
                ", age=" + age +
                '}';
    }

    // Getter&amp;Setter
}
public static void main(String[] args) {
    // 使用双冒号::来构造静态函数引用
    Function<String, Integer> fun = Integer::parseInt;
    Integer value = fun.apply("123");
    System.out.println(value);

    // 使用双冒号::来构造非静态函数引用
    String content = "Hello JDK8";
    Function<Integer, String> func = content::substring;
    String result = func.apply(1);
    System.out.println(result);

    // 构造函数引用
    BiFunction<String, Integer, User> biFunction = User::new;
    User user = biFunction.apply("mengday", 28);
    System.out.println(user.toString());

    // 函数引用也是一种函数式接口,所以也可以将函数引用作为方法的参数
    sayHello(String::toUpperCase, "hello");
}

// 方法有两个参数,一个是
private static void sayHello(Function<String, String> func, String parameter){
    String result = func.apply(parameter);
    System.out.println(result);
}

四、默认方法

  Java 8 新增了接口的默认方法。简单说,默认方法就是接口可以有实现方法,而且不需要实现类去实现其方法。

  我们只需在方法名前面加个 default [dɪˈfɔːlt]关键字即可实现默认方法。为什么要有这个特性?

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

  案例:

public interface Moveable {
    default void move(){
        System.out.println("I am moving");
    }
}

  Moveable 接口定义了一个 move() 方法并且提供了默认的实现。如果任意一个 class 实现这个接口都没有必要去实现这个 move() 方法,能够直接使用 instance.move() 进行调用。

  案例:

public class Animal implements Moveable{
    public static void main(String[] args){
        Animal tiger = new Animal();
        tiger.move();
    }
}
// I am moving

  如果该 class 想要自定义一个 move() 也能提供自定义的实现去覆写这个 move 方法。

五、Optional 类

  是一个可以为 null 的容器对象。如果值存在则 isPresent() [ˈpreznt , prɪˈzent] 方法会返回 true,调用 get() 方法会返回该对象。

  Optional 是个容器:它可以保存类型 T 的值,或者仅仅保存 null。Optional 提供很多有用的方法,这样我们就不用显式进行空值检测。

  Optional 类的引入很好的解决空指针异常。

  案例:

import java.util.Optional;

public class OptionalTester {
    public static void main(String args[]) {
        Integer value1 = null;
        Integer value2 = new Integer(10);
        // Optional.ofNullable - 允许传递为 null 参数
        Optional<Integer> a = Optional.ofNullable(value1);
        // Optional.of - 如果传递的参数是 null,抛出异常 NullPointerException
        Optional<Integer> b = Optional.of(value2);
        System.out.println(sum(a, b));
    }

    public static Integer sum(Optional<Integer> a, Optional<Integer> b) {
        // Optional.isPresent - 判断值是否存在
        System.out.println("第一个参数值存在: " + a.isPresent());
        System.out.println("第二个参数值存在: " + b.isPresent());
        // Optional.orElse - 如果值存在,返回它,否则返回默认值 3
        Integer value1 = a.orElse(new Integer(3));
        //Optional.get - 获取值,值需要存在
        Integer value2 = b.get();
        return value1 + value2;
    }
}

  使用示例:求和时如遇空数字则使用默认值 0 参与运算。

import java.util.Optional;

public class OptionalTester {
    public static void main(String args[]) {
        Integer value1 = null;
        Integer value2 = new Integer(10);
        //同样都是求和,如果数据为空则使用默认值 0 参与计算
        System.out.println(sum(value1, value2));
        System.out.println(sum(Optional.ofNullable(value1), Optional.ofNullable(value2)));
    }

    public static Integer sum(Optional<Integer> a, Optional<Integer> b) {
        return a.orElse(0) + b.orElse(0);
    }

    public static Integer sum(Integer a, Integer b) {
        if (a == null &amp;&amp; b == null)
            return 0;
        if (a == null)
            return 0 + b;
        if (b == null)
            return a + 0;
        return a + b;
    }
}

六、Base 64

  Base64 是一种用 64 个字符来表示任意二进制数据的方法,有些人也叫 Base64 加密。

  用记事本打开 exe、jpg、pdf 这些文件时,我们都会看到一大堆乱码,因为二进制文件包含很多无法显示和打印的字符,所以,如果要让记事本这样的文本处理软件能处理二进制数据,就需要一个二进制到字符串的转换方法。Base64 是一种最常见的二进制解编码方法。

  Base64 的原理很简单,首先,准备一个包含 64 个字符的数组:

['A', 'B', 'C', ... 'a', 'b', 'c', ... '0', '1', ... '+', '/']

  然后,对二进制数据进行处理,每 3 个字节一组,一共是 3x8=24bit,划为 4 组,每组正好 6 个 bit:
在这里插入图片描述
  这样我们得到 4 个数字作为索引,然后查表,获得相应的 4 个字符,就是编码后的字符串。

  所以,Base64 编码会把 3 字节的二进制数据编码为 4 字节的文本数据,长度增加 33%,好处是编码后的文本数据可以在邮件正文、网页等直接显示。 如果要编码的二进制数据不是 3 的倍数,最后会剩下 1 个或 2 个字节怎么办?Base64 用 \x00 字节在末尾补足后,再在编码的末尾加上 1 个或 2 个 = 号,表示补了多少字节,解码的时候,会自动去掉。

  java8 在 Base64 加解码上已经提供一套标准的工具

  String base64encodedString = Base64.getEncoder().encodeToString(byte数组);

  byte[] base64decodedBytes = Base64.getDecoder().decode(base64字符串);

  字符串获取 byte 数组时 .getBytes(“utf-8”); 解决中文乱码问题。

  案例:

String string = Base64.getEncoder().encodeToString("hello word,你好,世界".getBytes());
System.out.println(string);
byte[] bytes = Base64.getDecoder().decode(string);
System.out.println(new String(bytes));

  案例:

import java.io.FileInputStream;
import java.io.IOException;
import java.util.Arrays;
import java.util.Base64;

public class Hello {
    public static void main(String[] args) throws IOException {
        FileInputStream inputStream = new FileInputStream("c:/下载.png");
        byte[] bytes = new byte[10240000];
        int read = inputStream.read(bytes);
        if (read < 10240000){
            String base64encodedString = Base64.getEncoder().encodeToString(Arrays.copyOfRange(bytes,0,read));
            System.out.println(base64encodedString);
        }
    }
}

七、字符串拼接

  从 Java7 到目前为止,我们可以通过向 String.split() 方法中传递参数的方式来分割一个字符串,然后把分割后的字符串列表以数组的形式返回。

  但是,如果需要连接字符串或者通过分隔符连接的字符串来创建一个 CSV 文件,则必须遍历字符串列表或数组, 然后使用 StringBuilder 或 StringBuffer 对象拼接这些字符串,最后得到 CSV。

  在 Java8 中使得字符串拼接任务变得容易。现在你可以使用 String.join() 方法, 其中第一个参数是分隔符,然后可以传递多个字符串或实现了 Iterable 接口的实例作为第二个参数,下面的示例将返回。

import java.time.ZoneId;
public class StringJoinDemo {
    public static void main(String[] args){
        String joined = String.join("/","usr","local","bin");
        System.out.println(joined);
        String ids = String.join(", ", ZoneId.getAvailableZoneIds());
        System.out.println(ids);
    }
}

//usr/local/bin
//Asia/Aden, America/Cuiaba, Etc/GMT+9, Etc/GMT+8.....

八、== equals 与 instanceof

  == 判断内存地址是否相同(如果是简单类型则比较他们的值是否相等)

  equals 调用对象的 equals 方法判断两个对象是否相等,如果对象的类没有重写 equals 方法,则使用 object 的 equals 方法,object 的 equals 方法是比较内存地址。

  instanceof:关键字用来确定对象所属的类,或父类。

  System.out.println(student instanceof Student);//true

九、final

  final 中文意思:最后的,最终的。

  final 可以修饰变量、方法或者类

  • 当不希望父类的某个方法被子类覆盖(override)时,可以用 final 关键字修饰。
  • 当不希望类的某个变量的值被修改,可以用 final 修饰。如果一个变量是 final,则必须赋初值,否则编译出错。
  • 当类不希望被继承时,可以在类前面用 final 修饰。

注意:

  final 修饰的变量又叫常量,一般用全大写下划线命名。( Integer.MAX_VALUE )
  final 修饰的变量在定义时,必须初始化,并且以后不能再赋值。

章节练习:

  1.遍历集合:使用 Lambda 表达式遍历如下的集合,代码尽可能精简。

List<Employee> emps = Arrays.asList(new Employee(101, "张三", 18, 9999.99), 
    new Employee(102, "李四", 59, 6666.66),new Employee(103, "王五", 28, 3333.33), 
    new Employee(104, "赵六", 18, 7777.77),new Employee(105, "田七", 38, 5555.55));

  2.遍历 map:使用 Lambda 表达式遍历如下的 Map,代码尽可能精简。

Map<String, Employee> map = new HashMap<>();
map.put("101", new Employee(101, "张三", 18, 9999.99));
map.put("102", new Employee(102, "李四", 59, 6666.66));
map.put("103", new Employee(103, "王五", 28, 3333.33));
map.put("104", new Employee(104, "赵六", 8, 7777.77));
map.put("105", new Employee(105, "田七", 38, 5555.55));

  3.获取集合里面的某属性:使用 Stream 获取第一题里面的全部 id 的集合。

  4.字符串的 base64:使用 base64 加密与解密如下字符串:“abcd1234”。

  5.文件的 base64:使用 base64 算法获取 C:/Windows/win.ini 的 base64 字符串。

  6.乱序集合:创建一个方法 void shuffle(List arr),使其能在代码最精简,且最省内存的情况下完成数组的乱序。

  参考代码:

import org.junit.Test;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

public class Demo {
    /**
     * 1.遍历集合
     */
    @Test
    public void t1() {
        List<Employee> list = Arrays.asList(new Employee(101, "张三", 18, 9999.99),
                new Employee(102, "李四", 59, 6666.66), new Employee(103, "王五", 28, 3333.33),
                new Employee(104, "赵六", 18, 7777.77), new Employee(105, "田七", 38, 5555.55));
        list.forEach(System.out::println);

    }

    /**
     * 2.遍历 map
     */
    @Test
    public void t2() {
        Map<String, Employee> map = new HashMap<>();
        map.put("101", new Employee(101, "张三", 18, 9999.99));
        map.put("102", new Employee(102, "李四", 59, 6666.66));
        map.put("103", new Employee(103, "王五", 28, 3333.33));
        map.put("104", new Employee(104, "赵六", 8, 7777.77));
        map.put("105", new Employee(105, "田七", 38, 5555.55));
        map.forEach((k, v) -> System.out.println(k + "=" + v));
    }

    /**
     * 3.获取集合里面的某属性
     */
    @Test
    public void t3() {
        List<Employee> list = Arrays.asList(new Employee(101, "张三", 18, 9999.99),
                new Employee(102, "李四", 59, 6666.66), new Employee(103, "王五", 28, 3333.33),
                new Employee(104, "赵六", 18, 7777.77), new Employee(111, "田七", 38, 5555.55));
        List<Integer> collect = list.stream().map(Employee::getId).collect(Collectors.toList());
        System.out.println(collect);
    }

    /**
     * 4.字符串的 base64
     */
    @Test
    public void t4() {
        String s1 = "abcd1234";
        String string = Base64.getEncoder().encodeToString(s1.getBytes());
        System.out.println(string);
        byte[] decode = Base64.getDecoder().decode(string);
        System.out.println(new String(decode));
    }

    /**
     * 5.获取文件的 base64
     */
    @Test
    public void t5() throws IOException {
        InputStream stream = new FileInputStream("C:/Windows/win.ini");
        byte[] bytes = new byte[1024];
        int n = stream.read(bytes);
        String string = Base64.getEncoder().encodeToString(Arrays.copyOfRange(bytes, 0, n));
        System.out.println(string);
        byte[] decode = Base64.getDecoder().decode(string);
        System.out.println(new String(decode));
        stream.close();
    }

    /**
     * 6.乱序集合
     */
    @Test
    public void t6() {
        List<Integer> list = IntStream.range(1, 11).boxed().collect(Collectors.toList());
        System.out.println(list);
        shuffle(list);
        System.out.println(list);
    }

    
    public void shuffle(List<Integer> arr) {
        for (int i = 0; i < arr.size(); i++) {
            int index = (int) (Math.random() * arr.size());
            int temp = arr.get(i);
            arr.set(i, arr.get(index));
            arr.set(index, temp);
        }
    }
}

class Employee {
    private Integer id;
    private String name;
    private Integer age;
    private Double sal;

    public Employee(Integer id, String name, Integer age, Double sal) {
        this.id = id;
        this.name = name;
        this.age = age;
        this.sal = sal;
    }

    @Override
    public String toString() {
        return "Employee{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                ", sal=" + sal +
                '}';
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

faramita_of_mine

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值