一、输入/输出流
流是一组有序的数据序列,根据操作的类型,可分为输入流和输出流。Java语言定义了许多类专门负责各种方式的输入输出,这些类都被放在java.io包中。其中,所有输入流类都是抽象类InputStream (字节输入流)或抽象类Reader (字符输入流)的子类;而所有输出流都是抽象类OutputStream (字节输出流)或抽象类Writer ( 字符输出流)的子类。
1、输入流
InputStream类是字节输入流的抽象类,是所有字节输入流的父类。InputStream 类的具体层次结构该类中所有方法遇到错误时都会引发IOException异常。下面是对该类中的一些方法的简要说明。
- read()方法: 从输入流中读取数据的下一个字节。返回0~255范围内的int字节值。如果因为已经到达流末尾而没有可用的字节,则返回值为-1。
- read(byte[]b):从输入流中读入一定长度的字节,并以整数的形式返回字节数。
- mark(int readlimit)方法:在输入流的当前位置放置一个标记,readlimit 参数告知此输入流在标记位置失效之前允许读取的字节数。
- reset()方法:将输入指针返回到当前所做的标记处。
- skip(long n)方法:跳过输入流上的n个字节并返回实际跳过的字节数。
- markSupported()方法:如果当前流支持mark()/reset()操作就返回true。
- close方法:关闭此输入流并释放与该流关联的所有系统资源。
InputStream类的层次结构如下:
并不是所有的IputStream类的子类都支持InputStream 中定义的所有方法,如skip()、mark()、reset()等方法只对某些子类有用。
Java中的字符是Unicode编码,是双字节的。InputStream 是用来处理字节的,并不适合处理字符文本。Java 为字符文本的输入专门提供了一套 单独的类Reader,但Reader类并不是InputStream 类的替换者,只是在处理字符串时简化了编程。Reader 类是字符输入流的抽象类,所有字符输入流的实现都是它的子类。Reader 类的具体层次结构如下图所示。
2、输出流
OutputStream类是字节输出流的抽象类,此抽象类是表示输出字节流的所有类的超类。OutputStream类的具体层次如下图所示。
OutputStream类中的所有方法均返回void,在遇到错误时会引发IOException异常。下面对OutputStream类中的方法作简单的介绍。
- write(int b)方法:将指定的字节写入此输出流。
- write(byte[] b)方法:将b个字节从指定的byte数组写入此输出流。
- write(byte[ b,int off,int len)方法:将指定byte数组中从偏移量off开始的len个字节写入此输
- 出流。
- flush()方法:彻底完成输出并清空缓存区。
- close()方法: 关闭输出流。
Writer类是字符输出流的抽象类,所有字符输出类的实现都是它的子类。Writer类的层次结构如下图所示。
二、File类
1、文件的创建与删除
File类是java.io包中唯一代表磁盘文件本身的对象。
(1)File(String pathname)
该构造方法通过将给定路径名字符串转换为抽象路径名来创建一个新File实例。
语法如下:
new Fil(String pathname)
其中,pathname 指路径名称(包含文件名)。例如:
File file = new File("d:/.xt);
(2)File(String parent,String child)
该构造方法根据定义的父路径和子路径字符串(包含 文件名)创建一-个新的File对象。
语法如下:
new Fil(tring parent,String child)
- parent:父路径字符串。例如,D:或D:/doc.
- child:子路径字符串。 例如,ltter.txto.
(3)File(File f, String child)
该构造方法根据parent抽象路径名和child路径名字符串创建一一个新 File实例。
语法如下:
new File(File f,String child)
- f:父路径对象。例如,D:/doc/. .
- child:子路径字符串。例如,ltter.txt。
注:对于Microsoft Windows平台,包含盘符的路径名前缀由驱动器号和一个。”:”组成。如果路径名是绝对路径名,还可能后跟“\”。
2、获取文件信息
三、文件输入/输出流
1、FileInputStream类与FileOutputStream类
FileInputStream类与FileOutputStream类都用来操作磁盘文件。如果用户的文件读取需求比较简单,则可以使用FileInputStream类,该类继承自InputStream 类。FileOutputStream 类与FileInputStream 类对应,提供了基本的文件写入能力。FileOutputStream 类是OutputStream类的子类。
FileInputStream类常用的构造方法如下:
FilelnputStream(tring name);
FileInputStream(File file);
第一个构造方法使用给定的文件名name创建一个 FileInputStream 对象,第二个构造方法使用File对象创建FileInputStream 对象。第一个构造方法比较简单,但第二个构造方法允许在把文件连接输入流之前对文件作进一步分析。
FileOutputStream 类有与FileInputStream 类相同的参数构造方法,创建一个 FileOutputStream对象时,可以指定不存在的文件名,但此文件不能是一个已被其他程序打开的文件。下面的实例就是使用FileInputStream与FileOutputStream类实现文件的读取与写入功能的。
注:虽然Java在程序结束时自动关闭所有打开的流,但是当使用完流后,显式地关闭所有打开的流仍是一个好习惯。一个被打开的流有可能会用尽系统资源,这取决于平台和实现。如果没有将打开的流关闭,当另一个程序试图打开另一个流时,可能会得不到需要的资源。
2、FileReader类和FileWriter类
使用FileOutputStream类向文件中写入数据与使用FileInputStream类从文件中将内容读出来,都存在一点不足,即这两个类都只提供了对字节或字节数组的读取方法。由于汉字在文件中占用两个字节,如果使用字节流,读取不好可能会出现乱码现象,此时采用字符流Reader或Writer类即可避免这种现象。
FileReader和FileWriter字符流对应了FileInputStream 和FileutputStream类。FileReader流顺序地读取文件,只要不关闭流,每次调用read0方法就顺序地读取源中其余的内容,直到源的末尾或流被关闭。
例:
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import javax.swing.*;
public class Ftest extends JFrame {
private JScrollPane scrollPane;
private static final long serialVersionUID = 1L;
private JPanel jContentPane = null; // 创建面板对象
private JTextArea jTextArea = null; // 创建文本域对象
private JPanel controlPanel = null; // 创建面板对象
private JButton openButton = null; // 创建按钮对象
private JButton closeButton = null; // 创建按钮对象
public Ftest() {
super();
initialize();
}
private void initialize() {
this.setSize(300, 200);
this.setContentPane(getJContentPane());
this.setTitle("JFrame");
}
private JButton getOpenButton() {
if(openButton == null) {
openButton = new JButton();
openButton.setText("写入文件");
openButton.addActionListener(new ActionListener() {
// 按钮单击事件
public void actionPerformed(ActionEvent e) {
File file = new File("word.txt");
try {
FileWriter out = new FileWriter(file);
String s = jTextArea.getText();
out.write(s);
out.close();
} catch (Exception e1) {
e1.printStackTrace();
}
}
});
}
return openButton;
}
private JButton getCloseButton() {
if(closeButton == null) {
closeButton = new JButton();
closeButton.setText("读取文件");
closeButton.addActionListener(new ActionListener() {
// 按钮的单击事件
public void actionPerformed(ActionEvent e) {
File file = new File("word.txt");
try {
FileReader in = new FileReader(file);
char byt[] = new char[1024];
int len = in.read(byt);
jTextArea.setText(new String(byt, 0, len));
in.close();
} catch (Exception e2) {
e2.printStackTrace();
}
}
});
}
return closeButton;
}
private JTextArea getJTextArea() {
if (jTextArea == null) {
jTextArea = new JTextArea();
}
return jTextArea;
}
private JPanel getControlPanel() {
if (controlPanel == null) {
FlowLayout flowLayout = new FlowLayout();
flowLayout.setVgap(1);
controlPanel = new JPanel();
controlPanel.setLayout(flowLayout);
controlPanel.add(getOpenButton(), null);
controlPanel.add(getCloseButton(), null);
}
return controlPanel;
}
private JPanel getJContentPane() {
if (jContentPane == null) {
jContentPane = new JPanel();
jContentPane.setLayout(new BorderLayout());
jContentPane.add(getScrollPane(), BorderLayout.CENTER);
jContentPane.add(getControlPanel(), BorderLayout.SOUTH);
}
return jContentPane;
}
protected JScrollPane getScrollPane() {
if (scrollPane == null) {
scrollPane = new JScrollPane();
scrollPane.setViewportView(getJTextArea());
}
return scrollPane;
}
public static void main(String[] args) {
Ftest thisClass = new Ftest();
thisClass.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
thisClass.setVisible(true);
}
}
输出:
四、带缓存的输入/输出流
缓存是IO的一种性能优化。缓存流为I/O 流增加了内存缓存区。有了缓存区,使得在流上执行skip()、mark0()和 reset()方法都成为可能。
1、BufferedInputream类与BufferedOutputream类
BufferedInputream类可以对所有InputStream类进行带缓存区的包装以达到性能的优化。BufferedInputStream类有两个构造方法:
BufferedInputStream(InputStream in);
BufferedInputStream(InputStream in,int size);
第一种形式的构造方法创建了-个带有32个字节的缓存流;第二种形式的构造方法按指定的大小来创建缓存区。一个最优的缓存区的大小,取决于它所在的操作系统、可用的内存空间以及机器配置。从构造方法可以看出,BufferedInputStream 对象位于InputStream 类对象之前。下图描述了字节数据读取文件的过程。
使用BufferedOutputStream输出信息和用OutputStream输出信息完全一-样, 只不过BufferedOutputStream有一个flush(方法用来将缓存区的数据强制输出完。BufferedOutputStream 类也有两个构造方法。
BufferedOutputStream(OutputStream in);
BufferedOutputStream(OutputStream in,int size);
第一种构造方法创建一个有32个字节的缓存区,第二种构造方法以指定的大小来创建缓存区。
注:flush()方法就是用于即使在缓存区没有满的情况下,也将缓存区的内容强制写入到外设,习惯上称这个过程为刷新。flush0方 法只对使用缓存区的OutputStream类的子类有效。当调用close()方法时,系统在关闭流之前,也会将缓存区中的信息刷新到磁盘文件中.
2、BufferedReader类与BufferedWriter类
BufferedReader类与BufferedWriter类分别继承Reader类与Writer类。这两个类同样具有内部缓存机制,并可以以行为单位进行输入/输出。根据BufferedReader类的特点,总结出如下所示的字符数据读取文件的过程。
BufferedReader类常用的方法如下。
- read()方法: 读取单个字符。.
- readLine()方法: 读取一个文本行,并将其返回为字符串。若无数据可读,则返回null。
BufferedWriter类中的方法都返回void。常用的方法如下。
- write(String s, int off, int len)方法:写入字符串的某一部分。
- flush()方法: 刷新该流的缓存。
- newLine()方法: 写入一个行分隔符。
在使用BufferedWriter类的Write0方法时,数据并没有立刻被写入输出流,而是首先进入缓存区中。如果想立刻将缓存区中的数据写入输出流,一定要调用flush()方法。
例:
import java.io.*;
public class Student { // 创建类
public static void main(String args[]) { // 主方法
// 定义字符串数组
String content[] = { "好久不见", "最近好吗", "常联系" };
File file = new File("word.txt"); // 创建文件对象
try {
FileWriter fw = new FileWriter(file); // 创建FileWriter类对象
// 创建BufferedWriter类对象
BufferedWriter bufw = new BufferedWriter(fw);
for (int k = 0; k < content.length; k++) { // 循环遍历数组
bufw.write(content[k]); // 将字符串数组中元素写入到磁盘文件中
bufw.newLine(); // 将数组中的单个元素以单行的形式写入文件
}
bufw.close(); // 将BufferedWriter流关闭
fw.close(); // 将FileWriter流关闭
} catch (Exception e) { // 处理异常
e.printStackTrace();
}
try {
FileReader fr = new FileReader(file); // 创建FileReader类对象
// 创建BufferedReader类对象
BufferedReader bufr = new BufferedReader(fr);
String s = null; // 创建字符串对象
int i = 0; // 声明int型变量
// 如果文件的文本行数不为null,则进入循环
while ((s = bufr.readLine()) != null) {
i++; // 将变量做自增运算
System.out.println("第" + i + "行:" + s); // 输出文件数据
}
bufr.close(); // 将FileReader流关闭
fr.close(); // 将FileReader流关闭
} catch (Exception e) { // 处理异常
e.printStackTrace();
}
}
}
输出:
五、数据输入/输出流
数据输入/输出流(DataInputStream 类与DataOutputStream类)允许应用程序以与机器无关的方式从底层输入流中读取基本Java数据类型。也就是说,当读取一个数据时,不必再关心这个数值应当是哪种字节。
DataInputStream类与DataOutputStream类的构造方法如下。
- DataInputStream(InputStream in):使用指定的基础InputStream创建-个DataInputStream。
- DataOutputStream(OutputStream out):创建一个新的数据输出流,将数据写入指定基础输出流。
DataOutputStream类提供了如下3种写入字符串的方法。
- writeBytes(Sring s)。
- writeChars(String s)。
- writeUTF(String s)。
由于Java中的字符是Unicode编码,是双字节的,writeBytes只是将字符串中的每一-个字符的低字节内容写入目标设备中;而writeChars将字符串中的每-一个字符的两个字节的内容都写到目标设备中;writeUTF将字符串按照UTF编码后的字节长度写入目标设备,然后才是每- 一个字节的UTF编码。
DataInputStream类只提供了一个readUTF0方法返回字符串。这是因为要在一个连续的字节流读取一个字符串,如果没有特殊的标记作为一个字符串的结尾,并且不知道这个字符串的长度,就无法知道读取到什么位置才是这个字符串的结東。DataOutputStream类中只有writeUTF0方法向目标设备中写入字符串的长度,所以也能准确地读回写入字符串。
例:
import java.io.*;
public class Example_01 { // 创建类
public static void main(String[] args) { // 主方法
try {
// 创建FileOutputStream对象
FileOutputStream fs = new FileOutputStream("word.txt");
// 创建DataOutputStream对象
DataOutputStream ds = new DataOutputStream(fs);
ds.writeUTF("使用writeUFT()方法写入数据;"); // 写入磁盘文件数据
ds.writeChars("使用writeChars()方法写入数据;");
ds.writeBytes("使用writeBytes()方法写入数据.");
ds.close(); // 将流关闭
// 创建FileInputStream对象
FileInputStream fis = new FileInputStream("word.txt");
// 创建DataInputStream对象
DataInputStream dis = new DataInputStream(fis);
System.out.print(dis.readUTF()); // 将文件数据输出
} catch (Exception e) {
e.printStackTrace(); // 输出异常信息
}
}
}
输出:
本例使用记事本程序将word.txt 打开,如上图所示。尽管在记事本程序中看不出writeUTF()写入的字符串是“使用writeUFT()方法写入数据”,但程序通过readUTF()读回后显示在屏幕上的仍是“使用writeUFT()方法写入数据”。但如果使用writeChars()和writeBytes()方法写入字符串后,再读取回来就可能出错。
六、ZIP压缩输入/输出流
在java.util.zip包中的ZipOutputStream与ZipInputStream类来实现文件的压缩/解压缩。如要从ZIP压缩管理文件内读取某个文件,要先找到对应该文件的“目录进入点”(从它可知该文件在ZIP文件内的位置),才能读取这个文件的内容。如果要将文件内容写入ZIP文件内,必须先写入对应于该文件的“目录进入点”,并且把要写入文件内容的位置移到此进入点所指的位置,然后再写入文件内容。
Java实现了IO数据流与网络数据流的单一接口, 因此数据的压缩、网络传输和解压缩的实现比较容易。ZipEntry 类产生的对象,是用来代表一个 ZIP压缩文件内的进入点(entry)。 ZipInputStream类用来读取ZIP压缩格式的文件,所支持的包括已压缩及未压缩的进入点(entry)。ZipOutputeam类用来写出ZIP压缩格式的文件,而且所支持的包括己压缩及未压缩的进入点(entry)。
1、压缩文件
利用ZipOutputStream 类对象,可将文件压缩为.zip 文件。ZipOutputStream 类的构造方法如下:
ZipOutputStream(OutputStream out);
ZipOutputStream常用方法如表:
例:
import java.io.*;
import java.util.zip.*;
public class MyZip {
private void zip(String zipFileName, File inputFile) throws Exception {
ZipOutputStream out = new ZipOutputStream(new FileOutputStream(
zipFileName)); // 创建ZipOutputStream类对象
zip(out, inputFile, ""); // 调用方法
System.out.println("压缩中…"); // 输出信息
out.close(); // 将流关闭
}
private void zip(ZipOutputStream out, File f, String base)
throws Exception { // 方法重载
if (f.isDirectory()) { // 测试此抽象路径名表示的文件是否是一个目录
File[] fl = f.listFiles(); // 获取路径数组
out.putNextEntry(new ZipEntry(base + "/")); // 写入此目录的entry
base = base.length() == 0 ? "" : base + "/"; // 判断参数是否为空
for (int i = 0; i < fl.length; i++) { // 循环遍历数组中文件
zip(out, fl[i], base + fl[i]);
}
} else {
out.putNextEntry(new ZipEntry(base)); // 创建新的进入点
// 创建FileInputStream对象
FileInputStream in = new FileInputStream(f);
int b; // 定义int型变量
System.out.println(base);
while ((b = in.read()) != -1) { // 如果没有到达流的尾部
out.write(b); // 将字节写入当前ZIP条目
}
in.close(); // 关闭流
}
}
public static void main(String[] temp) { // 主方法
MyZip book = new MyZip(); // 创建本例对象
try {
// 调用方法,参数为压缩后文件与要压缩文件
book.zip("hello.zip", new File("src"));
System.out.println("压缩完成"); // 输出信息
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
输出:
注:使用ZipOutputStream 类将文件写入目标ZIP文件时,必须先使用ZipOutputStream 对象的putNextEntry()方法,写入个别文件的entry,将流内目前指到的位置移到该entry所指的开头位置。
2、解压缩ZIP文件
ZipInputStream类可读取ZIP压缩格式的文件,包括已压缩和未压缩的条目(entry)。ZipInputStream类的构造方法如下:
ZipInputStream(InputStream in);
ZipInputStream类的常用方法下表所示。
例:
import java.io.*;
import java.util.zip.*;
public class Decompressing {
public static void main(String[] temp) {
ZipInputStream zip;
try {
// 实例化对象,指明要进行解压的文件
zip = new ZipInputStream(new FileInputStream("hello.zip"));
// 获取下一个ZipEntry
ZipEntry entry = zip.getNextEntry();
// 如果entry不为空,并不在同一目录下
while((entry = zip.getNextEntry()) != null
&& !entry.isDirectory()) {
// 获取文件目录
File file = new File("d:\\eclipse\\eclipse-workspace\\File\\jieya\\" + entry.getName());
System.out.println(file);
if (!file.exists()) { // 如果该文件不存在
file.mkdirs();// 创建文件所在文件夹
file.createNewFile(); // 创建文件
}
zip.closeEntry();// 关闭当前entry
System.out.println(entry.getName() + "解压成功");
}
zip.close();// 关闭流
} catch (Exception e) {
e.printStackTrace();
}
}
}
输出:
注:ZipInputStream类来解压文件,必须先使用ZipInputStream类的getNextEntry()方法来取得其内的第一个ZipEntry。