day32_Stream&函数式接口

  • 函数式接口
  • 自定义函数式接口
  • 函数式编程
  • 常用的函数接口
  • Stream流
  • 方法引用

文件上传优化

文件的名称需要优化

服务端,保存文件的名称如果固定,那么最终会导致服务器硬盘只保留一个文件,对上传的文件名称需要进行优化

//文件名称定义规则
"beautiful" + System.currentTimeMillis() + new Random().nextInt(1000000) + ".jpg"

如何保证服务器端一直处于接收文件状态

服务器端接收一个文件之后仍不关闭,使用循环进行实现

//使用循环
while(true){
	Socket socket = serverSocket.accept();
   	...
}

服务器端接收客户端文件的效率问题

服务器端在接收文件时,假如某个客户端传入一个大文件,此时就不能再接收其它客户端的文件,可以使用多线程技术优化提升效率

while(true){
	Socket socket = serverSocket.accept();
    //使用多线程技术,提高效率
    //一个客户端上传文件、开启一个线程完成此任务
    new Thread(new Runnable(){
        //重写run方法
        @Override
        public void run(){
            InputStream is = socket.getInputStream();//使用网络字节输入流对象
            //指定一个目录
            File file = new File("D:\upLoad");
            if(!file.exist){
                file.mkdirs();
            }
            //防止同名文件被覆盖
            String fileName = "beautiful" + System.currentTimeMillis() + new Random().nextInt(1000000) + ".jpg";
            //构建一个本地的文件字节输出流对象
            FileOutputStram fos = new FileOutputStram(file+"\" + fileName);
            //完成读写
            int len = 0;
            char []chars =new char[1024]; 
            while((len = is.read(chars))!=-1){
                fos.write(chars,0,len);
            }
        }
    }).start;
}

//serverSocket.close()   服务器不需要关闭

函数式接口

​ 在Java当中指的是:有且仅有一个抽象方法的接口称为函数式接口。(其他方法不做要求)

​ 函数式接口,适用于函数式编程,在Java当中的函数式编程体现在Lambda表达式。只有确保接口当中有且仅有一个抽象方法,那么在Java中的Lambda才能进行顺利的推导(上下文环境)

备注:"语法糖" 是指使用更加便利、快捷的语法方式(原理不变)例如,Lambda是匿名内部类的语法糖
函数式编程四大核心接口:
Predicate
Function
Supplier
Consumer

格式:只有确保接口当中有且仅有一个抽象方法即可
修饰符  interface  接口名{
	//只能定义一个抽象方法
    返回值  方法名(参数列表);
}
public interface FuntionInterface{
    int sum(int a,int b);
}

@FunctionalInterface注解

​ 作用:检测接口是不是函数式接口,只有有且仅有一个抽象方法与该注解相容,Java8中专门为函数式接口引入的新注解。

自定义函数式接口的用途

​ 对于自定义函数式接口,一般充当方法的返回值和参数。

函数式编程

​ 能够在兼顾Java的面向对象特性基础上,通过Lambda表达式与后面的方法引用,为开发者打开函数式编程的大门

Lambda的延迟加载

​ 有些场景的代码运行执行后,结果不一定会被使用到,从而造成性能的浪费,而Lambda表达式是延迟执行的,正好可以解决此问题,提升性能。

showLog(4,()->{  //前面条件不满足,后面表达式就不加载了。 
	//重写run方法
});
//使用lambda表达式作为参数传递,只有满足条件,后面的接口方法实现才会被加载

备注:实际上使用匿名内部类也可以达到同样的效果,只是将代码操作延迟到另外一个对象当中通过调用来完成、方法来完成后面的代码执行取决于前面的条件的判断结果

​ 在Java中,Lambda表达式是作为匿名内部类的替代品,如果一个方法的参数是一个函数式接口类型,那么可以使用Lambda表达式替代

java.lang.Runnable接口就是一个函数式接口

java.util.Comparator接口也是一个函数式接口

public class LambdaDemo{
    //定义一个方法,返回值类型是Comparator接口
    public static Comparator<String> creatComparator(){
     		//常规
       	 	return new Comparator(){
               @Override
               public int compare(String o1,String o2){
                   return o1.length()-o2.length();
               }
           	}
        	//Lambda
        	return (o1,o2) -> o1.length()-o2.length();
    }
    public static void main(String []args){
        String []strs = {"aaa","bbb","ccccc"};
        Arrays.sort(strs,creatComparator);// 调用获得的比较器
    }
}

常用的函数式接口

​ JDK提供了大量的常用函数式接口,丰富Lambda表达式的使用场景。他们主要在java.util.function包中被提供。

​ Supplier接口,该接口中有且仅有一个无参的方法 T get(),它是用来获取一个泛型参数指定类型的对象数据。由于该接口是一个函数式接口,可以使用Lambda表达式对其操纵。

​ Supplier接口被称之为生产型接口,指定接口的泛型是什么类型,那么接口中的get()方法就会生产什么类型的数据

public static void main(String[] args) {
        //调用getString方法,参数传递一个Supplier<T>是一个函数式接口,可以使用Lambda
        System.out.println(getString(() ->{
            //生产返回一个字符串
            return  "Nike good boy!";
        }));
}
//定义一个方法,方法的参数传递一个Supplier<T>接口类型、泛型指定String,get方法就会返回一个String
    public static String getString(Supplier<String> sup){
        return sup.get();
    }

​ Consumer接口,被称为消费型接口,该接口刚好和Supplier接口相反,它不是用来生产一个数据,而是消费一个数据。至于数据的类型由泛型来指定。accept方法:消费一个指定泛型的数据

//定义一个方法、方法的参数传递一个Consumer<String>接口,传递一个字符串值
    public static void consumer(String s,Consumer<String> con){
        con.accept(s); //使用accept消费数据
    }

    public static void main(String[] args) {
        //调用消费方法
        consumer("abcdefg",name->{
            //把里面的字符串改为大写
            System.out.println(new StringBuilder(name.toUpperCase()).reverse());//GFEDCBA
        });
    }

默认方法:andThen

如果一个方法的参数和返回值全是Consumer类型的,那么就可以实现这样的效果:消费数据的时候

,首先做一个消费的操作,再做一个消费的操作,实现组合。可以通过Consumer接口当中的默认方法andThen来实现

//定义一个方法,方法的参数传递一个字符串和两个Consumer接口
    public static void consumer(String str, Consumer<String> con1,Consumer<String> con2){
        //con1.accept(str);
        //con2.accept(str);
        //andThen连续消费操作
        con1.andThen(con2).accept(str);
        //规则:con1连接con2,先执行con1消费数据,再执行con2消费数据
    }
    public static void main(String[] args) {
        //由于consumer方法的参数Consumer是一个函数式接口,可使用lambda
        consumer("Java31-中国最棒-都是业界大佬",(name1)->{
            //截取传入的字符串
            String sub = name1.substring(0, 6);
            System.out.println(sub);
        },(name2)->{
            String[] strs = name2.split("-");
            System.out.println(Arrays.toString(strs));
        });
    }

andThen方法不允许传入null对象,否则抛出空指针异常。

要想把两次消费的动作连接起来,需要传入两个Consumer接口,通过andThen方法实现消费动作

练习:定义一个字符串数组,存储每一个人的信息,如 "张三,20,北京",存储多人上述信息,使用Consumer接口,按照指定格式进行打印输出:姓名:Xxx ;年龄:xx;地址:xx。要求

  1. ​ 将打印姓名的动作作为第一个Consumer接口的规则

  2. ​ 将打印年龄的动作作为第二个Consumer接口的规则

  3. ​ 将打印地址的动作作为第三个Consumer接口的规则

    public static void print(String[] arr, Consumer<String> consumer1, Consumer<String> consumer2, Consumer<String> consumer3) {
            for (String s1 : arr) {
                consumer1.andThen(consumer2).andThen(consumer3).accept(s1);
                //consumer1.accept(s1);
                //consumer2.accept(s1);
                //consumer3.accept(s1);//多个accept与使用andThen效果相同
            }
        }
        public static void main(String args[]) {   //精简Lambda
            String[] arr = {"张三,20,北京", "Lily,21,郑州", "Bob,23,纳尼", "二毛,30,上海", "狗剩,19,东京"};
            print(arr, (name) -> System.out.print("姓名:" + name.split(",")[0] + ";	")
            , (age) -> System.out.print("年龄:" + age.split(",")[1] + ";	")
            , (address) -> System.out.println("地址:" + address.split(",")[2] + ";"));
        }
    

Stream流

​ 在Java1.8中,由于Lambda表达式这种函数式编程,JDK引入了一个全新的概念Stream流。用于解决已有集合类库的一些弊端。

给定一些集合的数据
public class StreamDemo{
    public static void main(String agrs[]){
        //构建一个集合
        List<String> list = new ArrayList();
        list.add("abc123");
        list.add("aaa22");
        list.add("bcd125");
        list.add("abcd120");
        list.add("bbb230");
        // 将字符串中包含1的元素取出
        List<String> list2 = new ArrayList();
        for(String str : list){
            if(str.contains("1")){
                list2.add(str);
            }
        }
        // 并将元素字符串不超过6个的元素取出来
        List<String> list3 = new ArrayList();
        for(String str : list2){
            if(str.length()<=6){
                list3.add(str);
            }
        }
        //遍历查看集合元素
        for(String str : list){
            System.out.println(str);
        }
    } 
}

​ 当我们需要对集合中的元素进行操作的时候,总是需要对集合不停的遍历,遍历再遍历。烦不烦呐?很烦 !then:

Java1.8可以使用Lambda表达式的衍生优化遍历集合的方式。---Stream流

public class StreamDemo{
    public static void main(String agrs[]){
        //构建一个集合
        List<String> list = new ArrayList();
        list.add("abc123");
        list.add("aaa22");
        list.add("bcd125");
        list.add("abcd120");
        list.add("bbb230");
        // 将字符串中包含1的元素取出
        // 将元素字符串不超过6个的元素取出来
        //遍历查看集合元素
        list.stream()  //stream来自Collection接口中
            .filter(str -> str.contains("1"))
            .filter(str -> str.length()<=6)
            .forEach(str->System.out.println(str));
    } 
}

一般我们把流式思想称之为 "生产流水线"

原文地址:https://www.cnblogs.com/mitoris/p/14160687.html