Java知识点——第六周总结

第六周总结

TCP多人聊天室实现

分析

  • 客户端
    功能:
    1. 数据发送
    2. 数据接收
    技术:
    1. socket
    2. 输入流和输出流
    3. 多线程,客户端功能模块有两个线程
    聊天:
    1. 群聊
    2. 私聊
    私聊前缀 @服务器用户ID号:msg
  • 服务器
    功能:
    1. 数据转发
    2. 用户注册
    技术:
    1. ServerSocket
    2. 每一个用户对应的Sokcet对象
    3. 多线程同时在线
    4. HashMap
    数据转发:
    私聊前缀判断
    群聊所有人发送

客户端实现

数据发送:
使用输出流发送数据给服务器
遵从Runnable接口
数据接收:
使用输入流从服务器端接收数据
遵从Runnable接口

客户端主方法:
用户名提交
数据发送
数据接收
多线程启动

资源关闭问题

-代码中操作了大量的输入流和输出流,这里都需要进行关闭操作。
DataInputStream, DataOutputStream, BufferedReader, Socket

以上这些资源都是Closeable接口的实现类,都有对应的Close方法
封装一个工具类:
提供一个closeAll方法,参数为符合Closeable接口的实现类对象。
这里可以考虑可变长参数
Closeable… closeable

可变长参数在方法中使用的过程里面是对应一个数组,这里完成可以使用增强for来使用

工具类名:
CloseUtil
public static void closeAll(Closeable… closeable)

功能拓展

  1. 用户退出
    用户输入指定字段之后可以退出
    客户端Socket服务
    服务端Socket服务
    涉及资源关闭,线程关闭

  2. 用户异常退出
    在运行过程中发现问题,需要及时处理,关闭对应的资源,终止对应的线程

  3. 服务器保存所有的聊天记录

JSON

JSON格式概述

  • JSON
    JavaScript
    JavaScript Object Notation
    (JavaScript Object Notation,JavaScript对象表示法,读作/ˈdʒeɪsən/)是一种由道格拉斯·克罗克福特构想和设计、轻量级的数据交换语言,该语言以易于让人阅读的文字为基础,用来传输由属性值或者序列性的值组成的数据对象。尽管JSON是JavaScript的一个子集,但JSON是独立于语言的文本格式,并且采用了类似于C语言家族的一些习惯

数据格式

JSON对象

{
“ID”:001,
“name”:“骚磊”,
“age”:16
}

特征:
1. 数据形式键值对形式
“键”:值
2. 数据支持 字符串,数字,true false
3. {} 大括号以内的数据

  • JSON对象数组

    • [
      {
      “ID”:1,
      “name”:“骚磊”,
      “age”:16
      },
      {
      “ID”:2,
      “name”:“骚杰”,
      “age”:66
      },
      {
      “ID”:3,
      “name”:“康康”,
      “age”:15
      }
      ]

特征:
1. 数据使用[]包含
2. 在[]都是JSON格式对象
3. 每一个对象之间使用逗号隔开,同时最后一个元素不需要逗号

  • JSON数据验证

    • JSON格式验证

解析JSON格式工具

  • 常用的工具:
    Gson,fastjson, Jackson
    以上都是第三方工具,需要导入对应的jar包按使用XML导包

  • FastJson内容

    • JSON核心类
      JSON核心类提供解析和转化方法,用于解析JSON数据格式,同时用于转换类对象到JSON格式,该类对象需要符合JavaBean规范
      –| JSONArray
      存在按照键值对方式解析获取数据,同时存在一定的List方法
      –| JSONObject
      获取对应的类对象,指定键值对对应数据的方法
  • 解析演示

注解

注解概述

  • 注解解释

    • 注释:
      解释代码,给程序员看

注解:
Java语言中的类、方法、变量、参数和包等都可以被标注。和Javadoc不同,Java标注可以通过反射获取标注内容。在编译器生成类文件时,标注可以被嵌入到字节码中。Java虚拟机可以保留标注内容,在运行时可以获取到标注内容。 当然它也支持自定义Java标注
JDK1.5之后的特征
用于说明程序
一般在框架中使用
格式:
@AnnotationName

文档注释:
@param @return @Exeception 从根本上是一个注释,不存在代码编译,不会生成对应的.class字节码问题,只是提供给JavaDoc API文件生成工具。作为标记生成对应的文档。

注解是有一部分参与编译
@Override并不是没编译就有效果了,是因为不管是Eclipse还是IDEA都可以预编译Java代码生成对应的.class文件的

  • 注解作用

    • 生成文档:
      代码中生成对应的JavaDoc API文档
      @param @return

    【IDEA JavaDoc工具使用参数】
    Other Command Line Arguments : -encoding utf-8 -charset utf-8
    解决中文乱码,因为IDEA默认编码集为UTF-8 Windows GKB

代码检查:
继承重写,或者说接口遵从之后的实现中,存在@Override

代码数据获取: [小框架]
通过反射获取指定注解中的一些内容,例如 配置,数据,操作,验证。。。

  • Java中预定义的一些注解

    • @Override:
      重写/实现方法的情况下,检查方法声明是否和父类或者接口中的方法声明一致。强制格式检查。

@Deprecated
标注当前方法已过时,例如 Data日期类内的一些方法

@SuppressWarnings(“all”)
压制警告,可以用于一些代码中存在明确无异常的情况下,压制一些警告

Java中自定义注解

  • Java中自定义注解的方式

    • 格式:
      public @interface AnnotationName {
      属性列表;
      }

Annotation注解是可以编译得到对应的.class字节码文件,验证了注解是可以参与编译过程的

通过反编译工具可以得到一下内容
【Annotation本质】
public interface MyAnnotation1 extends java.lang.annotation.Annotation {
}

MyAnnotation1
本质是一个interface,同时java.lang.annotation.Annotation 子接口

  • Annotation注解属性【难点】

    • 属性:
      开发书写代码使用注解的方式中,数据使用方式更加偏向于属性概念。
      使用
      1. 在书写代码中使用
      @MyAnnotation(id=1, name=“骚磊”, age=16)
      2. 利用反射时,会涉及到getXXX方法
      通过属性名获取对应值的概念来完成的

    【但是实际上是利用abstract方法来完成属性概念的】

属性使用的格式[实际按照方法格式操作]
1. 属性的值数据类型和对应具体数据 ==> 返回值类型和返回的数据
属性类型支持:
a. 基本数据类型
b. String类型
c. 其他的注解类型
d. 枚举类型
枚举就是一个带有名字的常量,为了更好的域阅读性和操作
e. 以上类型对相应的数组

	属性值要求
		a. 定义属性时可以使用default关键字,加上默认值,该属性在使用的过程中是
		没有强制要求属性值,如果没有赋予属性值,采用对应的默认值操作,如果赋
		值,使用对应值
		
		b. 如果注解中有且只有一个value属性,或者说注解中除value属性之外,都有
		默认值,不管是类,方法,成员变量,包使用当前注解是可以直接在括号内加入
		对应数据类型数值、
		
		c. 如果属性是数组类型, {}大括号保存,并且不同的内容,使用,隔开
2. 属性的键名字 ==> 方法的名字
  • 元注解

    • 给予注解的解释,用于约束注解的一些操作问题
      @Retention -
      标识这个注解怎么保存,是只在代码中,还是编入class文件中,或者是在运行时可以通过反射访问。
      RetentionPolicy.RUNTIME:当前注解会编译生成对应的.class字节码文件,并且可以加
      载到JVM中,参与代码执行
      RetentionPolicy.CLASS:

别纠结,记下就好:
RetentionPolicy.SOURCE:注解将被编译器丢弃(该类型的注解信息只会保留在源码里,源码经过编译后,注解信息会被丢弃,不会保留在编译好的class文件里)
@Override
对应属性RetentionPolicy.SOURCE
在代码编译过程中,检查方法格式是否正确,不参与代码运行和解析。

@Documented
标记这些注解是否包含在用户文档中。
是否可以通过JavaDoc工具,生成对应的API文档

@Target
标记这个注解应该是哪种 Java 成员。
属性:
ElementType
TYPE: 当前注解可以用于类声明
METHOD: 当前注解可以用于方法声明位置
FIELD:当前注解可以用于成员变量声明位置
@Inherited
标记这个注解是继承于哪个注解类(默认 注解并没有继承于任何子类)

【重点】
@Target目标
可以作用范围 类,方法,成员变量…
@Retention
RetentionPolicy.RUNTIME 常用

  • 使用反射获取注解中的内容【用途】
  • 使用注解测试代码运行【用途】

注解使用总结

    1. 注解以后大多数情况下,都是使用过程,而不是自定义,会使用到框架中预处理好的注解。
  1. 注解是给谁用的?
    a. 编译器
    b. 解析代码使用
    c. JVM运行代码使用
  2. 注解是一个标签,有时候是做标记的,有时候标签是有属性的。

函数式接口

函数式接口

  • 概述

    • 如果说一个接口内有且只有一个方法,而且该方法是一个缺省属性为public abstract方法,该接口可以称之为是一个函数式接口。
      自定义函数式接口,还有系统中提供的函数式接口
      Comparator Runnable

    可以直接理解JDK1.8的新特征,Lambda表达式来使用。

    Lambda表达式对比匿名内部类使用
    1. 简化了代码结构
    2. 节约了内存资源
    3. 让程序员更加关注,我要做什么,而不是为了做什么需要完成什么

  • @FunctionalInterface 使用

    • /**
  • 使用@FunctionalInterface检查函数式接口格式问题
  • 要求当前接口中有且只有一个缺省属性为public abstract的方法
  • @author Anonymous 2020/3/11 9:55
    */
    @FunctionalInterface
    public interface FunctionalType {
    void test();
    }
  • 使用自定义的函数式接口作为方法的参数使用

函数式编程思想

  • Lambda延迟执行

    • 日志记录

      • 日志是否保存会存在等级限制
        演示一个根据不同的等级来记录log日志
        要求:
        等级 == 1 记录log日志,其他情况不记录
    • 使用函数式接口提供日志信息功能

  • Lambda作为方法参数和返回值

Java中提供的常用函数式接口

  • JDK常用函数式接口概述

    • java.util.function包名 。提供了很多函数式接口
      规范了一些操作,提升了开发效率,更加专注于目的性!!!

    Supplier 生产者, 返回一个指定类型的数据
    Consumer 消费者, 消耗一个指定类型的数据
    Predicate 判断调节,过滤使用
    Function 类型转换,根据你指定的类型T, 转换成对应类型R

  • Supplier 生产者,返回一个指定的数据类型

    • java.util.function.Supplier
      有且只有一个方法
      T get();
      不需要参数,返回指定T类型数据
      什么都不吃,挤的都是输出。。。
    • 找出数组中最大值所在下标位置
    • 引出满足更多普适性代码的函数式接口使用方式
  • Consumer消费者,处理数据

    • Consumer
      操作使用的方式是
      void accept(T t);
      根据接口指定的数据类型接收对应数据,进行处理和消费,对外没有任何的返回
      至于处理的过程,展示,处理,计算。。。
    • andThen
  • Predicate 判断数据是否合适,返回true/false

    • Predicate一般用于条件判断,过滤数据的方法
      函数式接口中指定的方法
      boolean test(T t);
      处理T类型数据,返回boolean true / false
    • and 与
    • or 或
    • negate 非
    • ArrayList中使用Predicate删除指定数据
  • Function 类型转换

    • 使用R apply(T t)
      转换指定类型T到R
    • andThen

Stream流

Stream流引入

  • Stream流完全不是I/O流,按照流水线处理方式来考虑代码中的思想。
    JDK1.8 之后,我们拥有了Lambda表达式,让代码的中心偏向解决实际问题,直到重点,可以提高效率。
    Stream流中使用了大量Lambda表达式,利用Lambda操作方式,提供开发效率

传统遍历方式和Stream类处理方式对比

Stream流对应的思想

  • Stream流有一些特征:
    1. 带有很多Stream流操作的方法, filter,limit,map,sorted,skip…这些方法大多是都会使用到函数式接口,那就意味着有lambda表达式
    2. 整个Stream流模型操作过程中,只有执行到count,foreach这些方法,操作真正的执行中的模型,如果不存在结果导向,中间的所有操作是无效的,这里得益于Lambda表达式的延后性
    3. Stream流是存在一定的管道性 Pipelining 流水线

获取Stream流

  • java.util.stream.Stream JDK1.8的新特征
    1. 所有的Collection集合都有对应的Stream();
    2. 可以通过Stream类中的static Stream of()获取
      static Stream of(T… t);
      static Stream of(T t);

Stream常用方法

  • 延迟方法:
    返回值类型依然是Stream接口本身,并没有影响我们操作真正的资源
    允许链式操作,
    例如
    filter(XXX).limit(XXX).sorted(XXX).
    终结方法:
    返回值类型不是Stream接口本身,要么处理数据,要么返回其他类型数据,并且不再支持Stream流对象链式操作,count,foreach

    • foreach方法【终结方法】

      • void foreach(Consumer action);
        /*
        终结方法:
        需要一个Consumer接口进行操作处理,消耗一个数据
        Consumer接口是一个【函数式接口】那就可以使用Lambda表达式
        Consumer接口中方法是
        void accept(T t);
        */
    • filter方法

      • Stream filter(Predicate condition);
        /*
        filter是过滤方式,需要的参数是Predicate接口,Predicate是一个函数式接口,可以直接使用Lambda表达运行。
        这里返回值类型是Stream类对象,是经过过滤之后的Stream类型,可以进行链式操作
        Predicate接口中需要实现的方法
        boolean test(T t);
        */

        • stream has already been operated upon or closed

为何会出现这个错误?

因为调用终结方法后,Stream流已经被销毁,所以不能再对Stream流进行操作。

- map方法

	-  Stream map(Function fun);

/*
类型转换操作,得到的一个转换之后数据类型的Stream流对象
这里需要的参数是Function函数式接口,
R apply(T t);
T类型的数据转换成R类型数据
*/

- count方法【终结方法】

	- long count();

/*
返回当前Stream流对象中有多少个元素
类似有Collection接口下的size(). String的length();
【终结方法】
一旦执行Stream流对象被关闭
*/

-  limit方法

	- Stream limit(long maxSize);

/*
对于当前Stream流对象操作的数据进行限制操作,限制个数到maxSize
例如:
Stream流中保存的有10个元素,limit 5 ==> 前五个元素
*/

- skip方法

	- Stream skip(long n);

/*
返回值依然是一个Stream流对象,这里跳过当前Stream流对象前n个元素
*/

- concat方法

	- static Stream concat(Stream a, Stream b) 

/*
拼接两个Stream流对象,是一个静态方法,得到新的Stream流对象
*/

- 原始操作方式和Stream流方式对比

	- 1. 一个String类型的字符串集合,"1,骚磊,16"
  1. 过滤没有5的数据
  2. 跳过前三个数据
  3. 限制得到前5个数据
  4. 两个String类型集合字符串合并
  5. 转换成Person类型
  6. 展示数据

方法引用

Lambda冗余问题以及方法引用初识

方法引用小要求

  • testPrint(“郑州加油!!!”, str -> System.out.println(str));

testPrint(“郑州加油!!!”, System.out::println);

  1. 明确对象
    对象 ==> 调用者
    类对象,类名,super,this,构造方法,数组构造方法
  2. 明确的执行方法
    该方法只有名字不需要显式出现参数
  3. 需要处理的数据
    【联想,推导,省略】
  4. :: 方法引用格式

通过类对象来执行方法引用

    1. 明确对象
      类对象
  1. 明确执行的方法
    自定义
  2. 处理的数据
    简单要求为String类型

通过类名来执行方法引用

通过super关键字执行方法引用

通过this关键字执行方法引用

类构造方法引用

数组创建方式引用

单例模式

要求

  • 当前类有且只有一个对象,一旦当前类存在一个对象之后,无法在重新创建当前类的对象。就算是你要创建,代码返回的对象依然是上一次创建的对象。
    懒汉模式,饿汉模式

单例模式推导【懒汉】

另一种单例模式【饿汉】

NIO

BIO概述

  • BIO
    BIO ==> Basic IO (基本IO), Block IO(阻塞IO)
    Scanner操作,文件读写操作,Socket数据传输操作… 都是BIO

    比如TCP群聊,私聊聊天室
    Socket涉及到的IO,也是BIO
    资源浪费:
    1. 多线程,每一个Socket会对应一个线程,如果用户量巨大,会导致线程过
    多,资源处理过多
    2. 采用阻塞状态,一旦进入阻塞,代码无法执行其他操作。
    3. 承载量一般,吞吐量比较小,同时可靠性不佳

NIO概述

  • NIO
    NIO ==> New IO(新IO), Non-Block IO(非阻塞IO)
    NIO非阻塞IO,允许当前程序在处理IO事务时,不会影响其他程序的运行,可以在不使用多线程的情况下,满足IO操作要求。
    三大核心部分:
    通道
    Channel
    文件操作,网络数据传递操作使用的通道
    缓冲
    Buffer
    缓冲使用可以提高操作效率,减少不必要的读写次数
    选择器
    Selector
    真·核心 老大 boss

Buffer Channel完成文件操作

  • 常用API

    • java.nio.Buffer
      Buffer缓冲区
      ByteBuffer 字节缓冲 常用
      ShortBuffer
      IntBuffer
      LongBuffer
      CharBuffer 字符缓冲 常用
      FloatBuffer
      DoubleBuffer

常用方法:
public static ByteBuffer allocate(int capacity);
按照指定的字节数分配对应的缓冲区空间,保存字节数据
public byte get();
从字节缓冲区对象中读取一个byte类型数组
public final Buffer flip();
翻转缓冲区,回到缓冲区的开始位置。
public static ByteBuffer wrap(byte[] arr);
存入一个byte类型数组到缓冲区,会得到一个新的ByteBuffer
public static ByteBuffer put(byte[] b);
将字节数组存入缓冲区

Channel接口,通道接口
FileChannel 文件操作通道
DatagramChannel UDP协议数据包操作的Channel
ServerSocketChannel TCP服务端ServerSocket对应Channel
SocketChannel TCP客户端Socket对应Channel

首先操作文件,以FileChannel为例
public long read(ByteBuffer buffer);
从通道中读取数据到ByteBuffer中
public long write(ByteBuffer buffer);
从Buffer中写数据到通道中
public long transferFrom(ReadableByteChannel src, long position, long count)
从指定srcChannel中,指定位置position开始,读取count个元素,到当前通道中
文件复制操作。

public long	transferTo(long position, long count, WritableByteChannel target) 
将当前通道中的数据写入到target中,从当前通道的position位置开始,计数count
  • 操作文件数据

网络编程使用NIO【重点】

  • Selector选择器老大

    • Selector
      选择器,网络编程使用NIO的大哥!!!
      服务器可以执行一个线程,运行Selector程序,进行监听操作。
      新连接, 已经连接, 读取数据,写入数据

Selector常用方法:
public static Selector Open();
得到一个选择器对象
public int select(long timeout);
监听所有注册通道,存在IO流操作是,会将对应的信息SelectionKey存入到内部的集
合中,参数是一个超时时间
public Set selectionKeys();
返回当前Selector内部集合中保存的所有SelectionKey

  • SelectionKey

    • SelectionKey
      表示Selector和网络通道之间的关系
      int OP_ACCEPT; 16 需要连接
      int OP_CONNECT; 8 已经连接
      int OP_READ; 1 读取操作
      int OP_WRITE; 4 写入操作
      SelectionKey
      public abstract Selector selector();
      得到与之关联的 Selector 对象
      public abstract SelectableChannel channel();
      得到与之关联的通道
      public final Object attachment();
      得到与之关联的共享数据
      public abstract SelectionKey interestOps(int ops);
      设置或改变监听事件
      public final boolean isAcceptable();
      是否可以 accept
      public final boolean isReadable();
      是否可以读
      public final boolean isWritable();
      是否可以写
  • ServerSocketChannel

    • ServerSocketChannel
      服务端Socket程序对应的Channel通道
      常用方法:
      public static ServerSocketChannel open();
      开启服务器ServerSocketChannel通道,等于开始服务器程序
      public final ServerSocketChannel bind(SocketAddress local);
      设置服务器端端口号
      public final SelectableChannel configureBlocking(boolean block);
      设置阻塞或非阻塞模式, 取值 false 表示采用非阻塞模式
      public SocketChannel accept();
      [非阻塞]
      获取一个客户端连接,并且得到对应的操作通道
      public final SelectionKey register(Selector sel, int ops);
      [重点方法]
      注册当前选择器,并且选择监听什么事件
  • SocketChannel

    • SocketChannel
      客户端Socket对应的Channel对象

常用方法:
public static SocketChannel open();
打卡一个Socket客户端Channel对象
public final SelectableChannel configureBlocking(boolean block)
这里可以设置是阻塞状态,还是非阻塞状态
false,表示非阻塞
public boolean connect(SocketAddress remote);
连接服务器
public boolean finishConnect();
如果connect连接失败,可以通过finishConnect继续连接
public int write(ByteBuffer buf);
写入数据到缓冲流中
public int read(ByteBuffer buf); 、
从缓冲流中读取数据
public final SelectionKey register(Selector sel, int ops, Object attechment);
注册当前SocketChannel,选择对应的监听操作,并且可以带有Object attachment参数
public final void close();
关闭SocketChannel

XMind: ZEN - Trial Version

你可能感兴趣的:(Java知识点——第六周总结)