A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

第十一天网络编程网络通信协议
网络通信协议:     
通信协议是计算机必须遵守的规则, 只有遵守这些规则, 计算机之间才能进行通信.     
协议中对数据的传输格式, 传输速率, 传输步骤等做了统一规定, 通信双方必须同时遵守, 最终完成数据交换    (类似于一种语言, 语法语速做了规定)
TCP/IP协议:     
Transmission Control Protocol/Internet Protocol, 传输控制协议/因特网互联协议.   
它定义了计算机如何连入因特网, 以及数据如何在它们之间传输的标准. 它的内部包含一系列的用于处理数据通 信的协议, 并采用了4层的分层模型, 每一层都呼叫它的下一层所提供的协议来完成自己的需求
网络通信协议分类
UDP: User Datagram Protocol, 用户数据报协议   
特点:        
  • 无连接的不可靠协议        
  • 数据按包发送, 64K一个包        
  • 速度快效率高, 容易丢包   
  • 用于视频直播, 网络电话

TCP: Transmission Control Protocol, 传输控制协议     
特点:        
1.需要建立连接的可靠协议  电话        
2.数据传输无大小限制        
3.速度慢效率低   重发机制   
用于文件下载, 浏览网页   
TCP通信的三次握手: TCP协议中, 在发送数据的准备阶段, 客户端与服务器之间的三次交互, 以保证连接的可 靠   
1.客户端向服务端发送验证信息, 等待服务器确认   
2.服务端收到验证信息后, 回复客户端验证信息, 同时发送自己的一条验证信息   
3.客户端收到服务端回复的信息, 确认自己之前发的信息无误, 并再次向服务器发回服务端的验证信息
网络编程三要素:IP地址
网络编程三要素:   
1.通信协议  TCP   
2.IP地址   
3.端口号      
IP地址: 互联网协议地址(Internet Protocol Address). 是网络中计算机的唯一标识   
版本:        
IPv4: 192.168.1.2        
IPv6: ABCD:EF01:2345:6789:ABCD:EF01:2345:6789   
特殊的IP地址: "127.0.0.1", "localhost", 都代表自己的电脑
常用DOS命令:
​    // 查看自己电脑的IP地址   
ipconfig   
// 查看是否能连接到指定IP地址   
ping IP地址   
ping 192.168.31.2
网络三要素:端口号
端口号: 计算机中进程的唯一标识   
​        端口号的取值范围: 0~65535的整数, 其中0~1024不建议使用    注意:   
​        通信的两端是2个计算机中的2个程序在相互通信, 所以2个程序都要有端口号. 端口号可以相同, 也可以不同, 相互之间能识别就行
Tcp通信
Tcp通信程序概述(上)
java.net.ServerSocket类: TCP服务端   
​        // 构造方法   
​        ServerSocket(int port): 创建一个TCP服务端, 并监听指定端口   
​        // 成员方法   
​        Socket accept(): 监听数据, 会阻塞. 收到数据后返回Socket对象            void close(): 关闭服务端ServerSocket
java.net.Socket类: TCP客户端   
​        // 构造方法   
​        Socket(String ip, int port): 创建TCP客户端对象   
​        // 成员方法   
​        OutputStream getOutputStream(): 获取输出流对象, 用于发送数据   
​        InputStream getInputStream(): 获取输入流, 用于接收数void shutdownOutput(): 关闭输出流, 发送结束标记   
​        void close(): 关闭客户端Socket
TCP严格区分为 客户端(Client) 与 服务端(Server)
两端通信时步骤:   
  • 服务端程序需要先启动, 等待客户端的连接   
  • 客户端主动连接服务器端, 连接成功才能通信. 服务端不可以主动连接客户端

两端之间以 "IO字节流" 进行通信
一个服务端可以和多个客户端同时通信
TCP通信方式概述(下)
服务端(ServerSocket)可以通过 accept() 方法等待一个客户端(Socket)主动连接, 从而得到一个客户端对象 (Socket), 来识别不同的客户端
服务端(ServerSocket)没有IO流, 是通过获取到"每个客户端对象(Socket)的IO流对象"来进行通信的.     
使用"客户端的InputStream"读取客户端发来的数据   
使用"客户端的OutputStream"向客户端回写数据
文件上传
文件上传堵塞问题
java.net.Socket类: TCP客户端   
void shutdownOutput(): 关闭输出流, 告知服务端数据发送完毕
文件上传优化
目前服务端有以下问题:   
  • 上传的图片因为写入文件名相同, 每次都会被覆盖, 如何解决?   
  • 上传一个文件服务端就结束了, 如何让服务端不停止, 一直接收文件上传   
  • 在同一个线程读大文件可能会比较慢, 能否利用多线程提高程序效率

解决方案:   
  • 上传文件的命名规则: 域名+毫秒值+随机数      
  • 将serverSocket.accept()直到最后的操作放入死循环中, 服务器就可以一直接收文件上传   
  • 当serverSocket.accept()得到Socket客户端对象后的操作, 全都放在子线程中执行

11天API
java.net.ServerSocket类: TCP服务端   
// 构造方法   
ServerSocket(int port): 创建一个TCP服务端, 并监听指定端口    // 成员方法   
Socket accept(): 监听数据, 会阻塞. 收到数据后返回Socket对象    void close(): 关闭服务端ServerSocket
java.net.Socket类: TCP客户端   
// 构造方法   
Socket(String ip, int port): 创建TCP客户端对象   
// 成员方法   
OutputStream getOutputStream(): 获取输出流对象, 用于发送数据   
InputStream getInputStream(): 获取输入流, 用于接收数据    void shutdownOutput(): 关闭输出流, 告知服务端数据发送完毕    void close(): 关闭客户端Socket
TCP协议下的常用类名称
客户端: Socket
服务端: ServerSocket
编写TCP协议下字符串数据传输程序
你好服务器  收到谢谢
客户端发送字符串的byte
服务端接收客户端的byte
服务端回写客户端内容
客户端再接收服务端的响应
Tcp协议下文件上传
客户端   
​        读取本地图片文件   
​        发送到服务端   
​        读取服务端的响应结果
服务端   
​        接收客户端的文件数据   
​        写入到服务端磁盘   
​        发送响应给客户端
软件结构
C/S: Client Server 客户端 服务端   
B/S: Browser Server 浏览器 服务端
网络通信三要素
  • 通信协议: 怎么传输
  • IP地址: 传给哪个主机
  • 端口号: 传给主机的哪个进

第十二天函数式接口函数式接口格式
是要确保接口中有且仅有一个抽象方法即可:
​        修饰符 interface 接口名称 {                    public abstract 返回值类型 方法名称(可选参数信息);                    // 其他非抽象方法内容        }
由于接口当中抽象方法的public abstract是可以省略的,所以定义一个函数式接口很简单:
​        public interface MyFunctionalInterface {                        void myMethod();        }
@FunctionalInterface注解
与@Override注解的作用类似,Java 8中专门为函数式接口引入了一个新的注解:@FunctionalInterface`。该注解可用于一个接口的定义上:
​        @FunctionalInterface        public interface MyFunctionalInterface {                void myMethod();        }
一旦使用该注解来定义接口,编译器将会强制检查该接口是否确实有且仅有一个抽象方法,否则将会报错。需要注意的是,即使不使用该注解,只要满足函数式接口的定义,这仍然是一个函数式接口,使用起来都一样。
自定义函数式接口
对于刚刚定义好的MyFunctionalInterface函数式接口,典型使用场景就是作为方法的参数:
public class Demo09FunctionalInterface {                // 使用自定义的函数式接口作为方法参数        private static void doSomething(MyFunctionalInterface inter) {                inter.myMethod(); // 调用自定义的函数式接口方法        }
public static void main(String[] args) {                // 调用使用函数式接口的方法                doSomething(() -> System.out.println("Lambda执行啦!"));        }}       
函数式编程Lambda写法:
使用Lambda必然需要一个函数式接口
@FunctionalInterfacepublic interface MessageBuilder {             String buildMessage();}
使用Lambda作为参数和返回值
如果抛开实现原理不说,Java中的Lambda表达式可以被当作是匿名内部类的替代品。如果方法的参数是一个函数式接口类型,那么就可以使用Lambda表达式进行替代。使用Lambda表达式作为方法参数,其实就是使用函数式接口作为方法参数。
例如java.lang.Runnable接口就是一个函数式接口,假设有一个startThread方法使用该接口作为参数,那么就可以使用Lambda进行传参。这种情况其实和Thread类的构造方法参数为Runnable`没有本质区别。
public class Demo04Runnable {    private static void startThread(Runnable task) {              new Thread(task).start();    }
  public static void main(String[] args) {              startThread(() -> System.out.println("线程任务执行!"));    }
}
类似地,如果一个方法的返回值类型是一个函数式接口,那么就可以直接返回一个Lambda表达式。当需要通过一个方法来获取一个java.util.Comparator`接口类型的对象作为排序器时,就可以调该方法获取。
常用函数式接口Supplier接口
java.util.function.Supplier<T>接口仅包含一个无参的方法:T get()。用来获取一个泛型参数指定类型的对象数据。由于这是一个函数式接口,这也就意味着对应的Lambda表达式需要“对外提供”一个符合泛型类型的对象数据
Consumer
java.util.function.Consumer<T>`接口则正好与Supplier接口相反,它不是生产一个数据,而是消费一个数据,其数据类型由泛型决定。
抽象方法:accept
Consumer接口中包含抽象方法void accept(T t)`,意为消费一个指定泛型的数据。
默认方法:andThen
如果一个方法的参数和返回值全都是Consumer类型,那么就可以实现效果:消费数据的时候,首先做一个操作,然后再做一个操作,实现组合。而这个方法就是Consumer接口中的default方法andThen.
Predicate接口
有时候我们需要对某种类型的数据进行判断,从而得到一个boolean值结果。这时可以使用java.util.function.Predicate<T>接口
抽象方法:test
Predicate接口中包含一个抽象方法:boolean test(T t)
默认方法:and
既然是条件判断,就会存在与、或、非三种常见的逻辑关系。其中将两个Predicate条件使用“与”逻辑连接起来实现“并且”的效果时,可以使用default方法and`
默认方法:or
与and的“与”类似,默认方法or实现逻辑关系中的“”.
默认方法:negate
“与”、“或”已经了解了,剩下的“非”(取反)也会简单。默认方法negate的JDK源代码为:
default Predicate<T> negate() {    return (t) -> !test(t);}
Function接口
java.util.function.Function<T,R>接口用来根据一个类型的数据得到另一个类型的数据,前者称为前置条件,后者称为后置条件。
默认方法:andThen
Function接口中有一个默认的andThen方法,用来进行组合操作。JDK源代码如:
default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {    Objects.requireNonNull(after);    return (T t) -> after.apply(apply(t));}
第十三天Stream流Stream流式思想便利集合进行过滤
Stream流式思想:   
JDK 8 出现, 是函数式编程中的一大特性   
关注做什么, 而不是怎么做
public class Demo02Stream {   
​        public static void main(String[] args) {        
//创建一个List集合,存储姓名        
List<String> list = new ArrayList<>();        
list.add("张无忌");        
list.add("周芷若");        
list.add("赵敏");        
list.add("张强");        
list.add("张三丰");
        // 使用Stream流式操作        
list.stream()            
.filter(name -> name.startsWith("张"))      
// 第一次过滤: 只要以张开头的元素            
.filter(name -> name.length()==3)           
// 第二次过滤: 只要姓名长度为3的人            
.forEach(name -> System.out.println(name));
// 遍历查看结果   
}
}
流式思想概述
Stream流式思想处理数据的方式:     
让代码的执行像流水线一样, 先设计好处理方案, 然后一按开关开始执行    流相比于集合的2个优点:     
1.Pipelining(管道特性): "可以链式调用"        
Stream流对象的 延迟方法 调用后, 会返回新的Stream流对象, 可以链式调用        
每个方法类似于一条一条的管道, 衔接了不同的处理方案   
2.内部迭代特性: "不用写for循环"        
集合遍历通过 Iterator 或者 增强for, 显式的在集合外部进行迭代, 这叫做外部迭代        
Stream提供了内部迭代的方法 forEach(Consumer c), 可以直接调用遍历方法
使用Stream流的3个步骤:   
  • 获取数据源 (从"集合"或"数组"转换为"Stream"对象)   
  • 数据处理 (调用延迟方法, 编写处理方案)   
  • 获得结果 (调用终结方法, 启动开关

两种获取Stream流的方式
获取Stream流对象的2种方式:   
1.利用"Collection接口"中的默认方法 default Stream<E> stream() 方法: 集合转Stream对象   
2.利用"Stream接口"中的静态方法 static <T> Stream<T> of(T... values): 数组转Stream对象   
java.util.Collection<E>接口:   
// 默认方法   
default Stream<E> stream(): 将"集合"转换为Stream对象   
java.util.stream.Stream<T>接口: 管道接口, 泛型为流中元素的类型   
// 静态方法   
static<T> Stream<T> of(T... values): 将"数组"转换为Stream对象
Stream API: 方法分类, forEach()遍历
延迟方法: (具有延迟执行的特性)   
返回值类型"是Stream"类型的方法, 支持链式调用        
Stream filter(): 过滤
startsWith
判断当前字符串是否以参数字符串开头
filter
过滤符合条件的结果. 返回过滤后的流
Stream<String> stream2 = stream1.filter((String name) -> {            return name.startsWith("张");  
// 如果以张开头, 结果为true, 返回true正好会保留到 集合中      
});
Stream map(): 映射/转换      
map
将当前流中的T类型的元素, 转换R类型元素, 放入新流 并返回
Stream<Integer> stream2 = stream1.map((String num) -> {
// 元素从String 转换为 Integer. 返回流的泛型也要变化            return Integer.parseInt(num);        
});
limit
从前面选输入几就有几个元素
Stream.of("美羊羊", "喜羊羊", "懒羊羊", "灰太狼", "红太狼")                .limit(3)    // 只要前3个
Stream limit(): 截取      
skip  
从前面选输入几个就跳过几个元素
Stream skip(): 跳过
  Stream.of("美羊羊", "喜羊羊", "懒羊羊", "灰太狼", "红太狼") //                .skip(3)    // 不要前3个
终结方法:   
返回值类型"不是Stream"类型的方法, 不支持链式调用        
void forEach(): 遍历        
long count(): 统计   
注意:   
除了终结方法外, 其余方法均为延迟方法
方法引用
方法引用: Method Reference   
​        如果Lambda表达式仅仅是调用一个已经存在的方法, 那就可以通过方法引用来替代Lambda表达式   
作用: 简化Lambda表达式   
:: 方法引用运算符, 它所在的表达式被称为方法引用
Lambda表达式写法:
​     (String s) -> System.out.println(s)      
参数传递给System.out.println()方法去打印
方法引用写法:         
System.out::println   
引用System.out.println()方法中代码, 来作为Lambda中重写方法的实现方式
注意:   
Lambda中, 重写方法的"参数", 必须是方法引用的方法"要接收的类型", 否则会抛出异常   
(String s) -> System.out.println(s)  方法有个参数String s   
System.out::println                  引用的println方法必须能接收String类型的s
方法引用能简化以下场景: (方法名后不要写小括号)        
场景  格式  简化之前的Lambda  方法引用简化后   
  • 通过对象名引用成员方法     对象名::成员方法名   ()->person.eat()          person::eat   
  • 通过类名引用静态方法       类名::静态方法名     i -> Math.abs(i)          Math::abs   
  • 通过super引用父类成员方法  super::父类方法名   ()->super.eat();          super::eat   
  • 通过this引用本类成员方法   this::本类方法名    ()->this.eat();           this::eat   
  • 引用某个类的构造方法       类名::new          name->new Person(name)    Person::new   
  • 引用创建数组的方法         数据类型[]::new    length->new int[length];  int[]::new



0 个回复

您需要登录后才可以回帖 登录 | 加入黑马