1 网络编程入门

1.1 软件结构

  • C/S结构 :Client/Server结构,常见程序有QQ,迅雷等软件
  • B/S结构 :Browser/Server结构,常见浏览器有谷歌、Safari等

    1.2 网络通信协议

  • 网络通信协议 :计算机网络中,多台计算机实现连接必须遵守网络通信协议,它对数据的传输格式、传输速率、传输步骤等做了统一的规定

  • TCP/IP协议 :是Internet最基本、最广泛的协议,定义了计算机如何连入因特网,以及数据传输的标准,采用4层分层模型,每一层都呼叫它的下一层嗦提供的协议来完成自己的需求

    • 应用层:HTTP、FTP、TFTP、SMTP、SNMP、DNS
    • 传输层:TCP、UDP
    • 网络层:ICMP、IGMP、IP、ARP、RARP
    • 数据链路层/物理层:由底层网络定义的协议

      UDP

      特点
  • UDP是无连接通信协议,即在数据传输时,数据的发送端和接收端不建立逻辑连接,也就是不会相互确认对方的身份。

  • 数据被限制在64kb以内,超出此范围一次就不可发送

优点

  • 消耗资源小,通信效率高,通产用于音频、视频和普通数据的传输如视频会议等,这种情况即使丢失一两个数据包也不会对接收结果产生影响

缺点

  • 由于UDP的面向无连接性,不能保证数据的完整性

    TCP

    传输控制协议,TCP协议是面向连接的通信协议,提供了两台计算机之间可靠无差错的数据传输
    三次握手 TCP协议准备就单,客户端与服务器之间的三次交互

  • 第一次握手,客户端向服务器端发出连接请求,等待服务器确认

  • 第二次握手,服务器端向客户端回送响应,通知客户端收到了连接请求
  • 第三次握手,客户端再次向服务器端发送确认信息,确认连接

    1.3 网络编程三要素

    协议

    计算机网络通信必须遵守的规则,不再赘述

    IP地址

  • IPv4:4个字节表示计算机的地址 192.168.65.10

  • IPv6:16个字节表示的计算机地址,每两个字节一组,分成8组十六进制数 ABCD:EF01:2345::6789
  • 127.0.0.1localhost 代表本机地址

    端口号

  • 是一个逻辑端口,无法直接看到,当使用的网络软件一打开, 操作系统就会为网络软件分配一个随机的端口号

  • 端口号由两个字节组成,取值范围在0-65535之间
  • 1024之前的端口号已经被系统分配给已知的网络软件,网络软件的端口号不可重复,因此无法使用

常用端口号

  • 80 网络端口(http)
  • 3306 MySql端口
  • 1521 Oracle端口
  • 8080 Tomcat端口

    2 TCP通信程序

    两端通信的步骤

  • 服务端需要事先启动,等待客户端的连接

  • 客户端主动连接服务端,连接成功才能通信,服务端不可以主动连接客户端

java中的类

  • 客户端 java.net.Socket 向服务端发出连接请求
  • 服务端 java.net.ServerSocket 开启一个服务,等待客户端的连接

服务器需要明确

  • 多个客户端同时和服务器进行通信,服务器使用 accept 方法获取到请求的客户端对象
  • 服务器没有IO流,服务器使用每个客户端Socket中提供的的IO流和客户端进行交互

    2.1 Socket

    此类实现客户端套接字,套接字是两台机器间通信的端点
    1.构造方法

  • public Socket(String host, int port) 创建一个套接字并将其连接到指主机上的指定端口号

2.成员方法

  • getOutputStream() 返回此套接字的输出流
  • getInputStream() 返回此套接字的输入流
  • close() 关闭此套接字

    2.2 ServerSocket

    此类实现服务器套接字
    1.构造方法

  • public ServerSocket(int port)

2.成员方法

  • Socket accept() 侦听并接收到此套接字的连接

    2.3 文件上传

    客户端

  1. import java.io.FileInputStream;
  2. import java.io.IOException;
  3. import java.io.InputStream;
  4. import java.io.OutputStream;
  5. import java.net.Socket;
  6. public class Client {
  7. public static void main(String[] args) throws IOException {
  8. FileInputStream fis = new FileInputStream("src/Network/Upload/社会主义核心价值观.txt");
  9. Socket socket = new Socket("127.0.0.1", 8888);
  10. OutputStream os = socket.getOutputStream();
  11. byte[] bytes = new byte[1024];
  12. int len = 0;
  13. while ((len = fis.read(bytes)) != -1) {
  14. os.write(bytes, 0, len);
  15. }
  16. socket.shutdownOutput(); // 文件上传完毕后,给文件写上一个结束标记
  17. InputStream is = socket.getInputStream();
  18. while ((len = is.read(bytes)) != -1) {
  19. System.out.print(new String(bytes, 0, len));
  20. }
  21. fis.close();
  22. socket.close();
  23. }
  24. }

服务端

  1. import java.io.*;
  2. import java.net.ServerSocket;
  3. import java.net.Socket;
  4. import java.nio.charset.StandardCharsets;
  5. public class Server {
  6. public static void main(String[] args) throws IOException {
  7. ServerSocket server = new ServerSocket(8888);
  8. Socket socket = server.accept();
  9. InputStream is = socket.getInputStream();
  10. File dir = new File("/Users/stone/Musou/Java/Grammar/Upload");
  11. if(!dir.exists()){
  12. dir.mkdir();
  13. }
  14. FileOutputStream fos = new FileOutputStream(new File(dir,"社会主义核心价值观.txt"));
  15. byte[] bytes = new byte[1024];
  16. int len = 0;
  17. while((len=is.read(bytes))!=-1){
  18. fos.write(bytes,0,len);
  19. }
  20. OutputStream os = socket.getOutputStream();
  21. os.write("上传成功".getBytes(StandardCharsets.UTF_8));
  22. fos.close();
  23. socket.close();
  24. }
  25. }

2.4 模拟B/S

我还没怎么看懂这一部分😭

3 函数式编程

有且只有一个抽象方法的接口叫做函数式接口,函数式接口既可以作为参数,也可以作为返回值

3.1 性能浪费

使用Lambda有一个特点:延迟加载,因此可以被应用在日志的性能上面

创建一个拼接字符串的函数式接口

  1. public interface MessageBuilder {
  2. String buildMessage();
  3. }

使用lambda表达式传递参数,只有在level为1时才会拼接字符串,进行日志的打印。使用普通方法传递参数,则将字符串先行拼接后再进行传参,若其level不为1,则会存在性能浪费的情况

  1. public class Logger {
  2. public static void showLog(int level, MessageBuilder mb){
  3. if(level == 1){
  4. System.out.println(mb.buildMessage());
  5. }
  6. }
  7. public static void main(String[] args) {
  8. String msg1 = "Hello";
  9. String msg2 = "World";
  10. String msg3 = "Java";
  11. showLog(2,()-> {
  12. return msg1 + msg2 + msg3;
  13. });
  14. }
  15. }

3.2 常见的函数式接口

在声明前添加@FunctionalInterface来检验是否为函数式接口

Supplier

java.util.function.Supplier<T> 仅包含一个 T get() 方法获取泛型指定类型的数据

  1. import java.util.function.Supplier;
  2. public class DemoSupplier {
  3. public static int getMax(Supplier<Integer> sup) {
  4. return sup.get();
  5. }
  6. public static void main(String[] args) {
  7. int[] arr = {23, 12, 25, -3, 1, 6};
  8. int maxNum = getMax(() -> {
  9. int max = arr[0];
  10. for (int i = 1; i < arr.length; i++) {
  11. if (arr[i] > max) max = arr[i];
  12. }
  13. return max;
  14. });
  15. System.out.println(maxNum);
  16. }
  17. }

Consumer

Consumer<T> 中的 accept(T t) 方法消费一个对象
默认方法andThen

  1. // 可以把两个consumer接口组合到一起,再对对象进行消费
  2. /*
  3. Consumer<String> con1;
  4. Consumer<String> con2;
  5. String s = "Hello";
  6. con1.accept(s);
  7. con2.accept(s);
  8. //等价于 con1.andThen(con2).accept(s);
  9. */
  10. default Consumer<T> andThen(Consumer<? super T> after){
  11. Objects.requireNonNull(after);
  12. return (T t) -> {accept(t); after.accept(t);};
  13. }

使用

  1. import java.util.function.Consumer;
  2. public class DemoConsumer {
  3. public static void method(String s, Consumer<String> con1, Consumer<String> con2){
  4. con1.andThen(con2).accept(s);
  5. }
  6. public static void main(String[] args) {
  7. method("Hello",(t)->System.out.println(t.toUpperCase()),(t)->System.out.println(t.toLowerCase()));
  8. }
  9. }

应用:格式化打印信息

  1. import java.util.function.Consumer;
  2. public class DemoConsumer02 {
  3. public static void main(String[] args) {
  4. String[] array = {"迪丽热巴,女", "古力娜扎,女", "玛尔扎哈,男"};
  5. printInfo((s) -> {
  6. System.out.print("姓名:" + s.split(",")[0]);
  7. }, (s) -> {
  8. System.out.println(",性别:" + s.split(",")[1]);
  9. }, array);
  10. }
  11. public static void printInfo(Consumer<String> one, Consumer<String> two, String[] array) {
  12. for (String info : array) {
  13. one.andThen(two).accept(info);
  14. }
  15. }
  16. }

Predicate

Predicate<T> 包含一个抽象方法 boolean test(T t) 用于条件判断的场景
默认方法

  1. //此方法用于判断两个条件同时满足
  2. default Predicate<T> and(Predicate<? super T> other){
  3. Objects.requireNonNull(other);
  4. return(t) -> this.test(t) && other.test(t);
  5. }
  6. //此方法只需满足条件之一
  7. default Predicate<T> or(Predicate<? super T> other){
  8. Objects.requireNonNull(other);
  9. return(t) -> this.test(t) || other.test(t);
  10. }
  11. //此方法用于取反
  12. default Predicate<T> negate(){
  13. return(t) -> !t.test();
  14. }

Function

java.util.function.Function<T,R> 接口用来根据一个类型的数据得到另一个类型的数据, R apply(T t) 根据T的参数获取类型R的结果
默认方法

  1. //此方法用于将T变成R,再将R变成V
  2. default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
  3. Objects.requireNonNull(after);
  4. return (T t) -> after.apply(apply(t));
  5. }

练习:将一个字符串转化为整数+10后返回字符串

  1. package FunctionalInterface;
  2. import java.util.function.Function;
  3. public class DemoFunction {
  4. public static String change(Function<String, Integer> f1, Function<Integer, String> f2, String s) {
  5. // return f2.apply(f1.apply(s));
  6. return f1.andThen(f2).apply(s);
  7. }
  8. public static void main(String[] args) {
  9. String s = "1234";
  10. String r = change((str) -> Integer.parseInt(str) + 10, (i) -> i.toString(), s);
  11. System.out.println(r);
  12. }
  13. }

4 Stream流

4.1 传统方式遍历集合

  1. package Stream;
  2. import java.util.ArrayList;
  3. import java.util.Collections;
  4. import java.util.List;
  5. public class DemoForEach {
  6. public static void main(String[] args) {
  7. List<String> list = new ArrayList<>();
  8. Collections.addAll(list, "张无忌", "周芷若", "赵敏", "张三丰", "张强");
  9. List<String> listA = new ArrayList<>();
  10. for (String s : list) {
  11. if (s.startsWith("张")) listA.add(s);
  12. }
  13. List<String> listB = new ArrayList<>();
  14. for (String s : listA) {
  15. if(s.length() == 3) listB.add(s);
  16. }
  17. for (String s : listB) {
  18. System.out.println(s);
  19. }
  20. }
  21. }

4.2 Stream流遍历集合

使用list.stream().filter()方法

  1. //代码一下就简化了
  2. import java.util.ArrayList;
  3. import java.util.Collections;
  4. import java.util.List;
  5. public class DemoStream {
  6. public static void main(String[] args) {
  7. List<String> list = new ArrayList<>();
  8. Collections.addAll(list, "张无忌", "周芷若", "赵敏", "张三丰", "张强");
  9. list.stream().filter(name -> name.startsWith("张")).filter(name -> name.length() == 3).forEach(name -> System.out.println(name));
  10. }
  11. }

4.3 流式思想

Stream(流)是一个来自数据源的元素队列,是一个集合元素的函数模型,它并不是集合,也不是数据结构,其本身并不存储任何元素(或其地址值)。
image.png
特点

  • 元素是特定类型的对象,形成一个队列。Java中的Stream并不会存储元素,而是按需计算。
  • 数据源 :流的来源,可以是集合,数组等
  • Stream流属于管道流,只能被消费(使用)一次,方法调用完毕,数据则会流转到下一个Stream上

    获取流

    java.util.stream.Stream<T> 是Java8新加入的最常用的流接口(并不是一个函数式接口)
    获取流的方式

  • 所有的 Collection 集合都可以通过 Stream 默认方法获取流

  • Stream 接口的静态方法 of 可以获取数组对应的流

根据Collection获取流
Collection 接口的default方法 stream 可以用来获取流,所以其所有实现类均可以获取流

  1. import java.util.*;
  2. import java.util.stream.Stream;
  3. public class DemoGetStream {
  4. public static void main(String[] args) {
  5. List<String> list = new ArrayList<>();
  6. Stream<String> stream1 = list.stream();
  7. Set<String> set = new HashSet<>();
  8. Stream<String> stream2 = set.stream();
  9. Map<String, String> map = new HashMap<>();
  10. Set<String> keySet = map.keySet();
  11. Collection<String> values = map.values();
  12. Set<Map.Entry<String, String>> entries = map.entrySet();
  13. Stream<String> stream3 = keySet.stream();
  14. Stream<String> stream4 = values.stream();
  15. Stream<Map.Entry<String, String>> stream5 = entries.stream();
  16. }
  17. }

根据Stream接口获取流

  1. import java.util.stream.Stream;
  2. public class DemoGetStream02 {
  3. public static void main(String[] args) {
  4. Stream<Integer> stream1 = Stream.of(1, 2, 3, 4, 5);
  5. Integer[] arr = {1, 2, 3, 4, 5};
  6. Stream<Integer> stream2 = Stream.of(arr);
  7. }
  8. }

常用方法

流模型常用的API可以被分为两种

  • 延迟方法 返回值类型仍然是 Stream 接口自身类型的方法,因此支持链式操作
    • Stream<T> filter(Predicate<? super T> predicate) 将一个流转换成另一个子集流
    • Stream<R> map(Function<? super T, ? extends R> mapper) 将流中元素映射到另一个流中,如:

Stream stream = Stream.of("1", "2", "3", "4");
stream.map((String s) -> Integer.parseInt(s)).forEach(i -> System.out.println(i));

  • Stream<T> limit(long maxSize) 对流进行截取,取用前n个
  • Stream<T> skip(long n) 跳过前n个元素(如果当前流的长度小小于等于n,则返回长度为0的空流)
  • static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b) 将流a和流b合并
    • 终结方法 返回值类型不再是 Stream 接口自身类型的方法,因此不再支持链式调用
  • long coun() 返回此流中的元素个数
  • forEach 用于遍历流中的数据,参数是一个 Consumer 接口

    5 方法引用

    双冒号 :: 为引用运算符,它所在的表达式被称为方法引用,如果Lambda要表达的函数方案已经存在于某个方法的实现中,那么则可以通过双冒号来引用该方法作为Lambda的替代者,以下两种写法完全等效
  • Lambda表达式写法 s -> System.out.println(s);
  • 方法引用写法 System.out::println

注意:Lambda中传递的参数一定是方法引用中的那个方法可以接收的类型,否则会抛出异常

5.1 通过对象名引用

创建一个函数式接口

  1. @FunctionalInterface
  2. public interface Printable {
  3. void print(String str);
  4. }

创建一个存在的方法

  1. public class MethodReferenceObject {
  2. public void printUppercase(String str){
  3. System.out.println(str.toUpperCase());
  4. }
  5. }

使用方法引用优化Lambda

  1. public class ObjectMethodReferenceTest {
  2. public static void printString(String str, Printable p){
  3. p.print(str);
  4. }
  5. public static void main(String[] args) {
  6. String str = "world";
  7. MethodReferenceObject obj = new MethodReferenceObject();
  8. printString(str, obj::printUppercase);
  9. }
  10. }

5.2 通过类名引用

创建一个函数式接口

  1. public interface Calcable {
  2. int calAbs(int number);
  3. }

通过类名进行方法引用

  1. public class DemoTest {
  2. public static int method(int number, Calcable c) {
  3. return c.calAbs(number);
  4. }
  5. public static void main(String[] args) {
  6. int number1 = method(-20, (n) -> Math.abs(n)); //使用Lambda
  7. int number2 = method(-10, Math::abs); //使用方法引用
  8. System.out.println(number1);
  9. System.out.println(number2);
  10. }
  11. }

5.3 通过super引用

定义一个函数式接口

  1. @FunctionalInterface
  2. public interface Greetable {
  3. void greet();
  4. }

定义一个父类

  1. public class Human {
  2. public void sayHello(){
  3. System.out.println("Hello, I am Human.");
  4. }
  5. }

子类进行方法引用

  1. public class Man extends Human {
  2. @Override
  3. public void sayHello() {
  4. System.out.println("Hello, I am Man.");
  5. }
  6. public void method(Greetable g) {
  7. g.greet();
  8. }
  9. public void show() {
  10. //1 使用Lambda
  11. method(()->{
  12. Human h = new Human();
  13. h.sayHello();
  14. });
  15. //2 使用super
  16. method(() -> super.sayHello());
  17. //3 使用方法引用
  18. method(super::sayHello);
  19. }
  20. public static void main(String[] args) {
  21. new Man().show();
  22. }
  23. }

5.4 通过this引用

定义一个函数式接口

  1. @FunctionalInterface
  2. public interface Richable {
  3. void buy();
  4. }

方法引用

  1. public class Husband {
  2. public void buyHouse() {
  3. System.out.println("北京二环内买一套四合院");
  4. }
  5. public void marry(Richable r) {
  6. r.buy();
  7. }
  8. public void soHappy() {
  9. marry(() -> this.buyHouse()); //使用lambda
  10. marry(this::buyHouse); //使用方法引用
  11. }
  12. public static void main(String[] args) {
  13. new Husband().soHappy();
  14. }
  15. }

5.5 构造方法引用

类的构造器引用

创建一个Person类

  1. public class Person {
  2. private String name;
  3. public Person(String name) {
  4. this.name = name;
  5. }
  6. @Override
  7. public String toString() {
  8. return "Person{" +
  9. "name='" + name + '\'' +
  10. '}';
  11. }
  12. }

创建一个返回Person类对象的接口

  1. @FunctionalInterface
  2. public interface PersonBuilder {
  3. Person buildPerson();
  4. }

使用构造器引用

  1. public class Demo {
  2. public static void main(String[] args) {
  3. printName("迪丽热巴", (String name) -> {
  4. return new Person(name);
  5. });//使用lambda表达式
  6. // 使用类的构造器引用
  7. printName("古力娜扎", Person::new);
  8. }
  9. private static void printName(String name, PersonBuilder pb) {
  10. System.out.println(pb.buildPerson(name));
  11. }
  12. }

数组的构造器引用

创建一个定义数组的函数式接口

  1. @FunctionalInterface
  2. public interface ArrayBuilder {
  3. int[] buildArray(int length);
  4. }

使用数组的方法引用

  1. public class DemoArray {
  2. public static int[] createArray(int length, ArrayBuilder ab) {
  3. return ab.buildArray(length);
  4. }
  5. public static void main(String[] args) {
  6. int[] array1 = createArray(10, (len) -> new int[len]); // 使用lambda表达式
  7. int[] array2 = createArray(5, int[]::new); //使用方法引用
  8. for (int i : array1) {
  9. System.out.println(i);
  10. }
  11. for (int i : array2) {
  12. System.out.println(i);
  13. }
  14. }
  15. }