流分为输入流和输出流。输入流:数据流入程序;输出流:数据从程序流出。
在java.io
包中,流分为两种:字节流和字符流。
- 字节流:InputStream, OutputStream
- 字符流:Reader, Writer
不管使用的字节流还是字符流,其基本的操作流程几乎是一样的,以文件操作为例。
- 根据文件路径创建File对象;
- 根据字节流或字符流的子类实例化父类对象;
- 进行数据的读取或者写入操作;
- 关闭流(close()).
对于I/O操作属于资源处理,所有的资源处理操作(I/O操作、数据库操作、网络)最后必须要进行关闭。
这篇博客主要介绍字节的输入流和输出流。
如果想要通过程序将数据流出,则通过java.io.OutputStream
。
OutputStream类的结构:
public abstract class OutputStream implements Closeable, Flushable
由以上可知OutputStream是一个抽象类,所以无法直接实例化,必须通过子类实例化。
OutputStream子类如下:
文件输出流是用于将数据写入到输出流File中。
FileOutputStream中的构造方法如下:
- 创建输出流写入指定的File对象(覆盖):public FileOutputStream(String name) throws FileNotFoundException
- 创建输出流写入指定的File对象(追加):public FileOutputStream(String name, boolean append) throws FileNotFoundException
举例:实现文件内容的输出
public class TestStream {
public static void main(String[] args) {
String directory = "D:"+File.separator+"test1"+File.separator
+"java"+File.separator+"hh.txt";
//File和本地文件系统相关联
File file = new File(directory);
OutputStream out = null;
try {
out = new FileOutputStream(file);
//写入一个字节
out.write('b');
out.write('\n');
//写入字节数组
out.write("hello world!!!\n".getBytes());
//写入指定字节长度
String str = "I love programming!!!";
byte[] bytes = str.getBytes();
out.write(bytes, 0 , 8);
//刷新此字节流并强制缓冲区的输出字节被写出
out.flush();
out.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally{
if(out!=null){
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
在上面中,需要注意两点:
另外,以上用到了OutputStream的三个方法:
- 将给定的字节数组内容全部输出:public void write(byte b[]) throws IOException
- 将部分字节内容输出:public void write(byte b[], int off, int len) throws IOException
- 输出单个字节:public abstract void write(int b) throws IOException;
通过上述代码发现,手动关闭流对象过于繁琐,所以JDK1.7
后提供了自动关闭功能。这是因为OutputStream实现了AutoCloseable
接口,Java虚拟机会帮我们自动关闭
。
举例:实现自动关闭
public class TestStream {
public static void main(String[] args) {
String directory = "D:"+File.separator+"test1"+File.separator
+"java"+File.separator+"hh.txt";
//File和本地文件系统相关联
File file = new File(directory);
//在括号内实例化对象,最后会实现自动关闭
try ( OutputStream out = new FileOutputStream(file);){
out.write(49); //字符1
out.write("\n".getBytes());
String str = "helloworld";
byte[] buff = str.getBytes();
out.write(buff, 0,5);
out.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
OutputStream是将数据输出到文件中,那么使用InputStream就是读取文件的内容。
InputStream的定义如下:
public abstract class InputStream implements Closeable
因为也是抽象类,所以必须使用子类FileInputStream,从文件系统中的文件获取输入字节流。
InputStream中提供了如下方法:
- public int read(byte b[])throws IOException:读取数据到字节数组中,返回数据的读取个数。
- public int read(byte b[], int off, int len)throws IOException:读取传递数组部分内容。
- public abstract int read()throws IOException:读取单个字节。
这三种,当读取没有数据时返回-1.
举例:读取文件内容
public class TestStream {
public static void main(String[] args) {
String directory = "D:"+File.separator+"test1"+File.separator
+"java"+File.separator+"hh.txt";
//File和本地文件系统相关联
File file = new File(directory);
if(file.exists()&&file.isFile()){
try (InputStream in = new FileInputStream(file);){
byte[] data = new byte[7];
int len = in.read(data);
System.out.println("读取数据的长度:"+len);
System.out.println("读取的数据:"+new String(data));
//读取单个字节
System.out.println(in.read());
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}else{
throw new RuntimeException("文件不存在或者文件是个目录");
}
}
}
但以上中产生了一个问题:不能确定是否读完,只能一直读。
举例:采用循环的方式
public class TestStream {
public static void main(String[] args) {
String directory = "D:"+File.separator+"test1"+File.separator
+"java"+File.separator+"hh.txt";
//File和本地文件系统相关联
File file = new File(directory);
if(file.exists()&&file.isFile()){
try (InputStream in = new FileInputStream(file);){
int d = -1;
while((d = in.read())!= -1){
System.out.print((char)d); //读出来的为字节,强制转换
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}else{
throw new RuntimeException("文件不存在或者文件是个目录");
}
}
}
一个一个字节读取,效率太慢,所以采用缓冲区的读取方式。
举例:缓冲区方式读取
public class TestStream {
public static void main(String[] args) {
String directory = "D:"+File.separator+"test1"+File.separator
+"java"+File.separator+"hh.txt";
//File和本地文件系统相关联
File file = new File(directory);
if(file.exists()&&file.isFile()){
try (InputStream in = new FileInputStream(file);){
byte[] buff = new byte[1024];
int len = -1;
while((len = in.read(buff))!=-1){
//有了偏移量,从0开始读取
System.out.print(new String(buff,0,len));
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}else{
throw new RuntimeException("文件不存在或者文件是个目录");
}
}
}
简单总结,以文件操作为例:
1. 对于OutputStream:就是将数据流出,将数据写到文件里;常用的使用的方法就是write(byte b[])
。
2. 对于InputStream:就是读取文件中的数据,流入程序;常用的使用方法就是read(byte b[], int offset, int len)
。
3. 对于OutputStream来说,写入到文件中时,若文件没有,可以自动创建;而对于InputStream来说,文件必须存在。