Java IO即Java输入输出系统。在Java IO中,流是一个核心的概念。流的本质是一组有序的数据集合。既可以从流中读取数据,也可以往流中写数据。在Java IO中流既可以是字节流(以字节为单位进行读写),也可以是字符流(以字符为单位进行读写)。
流的分类:
1.按照流的方向:
- 输入流:InputStream和Reader(读取外部数据(文件、磁盘,网络等)到程序中)
- 输出流:OutputStream和Writer(将程序内的数据(内存)输出到磁盘光盘等设备)
2.按照流的操作单元:
- 字节流:InputStream和OutputStream
- 字符流:Reader和Writer
3.按照角色划分:
- 节点流(介质流):数据从/向一个介质(磁盘、鼠标、屏幕)读/写的流称为节点流。也称之为低级流
- 处理流(高级流):和节点流直接相连接的流
下表清晰的区分了字符流,字节流,输入流,输出流:
字节流 | 字符流 | |
---|---|---|
输入流 | InputStream | Reader |
输出流 | OutputStream | Writer |
字节流,字符流的特点:
字节流:inputStram,outputStram
1.二进制数据:将数据解释成原始的二进制数
2.byte数据:读写均为字节数据
3.效率高:二进制数不需要编码和解码,比文本I/O效率高
4.可移植:与主机的编码无关
字符流:reader , writer
1.字符数据:字符流将原始数据解释成字符序列
2.依赖编码方式:文本数据存储依赖文件编码方式,字符流的输入和输出需要进行编码和解码
字节流字符流的区别:
- 存储的方式不同:
字节流:存储的数据是0101形式
字符流:存储的是字符 - 读写的单元不同:
字节流:以字节(8bit)为单位
字符流:以字符为单位,跟比码表映射字符,一次可以读取多个字节 - 处理的对象不同
字节流:能处理所有类型的数据(比如:图片,AVI视频)
字符流:只能处理字符类型的数据 - 处理效率
字节流效率较高,直接进行读取
字符流效率较低,涉及到编码解码的过程 - 可移植性
字节流可移植行高,和平台无关
字符流可移植性低,要考虑编码解码过程,编码和解码的码表需要保持一致
节点流和处理流区别:
- 节点流直接和介质相连接
- 处理流直接和节点流相连接
下图是具体的Java IO类的集成关系:
下面,对字节,字符流基类的操作步骤及其常用方法做以简单的介绍:
-
InputStream 字节输入流基类
操作步骤:
1.打开字节输入流,可能会抛出FileNotFoundException异常
FileInputStream inputStream = new FileInputStream(path);2.读取数据操作,-1表示读取结束,可能会抛出IOException异常
int read = inputStream.read();3.关闭流,可能会抛出IOException异常
inputStream.close();读操作介绍:
每次读取一个字节,返回的结果为对应字节的数据:
int read = inputStream.read();批量读取数据,将数据读入到arr的byte数组中,返回值为int类型,表示读取的有效数据的个数:
inputStream.read(byte[] arr);批量读取数据,b[] 表示的是数据读入到的缓存地址,off表示读入的缓存起始位置,len表示从起始位置开始写入的数量,read方法返回值表示读取有效数据个数:
int read(byte b[], int off, int len);InputStream实例:
//用字节流读文件
public static void inToFile() throws IOException {
String path = new String("d:/helllo hcy.txt");
File file = new File(path);
byte[] b = new byte[(int) file.length()];
//因为是用字节流来读媒介,所以对应的是InputStream
//又因为媒介对象是文件,所以用到子类是FileInputStream
FileInputStream ips = new FileInputStream(file);
int size = ips.read(b);
String s = new String(b);
System.out.println("文件大小:"+size+" 文件内容:"+s);
ips.close();
}
-
OutputStream 字节输出基类
操作步骤:
1.打开字节输出流,可能会抛出FileNotFoundException异常
FileOutputStream outputStream = new FileOutputStream(path);
写操作时,文件不存在,会自动创建文件,并写入数据,当文件目录路径不合法时则会抛出异常2.写操作,可能会抛出IOException异常
void write(int b) 每次写入一个字节3.关闭流,可能会抛出IOException异常
outputStream.close();写操作介绍:
void write(int b) 每次写入一个字节void write(byte b[]) 批量写入,写入类型为byte[]
void write(byte b[], int off, int len) 批量写入,可以自定义从byte数组的起始和读取长度
void flush(); 刷新缓存
OutputStream 实例:
//用字节流写文件
public static void outToFile() throws IOException {
String path = new String("d:/helllo hcy.txt");
String s = new String("hello hcy!");
byte[] b = s.getBytes();
File file = new File(path);
//因为是用字节流来写媒介,所以对应的是OutputStream
//又因为媒介对象是文件,所以用到子类是FileOutputStream
FileOutputStream ops = new FileOutputStream(file);
ops.write(b);
ops.close();
}
-
Reader字符输入流基类
操作步骤
1.打开字符读操作流,可能会抛出FileNotFoundException异常
FileReader reader = new FileReader(path);2.读操作,可能会抛出IOException异常
reader.read();3.关闭字符读操作流
reader.close();读操作介绍:
int read()
每次读取一个字符,读操作结束的返回值-1,返回值就是字符对应的ASCII码的数字int read(char cbuf[])
批量读取数据,读操作结束的返回值-1,数据读取到char []数组中,返回值表示读取有效数据个数int read(char cbuf[], int offset, int length)
批量读取数据,读操作结束的返回值-1,数据读取到char []数组中,可以指定读取数据的起始和大小,返回值表示读取有效数据个数Reader实例:
//用字符流读文件
public static void ReToFile() throws IOException {
String path = new String("d:/helllo hcy.txt");
File file = new File(path);
char[] b = new char[(int)file.length()];
//因为是用字符流来读媒介,所以对应的是Reader
//又因为媒介对象是文件,所以用到子类是FileReader
Reader reader = new FileReader(file);
int size = reader.read(b);
String s = new String(b);
System.out.println("文件大小:"+size+" 文件内容:"+s);
reader.close();
}
-
Writer字符输出流基类
操作步骤
1.打开字符写操作流,可能会抛出IOException异常
FileWriter writer = new FileWriter(path,true);
构造函数 FileWriter(String fileName, boolean append) 第二个参数表示数据是否追加, true:追加的形式写入 false:覆盖原内容的形式写入 ,默认是false2.写操作,可能会抛出IOException异常
writer.write(99);3.关闭写操作流,可能会抛出IOException异常
writer.close();写操作介绍:
void write(int c) 写入单个的字符void write(char cbuf[]),批量的写入字符,数据写入类型是char []
void write(char cbuf[], int off, int len)批量的写入字符,自定义起始和长度
void write(String str),直接写入字符串
void write(String str, int off, int len)直接写入字符串,自定义写入字符串子集
writer.append(“append”);追加数据,构造函数时append参数设置为true起作用
writer.flush();刷新缓存
Writer实例:
//用字符写文件
public static void WiToFile() throws IOException {
String path = new String("d:/helllo hcy.txt");
File file = new File(path);
String s = new String("hello hcy!");
//因为是用字符流来读媒介,所以对应的是Writer
//又因为媒介对象是文件,所以用到子类是FileWriter
Writer writer = new FileWriter(file,true);//表示允许数据追加
writer.write(s);
//追加数据
writer.append("Here we are");
writer.close();
}
下图是Java IO中各类所负责的媒介:
使用流的规律:
1.明确是读操作还是写操作
读:InputStream/Reader
写:OutputStream/Writer
2.明确是操作字节还是字符(明确操作基类)
读:
- 字节:InputStream
- 字符:Reader
写:
- 字节:OutputStream
- 字符:Writer
3.操作的具体介质(明确操作的具体类)
读:
- 文件:File
- 内存:char,array,double
- 网络:Socket
- 键盘:Sysem.in
写:
- 文件:File
- 内存:char,array
- 网络:Socket
- 屏幕:System.out
4.是否需要额外操作
缓冲区:BufferXXX
转换:InputStreamReader、OutputStreamWriter
随机访问文件类:RandomAccessFile
- 随机读取File文件:
有时候我们希望读取文件的一部分,或者是说随机的读取文件,那么我们就可以利用RandomAccessFile。RandomAccessFile提供了seek()方法,用来定位将要读写文件的指针位置,我们也可以通过调用getFilePointer()方法来获取当前指针的位置,具体看下面的例子:
//随机读取文件
public static void randomAccessRead() throws IOException {
String path = new String("d:/helllo hcy.txt");
byte[] bytes = new byte[5];
//创建一个RandomAccessFile对象
RandomAccessFile raf = new RandomAccessFile(path,"rw");
//通过seek()方法来移动读写位置的指针
raf.seek(5);
//获取当前指针的位置
long pointerStart = raf.getFilePointer();
//读文件
raf.read(bytes);
long pointerEnd = raf.getFilePointer();
String s = new String(bytes);
System.out.println("pointerStart:"+pointerStart+"pointerEnd:"+pointerEnd);
System.out.println("读取内容:"+s);
raf.close();
}
- 随机写入File文件:
//用字符写文件
public static void WiToFile() throws IOException {
String path = new String("d:/helllo hcy.txt");
File file = new File(path);
String s = new String("hello hcy!");
//因为是用字符流来读媒介,所以对应的是Writer
//又因为媒介对象是文件,所以用到子类是FileWriter
Writer writer = new FileWriter(file,true);//表示允许数据追加
writer.write(s);
//追加数据
writer.append("Here we are");
writer.close();
}