第六周总结
TCP多人聊天室实现
分析
- 客户端
功能:
1. 数据发送
2. 数据接收
技术:
1. socket
2. 输入流和输出流
3. 多线程,客户端功能模块有两个线程
聊天:
1. 群聊
2. 私聊
私聊前缀 @服务器用户ID号:msg
- 服务器
功能:
1. 数据转发
2. 用户注册
技术:
1. ServerSocket
2. 每一个用户对应的Sokcet对象
3. 多线程同时在线
4. HashMap<Integer, 用户>
数据转发:
私聊前缀判断
群聊所有人发送
客户端实现
数据发送:
使用输出流发送数据给服务器
遵从Runnable接口
数据接收:
使用输入流从服务器端接收数据
遵从Runnable接口
客户端主方法:
用户名提交
数据发送
数据接收
多线程启动
资源关闭问题
-代码中操作了大量的输入流和输出流,这里都需要进行关闭操作。
DataInputStream, DataOutputStream, BufferedReader, Socket
以上这些资源都是Closeable接口的实现类,都有对应的Close方法
封装一个工具类:
提供一个closeAll方法,参数为符合Closeable接口的实现类对象。
这里可以考虑可变长参数
Closeable… closeable
可变长参数在方法中使用的过程里面是对应一个数组,这里完成可以使用增强for来使用
工具类名:
CloseUtil
public static void closeAll(Closeable… closeable)
功能拓展
- 用户退出
用户输入指定字段之后可以退出
客户端Socket服务
服务端Socket服务
涉及资源关闭,线程关闭
- 用户异常退出
在运行过程中发现问题,需要及时处理,关闭对应的资源,终止对应的线程
- 服务器保存所有的聊天记录
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格式工具
- 常用的工具:
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文件的
代码检查:
继承重写,或者说接口遵从之后的实现中,存在@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 子接口
属性使用的格式[实际按照方法格式操作]
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 常用
- 使用反射获取注解中的内容【用途】
- 使用注解测试代码运行【用途】
注解使用总结
-
- 注解以后大多数情况下,都是使用过程,而不是自定义,会使用到框架中预处理好的注解。
- 注解是给谁用的?
a. 编译器
b. 解析代码使用
c. JVM运行代码使用
- 注解是一个标签,有时候是做标记的,有时候标签是有属性的。
函数式接口
函数式接口
函数式编程思想
- Lambda延迟执行
- 日志记录
- 日志是否保存会存在等级限制
演示一个根据不同的等级来记录log日志
要求:
等级 == 1 记录log日志,其他情况不记录
- 使用函数式接口提供日志信息功能
- Lambda作为方法参数和返回值
Java中提供的常用函数式接口
- JDK常用函数式接口概述
- java.util.function包名 。提供了很多函数式接口
规范了一些操作,提升了开发效率,更加专注于目的性!!!
Supplier 生产者, 返回一个指定类型的数据
Consumer 消费者, 消耗一个指定类型的数据
Predicate 判断调节,过滤使用
Function<T,R> 类型转换,根据你指定的类型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<T,R> 类型转换
- 使用R apply(T t)
转换指定类型T到R
- andThen
Stream流
Stream流引入
- Stream流完全不是I/O流,按照流水线处理方式来考虑代码中的思想。
JDK1.8 之后,我们拥有了Lambda表达式,让代码的中心偏向解决实际问题,直到重点,可以提高效率。
Stream流中使用了大量Lambda表达式,利用Lambda操作方式,提供开发效率
传统遍历方式和Stream类处理方式对比
Stream流对应的思想
- Stream流有一些特征:
- 带有很多Stream流操作的方法, filter,limit,map,sorted,skip…这些方法大多是都会使用到函数式接口,那就意味着有lambda表达式
- 整个Stream流模型操作过程中,只有执行到count,foreach这些方法,操作真正的执行中的模型,如果不存在结果导向,中间的所有操作是无效的,这里得益于Lambda表达式的延后性
- Stream流是存在一定的管道性 Pipelining 流水线
获取Stream流
- java.util.stream.Stream JDK1.8的新特征
- 所有的Collection集合都有对应的Stream();
- 可以通过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<? super T> action);
/*
终结方法:
需要一个Consumer接口进行操作处理,消耗一个数据
Consumer接口是一个【函数式接口】那就可以使用Lambda表达式
Consumer接口中方法是
void accept(T t);
*/
- filter方法
- Stream filter(Predicate<? super T> condition);
/*
filter是过滤方式,需要的参数是Predicate接口,Predicate是一个函数式接口,可以直接使用Lambda表达运行。
这里返回值类型是Stream类对象,是经过过滤之后的Stream类型,可以进行链式操作
Predicate接口中需要实现的方法
boolean test(T t);
*/
- stream has already been operated upon or closed
为何会出现这个错误?
因为调用终结方法后,Stream流已经被销毁,所以不能再对Stream流进行操作。
- map方法
- <R> Stream<R> map(Function<? super T, ? super R> fun);
/*
类型转换操作,得到的一个转换之后数据类型的Stream流对象
这里需要的参数是Function函数式接口,
R apply(T t);
T类型的数据转换成R类型数据
*/
- count方法【终结方法】
- long count();
/*
返回当前Stream流对象中有多少个元素
类似有Collection接口下的size(). String的length();
【终结方法】
一旦执行Stream流对象被关闭
*/
- limit方法
- Stream<T> limit(long maxSize);
/*
对于当前Stream流对象操作的数据进行限制操作,限制个数到maxSize
例如:
Stream流中保存的有10个元素,limit 5 ==> 前五个元素
*/
- skip方法
- Stream<T> skip(long n);
/*
返回值依然是一个Stream流对象,这里跳过当前Stream流对象前n个元素
*/
- concat方法
- static Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b)
/*
拼接两个Stream流对象,是一个静态方法,得到新的Stream流对象
*/
- 原始操作方式和Stream流方式对比
- 1. 一个String类型的字符串集合,"1,骚磊,16"
- 过滤没有5的数据
- 跳过前三个数据
- 限制得到前5个数据
- 两个String类型集合字符串合并
- 转换成Person类型
- 展示数据
方法引用
Lambda冗余问题以及方法引用初识
方法引用小要求
- testPrint(“郑州加油!!!”, str -> System.out.println(str));
testPrint(“郑州加油!!!”, System.out::println);
- 明确对象
对象 ==> 调用者
类对象,类名,super,this,构造方法,数组构造方法
- 明确的执行方法
该方法只有名字不需要显式出现参数
- 需要处理的数据
【联想,推导,省略】
- :: 方法引用格式
通过类对象来执行方法引用
- 明确执行的方法
自定义
- 处理的数据
简单要求为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