Java进阶-网络编程、函数式接口、Stream流、方法引用

网络编程

软件结构

CS结构

BS结构

网络通信协议



                                   

协议分类

UDP:无连接通信协议,耗资小,通信效率高,不能保证数据完整,64k以下,数据报


    


     三次握手:
        

              

                 

IP地址:互联网协议地址(Internet Protocol Address)给互联网中的计算机唯一编号

IPv4:是一个32位的二进制数

IPv6

端口号:0~65535  (前1024不可用)



 

TCP通信程序 --- 客户端-Socket类    服务器端-ServerSocket类

①客户端给服务器发送数据
②服务器端读取客户端发送的数据
③服务器端给客户发送数据
④客户端读取服务器端发送的数据

1、服务器使用客户端的IO流读取或发送数据
2、服务器通过accept方法获取请求的客户端对象


服务器客户端之间读写数据,使用网络字节输入出流

通过:Socket.getOutputStream
           Socket.getInputStream         
获取此套接字的字节输入出流

 

Socket类 --- 客户端 -- 主要是指定服务器IP及端口,使用自己的读写方法


Socket创建对象,参数String host 为服务器IP,int port为服务器端口号


实现步骤:

1、创建Socket对象指定服务器IP和端口2、获取网络流中的输出流并write给服务器发数据 
注意事项:


 

ServerSocket --- 服务器端 --主要是指定端口,获取Socket,通过其的方法读写



成员方法 accept
        accept()方法获取请求中的Socket
实现步骤:

1、创建ServerSocket对象指定端口2、使用accept获取请求的Socket3、使用Socket中的获取网络输入流对象
4、使用输入流的read方法读取数据5、获取网络流输出流对象,并使用write给客户端写回数据


 

TCP综合案例---文件上传


注意事项:


服务器端:
        数据源:客户端
        目的地:服务器硬盘d:\\upload\\1.jpg
客户端:
        数据源:c:\\1.jpg
        目的地:服务器

客户端

服务器

阻塞问题

客户端本地字节输入刘读取文件时,循环没有读取到-1结束标记,因此,网络字节输出流write,就没有写入-1,
传给服务器的数据,循环读取始终都没有-1,就会死循环,后面的回传也没有执行,
没有服务器回传的数据,客户端的读取也会死循环

解决办法:
           在客户端本地读取和网络字节数出后,加上socket.shutdownOutput()

解决办法:

文件上传优化

1、文件名自定义规则:
          自定义规则:域名+毫秒值+随机数

2、服务器优化:
          死循环---一直处于监听状态

          多线程---多个客户端同时上传---提高效率
                    每次循环就创建一个多线程,重写run方法,将服务器执行代码放入run中
                    由于接口的run方法没有抛出异常重写的run方法也不能抛出异常,只能用try---catch
  
         

 

模拟BS服务器--------(浏览器访问服务器)

将Web文件包加入到工程中,创建好服务器,即可通过浏览器访问html文件


思路:
         1、开启服务器
         2、通过浏览器访问服务器端口号,访问指定路径的html文件
         3、服务器捕获有该请求的Socket,读取请求数据
         4、读取第一行,并截取html文件地址
         5、得到该请求路径,服务器从本地读取该html文件
         6、服务器回写:  1、HTML协议响应头--固定格式   2、一读一写复制文件









优化:死循环-一直监听 + 多线程(run中try...catch)


函数式接口

 
 
     


 





    

函数式编程

Lambda延迟执行 

性能浪费的日志案例



调用该方法传入的第二个参数,是一个拼接后的字符串,如果level不满足=1,那么字符串就白拼接了,存在浪费
改进:

         将第二个参数,改为函数式接口代替,所以可以使用Lambda表达式 
         调用方法时,只有满足条件Level==1,才会调用接口中的方法区return字符串拼接

         由于Lambda表达式的延迟执行,先判断,再执行接口函数,所以不会存在性能浪费 

使用Lambda作为参数和返回值



①Lambda作为方法参数


②Lambda作为返回值类型


在这之前可以使用接口的匿名内部类

 

常用的函数式接口

Supplier接口---生产型---指什么类型输出什么类型

无参抽象方法get( )---重写后,对Supplier泛型执行的类型,进行生产(自定义操作--返回指定类型数据)



测试---使用Supplier作为参数,通过Lambda表达式求出数组中最大值

public class Demo02Test {
    //定义一个方法,用于获取int类型数组中元素的最大值,方法参数传递Supplier接口,泛型使用Interger
    public static int getMax(Supplier<Integer> sup){
        return sup.get();
    };
    public static void main(String[] args) {
        //定义一个int类型数组,并赋值
        int[] arr = {100,0,50,70,33,45};
        //调用getMax方法,参数Supplier是函数式接口,可以用Lambda表达式
        int n = getMax(() -> {
            //获取数组最大值,并返回
            //定义一个变量,存放数组第一个元素,记录数组元素的最大值
            int max = arr[0];
            //遍历数组,获取数组其他元素
            for (int i : arr) {
                //将其他元素和最大值比较
                if (i > max) {
                    //如果大于max,就替换max作为最大值
                    max = i;
                }
            }
            //返回最大值
            return max;
        });
        System.out.println(n);
    }
}

 

Consumer接口---消费型---指什么类型消费什么类型

抽象方法accept( )---重写后,对Consumer泛型执行的类型,进行消费(自定义操作)


    /*
        定义一个方法
        参数一传递一个字符串的姓名
        参数二传递Consumer接口,泛型使用String
        可以使用Consumer接口的accept方法消费字符串的姓名
     */
    public static void method(String name, Consumer<String> con){
        con.accept(name);
    }
    public static void main(String[] args) {
        //调用method方法,传递字符串姓名,另一个参数是Consumer函数式接口,可以用Lambda表达式
        method("赵丽颖",(String name)->{
            //消费方式一:输出
            System.out.println(name);
            //消费方式二:反转输出 --- 字符缓冲区中的方法reverse(),将字符反转
            String rename = new StringBuffer(name).reverse().toString();
            System.out.println(rename);
        });
    }

method方定义时,形参为字符串name和Consumer接口con,方法中调用了accept对字符串进行消费---accept参数为形参字符串name.
当调用method方法时,实参为字符串“赵丽颖”和Lambda表达式,方法体自动调用accept,
()中自定义一个变量代替被消费的字符串“赵丽颖”,Lambda表达式中()中的参数类型可以省略

默认方法:andThen---连接两个Consumer接口,再对数据进行消费




Objects.requireNonNull(after)

    //定义一个方法,参数传递一个字符串和两个Consumer接口,Consumer接口的泛型使用字符串
    public static void method(String s, Consumer<String> con1,Consumer<String> con2){
        //两个形参都使用了对应的accept方法,来消费形参字符串
        /*con1.accept(s);
        con2.accept(s);*/
        //使用andthen方法,把两个Consumer接口连接到一起,再消费数据
        con1.andThen(con2).accept(s);//con1连接con2,先执行con1消费,后执行con2消费
    };
    public static void main(String[] args) {
        //调用method方法,第一实参字符串,第二第三实参使用Lambda表达式,在参数时就重写了accept
        method("Hello",
                //参数二:Lambda表达式()中参数类型可省略
                (t)->{//其中accept()的参数就是字符串“Hello”
                    //消费方式:字符变大写
                    System.out.println(t.toUpperCase());
                },
                //参数三:
                (t)->{
                    //消费方式:字符变小写
                    System.out.println(t.toLowerCase());
                });
    }

con1.andThen(con2).accept(s);//先con1消费,再con2消费。
调用method方法时,还是需要两个参数使用Lambda表达式重写各自的accept消费方式

练习题---格式化打印信息


//定义一个方法,参数传递String类型的数组和两个Consumer接口,泛型使用String
    public static void printInfo(String[] arr, Consumer<String> con1, Consumer<String> con2) {
        //遍历字符数组
        for (String message : arr) {
            con1.andThen(con2).accept(message);
        }
    }
    public static void main(String[] args) {
        String[] arr = {"古力娜扎,女", "迪丽热巴,女", "马尔扎哈,男"};
        printInfo(arr,
                //参数二:Lambda表达式延迟执行,所有执行遍历得到message后,才分割
                (message) -> {
            String name = message.split(",")[0];//split分割时,一定在找对字符,否则分割失败
            System.out.print("姓名:" + name);
            },
                (message) -> {
            String age = message.split(",")[1];
            System.out.println("  性别:" + age + "。");
        });
    }

 

Perdicate接口   ---test()、and()、or()、negate()





默认方法 and  --- 连接两个Predicate,必须同时满足才返回true




 

默认方法 or  --- 连接两个Predicate,必须同时不满足,才返回fasle




 

默认方法 negate  --- 取反



练习题---集合信息筛选




/*
        定义一个方法
        参数传递一个包含人与那信息的数组
        传递两个Predicate接口,用于对数组信息进行过滤
        把满足条件的信息存到ArrayList集合中并返回
     */
    public static ArrayList<String> checkString(String[] array, Predicate<String> pre1, Predicate<String> pre2){
        //创建一个ArrayList集合,存储过滤后的信息
        ArrayList<String> list = new ArrayList<>();
        //遍历数组
        for (String person : array) {
            //and拼接两个Predicate,必须同时满足,才返回true
            boolean b = pre1.and(pre2).test(person);
            //如果返回true,将信息添加到ArrayList集合中
            if(b){
                list.add(person);
            }
        }
        //返回集合
        return list;
    }
    public static void main(String[] args) {
        String[] array = {"迪丽热巴,女","古力娜扎,女","马尔扎哈,男","赵丽颖,女"};
        //调用checkString方法,传递数组和Lambda表达式,使用Predicate两次对遍历的人员判断
        //接收返回的存储数据集合
        ArrayList<String> list = checkString(array, (str) -> {
            return str.split(",")[1].equals("女");
        }, (str) -> {
            return str.split(",")[0].length() == 4;
        });
        //遍历集合
        for (String s : list) {
            System.out.println(s);
        }
    }

 

Function接口

抽象方法:apply







定义方法,传入字符串和Function接口,方法体中调用apply方法返回转换结果
调用方法,传入字符串和Lambda表达式,重写apply方法---使用Integer类的parseInt(str),将String类型转换为Interger,并返回


默认方法:andThen

连接两个Function接口


先将String类型的“123”,转换为Integer类型,把转换后的结果加上10 增加后的Integer类型,转换为String

 

Stream流


IO流主要用来读写,Stream流可以对集合数组进行简化操作







 


 

流式思想 



   
两个基本特征:Pipelining中间操作(返回一个流对象)、内部迭代

   

流可以直接调用内部迭代方法:forEach等遍历方法

流的基本使用步骤:

1、获取数据源
2、数据转换
3、执行操作获取想要的结果

获取流   -----1、list.stream()   2、Stream.of(T...value)


常用的获取流的方法


jdk1.8之后,Collection接口中新增的 Stream() 默认方法---获取流

java.util.Stream包中的Stream<T>接口中的静态方法 of() -可以获取数组对应的流

 

根据Collection

根据Stream接口静态方法of()

 

常用方法   --1、 延迟方法(链式调用)filter、map
                 --2、终结方法  forEach、count



 

逐一处理:forEach  (与for循环中的forEach不同)

    
该方法会接收一个Consumer接口函数,会将每一个流元素交给该函数进行处理。

复习Consumer接口

 

过滤:filter




           

复习Predicate接口


Stream流的特点:
只能使用一次

 

映射:map


      

           

复习Function接口



 

统计个数:count




 

取用前几个:limit




          

参数是一个long型,集合长度大于参数,才进行截取,否则不进行操作
limit方法是一个延时方法,只对流中元素进行截取,返回一个新的流,可以继续调用Stream流中的方法

 

跳过前几个:skip




           

 

组合:concat




 

练习题---集合元素处理(传统方法)

 

方法引用


函数式接口做参数

       


 

通过对象名引用成员方法-------   



1、函数式接口--抽象方法


2、含有大写方法的类--大写方法


3、成员方法--参数-函数式接口--调用该接口抽象方法--传入字符串


4、调用成员方法


5、优化--使用方法引用--对象名引用成员方法(对象已经存在、成员方法也已经存在)

 

通过类名引用静态方法 ----  


1、函数式接口--抽象方法


2、成员方法(返回值int)--参数-number、函数式接口--调用该接口抽象方法--传入number


4、调用成员方法


5、优化--接口引用静态方法(接口已经存在、静态方法也已经存在)

 

通过super引用成员方法 ----  

1、函数式接口--抽象方法


2、创建一个Human父类--sayHello方法


3、子类Man--继承Human--创建方法method-参数为函数式接口-调用其方法


4、新建show方法中调用method--super引用成员方法


5、优化--使用方法引用--super引用成员方法(父类已经存在、成员方法也已经存在)


main方法调用子类show

通过this引用成员方法 ----  

1、函数式接口--抽象方法


2、Husband类--函数式接口做参数的方法method
          

3、新方法调用method,其中Lambda表达式,通过this用调用已存在的方法
 

4、优化--this引用成员方法
 

5、main方法创建对象,调用新方法
 

 

类的构造器引用 ----  

1、函数式接口--抽象方法(返回值为对象
         

2、printName方法--参数字符串和函数式接口,该方法调用接口方法,传递字符串,返回一个对象


3、main调用printName方法 ---- Lambda表达式中创建对象,传递字符串
 

4、优化--类的构造器引用
 

 

数组的构造器引用  ----   

1、定义一个方法,参数为创建数组的长度和函数式接口

2、调用该方法,参数传递整型和Lambda表达式,返回一个新数组,传入整型参数

3、数组构造器引用

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值