一、概述
- 按照流向可以分为:输入流、输出流。从数据源中读取数据是输入流,将数据写入到目的地是输出流。
- 按照数据处理单位可以分为:字节流、字符流。字节占1个byte即8位,字符占2个byte即16位。对于文本文件(txt、xml、java)应该使用字符流,字符涉及编码格式,对于二进制文件(jpg、doc、mp3)应该使用字节流,字节不涉及编码格式。
| 抽象基类 |
InputStream 字节输入流 |
OutputStream 字节输入流 |
Reader 字符输入流 |
Writer 字符输出流 |
| 缓存流 | BufferedInputStream | BufferedOutputStream | BufferedReader | BufferedWriter |
| 文件流 | FileInputStream | FileOutputStream | FileReader | FileWriter |
| 数组流 | ByteArrayInputStream | ByteArrayOutputStream | CharArrayReader | CharArrayWriter |
| 字符串流 | - | - | StringReader | StringWriter |
| 管道流 | PipedInputStream | PipedOutputStream | PipedReader | PipedWriter |
| 对象流 | ObjectInputStream | ObjectOutputStream | - | - |
| 转换流 | - | - | InputStreamReader | OutputStreamWriter |
| 打印流 | - | PrintStream | - | PrintWriter |
| 基本数据类型流 | DataInputStream | DataOutputStream | - | - |
1.1 字符流抽象基类
1.1.1 Writer
| close() |
abstract public void close() throws IOException 刷新后关闭流并释放资源,流不能再被使用。 |
| flush() |
abstract public void flush() throws IOException 刷新该流的缓冲,流可以继续使用。 |
| write() |
public void write(int c) throws IOException 写入单个字符(0~65535)。 |
|
public void write(char cbuf[]) throws IOException 写入字符数组。 | |
|
public void write(String str) throws IOException 写入字符串。 | |
| append() |
public Writer append(char c) throws IOException 续写字符。 |
|
public Writer append(CharSequence csq) throws IOException 续写字符序列。 |
1.1.2 Reader
| close() |
abstract public void close() throws IOException 关闭流并释放资源,流不能再使用。 |
| read() |
public int read() throws IOException 读取单个字符,返回的是字符在内存中对应的数字(0~65535),该方法再次调用会自动读取下一位,没有了则返回 -1。 |
|
public int read(char cbuf[]) throws IOException abstract public int read(char cbuf[], int off, int len) throws IOException 将读取的字符存入数组,返回的是读到的个数,没有了则返回 -1。 |
1.2 字节流抽象基类
1.2.1 OutputStream
| close() |
public void close() throws IOException 刷新后关闭流并释放资源,流不能再被使用。 |
| flush() |
public void flush() throws IOException 刷新该流的缓冲,流可以继续使用。 |
| write() |
public abstract void write(int b) throws IOException |
|
public void write(byte b[]) throws IOException 写入字节数组。 |
1.2.2 InputStream
| close() |
public void close() throws IOException 关闭流并释放资源,流不能再使用。 |
| available() |
public int available() throws IOException 返回的是读到的字节个数,\r 和 \n 各是一位。 |
| read() |
public abstract int read() throws IOException 读取单个字节,读取有效位int的最低8位,该方法再次调用会自动读取下一位,没有了则返回 -1。 |
|
public int read(byte b[]) throws IOException 将读取的字节存入数组,返回的是读到的个数,没有了则返回 -1。 |
1.3 Java7 特性
实现了 AutoClose 接口的对象,能自动关闭流资源。格式:try(流对象){读写代码}。
二、文件流
文件流可以直接操作文件的读写。
2.1 FileWriter
如果有同名文件会覆盖(因此续写模式从构造入手而不是write()方法入手)。
|
FileWriter() |
public FileWriter(File file) throws IOException 根据给定的文件构造,从开头写。 参数 append:true从结尾处追加续写,false覆盖。 |
|
public FileWriter(String fileName) throws IOException 根据给定的文件名构造(文件名前不带路径则为当前目录),从开头写。 append续写模式,true从结尾处写,false覆盖。 |
2.2 FileReader
| FileReader() |
public FileReader(File file) throws FileNotFoundException 根据给定的文件构造。 |
|
public FileReader(String fileName) throws FileNotFoundException 根据给定的文件名构造(文件名前不带路径则为当前目录)。 |
/*创建一个FileReader对象并和指定文件关联
要保证该文件是已经存在的,否则抛异常*/
try (FileReader fr = new FileReader("demo.txt")) {
int num = 0;
// 每次读取单个字符效率低下不推荐
// while ((num = fr.read()) != -1) {
// System.out.print((char)num);
// }
char[] buffer = new char[1024]; //定义一个数组用于存储读到的字符(建议为1024的整数倍)
while ((num = fr.read(buffer)) != -1) { //直到返回-1读不到数据了便结束
System.out.print(new String(buffer, 0, num)); //最后一次读取,不一定装满1024所以是num不是buffer.length,否则超过num后会把上一次存入的打印出来
}
} catch (IOException e) {
e.printStackTrace();
}
2.3 FileOutputStream
2.4 FileInputStream
三、缓存流
为了提高数据读写的速度,JavaAPI提供了带缓冲功能的流,在使用这些流类,会创建一个内部缓冲区数组。缓冲流要“套接”在相应的其他流之上(创建时需要传入其他流),对读写的数据提供了缓冲的功能,提高了读写的效率。写出的数据会先在内存中缓存,使用flush()将会使内存中的数据立刻写出。
3.1 BufferedWrite
将文本写入字符输出流,缓存各个字符,从而提供高效写入。
| BufferedWrite() |
public BufferedWriter(Writer out) 缓冲区的出现是为了提高流的效率,所以创建缓冲区需要提供一个流对象。 |
| newLine() |
public void newLine() throws IOException 写入一个换行符(回车换行效果)。 |
try (BufferedWriter bw = new BufferedWriter(new FileWriter("demo.txt"))) {
for (int i = 1; i < 5; i++) {
bw.write("Hello" + i);
bw.newLine(); //换行
bw.flush(); //每次刷新避免数据丢失,用到缓存流就要记得刷新
}
} catch (IOException e) {
e.printStackTrace();
}
3.2 BufferedReader
从字符输入流中读取文本,缓存各个字符,从而提供高效读取。
| BufferedReader() |
public BufferedReader(Reader in) 缓冲区的出现是为了提高流的效率,所以创建缓冲区需要提供一个流对象。 |
| readLine() |
public String readLine() throws IOException 读取一行文本(不包含换行符)。 |
try (BufferedReader br = new BufferedReader(new FileReader("demo.txt"))) {
String line = null;
while ((line = br.readLine()) != null) { //一行一行读更方便
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
通过字符缓存流复制文本文件
try (BufferedWriter bw = new BufferedWriter(new FileWriter("D:\\original\\demo.txt"));
BufferedReader br = new BufferedReader(new FileReader("E:\\destination\\demo_copy.txt"))
) {
String line = null;
while ((line = br.readLine()) != null) {
bw.write(line);
bw.newLine();
bw.flush();
}
} catch (IOException e) {
e.printStackTrace();
}
3.3 BufferedOutputStream
3.4 BufferedInputStream
四、对象流
Data***Stream 可以实现基本数据类型和String的输入输出,Object***Stream 可以实现自定义数据类型的输入输出,包含了 Data***Stream 的功能可以将其替代。
序列化:对象的持久化存储(将对象存储到文件中)。
并不是每一个对象都可以写到输出流,可以写入到输出流中的对象称为可序列化的。对象序列化机制允许把内存中的Java对象转换成平台无关的二进制流,从而允许把这种二进制流持久地保存在磁盘上,或通过网络将这种二进制流传输到另一个网络节点。当其它程序获取了这种二进制流,就可以恢复成原来的Java对象。序列化的好处在于可将任何实现了Serializable接口的对象转化为字节数据,使其在保存和传输时可被还原。
4.1 ObjectOutputStream
4.2 ObjectInputStream
五、字节数组流
ByteArrayInputStream、ByteArrayOutputStream
字节数组流,因为两个流对象操作的是数组,没有使用系统资源所以不会出现 IOException。
六、RandomAccessFile
上边讲的都只能追加或者覆盖,无法实现任意位置的读写,所以Java 提供了 RandomAccessFile 类, 允许从文件的任何位置进行数据的读写 。RandomAccessFile对象包含一个记录指针,用以标示当前读写处的位置。
- RandomAccessFile的read()和write()都会使指针往后移动,移动的值就是读取或者写入的字节长度。
- 指针移动时,seek方法传入的是字节长度,不是字符长度。
write()其实是在指针位置覆盖,所以要做插入操作时,必须先将插入点之后的数据暂存起来,在插入点插入数据后,再将暂存的数据追加到后边,这样才能实现插入。例如:要在第一行和第二行之前插入数据,也就是新插入的数据作为第二行,那么插入之前需要将第二行及之后的数据暂存起来,然后将数据插入(覆盖)到第二行,然后再将之前暂存的第二行(插入前的第二行)及之后的数据取出,追加都文件末尾。
| getFilePointer() |
long getFilePointer() 获取文件记录指针的当前位置 |
| seek() |
void seek(long pos) 将文件记录指针定位到pos位置 |
//RandomAccessFile的read和write都会改变指针位置。
val randomAccessFile = RandomAccessFile(file, "rw")
val data1 = "这是数据1\n"
randomAccessFile.write(data1.toByteArray())
randomAccessFile.write("这是数据2\n".toByteArray())
randomAccessFile.close()
val randomAccessFile1 = RandomAccessFile(file, "rw")
randomAccessFile1.seek(data1.toByteArray().size.toLong())
println("当前指针位置1," + randomAccessFile1.filePointer)
//取出data1之后的数据
var stringAfterData1 = ""
var buffer = ByteArray(1024)
var len: Int
while (randomAccessFile1.read(buffer).also { len = it } != -1) {
stringAfterData1 += String(buffer, 0, len)
}
println("当前指针位置2," + randomAccessFile1.filePointer)
randomAccessFile1.seek(data1.toByteArray().size.toLong())
randomAccessFile1.write("这一行数据插入在数据1和2之间\n".toByteArray())
//将插入之前data1之后的数据追加上
randomAccessFile1.write(stringAfterData1.toByteArray())
println("当前指针位置3," + randomAccessFile1.filePointer)
println("当前数据长度," + randomAccessFile1.length())
//重新移动指针到开头,取出所有数据
randomAccessFile1.seek(0)
while (randomAccessFile1.read(buffer).also { len = it } != -1) {
print(String(buffer, 0, len))
}
println("当前指针位置4," + randomAccessFile1.filePointer)
randomAccessFile1.close()
本文概述了Java中流的概念,包括输入流、输出流、字节流与字符流的区别,以及BufferedInputStream、BufferedOutputStream、BufferedReader和BufferedWriter等缓存流的作用。重点讲解了FileWriter和FileReader的使用,以及如何利用缓存流提高文件操作的效率。
448

被折叠的 条评论
为什么被折叠?



