黑马程序员技术交流社区

标题: day11-day14笔记整理 [打印本页]

作者: lst168    时间: 2018-12-2 16:46
标题: day11-day14笔记整理
day11-day14笔记整理
1、C/S结构:

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

2、B/S结构:

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

3、UDP: User Datagram Protocol, 用户数据报协议

  特点:

                1. 无连接的不可靠协议

                2. 数据按包发送, 64K一个包

                3. 速度快效率高, 容易丢包

用于视频直播, 网络电话

4、TCP: Transmission Control Protocol, 传输控制协议

   特点:

                1. 需要建立连接的可靠协议  电话

                2. 数据传输无大小限制

                3. 速度慢效率低   重发机制

        用于文件下载, 浏览网页

5、TCP通信的三次握手: TCP协议中, 在发送数据的准备阶段,客户端与服务器之间的三次交互, 以保证连接的可靠

1. 客户端向服务端发送验证信息, 等待服务器确认

2. 服务端收到验证信息后, 回复客户端验证信息, 同时发送自己的一条验证信息

3. 客户端收到服务端回复的信息, 确认自己之前发的信息无误, 并再次向服务器发回服务端的验证信息

6、网络编程三要素:

        1. 通信协议  TCP

        2. IP地址

        3. 端口号

7、常用API:

ServerSocket(int port)、Socket accept()、void close()、Socket(String ip, int port)、OutputStream getOutputStream()、InputStream getInputStream()、void shutdownOutput()、void close()

8、两端通信时步骤:

1. 服务端程序需要先启动, 等待客户端的连接

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

9、客户端与服务端:

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

服务端(ServerSocket)没有IO流, 是通过获取到"每个客户端对象(Socket)的IO流对象"来进行通信的.

        使用"客户端的InputStream"读取客户端发来的数据

        使用"客户端的OutputStream"向客户端回写数据

10、

file:///C:/Users/84593/AppData/Local/YNote/data/weixinobU7VjjPKKI36BdgB_iGOVVii9Sk/2dc55cab93f3473695ce5cefb18b3151/02_tcp%25E9%2580%259A%25E4%25BF%25A1%25E7%259A%2584%25E6%25A6%2582%25E8%25BF%25B0.bmp

11、TCP客户端实现步骤:

1.创建一个客户端对象 Socket, 构造方法传入服务器的IP地址和端口号

2.使用Socket对象中的方法 getOutputStream() 获取网络字节输出流OutputStream对象

3.使用网络字节输出流OutputStream对象中的方法 write(), 给服务器发送数据

4.使用Socket对象中的方法 getInputStream() 获取网络字节输入流InputStream对象

5.使用网络字节输入流InputStream对象中的方法 read(), 读取服务器回写的数据

6.释放资源(Socket)

12、TCP服务端实现步骤:

1.创建服务器ServerSocket对象并指定服务器占用的端口号

2.使用ServerSocket对象中的方法 accept(), 获取到请求的客户端对象Socket

3.使用Socket对象中的方法 getInputStream() 获取网络字节输入流InputStream对象

4.使用网络字节输入流InputStream对象中的方法 read(), 读取客户端发送的数据

5.使用Socket对象中的方法 getOutputStream() 获取网络字节输出流OutputStream对象

6.使用网络字节输出流OutputStream对象中的方法 write(), 给客户端回写数据

7.释放资源(Socket,ServerSocket)

13、 阻塞问题:

TCP客户端:void shutdownOutput(): 关闭输出流, 告知服务端数据发送完毕

14、B/S结构分析:

file:///C:/Users/84593/AppData/Local/YNote/data/weixinobU7VjjPKKI36BdgB_iGOVVii9Sk/512d8a7692a44f639d9fea69dc26f285/05_%25E6%25A8%25A1%25E6%258B%259Fbs%25E6%259C%258D%25E5%258A%25A1%25E5%2599%25A8%25E5%2588%2586%25E6%259E%2590.bmp

15、模拟Web服务器实现步骤:

将web目录赋值粘贴到当前模块中

        创建ServerSocket对象, 监听8080端口

        使用 while(true) 死循环, 在循环中调用ServerSocket对象的 accept() 方法, 获取客户端Socket对象

        在循环中创建线程并 start(), 在线程中传入Runnable的匿名内部类对象

        Runnable匿名内部类中编写读取请求返回页面的代码:

                1. 通过客户端socket获取InputStream, 经过转换流传递给BufferedReader, 便于读取一行字符串

                BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));

                2. 使用BufferedReader只读一行字符串即可, 然后按照空格切割字符串, 获取数组1索引的内容, 然后从1索引截取字符串到末尾

                        即: GET /day11/web/index.html HTTP/1.1 获取其中的 day11/web/index.html

                3. 创建FileInputStream指向截取出来的字符串作为文件路径

                4. 通过客户端socket对象获取OutputStream, 先写入以下内容:

                        OutputStream os = socket.getOutputStream();

                        os.write("HTTP/1.1 200 OK\r\n".getBytes());

                        os.write("Content-Type: text/html\r\n".getBytes());

                        os.write("\r\n".getBytes());

                5. 然后使用一次读取一个byte[]数组方式, 通过刚才创建的FileInputStream对象和socket获取的OutputStream对象, 边读文件边写回客户端

                6. 关闭FileInputStream和socket, 释放资源

16、函数是接口:

函数式接口: JDK 8 新特性

    有且仅有一个抽象方法的接口, 适用于函数式编程场景的接口

    (默认方法, 静态方法, 私有方法, 与 java.lang.Object 类中定义相同的抽象方法, 都不算作抽象方法)

17、函数式接口的使用:

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

18、Supplier生产型函数式接口:

函数式接口: 生产型函数式接口

T get(): 用于获取一个对象或值,至于获取什么值, 怎么获取, 需要我们根据应用场景编写Lambda来实现

19、Consumer消费型函数式接口:

函数式接口: 消费型函数式接口

void accept(T t): 用于消费(使用)一个对象或值. 至于怎么消费, 要我们根据应用场景编写Lambda实现

default Consumer<T> andThen(Consumer<? super T> after): 拼接两个Consumer接口的Lambda对象实现连续操作. 谁写前面, 谁先消费

20、Consumer消费型函数式接口:

函数式接口: 消费型函数式接口

default Consumer<T> andThen(Consumer<? super T> after): 拼接两个Consumer接口的Lambda对象实现连续操作. 谁写前面, 谁先消费

21、Predicate条件判断函数式接口:

函数式接口: 条件接口, 用于判断

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(): 非, 取相反结果   !

22、Function转换型函数式接口:

Function<T,R>: 根据一个 T类型的数据 转换为 另一个R类型的数据

R apply(T t): 将T转换为R. 至于T和R是什么类型, 以及如何转换, 需要传递Lambda表达式实现

default <V> Function<T, V> andThen(Function<? super R, ? extends V> after): 拼接多个Function转换

23、流相比于集合的2个优点:

1. Pipelining(管道特性): "可以链式调用"

        Stream流对象的 延迟方法 调用后, 会返回新的Stream流对象, 可以链式调用

  每个方法类似于一条一条的管道, 衔接了不同的处理方案

2. 内部迭代特性: "不用写for循环"

        集合遍历通过 Iterator 或者 增强for, 显式的在集合外部进行迭代, 这叫做外部迭代

        Stream提供了内部迭代的方法 forEach(Consumer c), 可以直接调用遍历方法

24、使用Stream流的3个步骤:

        1. 获取数据源 (从"集合"或"数组"转换为"Stream"对象)

        2. 数据处理 (调用延迟方法, 编写处理方案)

        3. 获得结果 (调用终结方法, 启动开关)

25、获取Stream流对象的2种方式:

        1. 利用"Collection接口"中的默认方法 default Stream<E> stream() 方法: 集合转Stream对象

        2. 利用"Stream接口"中的静态方法 static <T> Stream<T> of(T... values): 数组转Stream对象

26、Stream流延迟方法:

Stream filter()、Stream map()、Stream limit()、Stream skip()

27、Stream流终结方法:

void forEach()、long count()

28、Stream流的特点: 只能使用一次

每次调用延迟方法返回的Stream流对象, 都是经过处理后返回的"新的Stream流对象"

之前的Stream流在调用方法后, 已经使用过并关闭了, 不能再次使用, 否则会抛出异常

29、方法引用: Method Reference

        如果Lambda表达式仅仅是调用一个已经存在的方法, 那就可以通过方法引用来替代Lambda表达式

        作用: 简化Lambda表达式

        :: 方法引用运算符, 它所在的表达式被称为方法引用

30、方法引用能简化以下场景: (方法名后不要写小括号)

                场景                                                格式                          简化之前的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

31、测试分类:

        1. 黑盒测试:不需要写代码, 给输入值, 看程序是否能够输出期望的值

                比如你下载一个APP, 随便点点点, APP闪退了

        2. 白盒测试:需要写代码的. 关注程序具体的执行流程

                比如今天学习的JUnit

32、JUnit使用步骤:

        1. 定义一个测试类(也叫测试用例)

                    包名:xxx.xxx.xx.test

                被测试的类名:   Calculator   

                对应的测试类名: CalculatorTest

        2. 定义测试方法:可以独立运行

                被测试的方法名:       add()      

                对应的测试方法名: testAdd()  

                    建议测试方法的返回值是void, 参数列表是空参

        3. 在方法上加 @Test 注解

        4. 在 @Test 注解上按 Alt+Enter, 选择 "Add 'JUnit4' to Classpath" 导入JUnit依赖环境

        5. 在方法名上右键, 选择 "Run '方法名()'"

33、断言: Assert

        使用断言操作来判断结果是否符合预期:



                Assert.assertEquals(期望的结果, 运算的结果);



                如果 期望的结果 和 运算的结果 相等, 则认为测试通过, 否则测试失败

33、@Before、@After

@Before: 修饰的方法会"在每个测试方法执行 之前"被执行

@After:  修饰的方法会"在每个测试方法执行 之后"被执行



注意:

        @Before, @After 修饰的方法可以有多个, 但是谁先执行是由JUnit内部来决定的, 没有明显的规律

        所以不要写多个@Before, @After 修饰的方法

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

        成员变量(字段): Field类的对象

        构造方法: Constructor类的对象

        成员方法: Method类的对象

        好处:

                1. 可以在程序运行过程中, 操作这些对象

                2. 可以解耦, 提高程序的可扩展性

35、获取一个类的字节码对象的3种方式:

        1. Class.forName("全类名")

                将字节码文件加载进内存,返回Class对象

                适用场景: 多用于配置文件,将类名定义在配置文件中. 读取文件, 加载类

        2. 类名.class                         .

                通过类名的属性class获取

                适用场景: 多用于参数的传递  getConstructor(String.class, int.class)

        3. 对象.getClass()

                getClass()方法在Object类中定义

                适用场景: 多用于对象的获取字节码的方式 p.getClass()

                同一个类的字节码对象, 只有"唯一的一个"

36、Class方法的API:

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(): 使用当前类的空参构造, 创建一个对象

37、利用反射实现一个框架

分析:

        实现技术:

                1. 配置文件: Properties 加载配置信息

                2. 反射: 使用配置文件中的全类名, 获取该类的字节码对象, 创建该类对象, 调用指定的方法

步骤:

        建类Person:

                定义成员方法 public void eat(){}. 方法中输出"吃东西"

        建类Student:

                定义成员方法 public void sleep(){} 方法中输出"睡觉"

        在src目录下创建一个文件: pro.properties

                填写以下键值对:

                className=Person类的全名

                methodName=eat

        建类Test, 定义main方法, 在main方法中:

                创建Properties对象

                获取当前Test类的字节码对象, 使用字节码对象调用getClassLoader()获取一个类加载器对象

                使用类加载器对象调用 getResourceAsStream("pro.properties") 获取一个InputStream对象

                使用Properties对象的load方法加载InputStream对象

                        // 获取类加载器

            ClassLoader cl = 当前类名.class.getClassLoader();

            InputStream is = cl.getResourceAsStream("pro.properties");

            // 创建Properties对象, 加载流

            Properties properties = new Properties();

            properties.load(is);

                从Properties对象中根据键"className"和"methodName"分别获取类名字符串和方法名字符串

                使用Class.forName("类的全名")方法传入读取到的类名, 获取该类字节码对象

                通过字节码对象创建该类对象, 并获取指定方法名的Method对象

                通过Method对象的invoke方法调用该方法, 查看是否调用吃饭方法

        修改配置文件中的类名为Student类的全名, 方法为sleep, 重新运行代码查看是否调用了睡觉方法

38、注解:

      注解: 说明程序的。给计算机看的

注释: 用文字描述程序的。给程序员看的

使用注解: @注解名称

作用分类:

        1. 编写文档: 通过代码里标识的注解生成文档

                (生成API文档 @author @version @since @param @return)

        2. 代码分析: 通过代码里标识的注解对代码进行分析 (使用反射)

        (JUnit提供的 @Test @Before @After)

        3. 编译检查: 通过代码里标识的注解让编译器能够实现基本的编译检查

                (@Override @FunctionalInterface)

39、JDK中预定义的一些注解:

        @Override: 检测被该注解标注的方法是否是"重写"父类(接口)的

        @Deprecated: 该注解标注的内容,表示已过时

        @SuppressWarnings: 压制警告. 一般传递参数all  @SuppressWarnings("all")

40、定义了属性,在使用注解时, 需要"给属性赋值" (其实是抽象方法的返回值)

        1. 属性使用 default 关键字指定默认值, 则可以不赋值

        2. 如果只有一个名为"value"的属性需要赋值, 则 value 可以省略, 直接写值即可

        3. 给数组赋值时,值使用{}包裹。如果数组中只有一个值,则{}可以省略

41、常用元注解:

        @Target: 描述注解能够作用的位置

                ElementType枚举的常用取值:

                        TYPE:可以作用于类上

                        METHOD:可以作用于方法上

                        FIELD:可以作用于成员变量上

                示例: @Target(value = {ElementType.TYPE, ElementType.METHOD})

        @Retention: 描述注解被保留的阶段

                RetentionPolicy枚举的取值:

                        SOURCE: 保留到源代码阶段

                        CLASS: 保留到类对象阶段

                        RUNTIME: 保留到运行时阶段

                示例: @Retention(RetentionPolicy.RUNTIME):保留注解到class字节码文件中并被JVM读取到

        @Documented: 加上后, 当前注解会被抽取到api文档中

        @Inherited: 加上后, 当前注解会被子类继承

42、获取注解属性值的步骤:

        1. 获取注解定义位置的对象 (Class对象(类注解), Field对象(成员变量注解), Method对象(方法注解))

        2. 调用 ProAnno a = cls.getAnnotation(ProAnno.class) 方法获取注解对象

        3. 通过注解对象调用抽象方法获取属性值

       

        // 比如获取一个类上的注解

    注解类型 注解变量名 = 被注解的类.class.getAnnotation(注解名.class);

    数据类型 变量名 = 注解变量名.抽象方法();



    ProAnno proAnno = Test.class.getAnnotation(ProAnno.class);









欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2