Java IO流(二)
IO流常用基类
字节流的抽象基类:
InputStream
OutputStream
字符流的抽象基类:
Reader
Writer
注意:由这四个类派生出来的子类名称都是以其父类名作为子类名的后缀的。例如,InputStream的子类——FileInputStream,Reader的子类——FileReader。
字节流
字节流的基本操作与字符流类同,但它不仅可以操作字符,还可以操作其他媒体文件。这里,我列出比较常用的字节流,如下:
FileInputStream
FileOutputStream
BufferedInputStream
BufferedOutputStream
字节输出流——FileOutputStream
首先我为读者介绍一下字节输出流——FileOutputStream。以一个例子来作为我的开场白。
例,向一个文本文件中写入数据。
这里我使用字节输出流——FileOutputStream向一个文本文件中写入数据,此处直接给出示例代码。
public class FileOutputStreamDemo {
public static void main(String[] args) throws IOException {
/*
* 将数据写入到文件中。
* 使用字节输出流。
* FileOutputStream。
*/
File dir = new File("tempfile");
if (!dir.exists()) {
dir.mkdir();
}
// 1、创建字节输出流对象,用于操作文件,在对象初始化时必须明确数据存储的目的地。
// 输出流所关联的目的地,如果不存在,会自动创建。如果存在,则覆盖。
FileOutputStream fos = new FileOutputStream("tempfile\\fos.txt");
// 2、调用输出流的写功能。
// String str = "abcde";
// byte[] buf = str.getBytes();
fos.write("abcde".getBytes());
// 3、释放资源。
fos.close();
}
}
这里我们须注意一点——创建字节输出流对象用于操作文件,在对象初始化时必须明确数据存储的目的地,输出流所关联的目的地,如果不存在,则会自动创建;如果存在,则会覆盖。
对IOException的处理方式
在使用字节输出流——FileOutputStream向一个文本文件中写入数据时,就不可避免地发生IOException异常,对该异常的正确处理方式究竟是怎样的呢?下面已给出。
public class IOExceptionDemo {
public static void main(String[] args) {
FileOutputStream fos = null;
try {
fos = new FileOutputStream("tempfile\\fos.txt");
fos.write("hello".getBytes());
} catch (IOException e) {
// 自处理.... 但通常将异常信息写入到日志文件中以进行记录。
System.out.println(e.toString() + "----------------");
} finally {
if (fos != null) // 放在try代码块里面亦可
try {
fos.close();
} catch (IOException e) {
// 一般可以throw RuntimeException异常,或者将异常信息写入到日志文件中以进行记录。
throw new RuntimeException("关闭失败" + e);
}
}
}
}
注意:最后无论如何都应关闭资源,所以应放在finally代码块中。以后在对IO流的操作中,出现IOException异常,就按照上述代码进行处理。
字节输出流的续写和换行
现在思考这样一个问题:如果想在原有文件上继续加入新的数据,咋整呢?很简单,创建一个FileOutputStream对象时,传递一个true参数,代表不覆盖已有的文件,即在已有文件的末尾处进行数据续写。示例代码如下:
public class IOExceptionDemo {
private static final String LINE_SEPARATOR = System.getProperty("line.separator");
public static void main(String[] args) {
FileOutputStream fos = null;
try {
fos = new FileOutputStream("tempfile\\fos.txt", true); // 传入true实现续写。
String str = LINE_SEPARATOR + "hello";
fos.write(str.getBytes());
} catch (IOException e) {
// 自处理....
System.out.println(e.toString() + "----------------");
} finally {
if (fos != null)
try {
fos.close();
} catch (IOException e) {
throw new RuntimeException("关闭失败" + e);
}
}
}
}
注意:\r\n在Windows中表示行终止符,但在其他系统中并不是这样,所以为了更加通用,使用System类的getProperty(“line.separator”)方法得到系统的行终止符。
字节输入流——FileInputStream
我们知道如何使用字节输出流——FileOutputStream向一个文本文件中写入数据之后,就要考虑将已有文件的数据读取出来。
现在就有这样一个需求:从硬盘的一个文件中读取内容。
分析:既然是读取文件,显然要使用InputStream,而且又因为是要操作文件,所以最终应使用FileInputStream。使用字节输入流——FileInputStream读取文件有三种方式,下面我会详述这三种方式。
首先使用第一种方式来读取文件,给出示例代码如下,以供读者参考。
public class FileInputStreamDemo {
public static void main(String[] args) throws IOException {
// 为了确保文件一定在读之前是存在的,将字符串路径封装成File对象。
File file = new File("tempfile\\fos.txt");
if (!(file.exists())) {
throw new RuntimeException("要读取的文件不存在");
}
// 创建文件字节读取流对象时,必须明确与之关联的数据源。
FileInputStream fis = new FileInputStream(file);
// 调用读取流对象的读方法。read();
int by = 0;
while ((by = fis.read()) != -1) {
System.out.println(by);
}
// 关闭资源
fis.close();
}
}
我觉得总有初学者对以上程序代码感到一头雾水的,但是没关系啦!下面我就会画一张图来解释其原理,图画的糙,还请读者谅解。
这里写图片描述
接下来就使用第二种方式来读取文件,同样给出示例代码如下(其中加入了对IOException异常的处理),以供读者参考。
public class FileInputStreamDemo2 {
private static final int DEFAULT_SIZE = 1024; // 缓冲区大小可以是1024的整数倍。
public static void main(String[] args) {
// 演示第二种读取方式,read(byte[]);
FileInputStream fis = null;
try {
fis = new FileInputStream("tempfile\\fos.txt");
// 创建一个字节数组。
byte[] buf = new byte[DEFAULT_SIZE];
int len = 0;
// 调用read(byte[])方法
while ((len = fis.read(buf)) != -1) { // len记录的是往字节数组里存储的字节个数。
System.out.println(new String(buf, 0, len)); // 将字节数组转成字符串,打印并看一下效果。
}
} catch (IOException e) {
// 将异常信息写入到日志文件中以进行记录。
} finally {
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
// 一般可以throw RuntimeException异常,或者将异常信息写入到日志文件中以进行记录。
e.printStackTrace();
}
}
}
}
}
从以上程序代码中,可知在创建缓冲区(一个字节数组)时,缓冲区的大小一般设置为1024的整数倍。同上,下面我画一张糙图来解释上述程序代码。
这里写图片描述
最后,使用第三种方式来读取文件,给出示例代码如下,供读者参考。
public class FileInputStreamDemo3 {
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream("tempfile\\fos.txt");
System.out.println(fis.available()); // 获取与之关联的文件的字节数。
byte[] buf = new byte[fis.available()]; // 定义一个刚刚好的缓冲区,不用再循环了。不过慎用!!!
fis.read(buf);
String s = new String(buf);
System.out.println(s);
fis.close();
}
}
注意:不建议使用这种方式,因为可能会有内存溢出异常。
复制文本文件
现在有这样一个需求:复制一个文本文件。为了解决这个需求,首先就要分析一下破题思路,复制文本文件说到底就是一个读取源数据,并将数据写到目的地的过程,由于要操作设备上的数据,所以需要用到流,读用到了输入流,写用到了输出流,而且操作的还是文本文件,最终就需要用到字节流中操作文件的流对象了。解决该需求其实有两种方式,下面我会一一详述这两种方式。
首先使用第一种复制方式,即从源数据中读一个字节,就往目的地写一个字节。下面给出示例代码,供读者参考。
public class CopyTextTest {
public static void main(String[] args) throws IOException {
copyText();
}
public static void copyText() throws IOException {
// 1、创建一个输入流和源数据相关联。
FileInputStream fis = new FileInputStream("IO流.txt");
// 2、创建一个输出流,并通过输出流创建一个目的地。
FileOutputStream fos = new FileOutputStream("tempfile\\io_copy.txt");
// 3、读一个,写一个。
int by = 0;
while ((by = fis.read()) != -1) {
fos.write(by);
}
fos.close();
fis.close();
}
}
接着再来看第二种复制方式,同样给出示例代码如下。
public class CopyTextByBufTest {
public static void main(String[] args) {
copyTextByBuf();
}
public static void copyTextByBuf() {
FileInputStream fis = null;
FileOutputStream fos = null;
try {
fis = new FileInputStream("IO流.txt");
fos = new FileOutputStream("tempfile\\io_buf_copy.txt");
// 创建一个缓冲区
byte[] buf = new byte[1024];
// 定义记录个数的变量
int len = 0;
// 循环读写
while ((len = fis.read(buf)) != -1) {
fos.write(buf, 0, len);
}
} catch (IOException e) {
// 异常日志。
} finally {
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
// 异常日志。
e.printStackTrace();
}
}
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
// 异常日志。
e.printStackTrace();
}
}
}
}
}
相比第一种复制方式,此方式的复制效率会更高,所以建议使用第二种复制方式。
字节流的缓冲区对象
现在有这样一个需求:复制一个图片。其解决思路如下:
用字节读取流对象和图片关联。
用字节写入流对象创建一个图片文件,用于存储获取到的图片数据。
通过循环读写,完成数据的存储。
关闭资源。
通过以上思路,不难写出如下代码,程序代码与复制一个文本文件非常相似。
import java.io.*;
class CopyPic {
public static void main(String[] args) {
FileOutputStream fos = null;
FileInputStream fis = null;
try {
fos = new FileOutputStream("c:\\2.jpg");
fis = new FileInputStream("c:\\1.jpg");
byte[] buf = new byte[1024];
int len = 0;
while((len=fis.read(buf)) != -1) {
fos.write(buf, 0, len);
}
} catch(IOException e) {
throw new RuntimeException("复制文件失败");
} finally {
try {
if(fis != null)
fis.close();
} catch(IOException e) {
throw new RuntimeException("读取关闭失败");
}
try {
if(fos != null)
fos.close();
} catch(IOException e) {
throw new RuntimeException("写入关闭失败");
}
}
}
}
我觉得总有初学者对以上程序代码感到一头雾水的,但是没关系啦!下面我就会画一张图来解释其原理,图画的糙,还请读者谅解。
这里写图片描述
复制一个图片的需求解决完之后,接下来就要引出字节流的缓冲区对象了,它们分别是BufferedOutputStream和BufferedInputStream,它俩是怎样使用的呢?下面同样以复制一个图片的案例来看看字节流的缓冲区对象到底是如何应用在代码中的。
public class CopyPicByBufferDemo {
public static void main(String[] args) throws IOException {
copyPicByBuffer();
}
public static void copyPicByBuffer() throws IOException {
// 演示缓冲区。
// 1,创建具体的流对象。
FileInputStream fis = new FileInputStream("tempfile\\1.jpg");
// 对流进行缓冲。
BufferedInputStream bufis = new BufferedInputStream(fis);
FileOutputStream fos = new FileOutputStream("tempfile\\copy_1.jpg");
BufferedOutputStream bufos = new BufferedOutputStream(fos);
byte[] buf = new byte[1024];
int len = 0;
while ((len = bufis.read(buf)) != -1) { // 缓冲区的读方法,从缓冲区里面读出来的数据
bufos.write(buf, 0, len); // 缓冲区的写方法,向缓冲区里面写数据。
bufos.flush(); // 刷新可有可无,因为缓冲区满了之后会自动刷新。
}
fos.close();
fis.close();
}
}
转载地址:https://blog.csdn.net/yerenyuan_pku/article/details/78231697