12.3.1 字节流(1)
字节流主要操作byte类型数据,以byte数组为准,主要操作类是OutputStream类和InputStream类。
1.字节输出流:OutputStream
OutputStream是整个IO包中字节输出流的最大父类,此类的定义如下:
- public abstract class OutputStream
- extends Object
- implements Closeable, Flushable
从以上定义中可以发现,OutputStream类是一个抽象类,如果要使用此类,则首先必须通过子类实例化对象。如果现在要操作的是一个文件,则可以使用FileOutputStream类,通过向上转型后,可以为OutputStream实例化,在OutputStream类中的主要操作方法如表12-3所示。
表12-3 OutputStream类的常用方法
序号 | 方 法 | 类型 | 描 述 |
1 | public void close() throws IOException | 普通 | 关闭输出流 |
2 | public void flush() throws IOException | 普通 | 刷新缓冲区 |
3 | public void write(byte[] b) throws IOException | 普通 | 将一个byte数组写入数据流 |
4 | public void write(byte[] b,int off,int len) throws | 普通 | 将一个指定范围的 byte数组写入数据流 |
5 | public abstract void write(int b) throws IOException | 普通 | 将一个字节数据写入数据流 |
此时使用FileOutputStream子类,此类的构造方法如下:
public FileOutputStream(File file) throws FileNotFoundException
操作时必须接收File类的实例,指明要输出的文件路径。
U提示:关于Closeable和Flushable接口的说明。
在OutputStream类的定义中可以发现此类实现了Closeable和Flushable两个接口,那么这两个接口的定义如下。
Closeable接口:
- public interface Closeable{
- void close() throws IOException
- }
- Flushable接口:
- public interface Flushable{
- void flush() throws IOException
- }
这两个接口的作用从其定义方法中可以发现,Closeable表示可关闭,Flushable表示可刷新,而且在OutputStream类中已经有了这两个方法的实现,所以操作时用户一般不会关心这两个接口,而直接使用OutputStream类即可。
范例:向文件中写入字符串
- package org.lxh.demo12.byteiodemo;
- import java.io.File;
- import java.io.FileOutputStream;
- import java.io.OutputStream;
- public class OutputStreamDemo01 {
- public static void main(String[] args) throws Exception { // 异常抛出,
- 不处理
- // 第1步:使用File类找到一个文件
- File f = new File("d:" + File. separator + "test.txt"); // 声明File
- 对象
- // 第2步:通过子类实例化父类对象
- OutputStream out = null; // 准备好一个输出的对象
- out = new FileOutputStream(f); // 通过对象多态性,进行实例化
- // 第3步:进行写操作
- String str = "Hello World!!!"; // 准备一个字符串
- byte b[] = str.getBytes(); // 只能输出byte数组,所以将字符串变为
- byte数组
- out.write(b); // 将内容输出,保存文件
- // 第4步:关闭输出流
- out.close(); // 关闭输出流
- }
- }
程序运行结果:
12.3.1 字节流(2)
可以发现内容已经成功地写入到文件中,以上程序在实例化、写、关闭时都有异常 发生,为了方便起见,直接在主方法上使用thorws关键字抛出异常,可以减少try…catch语句。
U提示:文件不存在则会自动创建。
在以上操作的test.txt文件,在操作之前本身是不存在的,但是操作之后程序会为用户自动创建新的文件,并将内容写入到文件之中。
以上操作是直接将一个字符串变为byte数组,然后将byte数组直接写入到文件中,当然也可以通过循环把每一个字节一个个地写入到文件之中 。
范例:使用write(int t)的方式写入文件内容
- package org.lxh.demo12.byteiodemo;
- import java.io.File;
- import java.io.FileOutputStream;
- import java.io.OutputStream;
- public class OutputStreamDemo02 {
- public static void main(String[] args)
throws Exception { // 异常抛出,- 不处理
- // 第1步:使用File类找到一个文件
- File f = new File("d:" + File.
separator + "test.txt");// 声明File- 对象
- // 第2步:通过子类实例化父类对象
- OutputStream out = null;
// 准备好一个输出的对象- out = new FileOutputStream(f);
// 通过对象多态性,进行实例化- // 第3步:进行写操作
- String str = "Hello World!!!";
// 准备一个字符串- byte b[] = str.getBytes();
// 只能输出byte数组,所以将字符串变- 为byte数组
- for (int i = 0; i < b.length; i++) {
- out.write(b[i]);
// 将内容输出- }
- // 第4步:关闭输出流
- out.close();
// 关闭输出流- }
- }
上面程序是将byte数组中的内容一个个地写入到文件之中,实现的功能与上一个程序是一致的。以上两种做法并没有什么不同,两者可以任意使用。
2.追加新内容
之前的所有操作中,如果重新执行程序,则肯定会覆盖文件中的已有内容,那么此时可以通过FileOutputStream向文件中追加内容,FileOutputStream的另外一个构造方法如下:
- public FileOutputStream(File file,boolean
append) throws FileNotFoundException
在构造方法中,如果将append的值设置为true,则表示在文件的末尾追加内容。
范例:修改之前的程序,追加文件内容
- package org.lxh.demo12.byteiodemo;
- import java.io.File;
- import java.io.FileOutputStream;
- import java.io.OutputStream;
- public class OutputStreamDemo03 {
- public static void main(String[] args)
throws Exception { // 异常抛出,- 不处理
- // 第1步:使用File类找到一个文件
- File f = new File("d:" + File.separator
+ "test.txt");// 声明File- 对象
- // 第2步:通过子类实例化父类对象
- OutputStream out = null;
// 准备好一个输出的对象- out = new FileOutputStream(f,true);
// 此处表示在文件末尾追加内容- // 第3步:进行写操作
- String str = "Hello World!!!";
// 准备一个字符串- byte b[] = str.getBytes();
// 只能输出byte数组,所以将字符串变- 为byte数组
- for (int i = 0; i < b.length; i++) {
- out.write(b[i]);
// 将内容输出- }
- // 第4步:关闭输出流
- out.close();
// 关闭输出流- }
- }
程序运行结果:
可以发现,每次执行后,内容会自动追加到文件的末尾。
提问:如何增加换行?
上面程序确实在文件之后追加内容,可是内容是紧跟在原有内容之后的,如何在文件中增加换行,使文件内容显示更加清晰呢?
回答:使用\r\n增加换行。
如果要换行,则直接在字符串要换行处加入一个"\r\n"。
- // 第3步:进行写操作
- String str = "\r\n Hello World!!!"; // 准备一个字符串
程序运行结果:
12.3.1 字节流(3)
3.字节输入流InputStream
既然程序可以向文件中写入内容,则可以通过InputStream从文件中把内容读取进来。InputStream类的定义如下:
- public abstract class InputStream
- extends Object
- implements Closeable
与OutputStream类一样,InputStream本身也是一个抽象类,必须依靠其子类,如果现在从文件中读取,子类肯定是FileInputStream。InputStream类中的主要方法如表12-4所示。
表12-4 InputStream类的常用方法
序号 | 方 法 | 类型 | 描 述 |
1 | public int available() throws IOException | 普通 | 可以取得输入文件的大小 |
2 | public void close() throws IOException | 普通 | 关闭输入流 |
3 | public abstract int read() throws IOException | 普通 | 读取内容, 以数字的方式读取 |
4 | public int read(byte[] b) throws IOException | 普通 | 将内容读到byte数组中, 同时返回读入的个数 |
FileInputStream类的构造方法如下:
- public FileInputStream(File file) throws FileNotFoundException
范例:从文件中读取内容
- package org.lxh.demo12.byteiodemo;
- import java.io.File;
- import java.io.FileInputStream;
- import java.io.InputStream;
- public class InputStramDemo01 {
- public static void main(String[] args) throws Exception { // 异常抛出,
- 不处理
- // 第1步:使用File类找到一个文件
- File f = new File("d:" + File.separator + "test.txt"); // 声明File
- 对象
- // 第2步:通过子类实例化父类对象
- InputStream input = null; // 准备好一个输入的对象
- input = new FileInputStream(f); // 通过对象多态性进行实例化
- // 第3步:进行读操作
- byte b[] = new byte[1024]; // 所有的内容读到此数组中
- input.read(b); // 把内容取出,内容读到byte数组中
- // 第4步:关闭输入流
- input.close(); // 关闭输入流
- System.out.println("内容为:" + new String(b)); // 把byte数组变为字符
- 串输出
- }
- }
程序运行结果:
内容为:Hello World!!!_
(后面会有大量的空格,此处省略其显示)
内容已经被读取进来,但是发现后面有很多个空格,这是因为开辟的byte数组大小为1024,而实际的内容只有14个字节,也就是说存在1010个空白的空间,在将byte数组变为字符串时也将这1010个无用的空间转为字符串,这样的操作肯定是不合理的。如果要想解决以上的问题,则要观察read方法,在此方法上有一个返回值,此返回值表示向数组中写入了多少个数据。
范例:修正以上的错误
- package org.lxh.demo12.byteiodemo;
- import java.io.File;
- import java.io.FileInputStream;
- import java.io.InputStream;
- public class InputStreamDemo02 {
- public static void main(String[] args) throws Exception { // 异常抛出,
- 不处理
- // 第1步:使用File类找到一个文件
- File f = new File("d:" + File. separator + "test.txt");// 声明File
- 对象
- // 第2步:通过子类实例化父类对象
- InputStream input = null; // 准备好一个输入的对象
- input = new FileInputStream(f); // 通过对象多态性进行实例化
- // 第3步:进行读操作
- byte b[] = new byte[1024]; // 所有的内容读到此数组中
- int len = input.read(b); // 将内容读出
- // 第4步:关闭输入流
- input.close(); // 关闭输入流
- System.out.println("读入数据的长度:" + len);
- System.out.println("内容为:" + new String(b, 0, len));// 把byte数组变
- 为字符串输出
- }
- }
程序运行结果:
读入数据的长度:14
内容为:Hello World!!!
此时,再次运行程序,发现没有多余的空格产生,这是因为程序在最后只是将byte数组指定范围中的内容变成了字符串。
注意:注意输入文件的路径。
在使用FileInputStream读取时如果指定的路径不存在,则程序运行会出现异常。
12.3.1 字节流(4)
以上问题是否有其他的方式解决,因为虽然最后指定了byte数组的范围,但是程序依然开辟了很多的无用空间,这样肯定会造成资源的浪费,那么此时能否根据文件的数据量来选择开辟空间的大小呢?要想完成这样的操作,则要从File类中着手,因为在File类中存在一个length()的方法,此方法可以取得文件的大小。
范例:开辟指定大小的byte数组
- package org.lxh.demo12.byteiodemo;
- import java.io.File;
- import java.io.FileInputStream;
- import java.io.InputStream;
- public class InputStreamDemo03 {
- public static void main(String[] args)
throws Exception { // 异常抛出,- 不处理
- // 第1步:使用File类找到一个文件
- File f = new File("d:" + File.
separator + "test.txt");// 声明File- 对象
- // 第2步:通过子类实例化父类对象
- InputStream input = null;
// 准备好一个输入的对象- input = new FileInputStream(f);
// 通过对象多态性进行实例化- // 第3步:进行读操作
- byte b[] = new byte[(int)f.length()];
// 所有的内容读到此数组中,数组- 大小由文件决定
- input.read(b);
// 将内容读出- // 第4步:关闭输入流
- input.close();
// 关闭输入流- System.out.println("内容为:" + new
String(b)); // 把byte数组变为字符- 串输出
- }
- }
程序运行结果:
内容为:Hello World!!!
除以上方式外,也可以通过循环从文件中一个个地把内容读取进来,直接使用read()方法即可。
范例:使用read()通过循环读取
- package org.lxh.demo12.byteiodemo;
- import java.io.File;
- import java.io.FileInputStream;
- import java.io.InputStream;
- public class InputStreamDemo04 {
- public static void main(String[] args)
throws Exception { // 异常抛出,- 不处理
- // 第1步:使用File类找到一个文件
- File f = new File("d:" + File.
separator + "test.txt"); //声明File- 对象
- // 第2步:通过子类实例化父类对象
- InputStream input = null;
// 准备好一个输入的对象- input = new FileInputStream(f);
// 通过对象多态性进行实例化- // 第3步:进行读操作
- byte b[] = new byte[(int) f.length()];
// 所有的内容读到此数组中- for (int i = 0; i < b.length; i++) {
- b[i] = (byte)input.read();
// 将内容读出- }
- // 第4步:关闭输入流
- input.close();
// 关闭输入流- System.out.println("内容为:" +
new String(b)); // 把byte数组变为字符- 串输出
- }
- }
程序运行结果:
内容为:Hello World!!!
但是,以上程序是在明确知道了具体数组大小的前提下开展的,如果此时不知道要输入的内容有多大,则只能通过判断是否读到文件末尾的方式来读取文件。
范例:另一种方式的读取
- package org.lxh.demo12.byteiodemo;
- import java.io.File;
- import java.io.FileInputStream;
- import java.io.InputStream;
- public class InputStreamDemo05 {
- public static void main(String[] args)
throws Exception { // 异常抛出,- 不处理
- // 第1步:使用File类找到一个文件
- File f = new File("d:" + File.separator
+ "test.txt"); // 声明File- 对象
- // 第2步:通过子类实例化父类对象
- InputStream input = null;
// 准备好一个输入的对象- input = new FileInputStream(f);
// 通过对象多态性进行实例化- // 第3步:进行读操作
- int len = 0;
// 用于记录读取的数据个数- byte b[] = new byte[1024];
// 所有的内容读到此数组中- int temp = 0;
// 接收读取的每一个内容- while ((temp = input.read()) != -1) {
- // 将每次的读取内容给temp变量,
如果temp的值不是-1,则表示文件没有读完- b[len] = (byte) temp;
- len++;
- }
- // 第4步:关闭输入流
- input.close();
// 关闭输入流- System.out.println("内容为:" +
new String(b, 0, len));- }
- }
程序运行结果:
内容为:Hello World!!!
以上几种读取字节流的方式,读者最好都掌握,因为随着开发的需要,都有可能使用。
U提示:文件读到末尾了,则返回的内容为-1。
以上程序代码中要判断temp接收到的内容是否是-1,正常情况下是不会返回-1的,只有当输入流的内容已经读到底,才会返回这个数字,通过此数字可以判断输入流中是否还有其他内容。