I/O
流概述
1.流是一组有序的数据序列,根据操作的类型,可分为输入流和输出流两种。I/O(Input/Output,输入/输出)流提供了一条通道程序,可以使用这条通道把源中的字节序列送到目的地。
输入/输出流
Java语言定义了许多类专门负责各种方式的输入/输出,这些类都被放在java.io包中。其中,所有输入流类都是抽象类InputStream(字节输入流) 或抽象类Reader(字符输入流) 的子类;而所有输出流都是抽象类 OutputStream(字节输出流) 或抽象类Writer(字符输出流) 的子类。
输入流
Inputstream类中所有方法遇到错误时都会引发IOException异常
Java中的字符是Unicode编码,是双字节的
Reader类具体层次结构:
输出流
OutputStream类中的所有方法均返回void,在错误时会引发IOException异常
File类
File类是java.io包中唯一代表磁盘文件本身的对象。
文件的创建与删除
通常使用一下3种构造方法来创建文件对象:
- File(String pathname) :其中,pathname指路径名称(包含文件名)
- File(String parent,String child):parent父路径字符串,child 子路径字符串
- File(File f,String child):f父路径对象,child 子路径字符串
若:
File file = new File("word.txt");
若当前目录中不存在名为word文件,File类对象可以通过调用createNewFile()方法创建一个名称为word.txt文件;如果存在word.txt文件,可以通过文件对象的delete()方法将其删除。
例:
import java.io.*;
public class FileTest { // 创建类FileTest
public static void main(String[] args) { // 主方法
File file = new File("word.txt"); // 创建文件对象
if (file.exists()) { // 如果该文件存在
file.delete(); // 将文件删除
System.out.println("文件已删除"); // 输出的提示信息
} else { // 如果文件不存在
try { // try语句块捕捉可能出现的异常
file.createNewFile(); // 创建该文件
System.out.println("文件已创建"); // 输出的提示信息
} catch (Exception e) { // catch处理该异常
e.printStackTrace(); // 输出异常信息
}
}
}
}
//测试过了,单单创建file对象对文件夹里的文件没有什么用,只有createNewFile()、delete()等方法对文件生效,假如没有delete(),而文件夹里又有word文件,那么没影响。
获取文件信息
文件输入/输出流
FileInputStream与FileOutputStream类
FileInputStream类常用构造方法:
- FileInputStream(String name)
- FileInputStream(File file)
创建一个FileOutputStream对象时,可以指定不存在的文件名,但此文件不能是一个已被其他程序打开的文件。
例:
import java.io.*;
public class FileTest { // 创建类
public static void main(String[] args) { // 主方法
File file = new File("word.txt"); // 创建文件对象
try { // 捕捉异常
// 创建FileOutputStream对象
FileOutputStream out = new FileOutputStream(file);
// 创建byte型数组
byte buy[] = "我有一只小毛驴,我从来也不骑。".getBytes();
out.write(buy); // 将数组中信息写入到文件中
out.close(); // 将流关闭
} catch (Exception e) { // catch语句处理异常信息
e.printStackTrace(); // 输出异常信息
}
try {
// 创建FileInputStream类对象
FileInputStream in = new FileInputStream(file);
byte byt[] = new byte[1024]; // 创建byte数组
int len = in.read(byt); // 从文件中读取信息
// 将文件中信息输出
System.out.println("文件中的信息是:" + new String(byt, 0, len));
in.close(); // 关闭流
} catch (Exception e) {
e.printStackTrace(); // 输出异常信息
}
}
}
Java与其他语言编写的程序进行tcp/ip socket通讯时,通讯内容一般都转换成byte数组型
详情看这:Java中字符和byte数组之间的相互转换.
当使用完流后,显示地关闭所有打开的流仍是一个好习惯。
FileReader和FileWriter类
FileOutputStream类与FileInputStream类只提供了对字节或字节数组的读取方法。由于汉字在文件中占用两个字节,如果使用字节流,读取不好可能会出现乱码现象,此时采用字符流Reader或Writer类即可避免这种现象。
例:这里只copy用到FileReader和FileWrite的部分
public void actionPerformed(ActionEvent e) {
// 创建文件对象
File file = new File("word.txt");
try {
// 创建FileWriter对象
FileWriter out = new FileWriter(file);
// 获取文本域中文本
String s = jTextArea.getText();
out.write(s); // 将信息写入磁盘文件
out.close(); // 将流关闭
} catch (Exception e1) {
e1.printStackTrace();
}
}
File file = new File("word.txt"); // 创建文件对象
try {
// 创建FileReader对象
FileReader in = new FileReader(file);
char byt[] = new char[1024]; // 创建char型数组
int len = in.read(byt); // 将字节读入数组
// 设置文本域的显示信息
jTextArea.setText(new String(byt, 0, len));
in.close(); // 关闭流
} catch (Exception e1) {
e1.printStackTrace();
}
带缓存的输入/输出流
缓存流为I/O流增加了内存缓存区,有了缓存区,使得在流上执行skip()、mark()和reset()方法都成为可能.
BufferedInputStream与 BufferedOutputStream类
//Buffer即缓存区
BufferedReader与BufferedWriter类
这两个类同样具有内部缓存机制,并可以以行为单位 进行输入/输出
BufferedReader类常用方法:
- read():读取单个字符
- readLine():读取一个文本行,并将其返回为字符串。若无数据可读,则返回null
BufferedWriter: - wirter(Strings,int off,int len)方法:写入字符串的某一部分
- flush()方法:刷新该流的缓存
- newLine()方法:写入一个分隔符
在使用BufferWriter类的Write()方法时,数据并没有立刻被写入输入流,而是首先进入缓存区中。如果想立刻将缓存区中的数据写入输出流,一定要调用flush()方法。
数据输入/输出流
DataInputStream类与DataOutputStream类,允许应用程序以与机器无关的方式从底层输入流中读取基本Java类型。
DataOutputStream类提供如下三种写入字符串的方法:
- writeBytes(String s)
- writeChars(String s)
- writeUTF(String s)
writeUTF将字符串按照UTF编码后的字节长度写入目标设备,然后才是每一个字节的UTF编码
DataInputStream类只提供了一个**readUTF()**方法返回字符串
DataOutPutStream类中只有writeUTF()方法向目标设备中写入字符串的长度,所以也能准确地读回写入字符串。
ZIP压缩输入/输出流
ZipEntry类产生的对象,是用来代表一个ZIP压缩文件内的进入点(entry)
压缩文件
利用ZipOutStream类对象可将文件压缩为.zip文件。构造方法:
ZipOutputStream(OutputStream out);
ZipOutStream 类常用方法:
方法 | 返回值 | 说明 |
---|---|---|
putNextEntry(ZipEntry e) | void | 开始写入一个新的ZipEntry,并将流内的位置移至此entry所指数据的开头 |
write(byte[],int off,int len) | void | 将字节数组写入当前ZIP条目数据 |
finish() | void | 完成写入ZIP输出流的内容,无须关闭它所配合的OutputStream |
setComment(String comment) | void | 可设置此ZIP文件的注释文字 |
例:
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();
}
}
}
解压缩ZIP文件
ZipInputStream
常用方法:
方法 | 返回值 | 说明 |
---|---|---|
read(byte[] b,int off,int len) | int | 读取目标b数组内off偏移量的位置,长度是len字节 |
available | int | 判断是否已读完目前entry所指定的数据。已读完返回0,否则返回1 |
closeEntry() | void | 关闭当前ZIP条目并定位流以读取下一个条目 |
skip(long n) | long | 跳过当前ZIP条目中指定的字节数 |
getNextEntry() | ZipEntry | 读取下一个ZipEntry,并将流内的位置移至该entry所指数据的开头 |
createZipEntry(String name) | ZipEntry | 以指定的name 参数新建一个 ZipEntry 对象 |
实践与练习
- 编写程序,实现读取文件时出现一个表示读取进度的进度条。可使用javax.swing包提供的输入流类ProgressMonitorInputStream
查了下API的上述类:
public ProgressMonitorInputStream(Component parentComponent,
Object message,
InputStream in)构造一个对象,以监视输入流的进度。
参数:
message - 要在对话框(如果弹出)中放置的描述性文本。
parentComponent - 触发被监视操作的组件。
in - 要监视的输入流。
API中的例子:
InputStream in = new BufferedInputStream(
new ProgressMonitorInputStream(
parentComponent,
"Reading " + fileName,
new FileInputStream(fileName)));
由例子看出,主要完成parentComponent的设计即可。
import java.io.*;
import javax.swing.*;
public class Student {
public static void main(String[] temp) {
byte b[] = new byte[2];
try{
FileInputStream fis = new FileInputStream("word.txt");
ProgressMonitorInputStream in =
new ProgressMonitorInputStream(null,"读取文件",fis);
while(in.read(b)!=-1){
String s = new String(b);
System.out.print(s);
Thread.sleep(20);
}
}catch (Exception e) {
e.printStackTrace();
}
}
}
上面是答案,想歪了,根本不用设计parentComponent,直接用null都可以- -。然后Thread.sleep这线程很有意思。
- 编写程序,使用字符输入、输出流读取文件,将一段文字加密后存入文件,然后再读取,并将加密前与加密后的文件输出。
import java.io.*;
public class Example {
public static void main(String[] args) {
char a[] = "今天10点出发".toCharArray();
int n = 0;
try {
File out = new File("word.txt");
for (int i = 0; i < a.length; i++) {
a[i] = (char) (a[i] ^ 'R');
}
FileWriter fw = new FileWriter(out);
fw.write(a, 0, a.length);
fw.close();
FileReader fr = new FileReader(out);
char tom[] = new char[10];
System.out.println("加密后:");
while ((n = fr.read(tom, 0, 10)) != -1) {
String s = new String(tom, 0, n);
System.out.println(s);
}
fr.close();
fr = new FileReader(out);
System.out.println("明文:");
while ((n = fr.read(tom, 0, 10)) != -1) {
for (int j = 0; j < n; j++) {
tom[j] = (char) (tom[j] ^ 'R');
}
String str = new String(tom, 0, n);
System.out.println(str);
}
fr.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
- 编程实现当用户输入姓名和密码时,将每一个姓名和密码加在文件中,如果用户输入done,就结束程序
import java.io.*;
public class Example {
static final int lineLength = 81;
public static void main(String[] args) {
FileOutputStream fos;
byte[] phone = new byte[lineLength];
byte[] name = new byte[lineLength];
try {
fos = new FileOutputStream("word.txt");
while (true) {
System.err.println("请输入一个名字:");
if ("done".equalsIgnoreCase(new String(name, 0, 0, 4))) {
System.out.println("录入完毕");
break;
}
System.err.println("请输入电话号:");
readLine(phone);
for (int i = 0; phone[i] != 0; i++) {
fos.write(phone[i]);
}
fos.write(',');
for (int j = 0; name[j] != 0; j++) {
fos.write(name[j]);
}
fos.write('\n');
System.out.println("信息已经写入文件");
}
fos.close();
} catch (Exception e) {
// TODO 自动生成 catch 块
e.printStackTrace();
}
}
private static void readLine(byte[] name) throws IOException {
int b = 0, i = 0;
while ((i < (lineLength - 1)) && (b = System.in.read()) != '\n') {
name[i++] = (byte) b;
}
name[i] = (byte) 0;
}
}