1. 总体结构
该类表示读取文件数据的输入字节流,使用该类的主要步骤为:
- 找到目标文件
- 建立数据的输入通道
- 建立缓冲区
- 读取文件中的数据
- 关闭资源
代码示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| File file = new File("src/io/demo.txt");
InputStream is = new FileInputStream(file);
byte[] buf = new byte[1024];
int num = -1; while((num = is.read(buf)) != -1) { String content = new String(buf, 0, num); System.out.print(content); }
is.close();
|
2.2 FileOutputStream类
该类表示写入文件数据的输出字节流,下面我们结合FileInputStream类和FileOutputStream类来实现读取某个文件的数据并输出都新的文件中的功能。主要步骤为:
- 找到源文件
- 建立数据的输入通道
- 建立目的文件
- 建立数据的输出通道
- 建立缓冲区
- 读取源文件中的数据并写入缓冲区
- 将缓冲区中的文件数据写入目标文件
- 关闭流
代码示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| File srcFile = new File("src/io/demo.txt");
InputStream fis = new FileInputStream(srcFile);
File desFile = new File("src/io/desDemo.txt");
OutputStream fos = new FileOutputStream(desFile);
byte[] buf = new byte[1024];
int num = -1; while((num = fis.read(buf)) != -1) { fos.write(buf, 0, num); fos.flush(); }
fos.close(); fis.close();
|
在使用FileOutputStream写入数据到文件时,会覆盖到原始文件中的数据,可以使用FileOutputStream类的另一个构造方法来实现每次写入数据都添加到原文件的末尾。
假设存在目的文件desFile.txt,里面存在一些数据,如果我们使用如下操作向该文件写入新的数据,则会覆盖掉该文件中的原始数据。
1 2 3 4 5 6 7 8 9 10 11
| FileOutputStream fos = new FileOutputStream("src/io/desFile.txt");
for (int x = 0; x < 10; x++) { fos.write(("新的数据" + x).getBytes()); fos.write("\r\n".getBytes()); }
fos.close();
|
可以该更改FileOutputStream的构造方法的操作解决该问题:
1
| FileOutputStream fos = new FileOutputStream("src/io/desFile.txt", true);
|
后面的参数为true表示写入数据的可以追加在原有文件数据的尾部。
在上面演示文件输入输出流的时候,我们都是用字节数组来充当缓冲区,其不存在刷新缓冲区等功能。 Java有自带缓冲区的缓冲流BufferedInputStream和BufferedOutputStream,其为I/O流增加了内存缓冲区。
3.1 构造方法
- BufferedInputStream(InputStream)
- BufferedInputStream(InputStream in, int size)
第一种形式的构造方法创建了一个带有32个字节的缓冲流;第二个形式的构造方法按指定的大小来创建缓冲区。BufferedOutputStream的构造方法基本一样。
3.2 flush方法
该方法的作用是强制将缓存中的输出流(字节流,字符流等)强制输出。因为输出流在进行输出时,比如像某个文件中写入内容,其实是先将输出流写入到缓冲区,当缓冲区写满后才将缓冲区的内容输出到文件中。但是当主机完成输出流的输出后,有可能缓冲区这个时候还没有被填满,这样的话,就会一直等待主机发送内容,这时候,就可以使用flush将缓冲区的内容强制输出到文件中,清空缓冲区。 所以,一般在关闭输出流之前,要先调用flush方法强制缓冲区中的内容输出,并清空缓冲区。
3.3 字节数据读取文件与写入文件过程
代码示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| InputStream fis = null; BufferedInputStream bis = null; OutputStream fos = null; BufferedOutputStream bos = null;
try { try { fis = new FileInputStream("src/io/demo.txt"); bis = new BufferedInputStream(fis); fos = new FileOutputStream("src/io/desFile.txt"); bos = new BufferedOutputStream(fos); byte[] buf = new byte[1024]; int num = 0; while ((num = bis.read(buf)) != -1) { bos.write(buf, 0, num); bos.flush(); } } finally { bos.close(); bis.close(); } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); }
|
在io包中,提供了两个与平台无关的数据操作流数据输出流(DataOutputStream)和数据输入流 (DataInputStream)
通常数据输出流会按照一定的格式将数据输出,再通过数据输入流按照一定的格式将数据读入。
比如按照如下的格式将数据输出到文件:
代码示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| public void writeData() { FileOutputStream fos = null; DataOutputStream dos = null; try { try { File file = new File("f:" + File.separator + "demo.txt"); fos = new FileOutputStream(file); dos = new DataOutputStream(fos); String names[] = {"衬衣","手套","围巾"} ; float prices[] = {98.3f,30.3f,50.5f} ; int nums[] = {3,2,1} ; for(int i=0;i<names.length;i++){ dos.writeChars(names[i]) ; dos.writeChar('\t') ; dos.writeFloat(prices[i]) ; dos.writeChar('\t') ; dos.writeInt(nums[i]) ; dos.writeChar('\n') ; dos.flush(); }
} finally { dos.close(); fos.close(); } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } }
|
如果我们直接用记事本打开demo.txt文件,会出现乱码现象。
应该将使用DataOutputStream写入的数据通过DataInputStream读取出来:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| public void readData() { FileInputStream fis = null; DataInputStream dis = null;
try { try { File file = new File("f:" + File.separator + "demo.txt"); fis = new FileInputStream(file); dis = new DataInputStream(fis); String names = null; float price = 0.0f ; int num = 0 ; char temp[] = new char[200]; int len = 0 ; char c = 0 ;
while(true) { len = 0; while((c = dis.readChar()) != '\t') { temp[len++] = c; } names = new String(temp, 0, len); price = dis.readFloat(); dis.readChar(); num = dis.readInt(); dis.readChar() ; System.out.printf("名称:%s;价格:%5.2f;数量:%d\n",names,price,num) ; } } finally { dis.close(); fis.close(); } } catch (Exception e) { } }
|
运行结果:
1 2 3
| 名称:衬衣;价格:98.30;数量:3 名称:手套;价格:30.30;数量:2 名称:围巾;价格:50.50;数量:1
|
5. PrintStream
- PrintStream 是打印输出流,它继承于FilterOutputStream。是用来装饰其它输出流,它能为其他输出流添加了功能,使它们能够方便地打印各种数据值表示形式。
- 与其他输出流不同,PrintStream 永远不会抛出 IOException;它产生的IOException会被自身的函数所捕获并设置错误标记, 用户可以通过 checkError() 返回错误标记,从而查看PrintStream内部是否产生了IOException。
- PrintStream 提供了自动flush 和 字符集设置功能。所谓自动flush,就是往PrintStream写入的数据会立刻调用flush()函数。
- PrintStream能更为方便地输出数据到文件中。
举个例子:
比如我们使用FileOutputStream向指定文件写入数据:
1 2 3
| File file = new File("F:\\a.txt"); FileOutputStream outputStream = new FileOutputStream(file,true); outputStream.write(97);
|
上面的代码执行完之后,a.txt中的内容存的是a,因为write方法接收的为byte类型的数据,97对应的ASCII码为a。
假设我就想将97写入到文件中呢?那么得将第三行代码改为:
1
| outputStream.write("97".getBytes());
|
而PrintStream的出现,使我们写数据到文件变得十分方便,你传入的是什么,就会给你写入什么数据。原因是其内部帮我们自动转换好了。
下面介绍其一些方法的简单使用:
5.1 print方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| public class PrintStreamDemo { private static PrintStream ps;
public static void main(String[] args) { try { try { File file = new File("src/io/demo.txt"); ps = new PrintStream(new FileOutputStream(file), true, "UTF-8"); ps.print("打印流数据1"); ps.print("打印流数据2"); ps.print(true); ps.print('a'); ps.print(5.2); ps.print(5); } finally { ps.close(); } } catch (UnsupportedEncodingException e) { e.printStackTrace(); } catch (FileNotFoundException e) { e.printStackTrace(); } } }
|
运行结果(demo.txt中的数据):
5.2 println方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| public class PrintStreamDemo { private static PrintStream ps;
public static void main(String[] args) { try { try { File file = new File("src/io/demo.txt"); ps = new PrintStream(new FileOutputStream(file), true, "UTF-8"); ps.println("打印流数据1"); ps.println("打印流数据2"); ps.println(true); ps.println('a'); ps.println(5.2); ps.println(5); } finally { ps.close(); } } catch (Exception e) { e.printStackTrace(); } } }
|
运行结果(demo.txt中的数据):
1 2 3 4 5 6
| 打印流数据1 打印流数据2 true a 5.2 5
|
ObjectInputStream和ObjectInputStream类创建的对象被称为对象输入流和对象输出流。对象输出流可以将Java对象进行持久化存储,即实现对象的序列化。而对象输入流可以实现持久化对象的反序列化。
注意:被序列化的对象需要实现序列化接口Serializable。
假设需要对类User进行序列化并保存在user.dat文件中,然后再使用反序列化操作获取该类实例化对象。
User类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
| import java.io.Serializable;
public class User implements Serializable { private String name; private String gender; private int age; public User() { super(); }
public User(String name, String gender, int age) { super(); this.name = name; this.gender = gender; this.age = age; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getGender() { return gender; }
public void setGender(String gender) { this.gender = gender; }
public int getAge() { return age; }
public void setAge(int age) { this.age = age; }
@Override public String toString() { return "User [name=" + name + ", gender=" + gender + ", age=" + age + "]"; }
}
|
具体的序列化与反序列化操作:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
| import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.OutputStream;
public class ObjectStreamDemo { private OutputStream fos; private ObjectOutputStream oos; private InputStream fis; private ObjectInputStream ois;
public void serializableObj() { try { try { File file = new File("f:" + File.separator + "user.dat"); fos = new FileOutputStream(file); oos = new ObjectOutputStream(fos); oos.writeObject(new User("张三","男",22)); } finally { oos.close(); fos.close(); } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } }
public void reSerializableObj() { try { try { File file = new File("f:" + File.separator + "user.dat"); fis = new FileInputStream(file); ois = new ObjectInputStream(fis); User user = (User) ois.readObject(); System.out.println(user); } finally { ois.close(); fis.close(); } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } } public static void main(String[] args) { ObjectStreamDemo demo = new ObjectStreamDemo(); demo.serializableObj(); demo.reSerializableObj(); } }
|
文末:字节流的一些常用类,暂时就简单总结到了,其他诸如序列流SequenceInputStream和管道流PipedInputStream等暂时就不去总结了。休息!