流的概念和作用
流是一组有顺序的,有起点和终点的字节集合,是对数据传输的总称或抽象。即数据在两设备间的传输称为流,流的本质是数据传输,根据数据传输特性将流抽象为各种类,方便更直观的进行数据操作。
字符流和字节流
字符流的由来
因为数据编码的不同,而有了对字符进行高效操作的流对象。本质其实就是基于字节流读取时,去查指定的码表。
字节流和字符流的区别:
- 读写单位不同:字节流以字节(8bit)为单位,字符流以字符为单位,根据码表映射字符,一次可能读多个字节。
- 处理对象不同:字节流能处理所有类型的数据(如图片、avi等),而字符流只能处理字符类型的数据。
- 是否使用缓冲区:字符流使用了缓冲区(buffer),而字节流没有使用缓冲区
在字节流中输出数据主要是使用OutputStream完成,输入使用InputStream,在字符流中输出主要是使用Writer类完成,输入流主要使用Reader类完成(这四个都是抽象类)。
Java内用Unicode编码存储字符,字符流实际是由Java虚拟机将字节转化为2个字节的Unicode字符为单位的字符而成。基于这个特点,处理文本数据一般使用字符流,处理二进制数据(如图片、视频、音频)一般用字节流。
I/O类库的使用
Java中的I/O流类总结图:
I/O流类
a. 面向字节的输入输出流
InputStream和OutputStream是所有字节流的基类,是一种抽象类。
InputStream的常用方法:
方法 | 功能描述 |
---|---|
void close() | 关闭输入流 |
int read() | 从输入流中当前位置读取一个字节的二进制数据,以此数据为低位字节,补足16位的整型量(0~255)后返回,若输入流中当前位置没有数据,则返回-1 |
int read(byte b[]) | 从输入流当前位置连续读取多个字节并保存到数组中,返回所读取的字节数 |
int read(byte b[], int offset, int len) | 从输入连当前位置连续读取len长的字节,从数组offset位置处开始存放,返回所读取的字节数 |
int available() | 返回输入流中可以读取的字节数 |
long skip(long n) | 跳过字节流中n个字节,此方法有可能失效 |
boolean markSupported() | 判断输入流是否支持标记 |
void mark(int n) | 标记输入流的当前位置, 参数n表示读取n个字节前标记有效 |
void reset() | 将读取位置返回到对此输入流最后调用mark()方法的位置 |
通过代码验证一下上面的方法:
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
/**
* 字节输入流测试
* @author Ravior
*
*/
public class InputStreamTest {
public static void main(String[] args) {
try {
InputStream ins = new FileInputStream("data.txt");
System.out.println("可读取字节长度:"+ins.available());
int frist = ins.read();
System.out.println("读取字节:"+frist);
int second = ins.read();
System.out.println("读取字节:"+second);
// 标记输入流当前位置
if (ins.markSupported()) {
ins.mark(10);
} else {
System.out.println("不支持标记输入流当前位置");
}
int three = ins.read();
System.out.println("读取字节:"+three);
if (ins.markSupported()) {
ins.reset();
} else {
System.out.println("不支持标记输入流当前位置");
}
int four = ins.read();
System.out.println("读取字节:"+four);
// 跳过2个字节
ins.skip(2);
// 从输入流当前位置读取一个字节
int five = ins.read();
System.out.println("读取字节:"+five);
byte b[] = new byte[2];
// 从输入流当前位置连续读取字节保存到数组中,并返回读取的字节数
int readNum = ins.read(b);
System.out.println("读取的字节数:"+readNum);
byte b2[] = new byte[2];
// 从输入流当前位置读取1个字节,从数组1索引位开始存放
int readNum2 = ins.read(b2, 1, 1);
System.out.println("读取的字节数:"+readNum2);
System.out.println("字节数组0:"+b2[0]);
System.out.println("字节数组1:"+b2[1]);
// 关闭输入流
ins.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
OutputStream的常用方法:
方法 | 功能描述 |
---|---|
void close() | 关闭输出流 |
void flush() | 强制清空缓存区并向外设输出数据 |
void write(int b) | 将参数b的低位字节写入输出流 |
void write(byte b[]) | 将数组b写入输出流 |
void write(byte b[], int offset, int len) | 将数组b第offset位置开始,写入len个字节到输出流 |
下面,我们同样用代码验证一下上面的方法:
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
/**
* Java 字节输出流测试
* @author Ravior
*
*/
public class OutputStreamTest {
public static void main(String[] args) {
try {
OutputStream os = new FileOutputStream("data.txt");
// 写入一个字节
os.write(0);
os.write(new byte[]{1,0});
os.write(new byte[]{1}, 0, 1);
os.flush();
os.close();
System.out.println("写入输出流完成");
} catch (FileNotFoundException e) {
e.printStackTrace();
System.out.println("文件不存在");
} catch (IOException e) {
e.printStackTrace();
System.out.println("写入文件发生错误");
}
}
}
b. 面向字符的输入输出流
字符流是针对字符数据的特点进行过优化的,因而提供一些面向字符的有用特性,字符流的源或目标通常是文本文件。 Reader和Writer是java.io包中所有字符流的父类。由于它们都是抽象类,所以应使用它们的子类来创建实体对象,利用对象来处理相关的读写操作。Reader和Writer的子类又可以分为两大类:一类用来从数据源读入数据或往目的地写出数据(称为节点流),另一类对数据执行某种处理(称为处理流)。
Reader字符输入流
面向字符的输入流类都是Reader的子类,Reader提供的方法和InputStream类似。
方法 | 功能描述 |
---|---|
void close() | 关闭输入流 |
boolean markSupported() | 判断当前输入流是否支持mark |
void mark() | 标记输入流的当前位置 |
void reset() | 将读取位置返回到对此输入流最后调用mark()方法的位置 |
int read() | 从输入流中读取一个字符 |
int read(char[] ch) | 从输入流中读取字符数组 |
int read(char[] ch, int offset, int len) | 从输入连当前位置连续读取len长的字节,从数组offset位置处开始存放,返回所读取的字节数 |
boolean ready() | 判断流是否可以读取 |
long skip(long n) | 跳过流内的n个字符 |
网友评论