java8 新特性(接口更新+方法引用+函数式接口+Stream流)+java 9模块化

1.接口组成更新

接口的组成

  • 常量
    public static final
  • 抽象方法
    public abstract
  • 默认方法(java 8)
  • 静态方法(java 8)
  • 私有方法(java 9)

1.1 接口中的默认方法

方便实现接口的升级,而不破坏现有代码。
应用场景如已有接口和实现类,此时为新增实现类添加方法更新接口,用默认方法来定义。
原有的实现类不更新也不会报错,不用去大量的修改原有实现类,也不用写新的子接口去继承原有接口类。

  • 格式 :
    public default 返回值类型 方法名(参数列表){}
    如 public default void show3(){}
  • 注意事项
    • 默认方法不是抽象方法有方法体,所以不强制重写,但是可以被重写,重写时去掉default关键字
    • public可以省略,但是default不可以省略
public interface MyInterface {
	void show1();
	void show2();
	default show3();
}
//////////////////
public class MyInterfaceImplOne implements MyInterface{

	public void show1(){
		System.out.println("one show1");
	}
	public void show2(){
		System.out.println("one show2");
	}
}
/////////////////
public class MyInterfaceTwo implements MyInterface{

	public void show1(){
		System.out.println("two show1");
	}
	public void show2(){
		System.out.println("two show2");
	}
}
////////////////
public class MyInterfaceDemo {
	public static void main(String[] args){
		MyInterface my =new MyInterfaceImplOne();
		my.show1();
		my.show2();
	}	
}
//////////////
public class MyInterfaceThree implements MyInterface{

	public void show1(){
		System.out.println("3 show1");
	}
	public void show2(){
		System.out.println("3 show2");
	}
	public void show3(){
		System.out.println("3 show3");
	}
}

1.3 接口中静态方法

  • 格式:
    public static 返回值类型方法名(参数列表){}
    如 public static void show(){}
  • 注意事项
    • 静态方法只能通过接口名调用,不能通过实现类名或者对象名调用。
      (否则实现类继承多个接口,含有同名的静态方法,无法判断具体使用哪个。静态方法在接口中有方法体,不在实现类中实现)
    • public可以省略,static不能省略

1.4 接口中的私有方法

引入原因:java8埋下伏笔,两个默认方法或者静态方法中包含一段相同的代码,考虑提取成一个共性方法,仅在接口内的方法使用,所以时私有的

  • 格式:
    • private 返回值类型 方法名(参数列表){}
      如:private void show()
    • private static 返回值类型 方法名(参数列表){}
      如: private static void method{}
  • 注意事项
    • 默认方法可以调用私有的静态方法和非静态方法
    • 静态方法只能调用私有的静态方法

2. 方法引用

2.1概述

引用符:

  • ::为引用运算符,它所在的表达式称为方法引用

  • Lambda表达式:usePrintable(s->System.out.println(s);
    分析拿到参数s之后通过Lambda表达式,传递给System.out.println(s)

  • 方法引用: usePrintable(System.out::println);
    分析:直接使用System.out中的println方法取代Lambda,代码更加的简洁

  • 推导与省略

    • 如果使用Lambda,那么根据“可推到就是可省略”的原则,无需指定参数类型,也无需指定重载形式,它们都将被自动推导
    • 如果使用方法引用,同样可以根据上下文进行推导
    • 引用方法和lambda是孪生兄弟,要传递给Lambda体的操作,已经有了实现方法,就可以使用引用方法。
      实现接口的抽象方法的参数列表和返回值类型,必须与方法引用的方法的参数列表和返回值类型保持一致

2.2 Lambda表达式支持的方法引用

  • 常见的应用方式:
    • 引用类方法, 类名::静态方法
      例:Integer::parserInt
    • 引用对象的实例方法, 对象::成员方法
      例:“HelloWorld”::toUpperCase()将所有字符转换为大写
    • 引用类的实例方法, 类名::成员方法
      例String::substring
      注意**lambda表达式被类的实例方法替代的时候。引用处写法没差别,但是实际接口调用方法处,第一个参数作为调用者,后面的参数传递给方法体
    • 引用构造器,类名::new
      lambda表达式被构造器引用替代时,它的形参全部传递给构造器作为参数

例 引用“类方法"

public class ConvertDemo {

	public static void main(String[] args) {
		useConvert(Integer::parseInt);
		
	}
	private static void useConvert(Convert c){
		int number = c.convert("666");
		System.out.println(number);
	}

}
/////////
public interface Convert {
	public int convert(String s);
}

例 引用对象方法

public class PrinterDemo {

	public static void main(String[] args) {
		PrintString p = new PrintString();
		usePrinter(p::printUpper);
	}

	private static void usePrinter(Printer p){
		p.printUpperCase("HelloWorld");
	}
}
////////////
public interface Printer {
	public void printUpperCase(String s);
}
///////////
public class PrintString {
	public void printUpper(String s){
		String result = s.toUpperCase();
		System.out.println(result);
	}
}

例引用类的实例方法


public class MyStringDemo {

	public static void main(String[] args) {

	/*	useMyString((String s ,int x, int y)->{
			s.substring(2, 4)
		});*/
        
		useMyString((s,x,y)->s.substring(x,y));
		useMyString(String::substring);
                //lambda表达式被类的实例方法替代的时候
                //第一个参数作为调用者,后面的参数传递给方法体
		
	}
	private void useMyString(MyString my){
                //引用处写法没差别,但是实际接口调用方法处,第一个参数作为调用者,后面的参数传递给方法体
		String s = my.mySubString("hello world", 2, 4);
		System.out.println(s);
	}

}
////////
public interface MyString {
	String mySubString(String s,int x,int y);
}

例 引用构造器

public class StudnetDemo {

	public static void main(String[] args) {
		useStudentBuilder((name,age)->new Student(name,age));
		useStudentBuilder(Student::new);
                //lambda表达式被构造器引用替代时,它的形参全部传递给构造器作为参数
		
	}
	private void useStudentBuilder(StudentBuilder s){
		Student st = s.build("zhangmanyu", 39);
		System.out.println(st.getName()+","+st.getAge());
	}

}
//////////////
public interface StudentBuilder {
	Student build(String name, int age);
}

3 函数式接口

3.1 什么是函数式接口

函数式接口:有且只有一个抽象方法的接口
建议使用@FunctionalInterface注解标识函数式接口,方便阅读。且会自动检验接口是否符合函数式接口要求

3.2 函数式接口作为方法的参数

如果一个方法的参数是函数式接口,则可以用lambda表达式作为参数传递
例 需求:

  • 定义一个类(RunnableDemo),在类中提供两个方法
    一个方法startThread(Runnable r)方法参数Runnable是一个函数式接口
    一个方法是主方法,在该方法中调用startThread方法
public class RunnableDemo {

	public static void main(String[] args) {
		startThread(new Runnable(){

			public void run() {
				System.out.println(Thread.currentThread().getName()+"thread start");
			}
			
		});
		startThread(()->System.out.println(Thread.currentThread().getName()+"thread start"));
		
	}
	private static void startThread(Runnable r){
		new Thread(r).start();
	}
}

3.3 函数式接口作为方法的返回值

如果方法的返回值是一个函数式接口,则可以用lambda表达式作为结果返回
需求

  • 定义一个类(ComparatorDemo) 在类中提供两个方法
    一个方法是:ComparatorgetComparator(),返回值Comparator是一个函数式接口
    一个方法是主方法,在方法中调用Comparator方法
    例:
public class ComparatorDemo {

	public static void main(String[] args) {
		ArrayList<String> array = new ArrayList<String>();
		
		array.add("ccccc");
		array.add("dd");
		array.add("aaa");
		System.out.println("before sort "+ array);
		
		Collections.sort(array);
		System.out.println("after sort "+ array);
		
		Collections.sort(array, getComparator());
		System.out.println("after sort "+ array);
	}
        //
	static Comparator<String> getComparator(){
		return new Comparator<String>(){
			public  int compare(String s1, String s2) {
				return s1.length()-s2.length();
			}
		};
		//return ((s1,s2)->s1.length()-s2.length())
	}
}

注意 :Comparator是函数式接口,里面只有compareto方法式抽象方法。
其他方法都有方法体,equals方法是对Object里的方法的重新声明,为了加注释用的,也不是抽象方法

3.4 常用的函数式接口

java 8 在java.util.function包下提供了大量函数式接口
四个重点接口

  • Supplier接口
  • Consumer接口
  • Predicate接口
  • Function接口

3.4.1 Supplier接口

Supplier

  • T get()包含一个无参的get方法
  • 方法不需要参数,会按照某种实现逻辑(由Lambda表达式)返回一个数据
  • Supplier接口也被称为生产型接口,指定了接口的泛型,get方法就会生产相应类型的数据
    例: get方法的方法体对应return语句
public class SupplierDemo {

	public static void main(String[] args) {

		String s = getString(()->return "hello");
		System.out.println(s);
	}

	private static String getString(Supplier<String> sup){
		return sup.get();
	}
}

定义一个类SupplierTest,在类中提供两个方法
一个方法是: int getMax(Suppliersup)用于返回一个int数组中的最大值
一个方法是主方法,在主方法中调用getMax方法

public class SupplierTest {

	public static void main(String[] args) {
		int[] arr = {19,50,28,37,46};
		int maxvalue = getMax(()->{
                        //lambda表达式或者说匿名内部类,可以直接引用外面花括号内的变量
			int max = arr[0];
			
			for(int i =0; i<arr.length;i++){
				if(arr[i]>max){
					max = arr[i];
				}
			}
			return max;
		});
		System.out.println(maxvalue);
	}
	private static int getMax(Supplier<Integer> sp){
		return sp.get();
	}

}

3.4.2 Consumer接口

Consumer接口也被称为消费型接口,它消费的数据类型由泛型执行,接受单个输入参数并且不返回结果的操作。
Consumer包含两个方法

  • void accept(T t)对给定的参数执行此操作
  • default Consumer andThen(Consumer after):返回一个组合的Consumer,依次执行此操作,然后执行after操作
    如con1.andThen(con2).accept(name);
    例 accpet
package interfaces;

import java.util.function.Consumer;

public class ConsumerDemo {
    public static void main(String[] args) {
        operatorString("林青霞",name-> System.out.println(name));
        //operatorString("林青霞",System.out::println);
        operatorString("林青霞",name-> System.out.println(
                new StringBuilder(name).reverse().toString()
        ));

    }
    private static void operatorString(String name, Consumer<String> con){
        con.accept(name);
    }

}

例 andThen方法

package interfaces;

import java.util.function.Consumer;

public class ConsumerDemo {
    public static void main(String[] args) {
        operatorString("林青霞",name-> System.out.println(name));
        //operatorString("林青霞",System.out::println);
        operatorString("林青霞",name-> System.out.println(
                new StringBuilder(name).reverse().toString()
        ));

        operatorString2("林青霞",name-> System.out.println(name),
                name-> System.out.println(new StringBuilder(name).append("1111")));

    }
    //消费一个字符串数据
    private static void operatorString(String name, Consumer<String> con){
        con.accept(name);
    }
    //用不同的方式,消费同一个字符串数据两次
    private static void operatorString2(String name, Consumer<String> con1,Consumer<String>con2){
    /*    con1.accept(name);
        con2.accept(name);*/
        //返回组合的consumer,然后调用accept方法
        /*
         default Consumer<T> andThen(Consumer<? super T> after) {
        Objects.requireNonNull(after);
        //组合的consumer返回值使用lambda表达式,accept方法先调用con1的accept,再调用con2的accept
        return (T t) -> { accept(t); after.accept(t); };
    }
         */
        con1.andThen(con2).accept(name);
    }

}

  • String[]strArray =
  • 字符串数组中有多条信息,请按照格式:"姓名:XX,年龄:XX"的格式将信息打印出来
  • 要求:
    把打印姓名的动作作为第一个Consumer接口的Lambda实例
    把打印年龄的动作作为第二个Consumer接口的Lambda实例
    将两个Consumer接口按照顺序组合一起使用
package interfaces;

import java.util.function.Consumer;

/*
* String[]strArray = {"林青霞,30","张曼玉,35","王祖贤,33"}
* 字符串数组中有多条信息,请按照格式:"姓名:XX,年龄:XX"的格式将信息打印出来
* 要求:
  把打印姓名的动作作为第一个Consumer接口的Lambda实例
  把打印年龄的动作作为第二个Consumer接口的Lambda实例
  将两个Consumer接口按照顺序组合一起使用
 */
public class ConsumerDemo2 {
    public static void main(String[] args) {
        String[] strArray = {"林青霞,30", "张曼玉,35", "王祖贤,33"};
/*
错误示范,传给消费者的是每个字符串,不是数组,否则打印格式不对      
  operator(strArray,arr->{
            for(int i=0;i<arr.length;i++){
                System.out.println(arr[i].substring(0,arr[i].indexOf(",")));
        }},arr1->{
            for(int i=0;i<arr1.length;i++){
                System.out.println(arr1[i].substring(arr1[i].indexOf(",")+1));
            }
            }
        );
    }
    private static void operator(String[] arr, Consumer<String[]> con1, Consumer<String[]> con2){
        con1.andThen(con2).accept(arr);
    }*/

        printInfo(strArray,s -> System.out.print("姓名:"+s.split(",")[0])
        ,s1-> System.out.println(",年龄: "+s1.split(",")[1])
        );
    }
    private static void printInfo(String[] strArray,Consumer<String>con1,Consumer<String>con2){
        for(String s : strArray){
            con1.andThen(con2).accept(s);
        }
    }
}

3.4.3 Predicate接口

  • 谓词,该函数式接口是用来做判断参数是否满足指定条件,返回值为布尔值
  • Predicate常用的四个方法
    • boolean test(T t):对给定的参数进行判断(判断逻辑由lambda表达式实现),返回一个布尔值
    • default Predicate negate()返回一个逻辑的否定,对应逻辑非
      pre.negate().test(s)
    • default Predicate and(Predicate other)返回一个组合判断,对应短路与
    • default Predicate or (Predicate other)返回一个组合判断,短路或
      例 test和negate演示
public class PredicateDemo01 {
    public static void main(String[] args) {
        System.out.println(checkString("helloworld",s->s.startsWith("hello")));
    }
    //判断指定的字符串是否满足要求
    private static boolean checkString(String s, Predicate<String> pre){
       //return pre.test(s); //true
        return pre.negate().test(s);//false==!pre.test
    }
}

例 and和or演示

import java.util.function.Predicate;

public class PredicateDemo02 {
    public static void main(String[] args) {

        System.out.println(checkString1("hellow",
                s->s.length()>3,
                s1-> s1.length()<5));
    }
    //判断指定的字符串是否满足要求
    private static boolean checkString1(String s,Predicate<String>pre1,Predicate<String>pre2){
        //return pre1.test(s) && pre2.test(s);
        //return pre1.and(pre2).test(s);
        return pre1.or(pre2).test(s);
    }
    private static boolean checkString(String s, Predicate<String> pre){
       return pre.test(s); //true

    }
}


String[] strArray ={"林青霞,30","柳岩,34","张曼玉,35","王祖贤,33"}
字符串数组中有多条信息,请通过Predicate接口的拼装将符合要求的字符串筛选到集合ArrayList中,并遍历ArrayList集合
同时满足一下条件,姓名长度大于2,年龄大于33

public class PredicateTest {

	public static void main(String[] args) {
		String[] strArray ={"林青霞,30","柳岩,34","张曼玉,35","王祖贤,33"}
		myFilter(strArray,s->return s.split(",")[0].length()>2,
				s1->return Integer.parseInt(s1.split(",")[1]>33));

		
	}
	private static ArrayList myFilter(String[] strArray,Predicate<String>p1,
			Predicate<String>p2){
		ArrayList<String>array = new ArrayList<String>();
		for(String s:strArray){
			if(p1.and(p2).test(s)){
				array.add(s);
			}
		}
	}
}

3.4.4 Function接口

Function< T,R > T表示输入参数,R标识输出结果。用于对参数进行处理,转换后返回一个值,转换过程一般由Lambda表达式来实现

  • R apply(T t):将次函数应用于给定的参数
  • default Function andThen(Function after):返回一个组合函数,首先将该函数应用于输入,然后将after函数应用于结果
public class FunctionDemo {

    public static void main(String[] args) {
        convert("100", s -> Integer.parseInt(s));
        convert("100", Integer::parseInt);

        convert(100, i -> String.valueOf(i + 666));
        convert("100", Integer::parseInt, String::valueOf);


    }

    private static void convert(String s, Function<String, Integer> fun) {
        int i = fun.apply(s);
        System.out.println(i);
    }

    private static void convert(int i, Function<Integer, String> fun) {
        String s = fun.apply(i);
        System.out.println(s);
    }

    private static void convert(String s, Function<String, Integer> fun1,
                                Function<Integer, String> fun2) {
        String s1 = fun1.andThen(fun2).apply(s);
        System.out.println(s1);
    }
}

  • String s ="林青霞,30"
  • 按照一下要求进行操作:
    1.将字符串截取得到数字年龄部分
    2.将上一步的年龄字符串转换成为int类型的数据
    3.将上一步的int数据加70,得到一个int结果,在控制台输出
  • 请通过Function接口来实现函数拼接
public class FunctionTest {
	public static void main(String[] args) {
		String s="linqingxia,33";
		convert(s,str->str.split(",")[1],str1->Integer.parseInt(str1),i->i+70);
		convert(s,str->str.split(",")[1],Integer::parseInt,i->i+70);	
	}
	private static void convert(String s,Function<String,String>fun1,
			Function<String,Integer> fun2,Function<Integer,String>fun3){
		
		int i = fun1.andThen(fun2).andThen(fun3).apply(s);
		System.out.print(i);
	}
}

4 Stream流

since java 1.8, java.util.stream包

  • Stream流的使用
    • 生成流
      通过数据源(集合,数组等)生成流
      list.stream()
    • 中间操作
      一个流后面可以跟零个或者多个中间操作,其主要目的是打开流,做出某种程度的过滤/映射,然后返回一个新的流,交给下一个操作使用
      filter()
    • 终结操作
      一个流只能有一个终结操作,当这个操作执行后,流就被使用光了,无法再被操作。所以这必定是流的最后一个操作
      forEach()

4.1 体验Stream流

需求:按照下面的要求完成集合的创建和遍历

  • 创建一个集合,存储多个字符串元素
  • 把集合中所有以“张”开头的元素存储到一个新的集合
  • 把“张”开头的集合中的长度为3的元素存储到一个新的集合
  • 遍历上一步得到的集合
package io;

import java.util.ArrayList;

public class StreamDemo01 {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<String>();
        list.add("林青霞");
        list.add("张曼玉");
        list.add("王祖贤");
        list.add("张无忌");
        list.add("张敏");
        list.add("王羲之");

        ArrayList<String> zhangList = new ArrayList<String>();
        for(String s:list){
            if(s.startsWith("张")){
                zhangList.add(s);
            }
        }
        System.out.println(zhangList);

        ArrayList<String> threeList = new ArrayList<String>();
        for(String s: zhangList){
            if(s.length()==3){
                threeList.add(s);
            }
        }
        System.out.println(threeList);

        System.out.println("-------------");
        list.stream().filter(s -> s.startsWith("张")).filter(s->s.length()==3).forEach(System.out::println);
    }
}

使用Stream流的方式完成过滤操作

  • list.stream().filter(s -> s.startsWith("张")).filter(s->s.length()==3).forEach(System.out::println);
  • 直接阅读代码的字面意思即可完美展示无关逻辑方式的语义:生成流,过滤姓张的,过滤长度为3的,逐一打印(filter结合lambda表达式使用)
  • Stream流把真正的函数式编程风格引入到java中

4.2 Stream流的生成方式

  • Stream流的常见生成方式
    • Collection体系的集合可以使用默认方法stream()生成流
      default Stream stream()
    • Map体系的集合间接生成流,keySet,valueSet和entrySet可以分别生成集合
    • 数组可以通过Stream接口的静态方法of(T ... values)生成流
package io;

import java.util.*;
import java.util.stream.Stream;

public class StreamDemo02 {
    public static void main(String[] args) {
        List<String> list = new ArrayList<String>();
        Stream<String> listStream = list.stream();

        Set<String> set = new HashSet<String>();
        Stream<String> setStream = set.stream();

        Map<String, String> map = new HashMap<String,String>();
        Stream<String> keyStream = map.keySet().stream();
        Stream<String> valueStream = map.values().stream();
        Stream<Map.Entry<String, String>> entryStream = map.entrySet().stream();

        //数组可以通过Stream接口的静态方法of(T...values)生成流
        String[] strArr = {"hello","world","java"};
        Stream<String> strArrStream = Stream.of(strArr);
        Stream.of("hello","world","java");
        Stream<Integer> integerStream = Stream.of(10, 20, 30);
    }
}

4.3 Stream流的常见中间操作方法

  • Stream filter(Predicate predicate):用于对流中的数据进行过滤,返回的新的流由与成功匹配给定谓词的元组组成
    Predicate函数式接口中的方法 boolean test(T t)对给定的参数进行判断,返回一个boolen值
  • Steam limit(long maxSize)返回此流中的元素组成的流,截取当前指定参数个数的数据
  • Stream skip(long n)跳过指定参数个数的数据,返回由该流的剩余元素组成的流
  • static concat(Stream a, Stream b)合并a和b两个流为一个流
  • Stream distinct()返回由该流中的不同元素(根据Object.equals(Object))组成的流
  • Stream sorted()返回由此流的元素组成的流,根据自然顺序排序
  • Steam sorted(Comparator)返回由该流的元素组成的流,根据提供的Comparator进行排序
  • Stream map(Function mapper)返回由给定函数应用于此流的元素的结果组成的流
    Function接口中的方法 R apply(T t)
  • IntStream mapToInt(ToIntFunction mapper)返回一个IntStream其中包含将给定函数应用于此流的元素的结果
    IntStream 表示原始Int流,获得该流后可以调用其特有方法
    ToIntFunction接口的方法 int applyAsInt(T value)
    例 filter
	public static void main(String[] args) {

		ArrayList<String>list = new ArrayList<String>();
		
		list.add("linqingxia");
		list.add("zhangmanyu");
		list.add("wangzuxian");
		list.add("zhaomin");
		list.add("zhangsanfeng");
		list.add("zhangmin");
		
		list.stream().filter(s->s.startWith("zhang")).forEach(System.out::println);
		
		System.out.println("----------");
		
		list.stream().filter(s->s.length()==3).forEach(System.out::println);
		System.out.println("----------");
		
		list.stream().filter(s->s.startWith("zhang")).filter(s->s.length()==3).
                              foreach(System.out.println);
		
	}

例 limit和skip

public class StreamDemo02 {

	public static void main(String[] args) {
		ArrayList<String>list = new ArrayList<String>();
		
		list.add("linqingxia");
		list.add("zhangmanyu");
		list.add("wangzuxian");
		list.add("zhaomin");
		list.add("zhangsanfeng");
		list.add("zhangmin");
		//输出前三个
		list.stream().limit(3).forEach(System.out::println);
		//跳过前三个输出后面的
		list.stream().skip(3).forEach(System.out.println);
		//跳过前两个只输出其后的两个
		list.stream().skip(2).limit(2).forEach(System.out::println);
	}
}

例:concact和distinct

public class StreamDemo03 {

	public static void main(String[] args) {
		ArrayList<String>list = new ArrayList<String>();
		
		list.add("linqingxia");
		list.add("zhangmanyu");
		list.add("wangzuxian");
		list.add("zhaomin");
		list.add("zhangsanfeng");
		list.add("zhangmin");
		
		Stream<String> s1 = list.stream().limit(4);
		
		Stream<String> s2 = list.steam().skip(2);
		
		Stream<String> s3 = Stream.concact(s1,s2);
		s3.forEach(System.out::println);
		
		Stream.concact(s1,s3).distinct().forEach();
		s4.forEach(System.out::println);
	}
}


public class StreamDemo04 {

	public static void main(String[] args) {
		ArrayList<String>list = new ArrayList<String>();
		
		list.add("linqingxia");
		list.add("zhangmanyu");
		list.add("wangzuxian");
		list.add("zhaomin");
		list.add("zhangsanfeng");
		list.add("zhangmin");
		
		list.stream().sorted().forEach(System.out::println);
		
		list.stream().sorted((s1,s2)).forEach(System.out::println);
	}

}

例 sort(),sort(comparator)

package streams;

import java.util.ArrayList;

public class StreamDemo04 {

    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<String>();

        list.add("linqingxia");
        list.add("zhangmanyu");
        list.add("wangzuxian");
        list.add("zhaomin");
        list.add("zhangsanfeng");
        list.add("zhangmin");

        //按照自然顺序排序
        list.stream().sorted().forEach(System.out::println);
        System.out.println("——————————————————————————————");
        //按照字符串长度排序
        list.stream().sorted((s1,s2)->s1.length()-s2.length()).
                forEach(System.out::println);
        System.out.println("----------------------------");
        //先按字符串长度排序,长度相同时按照自然顺序排
        list.stream().sorted((s1,s2)->{
            int num = s1.length()-s2.length();
            int num2 = num==0?s1.compareTo(s2):num;
            return num2;
        }).forEach(System.out::println);
    }
}

例 map和mapToInt方法

package streams;

import java.util.ArrayList;
/*
<R> Stream<R> map(Function mapper)返回由给定函数应用于刺溜的元素的结果组成的流
        Function接口中的方法 R apply(T t)
IntStream mapToInt (ToIntFunction mapper)返回一个IntStream其中包含将给定函数应用于
刺溜的元素的结果
IntStream表示原始int流
ToIntFunction 接口中的方法 int applyAsInt(T value)
 */
public class StreamDemo05 {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<String>();
        list.add("10");
        list.add("20");
        list.add("30");
        list.add("40");
        list.add("50");

        //需求,将集合中的字符串数据转换为整数之后在控制台输出
        list.stream().map(s->Integer.parseInt(s)).
                forEach(System.out::println);
        list.stream().map(Integer::parseInt).forEach(System.out::println);
        System.out.println("-------------------");
        list.stream().mapToInt(Integer::parseInt).forEach(System.out::println);
        System.out.println("-------------");
        //mapToInt返回IntStream,可以调用该流特有方法sum
        int sum = list.stream().mapToInt(Integer::parseInt).sum();
        System.out.println(sum);
    }
}

4.4 Stream常见终结操作方法

  • void forEach(Consumer action): 对此流的每个元素执行操作
    Consumer接口中的方法 void accept(T t)对给定的参数执行此操作
  • long count()返回此流中的元素数
package streams;

import java.util.ArrayList;

public class StreamDemo06 {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<String>();
        list.add("林青霞");
        list.add("张曼玉");
        list.add("王祖贤");
        list.add("张敏");
        list.add("张无忌");
        list.add("张三丰");

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

        //统计集合有几个以张开头的元素,并把结果输出
        long count = list.stream().filter(s -> s.startsWith("张")).count();
        System.out.println(count);
    }
}

例 Stream综合练习
现有两个ArrayList集合,分别存储6名男演员名称和6名女演员名称,要求完成如下操作

  • 男演员只要名字为3个字的前三个人 filter,limit
  • 女演员只要姓林的并且不要第一个 filter,skip
  • 把过滤后的男演员和女演员姓名合并到一起 contact
  • 把上一步操作后的元素作为构造方法的参数创建演员对象,遍历数据
    演员类Actor已经提供,厘米那有一个成员变量,一个带参构造方法,一级成员变量对应的get/set方法
package streams;

import java.util.ArrayList;
import java.util.stream.Stream;

public class StreamDemo07 {
    public static void main(String[] args) {
        ArrayList<String> manList = new ArrayList<String>();
        manList.add("陈道明");
        manList.add("王宝强");
        manList.add("徐峥");
        manList.add("黄晓明");
        manList.add("王刚");
        manList.add("严宽");

        ArrayList<String> womanList = new ArrayList<String>();
        womanList.add("张曼玉");
        womanList.add("王祖贤");
        womanList.add("林青霞");
        womanList.add("林霜");
        womanList.add("林黛玉");
        womanList.add("张琪");
        womanList.add("白冰");
        Stream<String> limit = manList.stream().
                                filter(s -> s.length() == 3).limit(3);
        Stream<String> skip = womanList.stream().
                                filter(s -> s.startsWith("林")).skip(1);
        //Stream.concat(limit,skip).map(s->new Actor(s)).
        //        forEach(System.out::println);
        //注意所有流操作熟练后可以直接写为一行
        Stream.concat(limit,skip).map(Actor::new).
                forEach(p->System.out.println(p.getName()));

    }
}

4.5 Stream流的收集操作

Stream转化为集合的操作,对数据使用Stream流的方式操作完毕后,可以把流中的数据收集到集合中
Stream流的收集方法

  • R collect(Collector collector)
  • 但是这个收集方法的参数是一个Collector接口

工具类Collectors提供了具体的收集方式

  • public static Collector toList():把元素收集到List集合中
  • public static Collector toSet()把元素收集到Set集合中
  • public static Collector toMap(Function keyMapper,Function valueMapper)把元素收集到Map集合中
    第一个参数对应key的lambda表达式,第二个参数对应value的lambda表达式
package streams;

import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class CollectDemo {
    public static void main(String[] args) {
        //stream->list
        List<String> list = new ArrayList<String>();

        list.add("林青霞");
        list.add("张曼玉");
        list.add("王祖贤");
        list.add("柳岩");
        list.add("林志玲");

        //得到名字为3个字的流
        Stream<String> strStream = list.stream().filter(s -> s.length() == 3);
        //操作完毕,将流收入List集合中并遍历
        List<String> names = strStream.collect(Collectors.toList());
        for(String name:names){
            System.out.println(name);
        }

        //stream->set
        Set<Integer> set = new HashSet<Integer>();
        set.add(10);
        set.add(20);
        set.add(30);
        set.add(40);
        set.add(50);
        //得到年龄大于25的流
        Stream<Integer> integerStream = set.stream().filter(i -> i > 25);
        Set<Integer> ages = integerStream.collect(Collectors.toSet());
        for(int age:ages){
            System.out.println(age);
        }

       //stream -> map
        HashMap<String, Integer> persons = new HashMap<>();
        persons.put("林青霞",10);
        persons.put("张曼玉",20);
        persons.put("王祖贤",30);
        persons.put("柳岩",40);
        persons.put("王心凌",50);

        Stream<Map.Entry<String, Integer>> entryStream = persons.entrySet().stream().
                filter(keyentry -> keyentry.getKey().length() == 3).filter(str -> str.getValue() > 25);
        Map<String, Integer> collect = entryStream.collect(
                Collectors.toMap(s -> s.getKey(), s1 -> s1.getValue()));
        Set<String> keySet = collect.keySet();
        for(String key:keySet){
            System.out.println(key+" , "+collect.get(key));
        }

        System.out.println("------------");
        //从数组生成流 然后steam-> map
        String[] strArrays = {"林青霞,10","张曼玉,20","王祖贤,30","柳岩,40","王菲,50"};
        Stream<String> stringStream = Stream.of(strArrays).
                filter(str -> str.split(",")[0].length() == 3).
                filter(str1 -> Integer.parseInt(str1.split(",")[1])>28);
        Map<String, String> collect1 = stringStream.collect(Collectors.toMap(s -> s.split(",")[0], s -> s.split(",")[1]));
        Set<String> keySet1 = collect1.keySet();
        for(String key:keySet1){
            System.out.println(key+" , "+collect1.get(key));
        }
    }
}

5. 模块化(java 9)

5.1 什么是模块化

即使运行一个小程序,只需要java的部分核心功能,JVM也要加载整个JRE环境。JAVA 9推出模块化系统。
Java被拆分为N多个模块,并允许Java程序可以根据需要选择加载程序必须的java模块,轻量化的方式来运行

5.2 模块化基本使用

  • 创建模块(按照以前的讲解方式创建模块,创建包,创建类,定义方法)
    为了体现模块的使用,我们创建2个模块,一个是myOne,一个是myTwo
    File->Project Structure->Modules->new module->
  • 在模块的src目录下新建一个名为module-info.java的描述性文件,该文件专门定义模块化,访问权限,模块依赖等信息
    描述性文件中使用模块导出和模块依赖来进行配置并使用
    File->New->module-info.java
  • 模块中所有未导出的包都是模块私有的,他们是不能在模块之外被访问的
    在myOne这个模块下的描述性文件中配置模块导出
    模块导出格式: export 包名;
  • 一个模块要访问其他的模块,必须明确指定依赖哪些模块,未明确指定依赖的模块不能访问
    在myTwo这个模块下的描述行文件中配置模块依赖
    模块依赖格式:requires 模块名;
    注意:写模块名报错,需要按下Alt+Enter提示,然后选择模块依赖
  • 在myTwo这个模块类中可以似乎用依赖模块下的内容
/////myOne里的module-info.java/////
module myOne{
	exports it.io;
}
/////myTwo里的module-info.java/////
module myTwo{
	requires myOne;
}

5.3 模块服务的使用

  • 服务:从java6开始,提供的一种服务机制,允许服务提供者和服务使用者之间完成解耦
    简单的说,就是服务使用指面向接口编程,但不清楚服务使用者的实现类
  • Java9 简化了服务机制,允许将服务接口定义在一个模块中。
    针对该服务接口提供不同的服务实现类,这些服务实现类可以分布在不同的模块中,
    服务实现模块则使用provides语句为服务接口提供实现类
    并使用uses语句在另一个模块中声明将使用该服务接口
    服务使用者只需要面向接口编程即可
  • 模块服务的使用步骤
    • 在myOne模块下常见一个包com.itheima_03,在该包下提供一个接口,接口中定义一个抽象方法
      public interface myService{
      void service();
      }
    • 在com.itheima_03包下创建一个包impl,在该包下提供接口的连个实现类itheima和Czxy
    • 在myOne这个模块下的描述性文件中添加看如下配置
      模块导出:export com.itheima_03; 包名
      服务提供:provides MyService with itheima;类名,指定MyService的服务实现类是itheima
    • 在myTwo这个模块下的描述性文件添加如下配置
      声明会使用服务接口:use MyService;
    • 真正使用,在myTwo这个模块的类中使用MyService接口提供的服务
      ServiceLoader:一种加载服务实现的工具,继承iterable可用增强for遍历
module myOne{
	exports com.itheima_01;
	exports com.itheima_03;
	
	provides MyService with Itheima;
}
/////////////////
module myOne{
	exports com.itheima_01;
	exports com.itheima_03;
	
	provides MyService with Itheima;
}
////////myTwo模块下test类/////
import java.util.ServiceLoader;

public class Test02 {

	public static void main(String args[]){
		//load services
		ServiceLoader<MyService> myServices = ServiceLoader.load(MyService.class);
		
		//tranvers service
		for(MyService my: myServices){
			my.service();//使用服务
		}
	}
}
posted @ 2021-04-29 15:25  晒网达人  阅读(346)  评论(0)    收藏  举报