JDK8 新特性学习笔记

java8新特性学习

java8的特色

  1. 速度更快(修改了HasMap、HasSet、CurrentHasMap等存储结构)
  2. 代码更少(增长了新的语法Lambda表达式)
  3. 强大的Stream API
  4. 便于并行
  5. 最大化减小空指针异常

1.Lambda表达式

1.一、Lambda表达式是什么?

Lambda是一个匿名函数,咱们能够把Lambda表达式理解为一段能够传递的代码(将代码像数据同样传递)。能够写出更简洁、更灵活的代码。做为一种更紧凑的代码风格,使Java的语言表达能力获得了提高。java

1.二、Lambda表达式的基础语法

Java8中引入了一个新的操做符“->”,该操做符称为箭头操做符或者Lambda操做符,该操做符将Lambda表达式拆分红两个部分:api

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

右侧:Lambda表达式中须要执行的功能,即Lambda体。app

语法格式一:无参数,无返回值dom

() -> System.out.println("hello Lambda");
复制代码

语法格式二:有一个参数,而且无返回值ide

(x) -> System.out.println(x);
复制代码

语法格式三:若只有一个参数,小括号能够省略不写函数

x -> System.out.println(x);
复制代码

语法格式四:有两个以上的参数,有返回值,而且Lambda体中有多条语句学习

Comparator<Integer> com = (x,y) -> {
    System.out.println("函数式接口");
    return Integer.compare(x,y);
};
复制代码

语法格式五:若Lambda体中只有一条语句,return和大括号均可以省略不写this

Comparator<Integer> com = (x,y) -> Integer.compare(x,y);
复制代码

语法格式六:Lambda表达式的参数列表的数据类型能够省略不写,由于JVM编译器经过上下文推断出数据类型spa

(Integer x,Integer y) -> Integer.compare(x,y);
复制代码

源代码

package com.zgy.deom1;

import java.util.Comparator;
import java.util.function.Consumer;

import org.junit.jupiter.api.Test;

public class TestLambda2 {

	/* * Lambda基本语法:参数列表 -> 方法体 */
	
	/* * 语法格式一:无参数,无返回值 * () -> System.out.println("Hello World"); */
	@Test
	public void test1() {
		int num = 0; //jdk8中当匿名内部类中使用的变量,会自动隐式添加final关键字,匿名内部类中依然不能将变量进行加减操做。
		Runnable r = new Runnable() {
			@Override
			public void run() {
				// TODO Auto-generated method stub
				System.out.println("hello world"+num);
			}
		};
		r.run();

		System.out.println("=============================");
		
		Runnable r1 = () -> System.out.println("hello lambda"+num);
		r1.run();
	}
	
	/* * 语法格式二:有一个参数,没有返回值 */
	@Test
	public void test2() {
		Consumer<String> c = (x) -> System.out.println(x);
		c.accept(1+"");
		
		//Lambda表达式中若是参数只有一个,那么括号能够省略不写
		Consumer<Object> c1 = x -> System.out.println("哈哈哈");
		c1.accept("ABC");
	}
	
	/* * 语法格式三:有两个参数,有返回值,Lambda体中有多条语句 */
	@Test
	public void test3() {
		Comparator<Integer> com = (x,y) -> {
			System.out.println("好好学习,每天向上!");
			return x+y;
		};
		
		int result = com.compare(20, 30);
		System.out.println("result:"+result);
		
		//若是Lambda体中只有一条语句,能够省略“{}”和return
		Comparator<Integer> c = (Integer x,Integer y) -> Integer.compare(x, y);
		int num = c.compare(30, 30);
		System.out.println("maxNub"+num);
		
		//因为JVM的类型推断,因此Lambda参数列表的数据类型,能够省略不写
		Comparator<Integer> c1 = (x,y) -> Integer.compare(x, y);
		int num1 = c1.compare(30, 31);
		System.out.println("maxNub"+num1);
	}
	
	/* * 调用自定的函数式接口 */
	@Test
	public void test4() {
		MyFun myFun = x -> x+100;
		int value = myFun.getValue(100);
		System.out.println("value:"+value);
	}
	
}
复制代码
package com.zgy.deom1;

@FunctionalInterface
public interface MyFun {

	int getValue(int x);
}
复制代码

练习代码

package com.zgy.deom1;


import java.util.Arrays;
import java.util.Collections;
import java.util.List;

import org.junit.jupiter.api.Test;

public class TestLambda {

	
	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,"赵六",8,7777.77),
			new Employee(105,"田七",38,5555.55)
			);
	
	/* * 使用Collections对员工列表进行排序,按工资从多到少比 * 关于Collections.sort()自定义排序规则,两个参数x和y,若是要按照升序,就是x在前面;相反若是要按照降序,就是y在前面。 */
	@Test
	public void test1() {
		
		Collections.sort(emps, (x,y) -> Double.compare(y.getSalary(), x.getSalary()));
		for (Employee employee : emps) {
			System.out.println(employee);
		}
	}
	
	/* * 定义一个函数式接口,传如一个参数,返回该参数的大写形式 */
	@Test
	public void test2() {
		
		String str = strHandler("zgy", x -> x.toUpperCase());
		System.out.println("str:"+str);
	}
	
	//小写转大写的方法
	public String strHandler(String str,MyFunction mf) {
		return mf.getValue(str);
	}
	
	/* * 声明一个带两个泛型的函数式接口,泛型类型为<T,R> T为参数,R为返回值,接口中声明对应的抽象方法 * 再使用接口做为参数,计算两个long型参数的和 * 再计算两个long型参数的积 */
	@Test
	public void test3() {
		
		//计算和
		System.out.println(getValue(100L, 200L, (x,y) -> x+y));
		
		//计算积
		System.out.println(getValue(500L, 900L, (x,y) -> x*y));
	}
	
	public long getValue(Long x,Long y,MyFunction2<Long, Long> mf) {
		
		return mf.getValue(x, y);
	}
}
复制代码
package com.zgy.deom1;

public class Employee {

	private int id;
	private String name;
	private int age;
	private double salary;
	
	public Employee() {}
	
	public Employee(int id, String name, int age, double salary) {
		super();
		this.id = id;
		this.name = name;
		this.age = age;
		this.salary = salary;
	}

	@Override
	public String toString() {
		return "Employee [id=" + id + ", name=" + name + ", age=" + age + ", salary=" + salary + "]";
	}

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

	public double getSalary() {
		return salary;
	}

	public void setSalary(double salary) {
		this.salary = salary;
	}
	
}
复制代码
package com.zgy.deom1;

@FunctionalInterface
public interface MyFunction {

	public String getValue(String x);
}
复制代码
package com.zgy.deom1;

@FunctionalInterface
public interface MyFunction2<T,R> {

	public R getValue(T t1, T t2);
}
复制代码

1.三、函数式接口

函数式接口:接口中只有一个抽象方法的接口,称为函数式接口,可使用注解@FunctionalInterface修饰,该注解的做用是能够检查该接口是否为函数式接口。

1.四、Java8内置四大核心函数式接口

Consumer:消费型接口

​ void accept(T t);

Supplier:供给型接口

​ T get();

Function<T, R>:函数型接口

​ R apply(T t);

Predicate:断言型接口

​ boolean test(T t);

其它子接口以下图

源代码

package com.zgy.deom1;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Random;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;

import org.junit.jupiter.api.Test;

/* * JDK8 内置四大核心函数式接口 */
public class TestLambda3 {

	/* * 消费式接口:传一个参数,没有返回值 * Consumer<T> void accept(T t); */
	@Test
	public void test1() {
		say("好好学习,每天向上!", x -> System.out.println(x+new Date()));
	}
	
	public void say(String str, Consumer<String> c) {
		c.accept(str);
	}
	
	/* * 供给式接口:没有参数,返回一个结果 * Supplier<T> T get(); */
	@Test
	public void test2() {
		List<Integer> listResult = getNumberArr(() -> {
			List<Integer> list = new ArrayList<>();
			for(int i=0; i<10; i++) {				
				list.add(new Random().nextInt());
			}
			return list;
		});
		
		for (Integer integer : listResult) {
			System.out.println("DATA:"+integer);
		}
	}
	
	public List<Integer> getNumberArr(Supplier<List<Integer>> s){
		return s.get();
	} 
	
	/* * 函数式接口:传入一个参数,返回一个结果 * Function<T,R> R apply(T t); */
	@Test
	public void test3() {
		System.out.println(apply("ZGY:", (x) -> x+new Date()));
	}
	
	public String apply(String a, Function<String, String> f) {
		return f.apply(a);
	};
	
	/* * 断言型接口:传入一个参数,返回一个布尔值 * Predicate<T> boolean test(T t); */
	@Test
	public void test4() {
		System.out.println(test(40, x -> x>0));
	}
	
	public String test(int x, Predicate<Integer> p) {
		if(p.test(x)) {
			return "大于0";
		}else {
			return "小于0";
		}
	}
}
复制代码

1.五、方法引用与构造器引用

  1. 方法引用:若Lambda体中的内容有方法已经实现了,咱们可使用“方法引用”(能够理解为方法引用是Lambda表达式的另一种表现形式)

    主要有三种语法格式:

    1. 对象 :: 实例方法名
    2. 类 :: 静态方法名
    3. 类 :: 实例方法名

    注意:

    1. Lambda体中调用方法的参数列表与返回值类型,要与函数式接口中抽象方法的函数列表和返回值类型保持一致。
    2. 若Lambda参数列表中的第一个参数是实例方法的调用者,第二个参数是实例方法的参数,能够用使用ClassName :: method
  2. 构造器引用:

    格式:ClassName :: new

    注意:须要调用的构造器参数列表要与函数式接口中抽象方法的参数列表保持一致

  3. 数组引用:

    格式:Type :: new;

源代码

package com.zgy.deom1;

import java.util.Comparator;
import java.util.function.BiPredicate;
import java.util.function.Consumer;
import java.util.function.Supplier;

import org.junit.jupiter.api.Test;

public class TestMethodRef {

	/* * 方法引用和构造器引用 */
	
	@Test
	public void test1() {
		//对象 :: 实例方法名
		Consumer<String> c = System.out::println;
		c.accept("ZGY");
		
		Employee e = new Employee(111,"ZGY",22,1000);
		Supplier<String> s = e::getName;
		System.out.println(s.get());
	}
	
	@Test
	public void test2() {
		//类名::方法名
		Comparator<Integer> c = Integer::compare;
		System.out.println(c.compare(800, 210));
	}
	
	@Test
	public void test3() {
		//类名::实例方法名
		BiPredicate<String, String> bp = String::equals;
		System.out.println(bp.test("AAA", "AAA"));
	}
	
	@Test
	public void test4() {
		//构造器引用
		Supplier<Employee> e = Employee::new;
		Employee employee = e.get();
		employee.setName("ZGY");
		System.out.println(employee);
	}
}
复制代码

二、Stream API

2.一、什么是Stream

流(Stream)究竟是什么?

流,是数据渠道,用于操做数据源(集合、数组等)所生成的元素序列。集合讲的是数据,而流讲的是计算!

注意:

  1. Stream本身不会存储元素。
  2. Stream不会改变源对象,相反他们会返回一个持有结果的新Stream。
  3. Stream是延迟执行的,这意味着它们会等到须要结果的时候才会执行。

2.二、Stream的操做三个步骤

  1. 建立Stream源码

    package com.zgy.deom1;
    
    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.List;
    import java.util.stream.Stream;
    
    import org.junit.jupiter.api.Test;
    
    /* * 建立Stream的几种方式 */
    public class TestStreamAPI1 {
    
    	@Test
    	public void test1() {
    		//1.能够经过Collection系列集合提供的stream()或parallelStream()
    		List<String> list = new ArrayList<>();
    		Stream<String> stream = list.stream();
    		
    		//2.经过Arrays中的静态方法stream()获取数组流
    		Employee[] employees = new Employee[10];
    		Stream<Employee> stream2 = Arrays.stream(employees);
    		
    		//3.经过Stream类中的静态方法of()
    		Stream<String> stream3 = Stream.of("AA","BB","CC","DD");
    		
    		//4.经过迭代的方式建立无限流
    		Stream<Integer> stream4 = Stream.iterate(0, x -> x+2);
    		stream4.limit(5).forEach(System.out::println);
    		
    		//5.经过生成的方式建立无限流
    		Stream<Double> stream5 = Stream.generate(() -> Math.random());
    		//下面limit、forEach分别是
    		stream5.limit(20).forEach(System.out::println);
    	}
    }
    复制代码
相关文章
相关标签/搜索