函数式接口,Stream流

本章内容

  • 文件上传优化
  • 函数式接口
  • 自定义函数接口
  • 函数式编程
  • 常用的函数式接口

文件上传优化

  1. 文件名称需要优化
    服务器端在保存文件的时候,名字如果固定,最终会导致服务器硬盘只会保留一个文件,对上传的文件名称需要进行优化。
//文件名称的优化
System.currentTimeMills() + new Random().nextInt(100000)+".jpg" //或者是其他
  1. 服务器端接收文件的优化
    服务器端接收一个文件之后就关闭了,之后的其他客户端无法继续上传文件,我们使用循环进行改进,可以不断的接收不同的客户端传输过来的文件。
//使用循环
while(true){
  Socket socket=serverSocket.accept();
    ...
    ...
  }
  1. 服务器端接收客户端文件的效率问题的优化
    服务器端在接收文件的时候,加入的某个客户端给你传输了一个大文件,此时就不能再接收其他用户文件,所以可以使用多线程技术优化接收效率
while(true){
   Scoket socket = serverSocket.accept();
    //使用多线程技术提高程序的效率
    //有一个客户端上传文件就开启一个线程完成文件的上传
    new Thread(new Runnable(){
        //重写run方法
        @Override
        public void run(){
       //使用网络字节输入流对象
         InputStream is = socket.getInputStream();
          //指定一个目录
            File file= new File("D:\\upload")
                if(!file.exits){
                    file.mkdirs();
                }
            // 防止同名的文件被覆盖
            String fileName = System.currentTimeMills() + new Random().nextInt(1000000)+".jpg";
            //构建一个本地的文件输出流对象
            new FileOutputStream(file + "\\"+ fileName);
            //完成文件的上传,最终把文件写入到服务器端的硬盘当中
        }
    }).start();
}

函数式接口

概念:
在Java中指的是,有且仅有一个抽象方法的接口就称为函数式接口。
函数式接口适用于函数式编程。在Java当中的函数式编程指的是Lambda表达式。所以函数式编程接口就是用来服务Lambda表达式的,只有保证接口中有且仅有一个抽象方法那么在Java中才能顺利的进行推导。
备注:“语法糖”是指适用语法更加便利,但是原理不变。

格式:

只有确保接口当中有且仅有一个抽象方法。

修饰符 interface InterfaceNam{
    //只能有一个抽象方法
    public abstract 返回值类型 方法名称(参数列表);
    //还可以定义其他的非抽象方法
}

示例:

public interface FuntionInterFaceName{
    
    public abstract void show01();
    public default void show02(){
        //..
    }
    //void show03(); 有且仅有一个抽象方法,才是函数式接口
    
}
@FunctionalInterface注解

与@Override注解类似,Java 8中专门为函数式接口引入的一个新注解FunctionalInterface 该注解主要是定义在接口上,一旦接口上使用该注解,编译器将会强制检查该接口是否是一个函数式接口,该接口中是不是有且仅有一个抽象方法

如果不是,编译报错。

@FunctionalInterface
//函数式接口的注解
public interface FunctionInterfaceOne {
    //定义一个抽象方法
    public abstract void method();
    //void show();
    default void show02(){
    }
}
自定义函数式接口的用途

一般用于方法的参数和返回值上

函数式编程

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

Lambda表达式的延迟加载

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

/*
使用Lambda优化刚才的日志输出案例
Lambda具有延迟加载效果
Lambda的使用前提是需要提供一个函数式接口

 */
public class Demo02Lambda {
    //定义一个显式日志的方法,方法的参数传递日志的等级和BuildLogMessage

    public static void showLog(int levle,BuildLogMessage log){

        if(levle<=3){
            System.out.println(log.buildMessage());
        }
    }
    public static void main(String[] args) {

        String message1="执行mysql";
        String message2="执行java.exe";
        String message3="执行tomcat.exe操作";
        // 调用showLog方法,参数是一个函数式接口————BuildLogMessage ,可以使用Lambda表达式
        /*  showLog(2, ()->{

         //返回一个拼接好的字符串
           return message1+message2+message3;
       });*/
       /* //简化Lambda表达式
        showLog(2, ()->message1+message2+message3);*/

        /*
        使用Lambda表达式作为参数传递
        只有满足条件,日志的等级<=3才会调用BuildLogMessage当中的方法buildMessage
        才会进行字符串的拼接
        如果条件不满足,日志的等级大于3
        BuildLogMessage接口当中的方法buildMessage也不会执行
        所以拼接字符串的动作也不会执行
         */
        showLog(6, ()->{
            System.out.println("不满足,不输出");
            return message1+message2+message3;
        });
    }
}

备注:实际上使用内部类也可以达到这样的效果,只是将代码操作延迟到另外一个对象当中,通过调用方法来完成,

后面代码的执行取决于前面条件的判断结果

使用Lambda作为方法的参数和返回值

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

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

代码:

public class Demo01Lambda{
    //定义一个方法,开启线程
    public static void startThread(Runnable r){
        new Thread(r).start();
    }
    
    public static void main(String []args){
    
        startThread(()->{
            System.out.println("开启一个新线程,线程任务被执行!");
        });
        //优化
         startThread(()->System.out.println("开启一个新线程,线程任务被执行!"));
}
    
}

如果一个方法的返回值是一个函数式接口,那么我们可以直接使用一个Lambda表达式。

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

public class Demo02{
    //定义一个方法,让方法的返回值是一个函数式接口
    public static Comprator<String> createComprator(){
        //此时返回值就是一个函数式接口
        return new Comprator(){
            @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","a","asadq","adsa","hieow"};
        Arrays.sort(strs,createComprator());
        System.out.println(Arrays.toString(strs));        
    }           
}
常用的函数式接口

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

Supplier接口

java.util.function.Supplier<T>接口,该接口中有且仅有以个无参方法: T get()用来获取一个泛型参数指定类型的对象数据,由于该接口是一个函数式接口,所以我们可以使用Lambda表达式来操作。

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

import java.util.function.Supplier;

//Supplier<T> 接口一般被称为生产型接口,
// 指定接口的泛型是什么类型,那么接口中的get方法就会生产什么类型的数据
public class Demo01Supplier {
  //定义一个方法,方法的参数传递一个Supplier<T> 接口,泛型指定位String,get方法就会返回一个String
    public static String getString(Supplier<String> sup){
        return sup.get();
    }
    //定义一个方法,方法的参数传递一个Supplier<T> 接口,泛型指定Integer
    public static int getNum(Supplier<Integer> sup){
        return sup.get();
    }
    public static void main(String[] args) {

        //调用getString方法,方法的参数传递一个Supplier<T> 接口,函数式接口,使用Lambda
        String s= getString(()->{
            return new String("你好Java"  );
        });
        System.out.println(s);

        //求一个int数组中的最值
        int [] arr={10,50,41,15};
        int num = getNum(() -> {
            //求出最大值
            int max = arr[0];
            for (int i : arr) {
                if (max < i) {
                    max = i;
                }
            }
            return max;
        });
        System.out.println(num);
    }
}
Consumer接口

java.util.funvtion.Consumer<T>接口刚好和Supplier接口相反,不是用来生产一个数据,而是用来消费一个数据

数据的类型有泛型来指定。

accept方法

意思就是消费一个指定泛型的数据

代码如下:

package com.zhiyou100.funvtiopnInterface.demo03;
import java.util.function.Consumer;
/*
accept()方法,消费一个指定类型的数据
Consumer是一个消费型接口,泛型指定什么类型数据,就使用accept消费什么类型数据
至于怎么消费,需要自己定义(统计,求和,输出...)
 */
public class Demo02Consumer {
    //定义一个方法,方法的参数传递一个Consumer<String>接口
    public static void consumer(String str,Consumer<String> con){
      //通过accept方法 消费字符串
        con.accept(str);
    }
    public static void main(String[] args) {
        //来调用消费者的consumer
        consumer("abcdefg", name ->{
          //把abcdefg转为大写
            String s = name.toUpperCase();
            String string = new StringBuffer(s).reverse().toString();//反转
            System.out.println(string);//GFEDCBA 
        });
    }
}

默认方法: andThen

如果一个方法的参数和返回值都是Consumer类型的,那么久可以实现这样的效果:消费数据的时候,首先做一个消费的操作,再做一次消费的操作。实现组合。 可以通过Consumer中 的默认方法andThen方法来实现

import java.util.Arrays;
import java.util.function.Consumer;

public class Demo03ConsumerAndThen {

    //定义一个方法,参数传递一个字符串和两个Consumer接口,Consumer的泛型指定String
    public static void consumer(String str, Consumer<String> con1,Consumer<String> con2){
       /* con1.accept(str);
        con2.accept(str);*/
        con1.andThen(con2).accept(str);
        //先执行con1消费数据,在执行con2消费数据

    }
    public static void main(String[] args) {
        //由于consumer方法的参数Consumer接口是一个函数式接口,使用Lambda表达式
        consumer("Java31-天下-第一", (name1)->{
            String sub = name1.substring(0, 6);
            System.out.println(sub); //Java31

        },(name2)->{
            String[] strs = name2.split("-");
            System.out.println(Arrays.toString(strs)); //[Java31, 天下, 第一]
        } );
    }
}

通过查看源码,andThen方法不允许传入null对象,否则就会抛出空指针异常

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

Stream

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

给定你一些集合的数据

public class Demo01Stream{
    public static void main(String []args){
        
        //先构建一个集合
        List<String> list=new ArrayList<String>();
        list.add("abc123");
        list.add("aaa12");
        list.add("bcd125");
        list.add("abcd120");
        list.add("bbb130");
        //需要字符串中包含数字1的元素,取出
        List<String> list2=new ArrayList<String>();
        for(String str:list){
            if(str.contains("1")){
                list2.add(str);
            }     
        }
         //需要元素中字符串长度不超过6个的元素
        List<String> list3=new ArrayList<Stirng>();
        for(String str: list2){
            if(str.length<=6){
                list3.add(str);
            }
        }     
        //遍历查看想要的元素集合
        for(String str: list3){
            System.out.println(str);
        }
    }
} 

当我们需要对集合当中的元素进行操作的时候,总是需要对集合不停的遍历…一定需要这样做吗?不一定

他只是你用来找到你需要的元素的一种方式,并不是目的。目的就是想要取出想要的元素并打印输出,以往的方式都是每次循环都要从头开始遍历,下一次还是需要从头开始

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

代码如下

public class Demo01Stream{
    public static void main(String []args){
        
        //先构建一个集合
        List<String> list=new ArrayList<String>();
        list.add("abc123");
        list.add("aaa12");
        list.add("bcd125");
        list.add("abcd120");
        list.add("bbb130");
        //需要字符串中包含数字1的元素,取出
        //需要元素中字符串长度不超过6个的元素
        //遍历查看想要的元素集合
        list.stream()
            .filter( str -> str.contains("1"))
            .filter( str -> str.length()<=6)
            .forEach(System.out.println(str)) ;
    }
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值