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

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

© 养只猫叫冰棍儿 初级黑马   /  2018-12-2 16:27  /  482 人查看  /  0 人回复  /   0 人收藏 转载请遵从CC协议 禁止商业使用本文

day11 网络编程网络编程基础
知识点
C/S结构:
全称为Client/Server结构, 是指 客户端 和 服务器 结构
常见程序有QQ, 迅雷等软件
B/S结构:
    全称为Browser/Server结构, 是指 浏览器 和 服务器 结构
    常见浏览器有IE, 谷歌, 火狐等
   

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

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

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. 客户端收到服务端回复的信息, 确认自己之前发的信息无误, 并再次向服务器发回服务端的验证信息
网络编程三要素:
    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个程序都要有端口号. 端口号可以相同, 也可以不同, 相互之间能识别就行

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"向客户端回写数据
java.net.Socket类: TCP客户端
        // 构造方法
        Socket(String 服务端ip, int 服务端port): 创建TCP客户端对象, 同时尝试连接到指定的服务端
                (客户端也会获取系统随机分配的一个端口号)
        // 成员方法
        OutputStream getOutputStream(): 获取输出流对象, 用于发送数据
        InputStream getInputStream(): 获取输入流, 用于接收数据
        void shutdownOutput(): 关闭输出流, 告知服务端数据发送完毕, 结束标记-1
        void close(): 关闭客户端Socket, 告知服务端数据发送完毕
       
实现步骤:
        1.创建一个客户端对象 Socket, 构造方法传入服务器的IP地址和端口号
        2.使用Socket对象中的方法 getOutputStream() 获取网络字节输出流OutputStream对象
        3.使用网络字节输出流OutputStream对象中的方法 write(), 给服务器发送数据
        4.使用Socket对象中的方法 getInputStream() 获取网络字节输入流InputStream对象
        5.使用网络字节输入流InputStream对象中的方法 read(), 读取服务器回写的数据
        6.释放资源(Socket)
        
注意:
        1.客户端和服务器端进行交互, 必须使用Socket中提供的网络流, 不能使用自己创建的流对象
        2.当我们创建客户端对象Socket的时候, 就会去请求服务器和服务器经过3次握手建立连接通路
                这时如果服务器没有启动, 那么就会抛出异常ConnectException: Connection refused: connect
                如果服务器已经启动, 那么就可以进行交互了
               
        // 创建客户端Socket对象, 指定要连接的服务端的IP和端口号
        Socket socket = new Socket("127.0.0.1", 8888);
        // 向服务端发送数据
        OutputStream os = socket.getOutputStream();
        os.write("你好服务器".getBytes());
        // 释放资源
        socket.close();
java.net.ServerSocket类: TCP服务端
    // 构造方法
    ServerSocket(int 服务端自己用的port): 创建一个TCP服务端, 并监听指定端口
    // 成员方法
    Socket accept(): 等待客户端连接, 会阻塞. 客户端连接后返回客户端的Socket对象
    void close(): 关闭服务端ServerSocket, 断开所有流
   
服务器的实现步骤:
    1.创建服务器ServerSocket对象并指定服务器占用的端口号
    2.使用ServerSocket对象中的方法 accept(), 获取到请求的客户端对象Socket
    3.使用Socket对象中的方法 getInputStream() 获取网络字节输入流InputStream对象
    4.使用网络字节输入流InputStream对象中的方法 read(), 读取客户端发送的数据
    5.使用Socket对象中的方法 getOutputStream() 获取网络字节输出流OutputStream对象
    6.使用网络字节输出流OutputStream对象中的方法 write(), 给客户端回写数据
    7.释放资源(Socket,ServerSocket)
        
    // 创建服务端ServerSocket对象, 并指定自己监听的端口
    ServerSocket server = new ServerSocket(8888);
    // 调用accept方法等待客户端连接
    Socket socket = server.accept();

    // 通过客户端获取输入流, 读取客户端发来的数据
    InputStream is = socket.getInputStream();
    byte[] bytes = new byte[1024];
    int len = is.read(bytes);
    System.out.println(new String(bytes, 0, len));

    // 通过客户端获取输出流, 向客户端回写数据
    OutputStream os = socket.getOutputStream();
    os.write("收到谢谢".getBytes());

    // 释放资源
    socket.close();
    // server.close();  // 客户端一般不关闭
day12 函数式接口
函数式接口: JDK 8 新特性
    有且仅有一个抽象方法的接口, 适用于函数式编程场景的接口
    (默认方法, 静态方法, 私有方法, 与 java.lang.Object 类中定义相同的抽象方法, 都不算作抽象方法)
自定义函数式接口:
        接口中有且只有一个抽象方法
@FunctionalInterface的作用:
        在接口上使用, 检测当前的接口是否为函数式接口
函数式接口的使用:
        作为方法的参数类型, 传递Lambda表达式, 代替匿名内部类方式
日志: 就是存储程序运行信息的文本文件.
我们平时打印输出一些信息是在控制台上, 如果将这些信息写入文本文件, 这些文件就是日志

Lambda具有"延迟执行"的特点:
    传递Lambda对象, 只有当符合执行条件时, 才会执行代码
   
    @FunctionalInterface
    interface MessageBuilder {
        String buildMessage();
    }

    public static void showLog(int level, MessageBuilder builder) { // 传递实现类对象
        if (level == 1) {
            String s = builder.buildMessage();
            System.out.println(s);
        }
    }
   
    showLog(1, new MessageBuilder(){
        @Override
        public String buildMessage() {
            return s1 + s2 + s3;
        }
    });
   
    showLog(2, () -> s1+s2+s3);
    // 传递Lambda对象, 即MessageBuilder实现类对象进方法
    // 判断2和1不相等, 所以不会执行builder.buildMessage()
    // 从而也不会执行重写方法中的s1+s2+s3
    // 避免了无意义的性能浪费
当一个方法的参数是一个函数式接口时, 可以使用Lambda表达式传递该参数, 简化匿名内部类的代码
接口: 可以作为方法的参数类型, 还可以作为方法的返回值类型


当一个方法的返回值是一个函数式接口时, Lambda表达式方式是否比匿名内部类方式简单
public static Comparator<String> getComparator(){
    return new Comparator<String>(){
        @Override
        public int compare(String o1, String o2) {
            // 编写比较的规则 升序
            return o1.length()-o2.length();
        }
    };
}
总结:

当一个方法的返回值是一个函数式接口时, 可以返回Lambda表达式, 简化匿名内部类的代码
常用函数式接口
Java中提供的常用 函数式接口:
    Supplier<T>: 生产型函数式接口         获取值  a和b的和, 获取数组最大值, 获取一个Person对象
    Consumer<T>: 消费型函数式接口         使用值  给它一个String让他打印, 给它一个对象
    Predicate<T>: 条件判断型函数式接口     判断值  判断字符串长度是否大于5
    Function<T, R>: 转换型函数式接口      转换值  给它一个String转换为Integer "123"->123
Supplier生产型函数式接口
知识点:

JDK 提供的函数式接口在哪个包中
Supplier<T> 接口有什么作用
总结:

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

public class Test {
    public static void main(String[] args) {
        // 调用方法
        String s = getString( ()->"赵丽颖" );
        System.out.println(s);  // 赵丽颖
        
        // 相当于
        getString(new Supplier<String>(){
            @Override
            public String get() {
                return "赵丽颖";
            }
        });
    }
   
    // 定义方法: 用来获取一个字符串 参数: 获取字符串的方式(代码)
    public static String getString(Supplier<String> sup) {  // 接口实现类对象
        return sup.get();
    }
}
Consumer消费型函数式接口
知识点:

Consumer<T> 接口有什么作用
总结:

java.lang.StringBuilder类: 可变字符序列, 类似于String, 线程不安全效率高   
    StringBuilder reverse(): 将StringBuilder内部保存的内容反转
    String toString(): 转换为String
        new StringBuilder("abc").reverse().toString(); -> "cba" "abc" -> "cba"
java.lang.StringBuffer类: 可变字符序列, 类似于String, 线程安全效率低
    StringBuffer reverse(): 将StringBuffer内部保存的内容反转
    String toString(): 转换为String


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

public class Test {

    // 定义方法用来消费一个字符串. 参数是 被消费的字符串 和 消费方式
    public static void method(String name, Consumer<String> con){
        con.accept(name);
    }

    public static void main(String[] args) {
        String name = "赵丽颖";

        // 消费这个字符串, 我们的消费方式是 反转字符串
        method(name, (String name)->{
            // 反转字符串
            String reName = new StringBuilder(name).reverse().toString();
            System.out.println(reName);  // 颖丽赵

        });
    }
}
Consumer消费型函数式接口: 默认方法andThen()
知识点:

Consumer<T>接口中的默认方法 Consumer<T> andThen(Consumer<? super T> after) 如何简化多个Consumer的消费
java.util.function.Consumer<T>函数式接口: 消费型函数式接口
    // 默认方法
    default Consumer<T> andThen(Consumer<? super T> after): 拼接两个Consumer接口的Lambda对象实现连续操作. 谁写前面, 谁先消费

public class Test {

    // 定义方法用来消费一个字符串. 参数是 被消费的字符串 和 消费方式1 和 消费方式2
    public static void method(String s, Consumer<String> con1, Consumer<String> con2){
//        con1.accept(s);
//        con2.accept(s);
        
        // 使用andThen简化
        con1.andThen(con2).accept(s);
    }

    public static void main(String[] args) {
        // 传入两种消费方式
        method("Hello",
               (t) -> {
                   System.out.println(t.toUpperCase()); // HELLO
               },
               (t) -> {
                   System.out.println(t.toLowerCase()); // hello
               });
    }
}
Predicate<T> 函数式接口有什么作用
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(): 非, 取相反结果   !
   
   
    boolean b = 判断方式1.or(判断方式2).and(判断方式3).negate().test("abc")

public class Test {
   
    // 定义方法用来检测字符串. 参数是 被检测的字符串 和 检测方式
    private static boolean checkString(String s, Predicate<String> pre) {
        return pre.test(s);
    }
   
    public static void main(String[] args) {
        String s = "abcde";
        
        boolean b = checkString(s, str -> str.length()>5);
        System.out.println(b);  // false
    }
}
Predicate条件判断函数式接口: 默认方法and()
知识点:
Predicate接口中的默认方法 and() 如何实现多个条件"与"的判断
default Predicate<T> and(Predicate<? super T> other): 与
Predicate条件判断函数式接口: 默认方法or(), negate()
知识点:
Predicate接口中的默认方法 or() 如何实现多个条件"或"的判断
Predicate接口中的默认方法 negate() 如何实现多个条件"非"的判断
default Predicate<T> or(Predicate<? super T> other): 或
default Predicate<T> negate(): 非, 取相反结果
Function转换型函数式接口
知识点:

函数 y = f(x)
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转换
Function转换型函数式接口: 默认方法andThen()
知识点:

Function接口中的 andThen() 如何组合多种转换方法

default <V> Function<T, V> andThen(Function<? super R, ? extends V> after): 连接多种转换操作, 先做什么, 再做什么
day13 Stream 方法引用
函数式编程:
    Stream流式思想:
        先编写所有处理方案, 最后再统一执行方案
        应用场景: 简化对集合/数组复杂操作的代码
        API
    方法引用:
        简化Lambda表达式, 直接引用一个已经存在的方法(成员方法, 静态方法, 构造方法)
Stream流传统方式遍历集合进行过滤
知识点:

传统方式过滤集合中的元素, 有哪些代码显得重复冗余

传统方式过滤集合中的元素, 要写很多次for循环, 代码重复冗余
使用Stream流式思想遍历集合进行过滤
知识点:

感受函数式编程中提供的Stream流式思想对集合过滤操作的简化
Stream流式思想:
    JDK 8 出现, 是函数式编程中的一大特性
    关注做什么, 而不是怎么做
流式思想概述
知识点:

Stream流式思想是如何处理数据的
流相比于集合有什么优点
使用Stream流的步骤是什么
流的数据源可以是什么
总结:

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

使用Stream流的3个步骤:
    1. 获取数据源 (从"集合"或"数组"转换为"Stream"对象)
    2. 数据处理 (调用延迟方法, 编写处理方案)
    3. 获得结果 (调用终结方法, 启动开关)
2种获取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(): 过滤
        Stream map(): 映射/转换
        Stream limit(): 截取
        Stream skip(): 跳过
终结方法:
    返回值类型"不是Stream"类型的方法, 不支持链式调用
        void forEach(): 遍历
        long count(): 统计
   
注意:
    除了终结方法外, 其余方法均为延迟方法

           -------------------------------------------------
创建数据源-> filter()过滤 | map()映射 | limit()截取 | skip()跳过 -> forEach() | count()结果
           -------------------------------------------------  
  获取流                            转换                                  聚合
                                 (延迟方法)                            (终结方法)
new ArrayList().stream().filter(...).map(...).limit(...).skip(...).forEach(...);
Stream.of(1,2,3).filter(...).map(...).limit(...).skip(...).count();


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

java.lang.String类:
    boolean startsWith(String prefix): 判断当前字符串是否以参数字符串开头

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

每次调用延迟方法返回的Stream流对象, 都是经过处理后返回的"新的Stream流对象"
之前的Stream流在调用方法后, 已经使用过并关闭了, 不能再次使用, 否则会抛出异常:
    java.lang.IllegalStateException: stream has already been operated upon or closed
Stream API: map()映射转换
总结:

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

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

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

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

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

如何理解Stream流:
    Stream不是集合, 也不是数组, 也不是某种数据结构, 所以Stream内部"不能存储"元素
    Stream是一种"函数模型", 规定一些对于集合或数组的处理方案:
        在调用"延迟方法"时, 就是在编写处理方案, 但并未真正执行方案
        在调用"终结方法"时, 才一次性按照处理方案来操作集合
        这也是流式操作"延迟执行"的特点

收集器的作用:
    收集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的转换方式

方法引用方法引用基本介绍总结:

方法引用: 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           方法引用简化后
    1. 通过对象名引用成员方法     对象名::成员方法名   ()->person.eat()          person::eat
    2. 通过类名引用静态方法       类名::静态方法名     i -> Math.abs(i)          Math::abs
    3. 通过super引用父类成员方法  super::父类方法名   ()->super.eat();          super::eat
    4. 通过this引用本类成员方法   this::本类方法名    ()->this.eat();           this::eat
    5. 引用某个类的构造方法       类名::new          name->new Person(name)    Person::new
    6. 引用创建数组的方法         数据类型[]::new    length->new int[length];  int[]::new
方法引用: 通过对象名引用成员方法
总结:

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

通过类名引用静态方法
    类名::静态方法名  Math.abs(1)   Math::abs
   
适用场景:
    当Lambda表达式中, 仅仅是"通过某个类名, 调用已有的静态方法"时, 就可以用这种方式简化
方法引用: 通过super引用父类成员方法
总结:

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

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

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

引用创建数组的方法
    数据类型[]::new
   
使用场景
    当Lambda表达式中, 仅仅是"创建一个数组对象"时, 就可以用这种方式简化
                   * @Retention:描述注解被保留的阶段
                        * @Retention(RetentionPolicy.RUNTIME):当前被描述的注解,会保留到class字节码文件中,并被JVM读取到
                * @Documented:描述注解是否被抽取到api文档中
                * @Inherited:描述注解是否被子类继承
day14 Junit单元测试:
* 测试分类:
    1. 黑盒测试:不需要写代码,给输入值,看程序是否能够输出期望的值。
    2. 白盒测试:需要写代码的。关注程序具体的执行流程。

* Junit使用:白盒测试
    * 步骤:
        1. 定义一个测试类(测试用例)
            * 建议:
                * 测试类名:被测试的类名Test       CalculatorTest
                * 包名:xxx.xxx.xx.test        cn.itcast.test

        2. 定义测试方法:可以独立运行
            * 建议:
                * 方法名:test测试的方法名        testAdd()  
                * 返回值:void
                * 参数列表:空参

        3. 给方法加@Test
        4. 导入junit依赖环境

    * 判定结果:
        * 红色:失败
        * 绿色:成功
        * 一般我们会使用断言操作来处理结果
            * Assert.assertEquals(期望的结果,运算的结果);

    * 补充:
        * @Before:
            * 修饰的方法会在测试方法之前被自动执行
        * @After:
            * 修饰的方法会在测试方法执行之后自动被执行
反射:框架设计的灵魂

* 框架:半成品软件。可以在框架的基础上进行软件开发,简化编码
* 反射:将类的各个组成部分封装为其他对象,这就是反射机制
    * 好处:
        1. 可以在程序运行过程中,操作这些对象。
        2. 可以解耦,提高程序的可扩展性。


* 获取Class对象的方式:
    1. Class.forName("全类名"):将字节码文件加载进内存,返回Class对象
        * 多用于配置文件,将类名定义在配置文件中。读取文件,加载类
    2. 类名.class:通过类名的属性class获取
        * 多用于参数的传递
    3. 对象.getClass():getClass()方法在Object类中定义着。
        * 多用于对象的获取字节码的方式

    * 结论:
        同一个字节码文件(*.class)在一次程序运行过程中,只会被加载一次,不论通过哪一种方式获取的Class对象都是同一个。


* Class对象功能:
    * 获取功能:
        1. 获取成员变量们
            * Field[] getFields() :获取所有public修饰的成员变量
            * Field getField(String name)   获取指定名称的 public修饰的成员变量

            * Field[] getDeclaredFields()  获取所有的成员变量,不考虑修饰符
            * Field getDeclaredField(String name)  
        2. 获取构造方法们
            * Constructor<?>[] getConstructors()  
            * Constructor<T> getConstructor(类<?>... parameterTypes)  

            * Constructor<T> getDeclaredConstructor(类<?>... parameterTypes)  
            * Constructor<?>[] getDeclaredConstructors()  
        3. 获取成员方法们:
            * Method[] getMethods()  
            * Method getMethod(String name, 类<?>... parameterTypes)  

            * Method[] getDeclaredMethods()  
            * Method getDeclaredMethod(String name, 类<?>... parameterTypes)  

        4. 获取全类名   
            * String getName()  


* Field:成员变量
    * 操作:
        1. 设置值
            * void set(Object obj, Object value)  
        2. 获取值
            * get(Object obj)

        3. 忽略访问权限修饰符的安全检查
            * setAccessible(true):暴力反射

* Constructor:构造方法
    * 创建对象:
        * T newInstance(Object... initargs)  

        * 如果使用空参数构造方法创建对象,操作可以简化:Class对象的newInstance方法


* Method:方法对象
    * 执行方法:
        * Object invoke(Object obj, Object... args)  

    * 获取方法名称:
        * String getName:获取方法名


* 案例:
    * 需求:写一个"框架",不能改变该类的任何代码的前提下,可以帮我们创建任意类的对象,并且执行其中任意方法
        * 实现:
            1. 配置文件
            2. 反射
        * 步骤:
            1. 将需要创建的对象的全类名和需要执行的方法定义在配置文件中
            2. 在程序中加载读取配置文件
            3. 使用反射技术来加载类文件进内存
            4. 创建对象
            5. 执行方法
注解:
* 概念:说明程序的。给计算机看的
* 注释:用文字描述程序的。给程序员看的

* 定义:注解(Annotation),也叫元数据。一种代码级别的说明。它是JDK1.5及以后版本引入的一个特性,与类、接口、枚举是在同一个层次。它可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明,注释。
* 概念描述:
    * JDK1.5之后的新特性
    * 说明程序的
    * 使用注解:@注解名称
   

* 作用分类:
    ①编写文档:通过代码里标识的注解生成文档【生成文档doc文档】
    ②代码分析:通过代码里标识的注解对代码进行分析【使用反射】
    ③编译检查:通过代码里标识的注解让编译器能够实现基本的编译检查【Override】


* JDK中预定义的一些注解
    * @Override :检测被该注解标注的方法是否是继承自父类(接口)的
    * @Deprecated:该注解标注的内容,表示已过时
    * @SuppressWarnings:压制警告
        * 一般传递参数all  @SuppressWarnings("all")

* 自定义注解
    * 格式:
        元注解
        public @interface 注解名称{
            属性列表;
        }

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

    * 属性:接口中的抽象方法
        * 要求:
            1. 属性的返回值类型有下列取值
                * 基本数据类型
                * String
                * 枚举
                * 注解
                * 以上类型的数组

            2. 定义了属性,在使用时需要给属性赋值
                1. 如果定义属性时,使用default关键字给属性默认初始化值,则使用注解时,可以不进行属性的赋值。
                2. 如果只有一个属性需要赋值,并且属性的名称是value,则value可以省略,直接定义值即可。
                3. 数组赋值时,值使用{}包裹。如果数组中只有一个值,则{}可以省略
   
    * 元注解:用于描述注解的注解
        * @Target:描述注解能够作用的位置
            * ElementType取值:
                * TYPE:可以作用于类上
                * METHOD:可以作用于方法上
                * FIELD:可以作用于成员变量上
        * @Retention:描述注解被保留的阶段
            * @Retention(RetentionPolicy.RUNTIME):当前被描述的注解,会保留到class字节码文件中,并被JVM读取到
        * @Documented:描述注解是否被抽取到api文档中
        * @Inherited:描述注解是否被子类继承

* 在程序使用(解析)注解:获取注解中定义的属性值
    1. 获取注解定义的位置的对象  (Class,Method,Field)
    2. 获取指定的注解
        * getAnnotation(Class)
        //其实就是在内存中生成了一个该注解接口的子类实现对象
   3. 调用注解中的抽象方法获取配置的属性值

* 案例:简单的测试框架
* 小结:
    1. 以后大多数时候,我们会使用注解,而不是自定义注解
    2. 注解给谁用?
        1. 编译器
        2. 给解析程序用
    3. 注解不是程序的一部分,可以理解为注解就是一个标签



0 个回复

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