JDK1.8特性

一、接口方法

JDK1.8中引入了一种新的机制:接口可以支持在声明方法的同时,提供实现。

  • 主要通过两种方式可以完成这种操作:
    • 默认方法
    • 静态方法

1. 默认方法

  • 默认方法存在的两大优势:
    • 可以让接口更优雅的升级,减少使用人员操作的负担
      不必随着接口方法增加,从而修改实现代码,因为默认方法在子类中可以不用实现
    • 可以让实现类中省略很多不必要方法的空实现

默认方法,实现类不需要重写,类似该方法继承下去,实现类构建实例对象之后可以调用

interface FirstInterface{ 
	//传统定义,抽象方法,没有方法体。 
	void before(); 
	//默认方法 
	default void test() { 
		System.out.println("Default method in FirstInterface"); 
	} 
}
class FirstClass implements FirstInterface{ 
	//所有实现类必须实现接口中未实现的方法。 
	@Override 
	public void before() { 
		System.out.println("我是FirstInterface中的抽象方法,所有实现类必须实现!"); 
	}
}
public class DefaultMethod { 
	public static void main(String[] args) { 
		FirstClass fc = new FirstClass();
		fc.test(); 
		//此处输出Default method in FirstInterface,对于默认方法,如果实现类中没有实现就是用默认的。
		fc.before(); 
		//此处输出我是FirstInterface中的抽象方法,所有实现类必须实现。 
	} 
}
  • 接口继承及默认方法冲突
    • Java中一个类只能继承一个父类,但是可以实现多个接口。
    • 方法调用的判断规则:
      • 类中声明的方法优先级最高。
        类或父类中,声明的方法要高于任何默认方法的优先级
      • 如果无法依据第一条进行判断,那么子接口的优先级更高
        例如,如果 B 接口继承了 A接口 ,那么 B 就比 A 更加具体,优先级更高
        所以,在上述例子中,B是子接口,优先级别更高,调用test方法后输出:defaut method test in B.
      • 最后,如果还是无法判断,那么继承了多个接口的类,必须通过实现(重写)方法来确定方法的调用

(1)一个类C,实现两个接口A、B,其中接口B继承接口A

interface A{ 
	default void test(){ 
		System.out.println("Default Method test in A"); 
	} 
}
interface B extends A{ 
	default void test(){ 
		System.out.println("default method test in B"); 
	} 
}
class C implements A,B{ 
	public static void main(String[] args){ 
		C c = new C(); c.test(); 
	} 
}
//运行结果: 
		default method test in B

(2)一个类C,继承父类A,实现接口B

class A{ 
	public void test(){ 
		System.out.println("Default Method test in A"); 
	} 
}
interface B { 
	default void test(){ 
		System.out.println("default method test in B"); 
	} 
}
class C extends A implements B{ 
	public static void main(String[] args){ 
		C c = new C(); c.test(); 
	} 
}
//运行结果:
		default method test in A

(3)一个类C,实现两个接口A、B,但是A和B之间没有子父接口关系

interface A{ 
	default void test(){ 
		System.out.println("Default Method test in A"); 
	} 
}
interface B { 
	default void test(){ 
		System.out.println("default method test in B") 
	} 
}
//如下代码编译会报错。 
/*
	class C implements A,B{ 
		public static void main(String[] args){ 
			C c = new C(); c.test(); 
		} 
	}
*/ 
//如果C需要同时实现A和B接口,那么必须显示覆盖 
class C implements A,B{ 
	public void test(){ 
		//如果在C中需要显示访问A/B的默认方法,可以使用接口名.super.方法名(); 
		A.super.test(); 
		B.super.test(); 
		//或者自己编写test方法的实现 
	} 
}

2. 静态方法

接口中的静态方法可以直接通过 接口名.静态方法名的形式进行访问。
接口中的静态方法,只能使用当前接口的名字来调用

public interface StaticMethod{ 
	public static void test(){ 
		System.out.println("我是StaticMethod接口中的静态方法!"); 
	} 
}
class A implements StaticMethod{} 
class Test{ 
	public static void main(String args[]){ 
		//运行成功 
		StaticMethod.test(); 
		//编译报错: 
		//Static method may be invoked on containing interface class only 
		//A.test(); 
	} 
}

二、Lambda

1. 行为参数化

在实际项目中,因为用户需求的变动,需要不断的增加方法,为此复制了大量的相同的代码。

  • 方法之间【相同】的地方,例如:
    • 方法的修饰符
    • 方法的返回类型
    • 方法的参数列表
    • 方法中对数据的遍历
  • 方法之间【不同】的地方,例如:
    • 方法的名字
    • 方法中遍历数据后的核心操作
  • 方法真正关心的重点:
    • 方法的参数列表
    • 方法中的核心操作代码
    • 方法的返回类型
interface Action{ 
	int action(int result,int i); 
}

(1)传统的计算方式

//定义累加的核心操作行为 
class Add implements Action{ 
	public int action(int result,int i){ 
		return result+i; 
	} 
}

//定义累乘的核心操作行为 
class Multiply implements Action{ 
	public int action(int result,int i){ 
		return result*i; 
	} 
}

class Test{ 
	public static void main(String[] args){ 
		int result = 0; 
		
		result = calculate(3,5,new Add()); 
		//[3,5]之间,累加的结果 
		System.out.println(result);
		
		result = calculate(3,5,new Multiply()); 
		//[3,5]之间,累乘的结果 
		System.out.println(result) 
	} 
}

(2)匿名内部类

class Test{ 
	public static void main(String[] args){ 
		int result = 0; 
		
		result = calculate(3,5,new Action(){ 
			public int action(int result,int i){ 
				return result+next; 
			} 
		}); 
		//[3,5]之间,累加的结果 
		System.out.println(result); 
		
		result = calculate(3,5,new Action(){ 
			public int action(int result,int i){ 
				return result*next; 
			} 
		}); 
		//[3,5]之间,累乘的结果 
		System.out.println(result) 
	} 
}

(3)函数式编程
函数式程序就是一个表达式。
允许函数式编程中没有任何返回值的

class Test{ 
	public static void main(String[] args){ 
		int result = 0; 
		/*
		//忽略掉之前匿名内部类形式中,不重要的部分 
		Action add = (int result,int i) -> { return result + i; };
		*/

		//简化写法 
		Action add = (result,i) -> result + i; 
		result = calculate(3,5,add); 
		//去掉中间变量add 
		//相当于,第三个参数,我们传了一个核心的计算行为 
		//这个核心的计算行为,就是对Action接口中action方法的实现 
		//result = calculate(3,5,(result,next) -> result + next); 
		//[3,5]之间,累加的结果 S
		ystem.out.println(result); 
	} 
}

2. Lambda使用

接口中有且只有一个抽象方法的时候,才可以使用Lambda表达式来对其进行实现。
有且只有一个抽象方法的接口,就是函数式接口。该接口中,也允许有其他的默认方法和静态方法。
@FunctionalInterface ,用来检查被标注的接口,是不是一个函数式接口

2.1 Lambda语法

Lambda表达式的格式为: () -> {}

  • () 表示参数列表
  • -> 后面跟的是函数主体
  • {} 函数主体,表达式的返回值,由这个函数主体中代码来决定

(1)抽象方法【无参】,【无返回值】

public class Test { 
	public static void main(String[] args) {
		/**
		* Action action1 = new Action(){ 
		* public void run(){} 
		* }; 
		*/ 
		Action action1 = () -> {}; 
		
		/**
		* Action action2 = new Action(){
		* 		public void run(){
		* 			System.out.println("hello");
		* 		}
		* }; 
		*/ 
		//函数主体中,如果只有一句代码,那么可以省去大括号 
		Action action2 = () -> System.out.println("hello"); 
		
		/**
		* Action action3 = new Action(){
		* 		public void run(){
		* 			int a = 1;
		* 			int b = 2;
		* 			System.out.println(a+b);
		* 		}
		* }; 
		*/ 
		//函数主体中,如果有多句代码,那么大括号必须要写 
		Action action3= () -> { 
			int a = 1; 
			int b = 2; 
			System.out.println(a+b); 
		}; 
	} 
}

interface Action{ 
	public void run(); 
}

(2)抽象方法【有参】,【无返回值】

  • 抽象方法是一个参数的情况
public class Test { 
	public static void main(String[] args) { 
		/**
		 * Action action = new Action(){ 
		 * 		public void run(int a){} 
		 * };
		 */ 
		 Action action = (int a) -> {}; 
		 
		 /**
		 * Action action1 = new Action(){ 
		 * public void run(int a){} 
		 * }; 
		 */ 
		  //只有一个参数时,可以不加小括号 
		 //并且参数的类型,JVM运行时会做自动推断的,及时不写,它也知道是int 
		 Action action1 = a -> {}; 
		 
		 /**
		 * Action action2 = new Action(){ 
		 * 		public void run(int a){ 
		 * 			System.out.println(a); 
		 * 		} 
		 * }; 
		 */ 
		 Action action2 = a -> System.out.println(a); 

		/**
		* Action action3 = new Action(){ 
		* 		public void run(int a){
		* 			a++;
		* 		}
		* }; 
		*/ 
		Action action3 = a -> a++; 
	} 
}

//抽象方法是一个参数的情况 
interface Action{ 
	public void run(int a); 
}
  • 抽象方法是多个参数的情况
public class Test { 
	public static void main(String[] args) { 
			/**
			 * Action action1 = new Action(){
	 		 * public void run(int a,int b){}
			 * }; 
			 */ 
			 Action action1 = (a,b) -> {}; 
	} 
}

//抽象方法是多个参数的情况 
interface Action{ 
	public void run(int a,int b); 
}

(3)抽象方法【无参】,【有返回值】

public class Test { 
	public static void main(String[] args) { 
		/**
		* Action action1 = new Action(){
		* 		public void run(){
		* 			return 1;
		* 		}
		* }; 
		*/ 
		//如果就一句代码,大括号可以去掉,return关键字也会省去 
		Action action1 = () -> 1; 
		
		/**
		* Action action2 = new Action(){
		* 		public void run(){
		* 			int num = 10;
		* 			return (int)(Math.random()*num);
		* 		}
		* }; 
		*/ 
		Action action2 = () -> { 
			int num = 10; 
			return (int)(Math.random()*num); 
		}; 
	} 
}

interface Action{ 
	public int run(); 
}

(4)抽象方法【有参】,【有返回值】

public class Test { 
	public static void main(String[] args) { 
		/**
		 * Action action1 = new Action(){
		 * 		public int run(int a,int b){
		 * 			return a+b;
		 * 		}
	 	* }; 
		 */ 
		 Action action1 = (a,b) -> a + b;
		 
		 /**
	 	 * Action action2 = new Action(){
	 	 * 	public int run(int a,int b){
	 	 * 		int num = a+b;
	 	 * 		return (int)(Math.random()*num);
	 	 * 	}
	 	 * }; 
	  	*/ 
	 	 Action action2 = (a,b) -> { 
	  		int num = a+b; 
	  		return (int)(Math.random()*num); 
	  	}; 
	} 
}

interface Action{ 
	public int run(int a,int b); 
}

2.2 常用接口

函数式接口都定义在 java.lang.function 包中
(1)Predicate
java.util.function.Predicate<T> 接口定义了一个名叫 test 的抽象方法,它接受泛型T 对象,并返回一个 boolean 类型的结果

  • and() 方法:俩个条件同时成立
public class Test { 
	public static void main(String[] args) { 
		Test t = new Test(); 
		Integer[] arr = {12,3,43,123,34,6,56,7}; 
		
		//条件1,数据大于10 
		Predicate<Integer> p1 = e -> e>10; 
		
		//条件2,数据小于50
		Predicate<Integer> p2 = e -> e<50; 
		
		//俩个条件同时成立 
		Predicate<Integer> p = p1.and(p2); 
		arr = t.filter(arr,p); 
		System.out.println(Arrays.toString(arr)); 
	}
	public Integer[] filter(Integer[] arr, Predicate<Integer> p){ 
		List<Integer> list = new ArrayList<>();
		for(Integer i: arr){ 
			//判断当前数据是否符合要求 
			if(p.test(i)){ 
				list.add(i); 
			} 
		}
		//把集合转为Integer类型数组 
		return list.toArray(new Integer[list.size()]); 
	} 
}

//运行结果: 
	[12, 43, 34]
  • or() 方法:俩个条件成立一个即可
public class Test { 
	public static void main(String[] args) { 
		Test t = new Test(); 
		Integer[] arr = {12,3,43,123,34,6,56,7}; 
		
		//条件1,数据小于10 
		Predicate<Integer> p1 = e -> e<10; 
		
		//条件2,数据大于50 
		Predicate<Integer> p2 = e -> e>50; 
		
		//俩个条件成立一个即可 
		Predicate<Integer> p = p1.or(p2); 
		arr = t.filter(arr,p); 
		System.out.println(Arrays.toString(arr)); 
	}
	public Integer[] filter(Integer[] arr, Predicate<Integer> p){ 
		List<Integer> list = new ArrayList<>();
		for(Integer i: arr){
			if(p.test(i)){ 
				//判断当前数据是否符合要求 
				list.add(i); 
			} 
		}
		//把集合转为Integer类型数组 
		return list.toArray(new Integer[list.size()]); 
	} 
}
//运行结果: 
		[3, 123, 6, 56, 7]
  • negate() 方法:获取条件相反的数据
public class Test { 
	public static void main(String[] args) { 
		Test t = new Test(); 
		Integer[] arr = {12,3,43,123,34,6,56,7}; 
		
		//条件1,数据大于10 
		Predicate<Integer> p1 = e -> e>10; 
		
		//获取条件1相反的数据 
		Predicate<Integer> p = p1.negate(); 
		arr = t.filter(arr,p); 
		System.out.println(Arrays.toString(arr)); 
	}
	public Integer[] filter(Integer[] arr, Predicate<Integer> p){ 
		List<Integer> list = new ArrayList<>();
		for(Integer i: arr){ 
			//判断当前数据是否符合要求 
			if(p.test(i)){ 
				list.add(i); 
			} 
		}
		//把集合转为Integer类型数组 
		return list.toArray(new Integer[list.size()]); 
	} 
}

//运行结果: 
		[3, 6, 7]
  • removeIf方法:移除符合条件的数据
public class Test { 
	public static void main(String[] args) { 
		Collection<String> col = new ArrayList<String>(); 
		col.add("abc"); 
		col.add("hello"); 
		col.add("world"); 
		col.removeIf((str)->str.contains("o")); 
		System.out.println(col); 
	} 
}

//运行结果: 
		[abc]

(2)Consumer
java.util.function.Consumer<T> 接口

  • accept方法:
public class Test { 
	public static void main(String[] args) { 
		Test t = new Test(); 
		Student stu = new Student("tom"); 
		//操作1,给stu对象的name属性值加前缀 
		Consumer<Student> consumer1 = (s) -> s.name = "start_"+s.name; 
		//操作2,给stu对象的name属性值加后缀 
		Consumer<Student> consumer2 = (s) -> s.name = s.name+"_"+System.currentTimeMillis(); 
		//操作3,给stu对象的name属性值,先加前缀,再加后缀 
		Consumer<Student> consumer3 = consumer1.andThen(consumer2); 
		//如果传入consumer1,表示只加前缀 
		//如果传入consumer2,表示只加后缀 
		//如果传入consumer3,表示先加前缀,再加后缀 
		t.operStu(stu,consumer3); 
		System.out.println(stu.name); 
	}
	public void operStu(Student stu, Consumer<Student> consumer){ 
		consumer.accept(stu); 
	} 
}
class Student{ 
	String name; 
	public Student(String name) { 
		this.name = name; 
	} 
}
//运行结果: 
		briup_tom_1598282322884
  • forEach方法:
public class Test { 
	public static void main(String[] args) { 
		Collection<String> col = new ArrayList<String>(); 
		col.add("abc"); 
		col.add("hello"); 
		col.add("world"); 
		//去掉中间变量,直接把Lambda表达式当前参数传入到forEach方法中 
		col.forEach((t)->System.out.println(t)); 
	} 
}

//运行结果: 
	abc 
	hello 
	world

(3)Function
java.util.function.Function<T, R> 接口

  • andThen方法:
public class Test { 
	public static void main(String[] args) {
		String str = "a-b-c-a-b-c"; 
		
		//传入字符串,返回数组,操作为把字符串按照 "-" 进行分割为字符串数组 
		// "a-b-c-a-b-c" 转换为 {"a","b","c","a","b","c"} 
		Function<String,String[]> f1 = s -> s.split("-"); 
		
		//传入字符串数组,返回Set<String>集合,目的是去除数组中重复的数据,存放把结果存放到Set集合中
		//{"a","b","c","a","b","c"} 转换为集合 [a, b, c] 
		Function<String[], Set<String>> f2 = arr -> { 
			Set<String> set = new HashSet<>();
			for(String string : arr){ 
				set.add(string); 
			}
			return set; 
		};
		
		//刚好,f1函数的结果,作为f2函数的参数,f1和f2组合成f3函数 
		//f3函数表示传入字符串,最后返回Set<String>集合 
		//其实内部是先将字符串交给f1函数转换数组,在将数组交给f2函数转换Set集合 
		//通过上面列出的andThen源码也可以看出是这个效果 
		Function<String,Set<String>> f3 = f1.andThen(f2); 
		Set<String> set = f3.apply(str); 
		System.out.println(set); 
	} 
}
//运行结果: 
	[a, b, c]
  • identity静态方法(泛型):该方法可以直接返回一个Function对象,传入一个参数,直接把该参数返回,不做任何操作
public class Test { 
	public static void main(String[] args) { 
		Function<String,String> f = Function.identity(); 
		//传入hello,返回hello 
		String result = f.apply("hello"); 
		System.out.println(result); 
	} 
}
//运行结果: 
		hello
  • computeIfAbsent(Map接口的默认方法):用来在指定key不存在时,计算一个value值和指定key匹配,并将该key=value写入map中。
public class Test { 
	public static void main(String[] args) { 
		Map<String,String> map = new HashMap<>(); 
		map.put("1", "tom"); 
		map.put("2", "mary"); 
		Function<String,String> f = key->"default"; 
		//如果key为3的时候,对应的值不存在 
		//那么使用f函数计算出一个value,并当前这个key-value存入到map集合中 
		
		map.computeIfAbsent("3",f); 
		//也可以去掉中间变量f,直接将Lambda表达式传入 
		//map.computeIfAbsent("3",key->"default");
		map.forEach((k,v)-> System.out.println(k+"-"+v)); 
	} 
}

//运行结果: 
	1-tom 
	2-mary 
	3-default

(4)Supplier
java.util.function.Supplier<T> 接口
Supplier接口中的方法,不需要参数,可以根据我们指定的算法,返回一个数据

public class Test { 
	public static void main(String[] args) { 
		//生成1-100直接的随机的奇数 
		Supplier<Integer> supplier = ()->{ 
			int num ; 
			do {
				num = (int) (Math.random()*100+1); 
			}while ( (num & 1)==0 ); 
				return num; 
		};
		for (int i = 0; i < 10; i++) { 
			System.out.println(supplier.get()); 
		} 
	} 
}
//运行结果: 
		79
		51
		69
		63
		31
		89
		81
		17
		97
		9

2.3 类型推断

(1)以Lambda表达式中没有接口的任何信息,JVM还能将其和接口匹配的上

public class Test { 
	public static void main(String[] args) { 
		Test t = new Test(); 
		
		//JVM根据上下文运行环境自动推断出 ()-{} 表达式对应的是接口是Runnable 
		t.test1(()->{}); 
		
		//JVM根据上下文运行环境自动推断出 ()-{} 表达式对应的是接口是Action 
		t.test2(()->{}); 
	}
	public void test1(Runnable run){ }
	public void test2(Action action){ } 
}
interface Action{ 
	void run(); 
}

(2)自动推断出Lambda表达式中参数的类型

public class Test { 
	public static void main(String[] args) { 
		Test t = new Test(); 
		
		//这俩种写法的效果是一样的,JVM根据环境自动推断出参数a和b的类型 
		t.test((int a,int b)->a+b); 
		t.test((a,b)->a+b); 
	}
	public void test(Action action){ }
}
interface Action{ 
	int run(int a,int b); 
}

2.4 重载解析

如果类中的方法进行了重载,那么在使用Lambda表达式的时候,很可能给它的类型推断带来问题。这时候可以做类型转换,来解决这个问题:

public class Test { 
	public static void main(String[] args) { 
		//编译通过,用强转的方式指定了表达式的对应的接口类 
		test(1,(Predicate<Integer>)num->num>0); 
	}
	public static void test(int a,Predicate<Integer> p){ }
	public static void test(int a,Function<Integer,Boolean> f){ } }

2.5局部变量

在Lambda表达式中,使用了局部变量,那么这个局部变量就一定要使用 final 修饰符进行修饰

JDK1.8中,被匿名内部类局部内部类、Lambda表示访问的局部变量,会默认加上final修饰符

public class Test { 
	public static void main(String[] args) { 
		int a = 1; 
		Runnable run = ()->{ 
			//这里访问局部变量a之后,a就自动变成了final修饰的常量(JDK1.8) 
			//也可以手动给局部变量a加上final修饰符 //变量a的值将不可被再次赋值,变为了常量 
			System.out.println(a); 
		}; 
	} 
}

三、方法引用

(1)使用Lambda表达式,可以表示一个函数,这个函数由 “参数列表、函数主体、返回类型” 这三部分组成,同时这个函数还是一个函数式接口中,唯一的抽象方法的实现
只要一个函数是无参的、无返回类型的,它就可以作为run方法的具体实现

public class Test { 
	public static void main(String[] args) { 
		//()-{} 该表达式 代表了一个函数,该函数无参,函数主体不执行任何代码,也不返回任何数据 
		//()-{} 该表达式 同时也是接口Action中,唯一的抽象方法run的具体实现 
		//因为run方法也是无参、无返回类型 
		Action a = ()->{}; 
	} 
}
interface Action{ 
	void run(); 
}

(2)Lambda表达式中,提供了特殊的语法,能让我们直接引用已经存在的方法,作为当前要表示的函数

public class Test { 
	public static void main(String[] args) { 
		//使用Lambda表达式,直接表示一个函数 
		//这个函数就作为run方法的具体实现 
		Action a1 = ()-> System.out.println("hello!"); 
		//使用Lambda表达式,引用一个已经存在的方法,作为当前要表示的一个函数 
		//这个被引用的方法,需要和Action接口中的抽象方法run保持一致:参数列表、返回类型 
		//这个方法就作为run方法的具体实现 
		Action a2 = Student::sayHello; 
		a1.run();   //输出hello 
		a2.run();  //输出hello 
	} 
}
interface Action{ 
	void run(); 
}
class Student{ 
	public static void sayHello(){ 
		System.out.println("hello!"); 
	} 
}

1. 静态方法引用

使用Lambda表达式可以引用类中的【静态】方式,语法要求为:类名::静态方法名
方法名字后面一定没有小括号

public class Test { 
	public static void main(String[] args) { 
		//只要函数的参数列表是String类型,函数的返回值是int类型,就可以作为Action接口的具体实现 
		Action a1 = str -> 1; 
		
		//使用 类名::静态方法名 的形式来引用当前类中的len方法 
		Action a2 = Test::len; 
		
		System.out.println(a1.run("hello"));//输出1 
		System.out.println(a2.run("hello"));//输出5 
	}
	public static int len(String str){ 
		return str.length(); 
	} 
}
interface Action{ 
	int run(String str); 
}

2. 实例方法引用

使用Lambda表达式可以引用类中的【非静态】方式,语法要求为:类名::非静态方法名
使用类名来引用方法

run方法的返回类型是int

  • run方法是【一个】参数
public class Test { 
	public static void main(String[] args) { 
		/*
		* Action a1 = new Action() { 
		*	@Override 
		* 	public int run(String str) { 
		* 		return 1; 
		*	} 
		* }; 
		*/ 
		
		Action a1 = str -> 1; 
		
		/**
		* Action a2 = new Action() { 
		* 	@Override 
		* 	public int run(String str) { 
		* 		return str.hashCode(); 
		* 	} 
		* }; 
		*/ 
		
		Action a2 = String::hashCode; 
		
		/**
		* Action a3 = new Action() { 
		* 	@Override 
		* 	public int run(String str) { 
		* 		return str.length(); 
		* 	} 
		* }; 
		*/ 
		
		Action a3 = String::length; 
	} 
}
interface Action{ 
	int run(String str); 
}
  • run方法是【俩个】参数
public class Test { 
	public static void main(String[] args) { 
		/**
		* Action a1 = new Action() { 
		* 	@Override 
		* 	public int run(String str,int i) { 
		* 		return 1; 
		* 	} 
		* }; 
		*/ 
		
		Action a1 = (str,i) -> 1; 
		
		/**
		* Action a2 = new Action() { 
		* 	@Override 
		* 	public int run(String str,int i) { 
		* 		return str.indexOf(i); 
		* 	} 
		* }; 
		*/ 
		
		Action a2 = String::indexOf; 
		
		/**
		* Action a3 = new Action() { 
		* 	@Override 
		* 	public int run(String str,int i) { 
		* 		return str.lastIndexOf(i); 
		* 	} 
		* }; 
		*/ 
		Action a3 = String::lastIndexOf; 
		System.out.println(a2.run("abc-abc",'a'));//输出 0 
		System.out.println(a3.run("abc-abc",'a'));//输出 4 
	} 
}
interface Action{ 
	int run(String str,int i);
}
  • run方法是【三个】参数
public class Test { 
	public static void main(String[] args) { 
		/**
		* Action a1 = new Action() { 
		* 	@Override 
		* 	public int run(String str,int i,int j) { 
		* 		return 1;
		* 	} 
		* }; 
		*/ 
		
		Action a1 = (str,i,j) -> 1; 
		
		/**
		* Action a2 = new Action() { 
		*	@Override 
		* 	public int run(String str,int i,int j) { 
		* 		return str.indexOf(i,j); 
		* 	} 
		* }; 
		*/ 
		
		Action a2 = String::indexOf; 
		
		/**
		* Action a3 = new Action() { 
		* 	@Override 
		* 	public int run(String str,int i,int j) { 
		* 		return str.lastIndexOf(i,j); 
		* 	} 
		* }; 
		*/ 
		
		Action a3 = String::lastIndexOf; 
	}
}
interface Action{ 
	int run(String str,int i,int j); 
}
  • run方法是【四个】参数
public class Test { 
	public static void main(String[] args) { 
		/**
		* Action a = new Action() { 
		* 	@Override 
		* 	public int run(MyHandler handler,int i, String str, List<Integer> list) { 
		* 		return handler.test(i,str,list); 
		* 	} 
		* }; 
		*/ 
		
		Action a = MyHandler::test; 
	} 
}
interface Action{ 
	int run(MyHandler handler, int i, String str, List<Integer> list); 
}
class MyHandler{ 
	public int test(int i, String str, List<Integer> list){ 
		return 0; 
	} 
}

3. 对象引用方法

使用对象来引用它的非静态方式,语法要求为:对象::非静态方法

任何一个对象的中的方法,只要是参数列表和返回类型和run方法保持一致, 都可以使用这个对象中的这个方法,来对Action接口进行实现

public class Test { 
	public static void main(String[] args) { 
		MyHandler handler = new MyHandler(); 
		
		/**
		* Action a1 = new Action() { 
		* 	@Override 
		* 	public String run(String str) { 
		* 		return str; 
		* 	} 
		* }; 
		*/ 
		Action a1 = str -> str; 
		
		//这里表示,使用handler对象的test方法,来对Action接口进行实现 
		//因为test方法的参数列表和返回类型,恰好是和run方法的参数列表和返回类型保持一致 
		Action a2 = handler::test; 
		System.out.println(a2.run("tom"));//输出:hello! tom 
	} 
}
interface Action{ 
	String run(String str); 
}
class MyHandler{ 
	public String test(String name){ 
		return "hello! "+name; 
	} 
}

4. 构造方法引用

引用类中的构造函数,语法要求为:类名::new

4.1 无参构造器

public class Test {
	public static void main(String[] args) { 
		/**
		* Action a1 = new Action() { 
		* 	@Override 
		* 	public Student run() { 
		* 		return new Student(); 
		* 	} 
		* }; 
		*/ 
		Action a1 = ()->new Student(); 
		//这里表示,使用Student类的无参构造函数,来对Action接口进行实现 
		//因为Student类的无参构造函数,恰好是和run方法的参数列表和返回类型保持一致 
		
		Action a2 = Student::new; 
		System.out.println(a2.run()); 
	} 
}
interface Action{ 
	Student run(); 
}
class Student{ }

4.2 有参构造器

public class Test { 
	public static void main(String[] args) { 
		/**
		* Action a1 = new Action() { 
		* 	@Override 
		* 	public Student run(String name) { 
		* 		return new Student(name); 
		* 	} 
		* }; 
		*/ 
		Action a1 = name->new Student(name); 
		
		//这里表示,使用Student类的有参构造函数,来对Action接口进行实现 
		//因为Student类的有参构造函数,恰好是和run方法的参数列表和返回类型保持一致 
		Action a2 = Student::new; 
		System.out.println(a2.run("tom")); 
	} 
}
interface Action{ 
	Student run(String name); 
}
class Student{ 
	private String name; 
	public Student(String name){
		this.name = name; 
	} 
}

4.3 数组构造

使用Lambda表达式可以引用数组类型的构造函数,语法要求为:数组类型::new

public class Test { 
	public static void main(String[] args) { 
		/**
		* Action a1 = new Action() { 
		* 	@Override 
		* 	public int[] run(int len) { 
		* 		return new int[len]; 
		* 	} 
		* }; 
		*/ 
		Action a1 = len -> new int[len]; 
		
		//这里表示,使用int数组的构造函数,来对Action接口进行实现 
		//因为int数组的构造函数,恰好是和run方法的参数列表和返回类型保持一致 
		Action a2 = int[]::new; 
		System.out.println(a2.run(5)); 
	} 
}
interface Action{ 
	int[] run(int len); 
}

四、Optional

java.util.Optional<T> 类,是用来防止NullPointerException异常的辅助类型, Optional 对象中封装的值,可以是null,也可以不是null

Optional 中常用的方法:

public class Test { 
	public static void main(String[] args) { 
		/**
		* of方法 为非null的值创建一个Optional对象 
		* 如果传入参数为null,则抛出NullPointerException 
		*/ 
		Optional<String> op1 = Optional.of("hello"); 
		
		/**
		* ofNullable方法 
		* ofNullable与of方法相似,唯一的区别是可以接受参数为null的情况 
		*/ 
		Optional<String> op2 = Optional.ofNullable(null); 
		
		/**
		* isPresent方法 如果值存在返回true,否则返回false 
		*get方法 如果Optional有值则将其返回,否则抛出NoSuchElementException 
		*/ 
		if(op1.isPresent()){ 
			System.out.println(op1.get()); 
		}
		if(op2.isPresent()){ 
			System.out.println(op2.get()); 
		}
		
		/**
		* ifPresent方法 如果Optional实例有值则为其调用consumer接口中的方法,否则不做处理 
		* Consumer: 
		* public void accept(T t); 
		*/ 
		op1.ifPresent(str->System.out.println(str));
		op2.ifPresent(str->System.out.println(str));
		//这个不执行 因为op2里面的值是 null 
		
		/**
		* orElse方法 如果有值则将其返回,否则返回指定的其它值 
		*/ 
		System.out.println(op1.orElse("如果op1中的值为null则返回这句话")); 
		System.out.println(op2.orElse("如果op2中的值为null则返回这句话")); 
		
		/**
		* orElseGet方法 orElseGet与orElse方法类似,区别在于得到的默认值的方式不同 
		* Supplier: 
		* public T get(); 
		*/ 
		System.out.println(op1.orElseGet(()->"自己定义的返回值")); 
		System.out.println(op2.orElseGet(()->"自己定义的返回值")); 
		
		/**
		* map方法 如果有值,则调用mapper的函数处理并得到返回值 
		* 返回值并且依然Optional包裹起来,其泛型和你返回值的类型一致 
		* public <U> Optional<U> map(Function<? super T, ? extends U> mapper) 
		*/ 
		Optional<Integer> map1 = op1.map(str->1); 
		System.out.println(map1.orElse(0)); 
		Optional<Double> map2 = op2.map(str->1.2); 
		System.out.println(map2.orElse(0D)); 
		
		/**
		* flatMap方法 如果有值,则调用mapper的函数返回Optional类型返回值,否则返回空 Optional 
		* flatMap与map方法类似,区别在于flatMap中的mapper返回值必须是Optional 
		* 调用结束时,flatMap不会对结果用Optional封装,需要我们自己把返回值封装为Optional 
		* public <U> Optional<U> flatMap(Function<? super T,Optional<U>> mapper); 
		*/ 
		Optional<String> flatMap = op1.flatMap(str->Optional.of(str+"_start")); 
		System.out.println(flatMap.get()); 
		
		/**
		* filter方法 如果有值并且满足断言条件返回包含该值的Optional,否则返回空Optional 
		* public Optional<T> filter(Predicate<? super T> predicate); 
		*/ 
		op1 = op1.filter(str->str.length()<10); 
		System.out.println(op1.orElse("值为null")); 
		op1 = op1.filter(str->str.length()>10); 
		System.out.println(op1.orElse("值为null")); 
	} 
}

五、Stream

1. 概述

  • java.util.stream.Stream接口,表示能应用在一组元素上,一次执行的操作序列,也就是可以对一组数据进行连续的多次操作。
  • Stream在使用的时候,需要指定一个数据源,比如 java.util.Collection 的子类,List或者Set都可以,但是Map类型的集合不支持。
  • Stream是对集合功能的增强,它提供了各种非常便利、高效的聚合操作,可以大批量数据操作,同时再结合Lambda表达式,就可以极大的提高编程效率。同时它提供串行和并行两种模式进行操作。
  • Stream的API提供了串行并行两种模式进行操作数据。
  • Stream操作分为中间操作或者最终操作两种:
    • 中间操作,返回Stream本身,这样就可以将多个操作依次串起来
      例如, map、flatMap、filter、distinct、sorted、peek、limit、skip、parallel、 sequential、unordered
    • 最终操作,返回一特定类型的计算结果
      例如, forEach、forEachOrdered、toArray、reduce、collect、min、max、count、 anyMatch、allMatch、noneMatch、findFirst、findAny、iterator
public class Test { 
	public static void main(String[] args) { 
		List<Integer> list = new ArrayList<>(); 
		Collections.addAll(list,1,2,3,4,5,6,7,8,9,10); 
		list = list.stream() //把集合list变为stream 
				   .filter(e->e%2==0) //过滤,保留偶数 
				   .sorted((e1,e2)->e2-e1) //排序,倒序 
				   .collect(Collectors.toList()); //结果收集到一个新的List集合 中并返回 	
		System.out.println(list); 
	} 
}
//运行结果: 
	[10, 8, 6, 4, 2]

2. 其他转Stream

public class Test { 
	public static void main(String[] args) { 
		Stream<String> stream = Stream.of("a", "b", "c"); 
	} 
}
  • 数组
public class Test { 
	public static void main(String[] args) { 
		String[] arr = {"a", "b", "c"}; 
		Stream<String> stream1 = Arrays.stream(arr); 
		Stream<String> stream2 = Stream.of(arr); 
	} 
}
  • 集合

只要是Collection类型的集合,都可以调用stream()方法,将集合转换为Stream对象

public class Test { 
	public static void main(String[] args) { 
		List<String> list = new ArrayList<>(); 
		Collections.addAll(list,"a", "b", "c"); 
		Stream<String> stream = list.stream(); 
	} 
}
  • 基本类型

对于基本数值类型,有专门的三种Stream类型:

  • IntStream
  • LongStream
  • DoubleStream
public class Test { 
	public static void main(String[] args) { 
		IntStream stream1 = IntStream.of(new int[]{1, 2, 3}); 
		
		//[1,3) 
		IntStream stream2 = IntStream.range(1, 3); 
		
		//[1,3] 
		IntStream stream3 = IntStream.rangeClosed(1, 3); 
	} 
}

3. Stream转其他

  • 数组
public class Test { 
	public static void main(String[] args) { 
		Stream<String> stream = Stream.of("hello","world","start"); 
		/**
		* <A> A[] toArray(IntFunction<A[]> generator); 
		* public interface IntFunction<R> { 
		* 	R apply(int value); 
		* } 
		*/ 
		String[] strArray = stream.toArray(String[]::new); } }
  • 集合
    一个Stream在代码中,只能使用一次,再次使用就会报错
public class Test { 
	public static void main(String[] args) { 
		Stream<String> stream = Stream.of("hello","world","start"); 
		List<String> list1 = stream.collect(Collectors.toList()); 
		//List<String> list2 = stream.collect(Collectors.toCollection(ArrayList::new)); 
		//Set<String> set3 = stream.collect(Collectors.toSet()); 
		//Set<String> set4 = stream.collect(Collectors.toCollection(HashSet::new)); 
	} 
}
  • 字符串
public class Test { 
	public static void main(String[] args) { 
		Stream<String> stream = Stream.of("hello","world"); 
		String result = stream.collect(Collectors.joining("-")); 
		System.out.println(result); 
	} 
}
//运行结果: 
		hello-world

4. Stream操作

4.1 最终操作

  • iterator: 返回迭代器对象
public class Test { 
	public static void main(String[] args) { 
		Stream<String> stream = Stream.of("hello","world","start"); 
		Iterator<String> it = stream.iterator(); 
		while(it.hasNext()){ 
			System.out.println(it.next()); 
		} 
	} 
}
  • forEach:将调Stream中的每个元素,交给一个Consumer函数处理
public class Test { 
	public static void main(String[] args) { 
		Stream<String> stream = Stream.of("hello","world","start"); 
		stream.forEach(System.out::println); 
	} 
}
  • count:统计流中的元素数,并返回结果
public class Test { 
	public static void main(String[] args) { 
		Stream<String> stream = Stream.of("hello","world","start"); 
		System.out.println(stream.count()); 
	} 
}
  • max:返回流中基于comparator所指定的比较规则,比较出的最大值
public class Test { 
	public static void main(String[] args) { 
		Stream<String> stream = Stream.of("hello","world","start"); 
		/**
		* Optional<String> max = stream.max(new Comparator<String>() { 
		* 	@Override 
		* 	public int compare(String o1, String o2) { 
		* 		return o1.compareTo(o2); 
		* 	} 
		* }); 
		*/ 
		Optional<String> max = stream.max(String::compareTo); 
		System.out.println(max.get()); 
	} 
}
  • min:返回流中基于comparator所指定的比较规则,比较出的最小值
public class Test { 
	public static void main(String[] args) { 
		Stream<String> stream = Stream.of("hello","world","start"); 
		/**
		* Optional<String> min = stream.min(new Comparator<String>() { 
		* 	@Override 
		* 	public int compare(String o1, String o2) { 
		* 		return o1.compareTo(o2); 
		* 	} 
		* }); 
		*/ 
		Optional<String> min = stream.min(String::compareTo); 
		System.out.println(min.get()); 
	} 
}
  • toArray: 使用调用流中的元素,生成数组返回。
public class Test { 
	public static void main(String[] args) { 
		Stream<String> stream = Stream.of("hello","world","start"); 
		/**
		* <A> A[] toArray(IntFunction<A[]> generator); 
		* public interface IntFunction<R> { 
		* 	R apply(int value); 
		* } 
		*/ 
		String[] strArray = stream.toArray(String[]::new); 
	} 
}
  • collect:将元素收集到一个可以修改的容器中,并返回该容器
public class Test { 
	public static void main(String[] args) { 
		Stream<String> stream = Stream.of("test","hello","world","java","tom","C","javascript"); 
		//把Stream中的元素,按照字符串长度进行分组,长度相同算是一组,并存放到同一个集合中 
		//map的key是字符串的长度,value是同一组的数据 
		Map<Integer, List<String>> map = stream.collect(Collectors.groupingBy(String::length)); 
		map.forEach((k,v)-> System.out.println(k+" : "+v)); 
	} 
}
//运行结果: 
	1 : [C] 
	3 : [tom] 
	4 : [test, java] 
	5 : [hello, world] 
	10 : [javascript]
  • Match:匹配操作,Stream中提供了多种匹配模式
    这些操作不能同时执行,因为一个Stream只能使用一次
public class Test { 
	public static void main(String[] args) { 
		Stream<String> stream = Stream.of("test","hello","world","java","tom","C","javascript"); 
		
		//所有元素匹配成功才返回true 否则返回false 
		boolean allMatch = stream.allMatch((s)->s.startsWith("j")); 
		System.out.println(allMatch); 
		
		//任意一个匹配成功就返回true 否则返回false 
		boolean anyMatch = stream.anyMatch((s)->s.startsWith("j")); 
		System.out.println(anyMatch); 
		
		//没有一个匹配的就返回true 否则返回false 
		boolean noneMatch = stream.noneMatch((s)->s.startsWith("j")); 
		System.out.println(noneMatch); 
	} 
}
  • findFirst:返回 Stream的第一个元素
public class Test { 
	public static void main(String[] args) { 
		Stream<String> stream = Stream.of("test","hello","world","java","tom","C","javascript"); 	
		Optional<String> first = stream.findFirst(); 
		System.out.println(first.get()); 
	} 
}

4.2 中间操作

  • filter:过滤方法,返回满足predicate指定的条件的所有元素的一个新流
public class Test { 
	public static void main(String[] args) { 
		Stream<String> stream = Stream.of("hello","world","start"); 
		stream.filter(e->e.contains("o")).forEach(System.out::println); 
	} 
}
//运行结果: 
	hello world
  • map:对调用流中的元素,应用Function所指定的操作,然后返回一个新流
    (1)map生成的是个1:1映射,每个输入元素,都按照规则转换成为另外一个元素
public class Test { 
	public static void main(String[] args) { 
		Stream<String> stream = Stream.of("hello","world","strat"); 
		List<Integer> list = stream.map(str -> str.length()) .collect(Collectors.toList()); 	
		list.forEach(System.out::println); 
	} 
}
//运行结果: 
		5
		5
		5

(2)map 方法可以和 reduce 方法配合使用, reduce 方法是将一组数据俩俩合并,最后得出一个结果

public class Test { 
	public static void main(String[] args) { 
		//1~10之间的数字累加 
		IntStream stream = IntStream.rangeClosed(1, 10); 
		//reduce方法需要提供一个起始值(种子) 
		//然后依照运算规则,和Stream中的第一个数据进行操作,得出结果 
		//再将这个结果和Stream中的第二个数据进行操作,再得出结果,依次类推,直到得出最 终结果 
		int result = stream.reduce(0, (a, b) -> a + b);
		System.out.println(result); 
	} 
}
//运行结果: 
		55
  • sorted: 排序
    (1)自然排序
public class Test { 
	public static void main(String[] args) { 
		Stream<String> stream = Stream.of("hello","world","start"); 
		//默认自然排序 
		stream.sorted().forEach(System.out::println); 
	} 
}//运行结果: 
		start hello world

(2)比较器排序

public class Test { 
	public static void main(String[] args) { 
		Stream<String> stream = Stream.of("hello","world","start"); 
		//比较器排序,注意Lambda表达式中返回的值前加了符号 
		stream.sorted((o1, o2) -> - o1.compareTo(o2)).forEach(System.out::println); 
	} 
}
//运行结果: 
		world
		hello 
		start
  • limit:返回 Stream 的前面 n 个元素
public class Test { 
	public static void main(String[] args) { 
		Stream<String> stream = Stream.of("test","javap","hello","world","java","tom","C","javascript"); 
		stream.limit(5).forEach(System.out::println); 
	} 
}
//输出结果: 
		test 
		javap 
		hello 
		world 
		java
  • skip:跳过前 n 个元素只要后面的元素
public class Test { 
	public static void main(String[] args) { 
		Stream<String> stream = Stream.of("test","javap","hello","world","java","tom","C","javascript"); 
		stream.skip(5).forEach(System.out::println); 
	} 
}//运行结果: 
		tom 
		C
		javascript
  • concat :拼接两个流
public class Test { 
	public static void main(String[] args) { 
		Stream<String> stream1 = Stream.of("hello","world"); 
		Stream<String> stream2 = Stream.of("tom","mary"); 
		Stream<String> result = Stream.concat(stream1, stream2); 
		result.forEach(System.out::println); 
	} 
}
//运行结果: 
		hello 
		world 
		tom 
		mary
  • distinct:去除重复数据
public class Test { 
	public static void main(String[] args) { 
		Stream<String> stream = Stream.of("test","test","hello","world","java","java","C","C"); 
		stream.distinct().forEach(System.out::println); 
	} 
}
//运行结果: 
		test 
		hello 
		world 
		java 
		C

4.3 静态方法

(1)Stream.generate ,通过Supplier接口,可以自己来控制数据的生成

public class Test { 
	public static void main(String[] args) {
		final Random random = new Random(); 
		//生成100个随机数,并输出 
		Stream.generate(()->random.nextInt(100)) 
								  .limit(100) 
								  .forEach(System.out::println); 
		//生成100个随机数,并存放到集合中 
		List<Integer> list = Stream.generate(()->random.nextInt(100)) 
								   .limit(100) 
								   .collect(Collectors.toList()); 
	} 
}

(2)Stream.iterate ,它跟 reduce 操作很像,需要接受一个起始值(种子),然后通过函数得出一个结果,再把结果当做参数传给函数,再得出第二个结果,依次类推,其实就是一个递归操作。

public class Test { 
	public static void main(String[] args) { 
		//生成一个等差数列,公差为3,从0开始获取前10个数字 
		Stream.iterate(0, n -> n + 3) 
			  .limit(10) 
			  .forEach(System.out::println); 
	} 
}
//运行结果: 
		0
		3
		6
		9
		12
		15
		18
		21
		24
		27

5. IO流和Stream

在IO流中,也有方法,可以将读取到的数据转换为Stream对象

从a.txt文件中,找出字符最长的一行,返回它的字符数

public class Test { 
	public static void main(String[] args) { 
		BufferedReader br = null; 
		try {
			br = new BufferedReader(new FileReader("src/com/briup/demo/a.txt")); 
			int maxLen = br.lines() //io流转为Stream 
						   .mapToInt(String::length) //每行字符串转换为它的字符长度 
						   .max() //获取最大长度的数字 
						   .getAsInt();//返回int类型的结果
			System.out.println(maxLen); 
		} catch (Exception e) { 
			e.printStackTrace(); 
		} finally { 
			if(br!=null){ 
				try {
					br.close(); 
				} catch (IOException e) { 
					e.printStackTrace(); 
				} 
			} 
		} 
	} 
}

6. 并行流

串行Stream上的操作是在一个线程中依次完成,而并行Stream则是在多个线程上同时执行。
在数据量较大的特定场景下,并行Stream比串行Stream的效率要高一些

创建并行Stream的俩种方式:

  • 调用串行Stream的 parallel() 方法,可以将其转换并行Stream
Stream<String> stream = Stream.of("hello","world","briup"); 
Stream<String> parallelStream = stream.parallel();
  • 调用集合对象的 parallelStream 方法,之后获取并行Stream
List<String> list = new ArrayList<>(); 
Collections.addAll(list,"hello","world","briup"); 
Stream<String> parallelStream = list.parallelStream();
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值