概述
1. IO 流简介
何为 IO 流?
在计算机中,内存和磁盘需要进行数据传输(内存从磁盘中读入数据,进行处理,然后写入磁盘中),而数据传输需要通道。所以,这里的 “IO 流”就是数据传输的通道。
内存和磁盘数据交互图如下:
大致意思:内存通过输入流从磁盘中读取数据,这个过程也称为读;内存通过输出流将数据保存到磁盘中,这个过程也称为写。
所以,简单来说,IO 流的作用就是 通过 IO 流,可以完成对磁盘文件的读和写。
2. IO 流的分类
- 按流的方向分(以内存为参照物):输入流(往内存中去)、输出流(从内存中出来)
- 按数据的读取方式:字节流、字符流
字节流和字符流的区别?
- 字节流:按照字节方式读取,一次读一个字节,可以读取任意类型的文件。如:文本文件、图片、视频文件,等…
- 字符流:按照字符方式读取,一次读取一个字符。它是为了更方便读取普通的文本文件,无法读取图片、word文件、视频文件等…
举个例子:
有一个 test.txt 文件,它里面的内容是:z爱中国
用字节流读:
第一次读:读一个字节,正好读到 ‘a’
第二次读:读一个字节,正好读到 ‘中’ 的一半
第三次读:读一个字节,读到 ‘中’ 的另一半
Tips:在 Windows 系统中,字母只占一个字节(在 Java 中,字母(char) 占用两个字节,但这个文件与 Java 无关,它只是 Windows 操作系统上的一个文件),而汉字占两个字节。
用字符流读:
第一次读:读一个字符,正好读到 ‘a’
第而次读:读一个字节,正好读到 ‘中’
3. Java 中的 IO 流
Java 中的 IO 流都在 java.io.* 包下,这块被称为“四大家族”(所有的 IO 类只有四类),四大家族的首领(这四类 IO 流的顶级父类)如下:
- java.io.InputStream:字节输入流
- java.io.OutputStream:字节输出流
- java.io.Reader:字符输入流
- java.io.Writer:字符输出流
特点:
- 四大家族的首领都是抽象类
- 所有的 IO 流都实现了 java.io.Closeable 接口,都是可关闭的(close() 方法),用完之后要关闭,不然会占用很多资源
- 所有的输出流都实现了 java.io.Flushable 接口,都是可刷新的(flush() 方法),用完之后,记得刷新
Java 中需要掌握的 IO 流有16个,如下:
文件专属(操作文件):
- java.io.FileInputStream
- java.io.FileOutputStream
- java.io.FileReader
- java.io.FileWriter
转换流(将字节流转换为字符流):
- java.io.InputStreamReader
- java.io.OutputStreamWriter
缓冲专属:
- java.io.BufferedInputStream
- java.io.BufferedOutputStream
- java.io.BufferedReader
- java.io.BufferedWriter
数据流专属:
- java.io.DataInputStream
- java.io.DataOutputStream
标准输出流:
- java.io.PrintStream
- java.io.PrintWriter
对象专属流:
- java.io.ObjectInputStream
- java.io.ObjectOutputStream
这里重点以“FileInputStream/FileOutputStream”举例,因为其它流与之相似
3.1 FileInputStream/FileOutputStream
3.1.1 FileInputStream
FileInputStream:文件字节输入流
场景:使用文件字节流读取磁盘中的一个文件temp.txt,其内容为:abcdef
示例一:
public class FileStreamDemo {
public static void main(String[] args) {
// 文件绝对路径名
String name = "E:\zzc\temp.txt";
FileInputStream fis = null;
try {
fis = new FileInputStream(name);
// 开始读
// read() 方法返回的是:读取到的“字节”本身。
// 例如:读取到的字符是'a',则返回97
int data = fis.read();
System.out.println(data); // 97
data = fis.read();
System.out.println(data); // 98
data = fis.read();
System.out.println(data); // 99
data = fis.read();
System.out.println(data); // 100
data = fis.read();
System.out.println(data); // 101
data = fis.read();
System.out.println(data); // 102
// 读取到文件末尾返回 -1
data = fis.read();
System.out.println(data); // -1
} catch (Exception e) {
e.printStackTrace();
} finally {
if (null != fis) {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
如果一个文件内容巨多,照上面那种方式肯定就不行了。所以,下面用循环的方式进行读取。
示例二:
public class FileStreamDemo {
public static void main(String[] args) {
// 文件绝对路径名
String name = "E:\zzc\temp.txt";
FileInputStream fis = null;
try {
fis = new FileInputStream(name);
int data = 0;
while ((data = fis.read()) != -1) {
System.out.println(data);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (null != fis) {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
但是,上面还会有一个问题:就是一次只读取一个字节,那么要读取所有字节,就要读取多次。这就会意味着内存要和磁盘频繁交互,这样性能会严重降低。所以,就要考虑一次能否读取多个字节。
查看 API 文档,可知:调用 read(byte[] b) 方法
示例三:
public class FileStreamDemo {
public static void main(String[] args) throws Exception{
// 文件绝对路径名
String name = "E:\zzc\temp.txt";
FileInputStream fis = new FileInputStream(name);
byte[] bytes = new byte[4];
int length = 0;
while ((length = fis.read(bytes)) != -1) {
// 将 byte 数组转换成字符串
System.out.print(new String(bytes, 0, length));
}
if (null != fis) {
fis.close();
}
}
}
3.1.2 FileOutputStream
FileOutputStream:文件字节输出流
场景:使用文件输出流向磁盘某个文件写入数据
示例一:
public class FileOutputStreamDemo {
public static void main(String[] args) throws Exception{
// temp.txt 文件不存在时,会自动新建
FileOutputStream fos = new FileOutputStream("temp.txt");
byte[] bytes = {97, 98, 99, 100};
fos.write(bytes);
// 写完之后,一定要刷新
fos.flush();
fos.close();
}
}
注意:如果文件不存在,则会新建文件。而且每次向此文件中写入数据之前,都会清空此文件。
那么,每次向此文件写入数据时,能不能向此文件中追加内容呢?
使用这个构造方法:
- FileOutputStream(String name, boolean append):创建一个向具有指定 name 的文件中写入数据的输出文件流。如果第二个参数为 true,则将字节写入文件末尾处,而不是写入文件开始处。
如何向文件中写入汉字呢?
使用如下方式:
String data = "我爱中国";
// 将字符串转换为字节数组
byte[] bytes = data.getBytes();
fos.write(bytes);
3.1.3 使用文件字节流
场景:使用文件字节流实现文件的赋值:把文件 temp.txt 中的内容复制到 test.txt 文件中去。
示例:
public class FileStreamDemo {
public static void main(String[] args) throws Exception{
FileInputStream fis = new FileInputStream("E:\zzc\temp.txt");
FileOutputStream fos = new FileOutputStream("E:\zzc\test.txt");
byte[] bytes = new byte[1024];
int len = 0;
while ((len = fis.read(bytes)) != -1) {
fos.write(bytes, 0, len);
}
if (null != fos) {
fos.flush();
}
if (null != fis) {
fis.close();
}
fos.close();
}
}
3.2 FileReader/FileWriter
3.2.1 FileReader
FileReader:文件字符输入流,只能读取普通文本,方便、快捷
场景:使用文件字符输入流读取一个txt文件(包含中文)
示例:
public class FileReaderDemo {
public static void main(String[] args) throws Exception{
FileReader fr = new FileReader("temp.txt");
char[] chars = new char[4];
int length = 0;
while ((length = fr.read(chars)) != -1) {
System.out.print(new String(chars, 0, length));
}
fr.close();
}
}
3.2.2 FileWriter
FileWriter:文件字符输出流,只能写普通文本
场景:使用文件字符输出流向一个txt文件写入数据(包含中文)
public class FileWriterDemo {
public static void main(String[] args) throws Exception {
FileWriter fw = new FileWriter("fw.txt");
char[] chars = {'我', '是', '中', '国', '人'};
fw.write(chars);
fw.flush();
fw.close();
}
}
3.3 BufferedReader/BufferedWriter
3.3.1 BufferedReader
BufferedReader:带有缓冲区的字符输入流。使用此流时,不需要自定义 byte/char 数组。自带缓冲。
示例一:
public class BufferedReaderDemo {
public static void main(String[] args) throws Exception {
BufferedReader br = new BufferedReader(new FileReader("temp.txt"));
String line = null;
while (null != (line = br.readLine())) {
System.out.print(line);
}
br.close();
}
}
示例二:
public class BufferedReaderDemo {
public static void main(String[] args) throws Exception {
BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("temp.txt")));
String line = null;
while (null != (line = br.readLine())) {
System.out.print(line);
}
br.close();
}
}
示例一与示例二的区别:示例二中使用了字节流来读取 temp.txt,但 BufferedReader 的构造方法中只能传入 Reader 类型的参数。所以,这里使用了 字节输入流来转换。
这里 BufferedWriter 就不说了。
3.4 DataOutputStream/DataInputStream
DataOutputStream:此流可以将数据以及数据类型写入文件中。
注意:此文件不是普通文件(无法用记事本打开)
3.5 PrintStream/PrintWriter
此类流是标准输出流。
3.5.1 PrintStream
PrintStream 是字节输出流,默认输出到控制台。
示例:
public class PrintStreamDemo {
public static void main(String[] args) {
String msg = "Hello PS";
PrintStream out = System.out;
// System.out.println(msg);
out.println(msg);
}
}
既然是默认输出到控制台。那么可以改变标准输出流的方向吗?
当然可以。
public class PrintWriterDemo {
public static void main(String[] args) throws Exception{
// 标准输出流不再指向控制台,而是指向 log 文件
PrintStream ps = new PrintStream(new FileOutputStream("log"));
// 修改输出方向,将输出方向从控制台修改为 log 文件
System.setOut(ps);
ps.print("我是中国人");
}
}
3.5.2 PrintWriter
示例:
public class PrintWriterDemo {
public static void main(String[] args) throws Exception{
// 若此文件不存在,则创建
PrintWriter pw = new PrintWriter("pw.txt");
String data = "Hello PrintWriter";
pw.write(data);
// 使用字符输出流,则需要调用 flush() 方法
// 字符内部也是用字节。相当于字符内部有缓存区,如果不调用 flush() 方法,那么数据只会存留在缓冲区中,
// 并不会输出到文本中去
pw.flush();
// 当调用 close() 时,它会先调用 flush() 方法。即使不显示调用 flush() 方法
pw.close();
}
}
3.6 ObjectOutputStream/ObjectInputStream
主要用于:对象的序列化和反序列化
示例(序列化):
public class User implements Serializable {
private static final long serialVersionUID = -2002612691013855369L;
private String name;
private int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
...
}
User 类实现了 Serializable 接口
public class ObjectOutputStreamDemo {
public static void main(String[] args) throws Exception{
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("users.txt"));
User user = new User("zzc", 18);
oos.writeObject(user);
oos.flush();
oos.close();
}
}
注意:参与序列化和反序列化的对象都要实现 Serializable 接口。
3.7 关于内存的流
public class MemoryStream {
public static void main(String[] args) throws Exception{
// 1. 内存字节流
ByteArrayOutputStream baos = new ByteArrayOutputStream();
String data = "Hello Memory";
// 将数据写入内存
baos.write(data.getBytes());
// 获取内存中的数据
byte[] bytes = baos.toByteArray();
//System.out.println(new String(bytes));
baos.close();
ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
int ch;
while ((ch = bais.read()) != -1) {
System.out.print((char) ch);
}
bais.close();
// 2. 内存字符数组流
CharArrayWriter caw = new CharArrayWriter();
char[] chs = {'H', 'e', 'l', 'l', 'o'};
caw.write(chs);
caw.close();
// 返回内存中数据的引用
char[] data = caw.toCharArray();
CharArrayReader car = new CharArrayReader(data);
int ch;
while ((ch = car.read()) != -1) {
System.out.print((char) ch);
}
car.close();
// 3. 内存字符串流
StringWriter sw = new StringWriter();
String data = "Hello World";
sw.write(data);
sw.close();
String s = sw.toString();
StringReader sr = new StringReader(s);
int ch;
while((ch = sr.read()) != -1) {
System.out.print((char) ch);
}
sr.close();
}
}
好了,一些流的基本使用就到这儿了。多看看它们的 API 文档吧。
最后
以上就是刻苦台灯为你收集整理的Java 中的 IO 流的基本使用1. IO 流简介2. IO 流的分类3. Java 中的 IO 流的全部内容,希望文章能够帮你解决Java 中的 IO 流的基本使用1. IO 流简介2. IO 流的分类3. Java 中的 IO 流所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复