概述
- 全程为 Block Input/Output(同步阻塞式输入/输出), 指的就是传统 IO, 是用于处理设备之间的数据传输. 如 读写文件, 网络通讯等
- 服务器实现模式为每次连接都会创建独立的线程
- 相关的类和接口在 java.io包中
- 数据的输入或输出操作是以
流(Stream)
的方式进行
IO流的分类
- 按照操作数据的单位: 字节流(8bit), 字符流(16bit)
- 字节流: 非文本文件, 就是按照二进制形式进行读写操作. 如 .mp3,.avi,.rmvb,.mp4,.jpg,.doc,.ppt等
- 字符流: 只能操作普通文本文件 如 .txt,.java,.c,.cpp等. 注 .doc,.excel,.ppt等文件不属于普通文本
- 按照流的角色: 节点流, 处理流
- 节点流: 数据源与程序之间直接建立流连接, 进行读写数据
- 处理流: 数据源与程序之间未直接连接, 而是建立在已存在的流(节点流或处理流)之上, 通过对数据的处理来提升了读写性能
- 按照数据流的流向: 输入流, 输出流
- Java的 IO流共涉及40多个类, 都是派生于以下抽象基类
抽象基类 | 字节流 | 字符流 |
---|---|---|
输入流 | InputStream | Reader |
输出流 | OutputStream | Writer |
文件流(节点流)
- 在文件写入时, 使用构造器 FileWriter(file)指定文件+路径, 如果已有相同名称的文件, 则会覆盖, 否则生成
- 使用构造器 FileWriter(file, true), 则同名文件不会被覆盖, 而是内容末端会追加流
public class IOWriterApp {
public static void main(String[] args) {
FileWriter fw = null;
try {
// 创建用于写的流对象
fw = new FileWriter(
"src" +File.separator+ "main" +File.separator+ "java" +File.separator+
"org" +File.separator+ "example" +File.separator+ "base" +File.separator+
"io" +File.separator+ "节点流01.txt", true
);
// 将数据写入流
fw.write("写入文本!\nabc!");
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fw != null)
try {
// 关闭流资源(IO 资源不属于内存资源, 所以 JVM垃圾回收机制不会自动回收, 因此只能显式关闭 IO资源)
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public class IOReaderApp {
public static void main(String[] args) {
FileReader fr = null;
try {
// 创建用于读的流对象
fr = new FileReader(
"src" +File.separator+ "main" +File.separator+ "java" +File.separator+
"org" +File.separator+ "example" +File.separator+ "base" +File.separator+
"io" +File.separator+ "节点流01.txt"
);
// 创建字符数组, 用于临时存放数据
char[] buf = new char[1024];
// 每次读取的字符个数
int len;
// 调用流对象的读取方法将流中的数据读入到数组中
while ((len = fr.read(buf)) != -1) {
System.out.print(new String(buf, 0, len));
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fr != null) {
try {
// 关闭流资源
fr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
缓冲流
- 为了提高数据读写速度, Java API提供了缓冲流, 内部提供缺省 8kb的数组用于数据缓冲
- 缓冲流是“套接”在已有字节或字符流之上的, 执行过程:
- 读取数据时, 是将数据按块读入到缓冲区(每次默认一次性从文件中读取 8kb数据, 存在缓冲区中), 直到缓冲区装满, 使用时从缓冲区获取已缓冲的数据
- 写入数据时, 不会直接写到文件, 而是先写到缓冲区中, 直到缓冲区满后, 会将缓冲区中的数据一次性写到文件中(也可以执行 flush()方法, 强制刷新缓冲区将内容写入到文件流中), 或最后关闭缓冲流时, 也会先刷新缓冲区, 再关闭流
- 按照数据操作单位分为:
- 字节缓冲流: BufferedInputStream& BufferedOutputStream
- 字符缓冲流: BufferedReader& BufferedWriter
public class IOBufferedApp {
public static void main(String[] args) {
BufferedReader br = null;
BufferedWriter bw = null;
try {
// 创建流对象
br = new BufferedReader(new FileReader(
"src" +File.separator+ "main" +File.separator+ "java" +File.separator+
"org" +File.separator+ "example" +File.separator+ "base" +File.separator+
"io" +File.separator+ "缓冲流-src02.txt"
)
);
bw = new BufferedWriter(new FileWriter(
"src" +File.separator+ "main" +File.separator+ "java" +File.separator+
"org" +File.separator+ "example" +File.separator+ "base" +File.separator+
"io" +File.separator+ "缓冲流-dest02.txt", true
)
);
String str;
// 读取一行, 不包含换行符
while ((str = br.readLine()) != null) {
// 将数据写入流
bw.write(str);
// 写入换行符
bw.newLine();
// 刷新缓冲区(清空)
bw.flush();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
// 关闭流对象
try {
if (bw != null) {
// 关闭缓冲流时, 会自动关闭内部被包装的流(节点流)
bw.close();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (br != null) {
br.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
转换流
- 转换流主要用于实现文件的编码和解码的功能
public class IOApp {
public static void main(String[] args){
BufferedReader br = null;
BufferedWriter bw = null;
try {
FileInputStream fis = new FileInputStream(
"src" +File.separator+ "main" +File.separator+ "java" +File.separator+
"org" +File.separator+ "example" +File.separator+ "base" +File.separator+
"io" +File.separator+ "转换流-src03.txt"
);
FileOutputStream fos = new FileOutputStream(
"src" +File.separator+ "main" +File.separator+ "java" +File.separator+
"org" +File.separator+ "example" +File.separator+ "base" +File.separator+
"io" +File.separator+ "转换流-dest03.txt", true
);
InputStreamReader isr = new InputStreamReader(fis, "UTF8");
OutputStreamWriter osw = new OutputStreamWriter(fos, "UTF8");
br = new BufferedReader(isr);
bw = new BufferedWriter(osw);
String str;
// 读取一行, 不包含换行符
while ((str = br.readLine()) != null) {
bw.write(str);
bw.newLine();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
// 关闭流对象
try {
if (bw != null) {
// 关闭缓冲流时, 会自动关闭内部被包装的流(节点流)
bw.close();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (br != null) {
br.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
标准输入& 输出流
- 通过 System.in(输入), System.out(输出). 默认输入设备是键盘, 输出是显示器
- 重定向: 通过 System类的 setIn, setOut方法, 更变默认设备
public class IOApp {
public static void main(String[] args) {
System.out.println("请输入信息(退出输入 exit):");
/** 1. 字节输入流(System.in), 赋给转换流, 再包装成缓冲流
* 2. 通过 System.in, 监听键盘输入的信息*/
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String s;
try {
// 读取用户输入的一行数据(阻塞程序)
while ((s = br.readLine()) != null) {
if ("exit".equalsIgnoreCase(s)) {
System.out.println("结束程序!");
break;
}
// 输出读取到的一行数据
System.out.println("输出信息: " + s);
System.out.println("继续输入信息:");
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (br != null) {
br.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
打印流
- 有2种打印流: PrintStream和 PrintWriter
- PrintStream和 PrintWriter的输出不会抛出 IOException异常
- PrintStream和 PrintWriter的第二个参数设置 true, 意为自动 flush
public class IOApp {
public static void main(String[] args) {
PrintStream ps = null;
try {
FileOutputStream fos = new FileOutputStream(
"src" +File.separator+ "main" +File.separator+ "java" +File.separator+
"org" +File.separator+ "example" +File.separator+ "base" +File.separator+
"io" +File.separator+ "打印流05.txt"
);
// 创建打印输出流, 第二个参数设置 true, 意为自动刷新模式(写入换行符时, 会自动刷新缓冲区并输出到文件)
ps = new PrintStream(fos, true);
if (ps != null) {
// 将控制台输出改成输出到文件
System.setOut(ps);
}
for (int i = 1; i <= 999; i++) {
System.out.print(i + ", ");
if (i % 50 == 0) {
// 每输出50个数后换行
System.out.println();
}
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} finally {
if (ps != null) {
ps.close();
}
}
}
}
数据流
- 数据流是用于读写基本数据类型和 String类的流
- 数据流有两个类: DataInputStream和 DataOutputStream. 分别“套接”在 InputStream和 OutputStream子类的流上
public class IODataOutputApp {
public static void main(String[] args) {
DataOutputStream dos = null;
try {
// 创建用于数据输出的流对象
dos = new DataOutputStream(
new FileOutputStream("src" +File.separator+ "main" +File.separator+ "java" +File.separator+
"org" +File.separator+ "example" +File.separator+ "base" +File.separator+
"io" +File.separator+ "数据流06.dat")
);
dos.writeUTF("测试 UTF");
dos.writeBoolean(true);
dos.writeLong(1234567890L);
dos.writeDouble(123.123456789);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (dos != null) {
dos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public class IODataInputApp {
public static void main(String[] args) {
DataInputStream dis = null;
try {
// 创建用于数据输入的流对象
dis = new DataInputStream(
new FileInputStream("src" +File.separator+ "main" +File.separator+ "java" +File.separator+
"org" +File.separator+ "example" +File.separator+ "base" +File.separator+
"io" +File.separator+ "数据流06.dat")
);
String utf = dis.readUTF();
boolean _boolean = dis.readBoolean();
long _long = dis.readLong();
double _double = dis.readDouble();
System.out.println(utf);
System.out.println(_boolean);
System.out.println(_long);
System.out.println(_double);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (dis != null) {
try {
dis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
对象流
- 对象流用于处理(存取)基本数据类型或对象
- 对象流有两个类: ObjectInputStream和 OjbectOutputStream
- 当对象存储时, 会将该对象序列化后写到磁盘(或进行网络传输)
- 序列化时, 对象必须实现 如下接口之一 Serializable或 Externalizable, 否则, 会抛出 NotSerializableException异常
- 反序列化时, 版本必须与序列化时的版本一致, 否则, 会抛出 InvalidCastException异常
- static和 transient修饰的成员变量, 不会被序列化
public class Person implements Serializable {
private static final long serialVersionUID = 1213831504600021305L;
private String name;
private Integer age;
public Person(String name, Integer age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public Integer getAge() {
return age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
public class IOObjectOutputApp {
public static void main(String[] args) {
ObjectOutputStream oos = null;
try {
oos = new ObjectOutputStream(
new FileOutputStream("src" +File.separator+ "main" +File.separator+ "java" +File.separator+
"org" +File.separator+ "example" +File.separator+ "base" +File.separator+
"io" +File.separator+ "对象流07.dat")
);
Person p = new Person("伟人1", 30);
oos.writeObject(p);
p = new Person("伟人2", 35);
oos.writeObject(p);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (oos != null) {
oos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public class IOObjectInputApp {
public static void main(String[] args) {
ObjectInputStream ois = null;
try {
ois = new ObjectInputStream(
new FileInputStream("src" +File.separator+ "main" +File.separator+ "java" +File.separator+
"org" +File.separator+ "example" +File.separator+ "base" +File.separator+
"io" +File.separator+ "对象流07.dat")
);
Person p = (Person) ois.readObject();
System.out.println(p);
p = (Person) ois.readObject();
System.out.println(p);
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} finally {
try {
if (ois != null) {
ois.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
随机存取文件流
- RandomAccessFile类在 java.io包下, 但直接继承于 java.lang.Object类. 它实现了 DataInput和 DataOutput接口, 也就意味着它既可以读也可以写
- RandomAccessFile类支持, 从任意指针(下标)开始读写文件:
- long getFilePointer(), 获取记录指针的当前位置
- void seek(long pos), 将指针定位到 pos位置
- 构造器的第二个参数表示文件的访问模式:
- r: 以只读方式打开
- rw:打开以便读取和写入
- rwd: 打开以便读取和写入; 同步文件内容的更新
- rws: 打开以便读取和写入; 同步文件内容和元数据的更新
*注 当模式为 r, 且文件不存在时, 会抛异常. 或模式为 rw, 且文件不存在, 则会自动创建文件
public class IOWriterApp {
public static void main(String[] args) {
insert(0, "ABCDEFG!");
insert(1, "HHH!");
}
public static void insert(int startPosition, String insertContent) {
RandomAccessFile raf = null;
try {
raf = new RandomAccessFile(
"src" +File.separator+ "main" +File.separator+ "java" +File.separator+
"org" +File.separator+ "example" +File.separator+ "base" +File.separator+
"io" +File.separator+ "随机存取文件流08.txt", "rw");
// 定位文件记录指针开始位置
raf.seek(startPosition);
// 开始输出(注 指定下标下已有内容会被覆盖)
raf.write(insertContent.getBytes());
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
raf.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public class IOReaderApp {
public static void main(String[] args) {
RandomAccessFile raf = null;
try {
raf = new RandomAccessFile(
"src" +File.separator+ "main" +File.separator+ "java" +File.separator+
"org" +File.separator+ "example" +File.separator+ "base" +File.separator+
"io" +File.separator+ "随机存取文件流08.txt", "r");
byte [] bytes = new byte[1024];
int eachLength;
while ((eachLength = raf.read(bytes)) != -1) {
System.out.print(new String(bytes, 0, eachLength));
}
System.out.println();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
raf.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
BIO网络编程实例
- 服务器:
- 使用 BIO模型编写服务器端, 监听 6666端口
- 使用线程池机制, 连接多个客户端
- 可以接收客户端发送的数据
- 客户端: telnet
public static void main(String[] args) throws Exception {
// 创建一个线程池
ExecutorService newCachedThreadPool = Executors.newCachedThreadPool();
// 创建ServerSocket
ServerSocket serverSocket = new ServerSocket(6666);
System.out.println("服务器启动了");
while (true) {
System.out.println("线程信息 id:" + Thread.currentThread().getId() + ", name:" + Thread.currentThread().getName());
// 监听, 等待客户端连接
System.out.println("等待连接...");
final Socket socket = serverSocket.accept();
System.out.println("连接到一个客户端");
// 创建一个线程
newCachedThreadPool.execute(new Runnable() {
public void run() {
// 可以和客户端通讯
handler(socket);
}
});
}
}
// 编写一个handler方法,和客户端通讯
public static void handler(Socket socket) {
try {
System.out.println("线程信息 id:" + Thread.currentThread().getId() + ", name:" + Thread.currentThread().getName());
byte[] bytes = new byte[1024];
//通过socket 获取输入流
final InputStream inputStream = socket.getInputStream();
//循环的读取客户端发送的数据
while (true) {
System.out.println("线程信息 id:" + Thread.currentThread().getId() + ", name:" + Thread.currentThread().getName());
System.out.println("read...");
int read = inputStream.read(bytes);
if (read != -1) {
// 输出客户端发送的数据
System.out.println(new String(bytes, 0, read));
} else {
break;
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
System.out.println("关闭和客户端的连接");
try {
socket.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
如果您觉得有帮助,欢迎点赞哦 ~ 谢谢!!