网络编程
软件结构
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、数组构造器引用