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

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

本帖最后由 休人负我 于 2018-12-10 11:21 编辑

day11


网络编程


软件结构
C/S结构:
        全称为Client/Server结构, 是指 客户端 和 服务器 结构
        常见程序有QQ, 迅雷等软件

B/S结构:
        全称为Browser/Server结构, 是指 浏览器 和 服务器 结构
        常见浏览器有IE, 谷歌, 火狐等

网络编程: 在一定的"协议"下, 实现两台计算机的通信的程序

网络通信协议
通信协议是计算机必须遵守的规则, 只有遵守这些规则, 计算机之间才能进行通信.
协议中对数据的传输格式, 传输速率, 传输步骤等做了统一规定, 通信双方必须同时遵守, 最终完成数据交换
        (类似于一种语言, 语法语速做了规定)

TCP/IP协议:
Transmission Control Protocol/Internet Protocol, 传输控制协议/因特网互联协议.
它定义了计算机如何连入因特网, 以及数据如何在它们之间传输的标准. 它的内部包含一系列的用于处理数据通信的协议, 并采用了4层的分层模型, 每一层都呼叫它的下一层所提供的协议来完成自己的需求


通信协议分类
UDP: User Datagram Protocol, 用户数据报协议
特点:
        1. 无连接的不可靠协议
        2. 数据按包发送, 64K一个包
        3. 速度快效率高, 容易丢包
用于视频直播, 网络电话

TCP: Transmission Control Protocol, 传输控制协议
特点:
        1. 需要建立连接的可靠协议  电话
        2. 数据传输无大小限制
        3. 速度慢效率低   重发机制
用于文件下载, 浏览网页
        
TCP通信的三次握手: TCP协议中, 在发送数据的准备阶段, 客户端与服务器之间的三次交互, 以保证连接的可靠
1. 客户端向服务端发送验证信息, 等待服务器确认
2. 服务端收到验证信息后, 回复客户端验证信息, 同时发送自己的一条验证信息
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通信

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)

两端通信时步骤:
1. 服务端程序需要先启动, 等待客户端的连接
2. 客户端主动连接服务器端, 连接成功才能通信. 服务端不可以主动连接客户端

两端之间以 "IO字节流" 进行通信
一个服务端可以和多个客户端同时通信

服务端(ServerSocket)可以通过 accept() 方法等待一个客户端(Socket)主动连接, 从而得到一个客户端对象(Socket), 来识别不同的客户端

服务端(ServerSocket)没有IO流, 是通过获取到"每个客户端对象(Socket)的IO流对象"来进行通信的.
        使用"客户端的InputStream"读取客户端发来的数据
        使用"客户端的OutputStream"向客户端回写数据


day12
函数式接口

JDK 8 新特性
    有且仅有一个抽象方法的接口, 适用于函数式编程场景的接口
    (默认方法, 静态方法, 私有方法, 与 java.lang.Object 类中定义相同的抽象方法, 都不算作抽象方法)

自定义函数式接口:
接口中有且只有一个抽象方法

@FunctionalInterface的作用:
在接口上使用, 检测当前的接口是否为函数式接口

函数式接口的使用
作为方法的参数类型, 传递Lambda表达式, 代替匿名内部类方式

Lambda延迟执行
Lambda具有"延迟执行"的特点:
传递Lambda对象, 只有当符合执行条件时, 才会执行代码

Lambda表达式作为方法参数
当一个方法的参数是一个函数式接口时, 可以使用Lambda表达式传递该参数, 简化匿名内部类的代码

Lambda表达式作为方法返回值
当一个方法的返回值是一个函数式接口时, 可以返回Lambda表达式, 简化匿名内部类的代码


常用函数式接口

Supplier生产型函数式接口
java.util.function.Supplier<T>函数式接口: 生产型函数式接口
// 抽象方法
T get(): 用于获取一个对象或值.
至于获取什么值, 怎么获取, 需要我们根据应用场景编写Lambda来实现

Consumer消费型函数式接口
java.util.function.Consumer<T>函数式接口: 消费型函数式接口
// 抽象方法
void accept(T t): 用于消费(使用)一个对象或值. 至于怎么消费, 要我们根据应用场景编写Lambda实现
// 默认方法
default Consumer<T> andThen(Consumer<? super T> after): 拼接两个Consumer接口的Lambda对象实现连续操作. 谁写前面, 谁先消费

Predicate条件判断函数式接口
java.util.function.Predicate<T>函数式接口: 条件接口, 用于判断
// 抽象方法
boolean test(T t): 判断参数传递的对象. 至于怎么判断, 判断什么, 需要我们编写Lambda表达式实现
// 默认方法 (用于连接多个判断条件)
default Predicate<T> and(Predicate<? super T> other): 与 &&
default Predicate<T> or(Predicate<? super T> other): 或 ||
default Predicate<T> negate(): 非, 取相反结果   !

Function转换型函数式接口
java.util.function.Function<T,R>: 根据一个 T类型的数据 转换为 另一个R类型的数据
T是 输入(input)的类型
R是 返回结果(result)的类型
有进有出, 所以称为"函数Function"
// 抽象方法
R apply(T t): 将T转换为R. 至于T和R是什么类型, 以及如何转换, 需要传递Lambda表达式实现
// 默认方法
default <V> Function<T, V> andThen(Function<? super R, ? extends V> after): 拼接多个Function转换




day13


Stream流
Stream流式思想:
JDK 8 出现, 是函数式编程中的一大特性
关注做什么, 而不是怎么做

Stream流式思想处理数据的方式:
让代码的执行像流水线一样, 先设计好处理方案, 然后一按开关开始执行
        
流相比于集合的2个优点:
        1. Pipelining(管道特性): "可以链式调用"
        Stream流对象的 延迟方法 调用后, 会返回新的Stream流对象, 可以链式调用
        每个方法类似于一条一条的管道, 衔接了不同的处理方案
        2. 内部迭代特性: "不用写for循环"
        集合遍历通过 Iterator 或者 增强for, 显式的在集合外部进行迭代, 这叫做外部迭代
        Stream提供了内部迭代的方法 forEach(Consumer c), 可以直接调用遍历方法

使用Stream流的3个步骤:
        1. 获取数据源 (从"集合"或"数组"转换为"Stream"对象)
        2. 数据处理 (调用延迟方法, 编写处理方案)
        3. 获得结果 (调用终结方法, 启动开关)


2种获取Stream流的方式

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: 方法分类

延迟方法: (具有延迟执行的特性)
返回值类型"是Stream"类型的方法, 支持链式调用
        Stream filter(): 过滤
        Stream map(): 映射/转换
        Stream limit(): 截取
        Stream skip(): 跳过
终结方法:
返回值类型"不是Stream"类型的方法, 不支持链式调用
        void forEach(): 遍历
        long count(): 统计
        
注意:
        除了终结方法外, 其余方法均为延迟方法

java.util.stream.Stream<T>接口: 管道接口
// 抽象方法
Stream<T> filter(Predicate<? super T> predicate): 过滤符合条件的结果. 返回过滤后的流

java.util.stream.Stream<T>接口: 管道接口
// 抽象方法
<R> Stream<R> map(Function<T, R> mapper): 将当前流中的T类型的元素, 转换R类型元素, 放入新流并返回

java.util.stream.Stream<T>接口: 管道接口
// 抽象方法
Stream<T> limit(long maxSize): 从流中获取前maxSize个. 如果maxSize大于等于元素个数, 则返回所有元素的流

java.util.stream.Stream<T>接口: 管道接口
// 抽象方法
Stream<T> skip(long n): 从流中跳过n个元素, 获取后面的元素. 如果n大于等于元素个数, 则全都跳过



java.util.stream.Stream<T>接口: 管道接口
// 抽象方法
long count(): 获取流中的元素个数 (终结方法)

java.util.stream.Stream<T>接口: 管道接口
// 抽象方法
void forEach(Consumer<? super T> action): 遍历流中的元素进行逐一消费. 并不保证元素的逐一消费动作在流中是被有序执行的


java.util.stream.Stream<T>接口: 管道接口
// 静态方法
static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b): 合并两个流的元素, 变成一个新的流. 两个流中的元素类型必须相同, 或有共同的父类


收集器

收集器的作用:
收集Stream操作后的结果, 转换为其他容器
对流操作完成之后, 如果需要将其结果进行收集, 例如转换为对应的"集合"或"数组"等


java.util.stream.Stream<T>接口: 流
Object[] toArray(): 将当前Stream流对象转换为Object[]数组
<R,A> R collect(Collector<? super T,A,R> collector): 将当前Stream流对象根据传入的Collector转换为集合或数组

java.util.stream.Collectors类: 收集器静态工具类, 提供不同转换方式的Collector

static <T> Collector<T,?,List<T>> toList(): Stream转List集合  

static <T> Collector<T,?,Set<T>> toSet(): Stream转Set集合

static <...> Collector<...> toMap(Function<? super T,? extends K> keyMapper,  Function<? super T,? extends U> valueMapper)
    Stream转Map集合.
    Function keyMapper: 生成key的转换方式
    Function valueMapper: 生成value的转换方式


方法引用

如果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表达式中, 仅仅是"通过某个对象, 调用已有的方法"时, 就可以用这种方式简化

通过类名引用静态方法
类名::静态方法名  
        
适用场景:
当Lambda表达式中, 仅仅是"通过某个类名, 调用已有的静态方法"时, 就可以用这种方式简化

通过super引用父类成员方法
super::父类方法名
        
适用场景
当Lambda表达式中, 仅仅是"在子类中, 调用父类某个已有的方法"时, 就可以用这种方式简化

通过this引用本类成员方法
this::本类方法名
        
适用场景:
当Lambda表达式中, 仅仅是"调用本类中, 某个已有的方法"时, 就可以用这种方式简化

引用某个类的构造方法
类名::new

使用场景
当Lambda表达式中, 仅仅是"调用某个类的构造方法, 来创建一个对象"时, 就可以用这种方式简化

引用创建数组的方法
数据类型[]::new
   
使用场景
当Lambda表达式中, 仅仅是"创建一个数组对象"时, 就可以用这种方式简化




day14





单元测试

测试:
        测试代码是否正确, 能否达到预期效果

测试分类:
        1. 黑盒测试:不需要写代码, 给输入值, 看程序是否能够输出期望的值
        2. 白盒测试:需要写代码的. 关注程序具体的执行流程

集成测试 (多个人, 多个模块)
接口测试 (网络请求的接口)
单元测试 (测试每个定义的方法)


反射

反射: 将类的各个组成部分, 封装为其他对象, 这就是反射机制

成员变量(字段): Field类的对象
构造方法: Constructor类的对象
成员方法: Method类的对象
好处:
        1. 可以在程序运行过程中, 操作这些对象
        2. 可以解耦, 提高程序的可扩展性

Java代码在计算机中的3个阶段:
        SOURCE: 源代码阶段
        CLASS: 类对象阶段
        RUNTIME: 运行时阶段

获取字节码对象的3种方式

1. Class.forName("全类名")
        将字节码文件加载进内存,返回Class对象
        适用场景: 多用于配置文件,将类名定义在配置文件中. 读取文件, 加载类
2. 类名.class                         .
        通过类名的属性class获取
        适用场景: 多用于参数的传递getConstructor(String.class, int.class)
3. 对象.getClass()
        getClass()方法在Object类中定义
        适用场景: 多用于对象的获取字节码的方式 p.getClass()
               
同一个类的字节码对象, 只有"唯一的一个"

Class的方法

java.lang.Class<T>类: 表示一个类的字节码对象, 其中包含该类中定义的内容
// 成员方法

// 1. 获取成员变量们
Field[] getFields(): 获取所有 public 的成员变量
Field getField(String name): 获取指定名称的 public 的成员变量
Field[] getDeclaredFields(): 获取所有的成员变量, 不考虑权限修饰符
Field getDeclaredField(String name): 获取指定名称的成员变量, 不考虑权限修饰符

// 2. 获取构造方法们
Constructor<?>[] getConstructors(): 获取所有 public 的构造方法
Constructor<T> getConstructor(Class<?>... parameterTypes): 获取指定的 public 构造方法
Constructor<?>[] getDeclaredConstructors(): 获取所有的构造方法, 不考虑权限修饰符
Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes): 获取指定的构造方法, 不考虑权限修饰符

// 3. 获取成员方法们:
Method[] getMethods(): 获取所有 public 的成员方法
Method getMethod(String name, Class<?>... parameterTypes) : 获取指定的 public 成员方法
Method[] getDeclaredMethods(): 获取所有的成员方法, 不考虑权限修饰符
Method getDeclaredMethod(String name, Class<?>... parameterTypes): 获取指定的成员方法, 不考虑权限修饰符


// 4. 获取Class对象代表的类的全类名
String getName(): 获取当前Class对象代表的类的全类名

// 5. 创建对象
T newInstance(): 使用当前类的空参构造, 创建一个对象


成员变量Field

java.lang.reflect.Field: 表示一个成员变量
// 成员方法 String name;   Person p = new Person(); p2.name = "abc";
void set(Object obj, Object value): 设置指定对象的成员变量的值 field.set(p1, "abc")
Object get(Object obj): 获取指定对象的成员变量的值 field.get(p1)
void setAccessible(boolean flag): 传true时忽略访问权限修饰符的安全检查. 暴力反射 field.set

构造方法Constructor

java.lang.reflect.Constructor<T>: 表示一个构造方法
// 成员方法
T newInstance(Object... initargs): 使用当前构造方法传入参数, 创建对象
void setAccessible(boolean flag): 注意: 构造方法不能利用此方法忽略权限, 会抛异常

成员方法Method

java.lang.reflect.Method类: 表示一个成员方法
// 成员方法 Person p = new Person();   p.eat("adf", 123);  
Object invoke(Object obj, Object... args): 使用指定对象和指定参数值调用此方法
String getName(): 获取方法名
void setAccessible(boolean flag): 传true时忽略访问权限修饰符的安全检查. 暴力反射


注解

注解: Annotation
        JDK 1.5 引入. 也叫元数据, 是一种代码级别的说明
        它可以声明在包, 类, 字段(成员变量), 方法, 局部变量, 方法参数等的前面, 用来对这些元素进行说明

注解: 说明程序的。给计算机看的
注释: 用文字描述程序的。给程序员看的

使用注解: @注解名称

作用分类:
        1. 编写文档: 通过代码里标识的注解生成文档
        (生成API文档 @author @version @since @param @return)
        2. 代码分析: 通过代码里标识的注解对代码进行分析 (使用反射)
        (JUnit提供的 @Test @Before @After)
        3. 编译检查: 通过代码里标识的注解让编译器能够实现基本的编译检查
        (@Override @FunctionalInterface)

JDK内置注解

@Override: 检测被该注解标注的方法是否是"重写"父类(接口)的
@Deprecated: 该注解标注的内容,表示已过时
@SuppressWarnings: 压制警告. 一般传递参数all  
        @SuppressWarnings("all")

格式和本质

public interface 接口名 {}

自定义注解格式:关键字 @interface

    元注解
    public @interface 注解名称 {
        属性; (接口中的抽象方法)
            属性;
            属性;
            ...
    }

@注解名称
   
注解的本质:
        注解本质上就是一个接口,该接口默认继承Annotation接口
        public interface MyAnno extends                 
    java.lang.annotation.Annotation {}

枚举

enum, enumeration. JDK 1.5 引入
        主要是用于定义一些"相关的"常量, 比如星期, 颜色, 用来进行区分

枚举定义方式:
public enum 枚举名 {
    枚举常量1, 枚举常量2, 枚举常量3;
}
使用方式:
枚举名.常量名

自定义注解: 属性定义

属性:
        接口中的"抽象方法"
   
属性的要求:
    1. 属性的"返回值类型"可以是以下类型:
        基本数据类型(8种)
        String
        枚举
        注解
        以上类型的数组
    2. 定义了属性,在使用注解时, 需要"给属性赋值" (其实是抽象方法的返回值)
        1. 属性使用 default 关键字指定默认值, 则可以不赋值
        2. 如果只有一个名为"value"的属性需要赋值, 则 value 可以省略, 直接写值即可
        3. 给数组赋值时,值使用{}包裹。如果数组中只有一个值,则{}可以省略

元注解

用于描述注解的注解
        
常用元注解:
        @Target: 描述注解能够作用的位置
                ElementType枚举的常用取值:
                        TYPE:可以作用于类上
                        METHOD:可以作用于方法上
                        FIELD:可以作用于成员变量上
        示例: @Target(value = {ElementType.TYPE, ElementType.METHOD})
        @Retention: 描述注解被保留的阶段
                RetentionPolicy枚举的取值:
                        SOURCE: 保留到源代码阶段
                        CLASS: 保留到类对象阶段
                        RUNTIME: 保留到运行时阶段
                示例: @Retention(RetentionPolicy.RUNTIME):保留注解到class字节码文件中并被JVM读取到
        @Documented: 加上后, 当前注解会被抽取到api文档中
        @Inherited: 加上后, 当前注解会被子类继承


解析注解

获取注解属性值的步骤:
        1. 获取注解定义位置的对象 (Class对象(类注解), Field对象(成员变量注解), Method对象(方法注解))
        2. 调用 ProAnno a = cls.getAnnotation(ProAnno.class) 方法获取注解对象
        3. 通过注解对象调用抽象方法获取属性值




0 个回复

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