Java - IO - 字节流
概述
什么是IO?
IO就是输入流输出流的意思。以内存为基准,分为输入input
和输出output
,即流向内存是输入流,流出内存的输出流。
- 输入流:把硬盘中的数据读取到内存中使用
- 输出流:把内存中的数据写入到硬盘中保存
上方的表格是顶级父类
字节流
一切皆字节
一切文件数据(文本、图片、视频等)在存储时,都是以二进制数字的形式保存,都一个一个的字节,那么传输时一样如此。
所以,字节流可以传输任意文件数据。在操作流的时候,我们要时刻明确,无论使用什么样的流对象,底层传输的始终为二进制数据。
字节输入流(InputStream)
java.io.InputStream ``抽象类
是表示字节输入流的所有类的超类,可以读取字节信息到内存中。
它定义了字节输入流的基本共性功能方法。
public abstract int read()
: 从输入流读取数据的下一个字节。public int read(byte[] b)
: 从输入流中读取一些字节数,并将它们存储到字节数组 b中 。public void close()
:关闭此输入流并释放与此流相关联的任何系统资源。
文件字节输入流(FileInputStream)
java.io.FileInputStream
类是文件输入流,继承InputStream抽象类。publicclass FileInputStream extends InputStream
作用是把硬盘文件中的数据,读取到内存中使用。
读取数据的原理(硬盘->内存):Java程序-->JVM-->操作系统-->调用本地方法-->读取文件内容
构造方法
Constructor | Description |
---|---|
FileInputStream(File file) * |
通过打开与实际文件的连接创建一个 FileInputStream ,该文件由文件系统中的 File 对象 file 命名。 |
FileInputStream(String name) |
通过打开与实际文件的连接来创建一个 FileInputStream ,该文件由文件系统中的路径名 name 命名。 |
FileInputStream(FileDescriptor fdObj) |
创建 FileInputStream 通过使用文件描述符 fdObj ,其表示在文件系统中的现有连接到一个实际的文件。 |
当你创建一个流对象时,必须传入一个文件路径。该路径下,如果没有该文件,会抛出FileNotFoundException
。
常用方法
方法 | 描述 |
---|---|
int read() | 从此输入流中读取一个数据字节。 |
int read(byte[] b) | 从此输入流中将最多 b.length 个字节的数据读入一个 byte 数组中。 |
int read(byte[] b, int off, int len) | 从此输入流中将最多 len 个字节的数据读入一个 byte 数组中。 |
void close() | 关闭此文件输入流并释放与此流有关的所有系统资源。 |
long skip(long n) | 从输入流中跳过并丢弃 n 个字节的数据。 |
protected void finalize() | 确保在不再引用文件输入流时调用其 close 方法。 |
int available() | 返回下一次对此输入流调用的方法可以不受阻塞地从此输入流读取(或跳过)的估计剩余字节数。 |
FileChannel getChannel() | 返回与此文件输入流有关的唯一 FileChannel 对象。 |
FileDescriptor getFD() | 返回表示到文件系统中实际文件的连接的 FileDescriptor 对象,该文件系统正被此 FileInputStream 使用。 |
*在文件字节输入流中,read()底层调用本地方法read0()
其他两个read方法调用本地方法readBytes(byte b[], int off, int len)
使用步骤
- 创建FileInputStream对象,构造方法中绑定数据源
- 使用FileInputStream对象中的read方法,读取数据
- 释放资源
package IO;
import java.io.FileInputStream;
import java.io.IOException;
public class FileInputStream_in {
public static void main(String args[]) throws IOException{
//read("1.txt");
//readMore("1.txt");
readAll("1.txt");
}
public static void read(String path) throws IOException{
FileInputStream fis = new FileInputStream(path);
int len =0;
while((len = fis.read())!=-1)
System.out.print((char)len);
fis.close();
}
public static void readMore(String path) throws IOException{
FileInputStream fis = new FileInputStream(path);
int len =0;
byte[] bytes = new byte[2];
while((len = fis.read(bytes))!=-1)
System.out.println(new String(bytes));
fis.close();
}
//优化上一个方法
public static void readAll(String path) throws IOException{
FileInputStream fis = new FileInputStream(path);
int len =0;
byte[] bytes = new byte[1024];
while((len = fis.read(bytes))!=-1)
System.out.print(new String(bytes,0,len));//只取有效部分
fis.close();
}
}
字节输出流(OutputStream)
java.io.OutputStream
抽象类是表示字节输出流的所有类的超类,将指定的字节信息写出到目的地。
它定义了字节输出流的基本共性功能方法。
public void close()
:关闭此输出流并释放与此流相关联的任何系统资源。public void flush()
:刷新此输出流并强制任何缓冲的输出字节被写出。public void write(byte[] b)
:将 b.length字节从指定的字节数组写入此输出流。public void write(byte[] b, int off, int len)
:从指定的字节数组写入 len字节,从偏移量 off开始输出到此输出流。public abstract void write(int b)
:将指定的字节输出流。
方法 | 描述 |
---|---|
void close() | 关闭此输出流并释放与此流有关的所有系统资源。 |
void flush() | 刷新此输出流并强制写出所有缓冲的输出字节。 |
void write(byte[] b) | 将 b.length 个字节从指定的 byte 数组写入此输出流。 |
void write(byte[] b, int off, int len) | 将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此输出流。 |
abstract void write(int b) | 将指定的字节写入此输出流。 |
*在字节输出流中,write的重载方法都调用write(int b) 方法
文件字节输出流(FileOutputStream)
java.io.FileInputStream
类是文件输入流,从文件中读取字节。
写入数据的原理(内存->硬盘):Java程序-->JVM-->操作系统-->调用本地方法-->把数据写入文件
构造方法
FileInputStream(File file)
: 通过打开与实际文件的连接来创建一个 FileInputStream ,该文件由文件系统中的 File对象 file命名。FileInputStream(String name)
: 通过打开与实际文件的连接来创建一个 FileInputStream ,该文件由文件系统中的路径名 name命名。
当你创建一个流对象时,必须传入一个文件路径。该路径下,如果没有该文件,会抛出FileNotFoundException
。
Constructor | Description |
---|---|
FileOutputStream(File file, boolean append) ** |
创建文件输出流以写入由指定的 File 对象表示的文件。 |
FileOutputStream(String name, boolean append) |
创建文件输出流以指定的名称写入文件。append是追加开关,默认false(关)。 |
FileOutputStream(String name) |
创建文件输出流以指定的名称写入文件。 |
FileOutputStream(File file) |
创建文件输出流以写入由指定的 File 对象表示的文件。 |
FileOutputStream(FileDescriptor fdObj) * |
创建文件输出流以写入指定的文件描述符,表示与文件系统中实际文件的现有连接。 |
其实不管哪个构造器都转成FileOutputStream(File file, boolean append)
形式
常用方法
方法 | 描述 |
---|---|
void write(int b) **核心 | 将指定字节写入此文件输出流。 |
void write(byte[] b) | 将 b.length 个字节从指定 byte 数组写入此文件输出流中。 |
void write(byte[] b, int off, int len) | 将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此文件输出流。 |
void close() | 关闭此文件输出流并释放与此流有关的所有系统资源。 |
protected void finalize() | 清理到文件的连接,并确保在不再引用此文件输出流时调用此流的 close 方法。 |
FileChannel getChannel() | 返回与此文件输出流有关的唯一FileChannel 对象。 |
FileDescriptor getFD() | 返回与此流有关的文件描述符。 |
底层调用本地(native)方法write(int b, boolean append)
和writeBytes(byte b[], int off, int len, boolean append)
在文件字节输出流中,write重载方法调用不同的本地方法。
实例
//用文件字节输出流向文件写入一个字节
import java.io.*;
public class fileIO {
public static void main(String args[]) throws IOException{
FileOutputStream fos = new FileOutputStream("1.txt");
fos.write(97);//输入一个字节 97的二进制1100001
fos.close();//打开记事本默认读字符97对应的ASCII表是a
}
}
package IO;
import java.io.*;
import java.util.Arrays;
public class FileOutputStream_ {
public static void main(String args[]) throws IOException{
fileOutOne("1.txt",97);//向1文件写入1个字节(8位)01100001
byte[] t = "你好".getBytes();
System.out.println(Arrays.toString(t)); //gdk中英都占两个字节。 utf-8 英文一字节,中文三字节
fileOutMore("2.txt",t);//向2文件写入你好字符串转化的字节数组。
byte[] b = {97,98,49,48,48};
fileOutMore1("3.txt",b);//向3文件写入一个规定字节数组。
}
//使用字节输出流向文件写入一个字节
public static void fileOutOne(String path,int num) throws IOException{
FileOutputStream fos = new FileOutputStream(path);//指向路径的文件
fos.write(num);//输入一个字节 97的二进制1100001
fos.close();//打开记事本默认读字符97对应的ASCII表是a
}
//使用字节输出流向文件写入多个字节
public static void fileOutMore(String path,byte[] bytes) throws IOException{
File f = new File(path);
FileOutputStream fos = new FileOutputStream(f);//指向具体文件
fos.write(bytes);//写入一个字节数组
fos.close();
}
//使用字节输出流向文件写入偏移的多个字节
public static void fileOutMore1(String path,byte[] bytes) throws IOException{
File f = new File(path);
FileOutputStream fos = new FileOutputStream(f);//指向具体文件
//byte[] b = {97,98,49,48,48}; 本应该是 ab100 偏移两位,写入三位得100
fos.write(bytes,2,3);//输入一个字节数组
fos.close();
}
}
追加 和 换行
package IO;
import java.io.FileOutputStream;
import java.io.IOException;
public class FileOutputStrean_append {
public static void main(String args[]) throws IOException{
fileOutAppend("1.txt","append".getBytes());//向1文件追加换行和写入一个单词
}
public static void fileOutAppend(String path,byte[] bytes) throws IOException{
FileOutputStream fos = new FileOutputStream(path,true);//true 标记追加
fos.write("\r\n".getBytes());//window系统下文件的换行符号
fos.write(bytes);//写入一个字节数组
fos.close();
}
}
复制文件
步骤
- 创建一个字节输入流对象,构造方法中绑定要读取的数据源
- 创建一个字节输出流对象,构造方法中绑定要写入的目的地
- 使用字节输入流对象的read方法读取数据
- 使用直接输出流对象的write方法将读取到的数据写入目的地文件
- 释放资源(栈顺序,先进后出)
package bytestream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class CopyFile {
public static void main(String[] args) throws IOException {
// 1. 创建一个字节输入流对象,构造方法中绑定要读取的数据源
FileInputStream fis = new FileInputStream("test.txt");
// 2. 创建一个字节输出流对象,构造方法中绑定要写入的目的地
FileOutputStream fos = new FileOutputStream("test2.txt");
// 3. 使用字节输入流对象的read方法读取数据
byte[] bytes = new byte[1024];
int len = 0;
while ((len = fis.read(bytes)) != -1) {
// 4. 使用直接输出流对象的write方法将读取到的数据写入目的地文件
fos.write(bytes, 0, len);
}
// 5. 释放资源(栈顺序,先进后出)
fos.close();
fis.close();
}
}
问题
中文字符的编码
- GBK 占两字节
- UTF-8 占三个字节
package bytestream;
import java.io.FileInputStream;
import java.io.IOException;
public class ByteStreamTest {
public static void main(String[] args) throws IOException {
// 当使用字节流读取字符时,存在一些问题
FileInputStream fis = new FileInputStream("test.txt");
int len = 0;
while ((len = fis.read()) != -1) {
System.out.println((char)len);
}
fis.close();
}
}
test.txt
123789456你好
运行结果
1
2
3
7
8
9
4
5
6
ä
½
å
¥
½
使用字节流读取字符数据出现乱码!
字节流存在的问题:当使用字节流读取文本文件时,可能会有一个小问题。就是遇到中文字符时,可能不会显示完整的字符,
那是因为一个中文字符可能占用多个字节存储。所以Java提供一些字符流类,以字符为单位读写数据,专门用于处理文本文件。