IO
流
定义:[流是一组有 顺序的,单向的,动态 的字符或者字节的集合]{.red}
《Java 编程思想》定义:流代表了任何有能力产出数据的 数据源对象 或者是有能力接收数据的 接收端对象(流为什么会是一种对象呢?)
核心:[屏蔽输入输出设备实际处理数据的细节]{.red}
理解:就是不需要手动创建缓冲区等结构去处理数据,只需要创建流对象就可以帮助我们完成数据的传输
分类:
按照流向分类:输入流和输出流
核心:[输入输出流的名称都是相对而言的]{.red}
输入流:从数据输入源流向数据接收源(外部设备数据流向内存)
输出流:从数据接收源流向数据接收源(内存数据流向外部设备)
按照功能分类:节点流(基本类)和处理流(装饰类)
核心:设计模式:装饰器模式(
Java I/O
就是围绕装饰器模式进行设计的)节点类:进行最基本的数据传输功能的类:
CharArrayReader、ByteArrayReader...
处理类:扩展数据传输功能的类:
BufferedReader...
按照数据单位分类:字节流和字符流
字节流:[可以读取任何类型的文件]{.red},主要用于读取二进制文件(图片、视频文件等):
InputStream、OutputStream..
字符流:[仅能够读取文本文件]{.red}:
Reader、Writer...
细节:
[设计之初时字节流是没有自带缓冲区的,后续添加的字符流就默认携带缓冲区]{.red}
JVM
内部运行采用UTF-16BE
编码,class
文件采用UTF-8
编码,Java
源代码可以采用任何形式编码注:
JVM
内部和class
文件采用的编码并不相同,也就意味着中间会发生转换
核心类
文件类
:::info
- 文件工具类在 JDK 1.7 之后又进行了更新 -> 提供了更加方便的方法
- 实际开发中已经很少直接操作文件了,通常借助数据库管理文件
:::
定义:提供操作文件相关的方法,[不包含读取和写入文件内容的操作]{.red}
特点:文件类既可以表示文件也可以表示目录
继承 & 实现:
- 实现
Serializable
接口:文件类可以进行序列化操作 - 实现
Comparable
接口:文件类可以相互比较
- 实现
构造方法:[只有有参构造器没有无参构造器]{.red}
/*最常使用的构造方法:传入文件的路径名称*/
public File(String pathname) {
// 如果传入的路径是空才会报错,如果传入的路径是错误的是不会抛出异常的
if (pathname == null) {
throw new NullPointerException();
}
}
/*第一个参数传入父目录,第二个参数传入子目录*/
public File(String parent, String child) {...}
/*传入文件的 URI 地址*/
public File(URI uri) {...}方法
创建文件、目录、删除文件、目录
public static void create() throws IOException
{
File file = new File("file.txt");
// 创建文件
file.createNewFile();
// 单级目录
File directory = new File("first");
// 创建单级目录
directory.mkdir();
// 多级目录
File directories = new File("first/second");
// 创建多级目录
directories.mkdirs();
// 删除文件或者目录
file.delete();
directory.delete();
}判断路径表示的是文件还是目录
public static void judge()
{
File file = new File("file.txt");
// 判断路径表示文件或者目录是否存在
file.exists();
// 判断是否是文件
file.isFile();
// 判断是否为目录
file.isDirectory();
}获取表示文件或者目录的路径
public static void getPath()
{
File directory = new File("D:\\JavaWeb\\Java 网络编程\\src\\main\\java\\myio");
// 获取路径表示的目录或者文件名称
directory.getName();
// 获取该目录或者文件的父路径
directory.getParent();
// 获取绝对路径
directory.getAbsolutePath();
}遍历目录
// 非递归遍历
public static void listFile(File directory)
{
// 如果文件不存在就创建目录
if (!directory.exists())
directory.mkdir();
// 如果不是目录就直接抛出异常
if (!directory.isDirectory())
throw new IllegalArgumentException("这不是目录!");
String[] files = directory.list();
for (String file : files)
{
System.out.println("子路径: " + file);
}
}
// 递归遍历
public static void listFileByRecursive(File directory)
{
// 如果文件不存在就创建目录
if (!directory.exists())
directory.mkdir();
// 如果传入的是文件就直接返回
if (directory.isFile())
return;
File[] files = directory.listFiles();
for (File file : files)
{
if (directory.isDirectory())
listFileByRecursive(file);
System.out.println("父路径: " + file.getParent() + "\t" + "子路径: " + file.getName());
System.out.println("绝对路径: " + file.getAbsolutePath());
}
}
网络类
InetSocketAddress 类
定义:用于表示套接字的类
[注:Socket 类构造方法内部就是创建了 InetSocketAddress 对象,所以也表示套接字]{.blue}
构造方法
// 服务器常调用的构造方法
public InetSocketAddress(int port) {
this(InetAddress.anyLocalAddress(), port);
}
// 客户端常调用的构造方法
public InetSocketAddress(String hostname, int port) {
checkHost(hostname);
InetAddress addr = null;
String host = null;
try {
addr = InetAddress.getByName(hostname);
} catch(UnknownHostException e) {
host = hostname;
}
holder = new InetSocketAddressHolder(host, addr, checkPort(port));
}
URL 类
定义:表示统一资源定位符,也就是表示某个网页的资源
构造方法
// 仅指定 URL 地址
// 不指定端口号时,访问该网址的时候使用默认端口号,但是调用获取端口号的方法时就会返回 -1
public URL(String spec) throws MalformedURLException {
this(null, spec);
}
// 访问的 URL 采用的协议、IP 地址、端口号、需要获取的资源的地址
public URL(String protocol, String host, int port, String file) throws MalformedURLException
{
this(protocol, host, port, file, null);
}方法
// 获取该 URL 携带的查询参数:? 之后表示的就是携带的参数
public String getQuery() {
return query;
}
// 获取该 URL 的端口号:如果没有指定就会返回 -1
public int getPort() {
return port;
}
// 获取 URL 采用的协议
public String getProtocol() {
return protocol;
}
// 获取该 URL 的流对象:可以借助流对象传输 URL 表示的资源信息
public final InputStream openStream() throws java.io.IOException {
return openConnection().getInputStream();
}
// 获取该 URL 表示的内容:实际获取的是网页的源代码(前端)
public final Object getContent() throws java.io.IOException {
return openConnection().getContent();
}
Socket 类
核心:[基于 TCP 协议设计的网络通信类]{.red}
前提:每个采用 TCP 协议并且想要参与网络通信的进程都需要创建 自己的 [Socket(套接字)]{.blue}
客户端(Socket 类):
构造方法:[需要传入访问的服务器的端口和服务器的主机地址]{.red}
/*Socket 类常用的构造方法*/
public Socket(String host, int port) throws UnknownHostException, IOException
{
this(host != null ? new InetSocketAddress(host, port) :
new InetSocketAddress(InetAddress.getByName(null), port),
(SocketAddress) null, true);
}
private Socket(SocketAddress address, SocketAddress localAddr, boolean stream) throws IOException {
// 客户端连接到服务器的端口号
connect(address)
}
服务器(ServerSocket 类):
构造方法:[仅需要传入服务器当前的端口号;默认主机地址为 127.0.0.1]{.red}
/*ServerSocket 类常用的构造方法*/
public ServerSocket(int port) throws IOException {
this(port, 50, null);
}
/*调用的另一个构造方法*/
public ServerSocket(int port, int backlog, InetAddress bindAddr) throws IOException {
...
/*将服务器创建的套接字绑定到相应的网卡设备上:如果没有绑定是没有办法通信的*/
bind(new InetSocketAddress(bindAddr, port), backlog);
}核心方法:
accept()
==阻塞式监听端口==:只要没有客户端连接就一直阻塞在当前位置监听;客户端发送连接请求,该方法立刻返回相应的客户端套接字
流类
:::info
所有传统的 I/O 工具类的核心方法都是输入输出和各式各样的重载,没有太大的难度
参考博客:【Java基础-3】吃透Java IO:字节流、字符流、缓冲流
:::
:::warning
- 装饰类通常会添加许多基础类没有的方法,所以可以额外增加基本类的功能
- [多个装饰类叠加的时候可能导致重写了同一个方法,所以这样就只能使用最外面那个装饰类的方法]{.blue}
:::
字节流框架图
:::info
通常不再使用字节流,因为效率相对字符流较低
:::

字节输入流
- 节点类(基本类)
ByteArrayInputStream
:数据输入源是 [字节数组]{.red}FileInputStream
:数据输入源是 [文件]{.red}PipeInputStream
:数据输入源是 [流]{.red}(从其他线程共用的管道流中获取数据)
- 处理类(装饰类)
BufferedInputStream
:为节点类的增加缓冲区功能([字节流默认没有缓冲机制所以需要手动添加缓冲区]{.red})DataInputStream
:为节点类可以读出各式各样的数据的功能,可以直接读出整型、字符、布尔、浮点数等类型,不需要通过字节进行转换PushBackInputStream
:已经被废弃的类LineNumberInputStream
- 序列化:
ObjectInputStream
:数据输入源是 [对象]{.red}
字节输出流
- 节点类(基本类)
ByteArrayOutputStream
FileOutputStream
PipeOutputStream
- 处理类(装饰类)
DataOutputStream
BufferedOutputStream
PrintStream
:[格式化打印流;可以将数据按照一定格式放入流中]{.red}
- 序列化:
ObjectOutputStream
字节流方法
读方法
节点类:① 仅列出常用的方法 ② 所有节点类的方法基本相同
// 每次仅读取一个字节
public synchronized int read();
// 每次读取一个数组大小的字节数量:相当于数组是缓冲区
public synchronized int read(byte b[], int off, int len);
// 判断还有多少字节数量没有读取
public synchronized int available();处理类:仅列出新增的方法
DataInputStream
// 读取布尔类型的数据
public final boolean readBoolean() throws IOException;
// 读取短整型的数据
public final short readShort() throws IOException;
// 读取整型数据
public final int readInt() throws IOException;
// 读取长整型的数据
public final long readLong() throws IOException;
// 读取字符
public final char readChar() throws IOException;
// 读取单精度浮点数的数据
public final float readFloat() throws IOException;
// 读取双精度浮点数的数据
public final double readDouble() throws IOExceptionBufferedInputStream
:只是为节点类增加了缓冲区,没有新增任何方法
写方法
节点类:① 仅列出常用的方法 ② 所有节点类的方法基本相同
// 写入单个字节:虽然写入的是整型,但是在实现中会将其强制转换为字节类型
public synchronized void write(int b);
// 写入字节数组
public synchronized void write(byte b[], int off, int len);
// 输出流中存在的字节数量
public synchronized int size();处理类:仅列出新增的方法
DataOutputStream
// 写入布尔类型的数据
public final boolean writeBoolean(boolean v) throws IOException;
// 写入短整型的数据
public final short writeShort(int v) throws IOException;
// 写入整型数据
public final void writeInt(int v) throws IOException;
// 写入长整型数据
public final void writeLong(long v) throws IOException'
// 写入单个字符
public final void writeChar(int v) throws IOException;
// 写入字符串
public final void writeChars(String s) throws IOException;
// 写入单精度浮点数的数据
public final float writeFloat(float v) throws IOException;
// 写入双精度浮点数的数据
public final double writeDouble(double v) throws IOExceptionPrintStream
:如果不使用格式化类,那么输出内容时需要自己手动添加换行符等结束信息,否则接收信息时可能接收不到// 仅列出常用的:还有各种重载
public void print(String s);
public void println(String x);
public PrintStream printf(String format, Object ... args);BufferedOutputStream
:只是为节点类增加了缓冲区,没有新增任何方法
字符流框架图

字符输入流
节点类:
CharArrayReader
:数据输入源是 [字符数组]{.red}StringReader
:数据输入源是 [字符串]{.red}PipeReader
:数据输入源是 [流]{.red}(从其他线程共用的管道流中获取数据)
处理类:
BufferedReader
:为节点类增加缓冲区功能(JDK 1.1
之后添加的 [字符流默认携带缓冲区]{.red})注:
BufferedReader
不是FilterReader
的子类
字节转换类:
InputStreamReader
:[将字节流转换成字符流,网络通信中经常使用该类]{.red}/*将客户端的字节流转换成字符流*/
BufferedReader reader = new BufferedReader(
new InputStreamReader(
socket.getInputStream()))FileReader
:[便捷类]{.red}- 负责将文件字节流转换成文件字符流
- [直接继承 InputStreamReader 类;内部直接调用父类的构造方法]{.red}
/*下面两者是完全等价的*/
InputStreamReader reader = new InputStreamReader(new FileInputStream(filename));
FileReader reader = new FileReader(filename);
字符输出流
- 节点类
CharArrayWriter
StringWriter
PipeWriter
- 处理类
BufferedWriter
PrintWriter
- 字节转换类:
OutputStreamWriter
FilterWriter
字符流方法
前提:
- 方法基本和字节流没有什么区别,只不过读出和写入的类型有一定的变化
- [字符流的所有方法默认自带缓冲区,即使使用缓冲区装饰类效果也一般]{.red}
读方法:
BufferedReader
// 最常使用的读取方法
public String readLine() throws IOException;
* 写方法
> **实例:结合网络类 + 流对象编写的例子**
```java
// 客户端
public static void main(String[] args) throws IOException
{
// 客户端访问服务器端口: 客户端的端口是操作系统随机分配的,不需要程序员决定,只需要指定访问哪个服务器端口
Socket client = null;
// 客户端向服务器发送数据
PrintWriter writer = null;
// 客户端接收服务器返回的数据
BufferedReader reader = null;
// 客户端输入需要发送的数据
BufferedReader consoleReader = new BufferedReader(
new InputStreamReader(
System.in));
client = new Socket(DEFAULT_SERVER_HOSTNAME, DEFAULT_SERVER_PORT);
System.out.println("客户端启动成功...");
// 向服务器端发送数据并且接收返回的数据
String string;
writer = new PrintWriter(
new BufferedWriter(
new OutputStreamWriter(
client.getOutputStream())));
// 客户端输入 exit 就退出
while (!EXIT.equals(string = consoleReader.readLine()))
{
System.out.println(string);
writer.println(string);
writer.flush();
reader = new BufferedReader(
new InputStreamReader(
client.getInputStream()));
System.out.println(string);
}
reader.close();
writer.close();
client.close();
}
// 服务器
public static void main(String[] args) throws IOException
{
// 创建服务器处理请求的端口 Socket
ServerSocket server = null;
// 服务器读取客户端传递的数据
BufferedReader reader = null;
// 服务器返回给客户端的数据
PrintWriter writer = null;
server = new ServerSocket(DEFAULT_SERVER_PORT);
System.out.printf("服务器[%d]:%s\n", DEFAULT_SERVER_PORT, "启动完成...");
while (true)
{
// 服务器等待客户端发出请求,采用阻塞式调用:如果没有客户端发出请求就一直等待
Socket client = server.accept();
System.out.printf("客户端[%d]:%s\n", client.getPort(), "建立连接");
/*
1. 读取客户端发出的请求并处理并向客户端返回消息
2. 传输过程中采用的是字节传输,所以 Socket 也只能获得字节流而不是字符流
*/
String string;
reader = new BufferedReader(
new InputStreamReader(
client.getInputStream()));
writer = new PrintWriter(
new BufferedWriter(
new OutputStreamWriter(
client.getOutputStream())));
while ((string = reader.readLine()) != null)
{
// 格式化输出客户端发送的消息
System.out.printf("客户端[%d]:%s\n", client.getPort(), string);
// 向客户端返回消息
writer.println("服务器处理完毕");
// 确保缓冲区中所有的内容都被推出: writer 关闭后会自动推出缓冲区中剩余的内容
writer.flush();
}
System.out.printf("客户端[%d]:%s", client.getPort(), "退出连接");
reader.close();
writer.close();
client.close();
}
}
实例:多人聊天室
服务器端
public enum MyServer |
package chatroom.v2.chatserver; |
package chatroom.v2.chatserver; |
客户端
package chatroom.v2.chatclient; |
package chatroom.v2.chatclient; |