要把程序数据保存到文件中,就一定要使用哪个I/O输入输出技术。Java中提供的I/O操作可以把数据保存到多种类型的文件。大多数的应用程序都需要与外部设备进行数据交换,最常见的外部设备包含磁盘和网络。IO就是指应用程序对这些设备的数据输入和输出。在程序中,键盘被用做文件输出。Java语言定义了许多类专门负责各种方式的输入输出,这些类都被放在java.io包中。
OutputStreamWriter 构造方法
为了达到最高的效率,避免频繁的进行字符与字节间的相互转换,最好不要直接使用这两个类来进行读写,可要考虑在 BufferedReader内包装 InputStreamReader,将 OutputStreamWriter 包装到 BufferedWriter 中。
BufferedReader in = new BufferedReader(new InputStreamReader(System.in))
BufferedWriter out = new BufferedWriter(new OutputStreamWriter(System.out))
范例10:
import java.io.*;
public class Test{
public static void main(String args[]) {
BufferedReader buf = null;
buf = new BufferedReader(new InputStreamReader(System.in));
String str = null;
while(true){
System.out.print("Please input number:");
try{
str = buf.readLine();
}catch(IOException e){}
int i = -1;
try{
i = Integer.parseInt(str);
i++;
System.out.println("Afrer modify:"+i);
break;
}catch(Exception e){}
}
}
}
运行结果:
Please input number:2
Afrer modify:3
一、File类
File类是IO包中唯一代表磁盘文件本身的对象。File类定义了一些与平台无关的方法来操纵文件,通过调用File类提供的各种方法,能够完成创建、删除、重命名文件、判断读写权限等等。
下面构造方法来生产File对象
File(String directoryPath), directoryPath是文件的路径名。
下面这个范例说明了File的几个方法
范例1:
import java.io.*;
public class Test{
public static void main(String args[]){
File f = new File("/home/dingfeng/java_study/test.txt");
if(f.exists()){
f.delete();
}else{
try{
f.createNewFile();
}catch(Exception e){}
}
//get file name
System.out.println("File name:"+f.getName());
//get file path
System.out.println("File path:"+f.getPath());
//get absolute path
System.out.println("File absolute path:"+f.getAbsolutePath());
//get parent folder name
System.out.println("Parent folder name:"+f.getParent());
//file is exists
System.out.println("File exists:"+f.exists());
//file can write
System.out.println("File writble:"+f.canWrite());
//file can read
System.out.println("File readable:"+f.canRead());
//file is a directory
System.out.println("File is a directory:"+f.isDirectory());
//file is a file
System.out.println("File is a file:"+f.isFile());
//file is absolute path name
System.out.println("File is absolute path:"+f.isAbsolute());
//last modifed time
System.out.println("File last modified time:"+f.lastModified());
//file length
System.out.println("File size:"+f.length()+" Bytes");
}
}
运行结果:
File name:test.txt
File path:/home/dingfeng/java_study/test.txt
File absolute path:/home/dingfeng/java_study/test.txt
Parent folder name:/home/dingfeng/java_study
File exists:false
File writble:false
File readable:false
File is a directory:false
File is a file:false
File is absolute path:true
File last modified time:0
File size:0 Bytes
还有其他的一些方法,可查阅Java的API手册就可以了。
二、RandomAccessFile类
RandomAccessFile类可以说是Java语言中功能最为丰富的文件访问类,提供了众多的访问方法。支持“随机访问”方式,可以跳转到文件的任意位置处读写数据。
RandomAccessFile有个位置指示器,指向当前读写处的位置,当读写n个字节后,文件指示器指向n个字节后面的下一个字节处。刚打开文件时,文件指示器指向文件的开头处,可以移动文件指示器到新的位置,随后的读写操作将从新的位置开始。RandomAccessFile在数据等长记录格式文件的随机(相对顺序而言)读取时有很大的优势,但该类仅限于操作文件,不能访问其他的IO设备、如网络、内存映像等。
范例2(查看网上的):
import java.io.*;
public class Test {
public static void main(String[] args) throws Exception {
RandomAccessFile file = new RandomAccessFile("file", "rw");
// 以下向file文件中写数据
file.writeInt(20);// 占4个字节
file.writeDouble(8.236598);// 占8个字节
file.writeUTF("这是一个UTF字符串");// 这个长度写在当前文件指针的前两个字节处,可用readShort()读取
file.writeBoolean(true);// 占1个字节
file.writeShort(395);// 占2个字节
file.writeLong(2325451l);// 占8个字节
file.writeUTF("又是一个UTF字符串");
file.writeFloat(35.5f);// 占4个字节
file.writeChar('a');// 占2个字节
file.seek(0);// 把文件指针位置设置到文件起始处
// 以下从file文件中读数据,要注意文件指针的位置
System.out.println("——————从file文件指定位置读数据——————");
System.out.println(file.readInt());
System.out.println(file.readDouble());
System.out.println(file.readUTF());
file.skipBytes(3);// 将文件指针跳过3个字节,本例中即跳过了一个boolean值和short值。
System.out.println(file.readLong());
file.skipBytes(file.readShort()); // 跳过文件中“又是一个UTF字符串”所占字节,注意readShort()方法会移动文件指针,所以不用加2。
System.out.println(file.readFloat());
//以下演示文件复制操作
System.out.println("——————文件复制(从file到fileCopy)——————");
file.seek(0);
RandomAccessFile fileCopy=new RandomAccessFile("fileCopy","rw");
int len=(int)file.length();//取得文件长度(字节数)
byte[] b=new byte[len];
file.readFully(b);
fileCopy.write(b);
System.out.println("复制完成!");
}
}
运行结果:
——————从file文件指定位置读数据——————
——————从file文件指定位置读数据——————
20
8.236598
这是一个UTF字符串
2325451
35.5
——————文件复制(从file到fileCopy)——————
复制完成!
三、流类
Java的流式输入/输出建立在4个抽象类的基础上:InputStream、OutputStream、Reader和Writer。它们用来创建具体流式子类。尽管程序通过具体子类进行输入/输出操作,但顶层的类定义了所有流类的基本通用功能。
InputStream和OutputStream被设计成字节流类,Reader和Writer被设计成字符流类。字节流类和字符流类形成分离的层次结构。一般来说,处理字符后字符串时应使用字符流类,处理字节或二进制对象时应使用字节流类。
一般在操作文件流时,不管是字节流还是字符流,都可以按照以下方式进行。
a、使用File类找到一个文件
b、通过File类的对象去实例化字节流或字符流的子类
c、进行字节(字符)的读、写操作
d、关闭文件流
1、字节流
字节流类为处理字节式输入/输出提供了丰富的环境。一个字节流可以和其他任何类型的对象并用,包括二进制数据。这样的多功能性使得字节流对很多类型的程序都很重要。因为字节流类以InputStream和OutputStream为顶层,从讨论着两个类开始。
(1)InputStream
InputStream是一个定义了Java流式字节输入模式的抽象类,该类的所有方法在出错时都会引发一个IOException异常(下面OutputStream一样),下表显示了该类的方法.
int | available() 返回此输入流下一个方法调用可以不受阻塞地从此输入流读取(或跳过)的估计字节数。 |
void | close() 关闭此输入流并释放与该流关联的所有系统资源。 |
void | mark(int readlimit) 在此输入流中标记当前的位置。 |
boolean | markSupported() 测试此输入流是否支持 mark 和 reset 方法。 |
abstract int | read() 从输入流中读取数据的下一个字节。 |
int | read(byte[] b) 从输入流中读取一定数量的字节,并将其存储在缓冲区数组 b 中。 |
int | read(byte[] b, int off, int len) 将输入流中最多 len 个数据字节读入 byte 数组。 |
void | reset() 将此流重新定位到最后一次对此输入流调用 mark 方法时的位置。 |
long | skip(long n) 跳过和丢弃此输入流中数据的 n 个字节。 |
(2)OutputStream
OutputStream定义了流式字节输出模式的抽象类,其方法如下表。
void | close() 关闭此输出流并释放与此流有关的所有系统资源。 |
void | flush() 刷新此输出流并强制写出所有缓冲的输出字节。 |
void | write(byte[] b) 将 b.length 个字节从指定的 byte 数组写入此输出流。 |
void | write(byte[] b, int off, int len) 将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此输出流。 |
abstract void | write(int b) 将指定的字节写入此输出流。 |
(3)FileInputStream
该类创建一个能从文件读取字节的InputStream类,常用构造方法
FileInputStream(File file) 通过打开一个到实际文件的连接来创建一个 FileInputStream ,该文件通过文件系统中的 File 对象 file 指定。 |
FileInputStream(FileDescriptor fdObj) 通过使用文件描述符 fdObj 创建一个 FileInputStream ,该文件描述符表示到文件系统中某个实际文件的现有连接。 |
FileInputStream(String name) 通过打开一个到实际文件的连接来创建一个 FileInputStream ,该文件通过文件系统中的路径名 name 指定。 |
比如:
InputStream f = new FileInputStream("/home/dingfeng/java_study/test.txt" );
File ff = new File("/home/dingfeng/java_study/test.txt");
InputStream f1 = new FileInputStream(ff);
尽管第一个构造方法可能更常用到,而第二个构造方法则允许把文件赋给输入流之前用File方法更进一步检查文件。
(4)FileOutputStream
创建一个可以向文件写入字节的类,常用构造方法
FileOutputStream(File file) 创建一个向指定 File 对象表示的文件中写入数据的文件输出流。 |
FileOutputStream(File file, boolean append) 创建一个向指定 File 对象表示的文件中写入数据的文件输出流。 |
FileOutputStream(FileDescriptor fdObj) 创建一个向指定文件描述符处写入数据的输出文件流,该文件描述符表示一个到文件系统中的某个实际文件的现有连接。 |
FileOutputStream(String name) 创建一个向具有指定名称的文件中写入数据的输出文件流。 |
FileOutputStream(String name, boolean append) 创建一个向具有指定 name 的文件中写入数据的输出文件流。 |
下例中,用FileOutputStream向文件中写入一个字符串,并用FileInputStream读出写入的内容。
范例3:
import java.io.*;
public class Test {
public static void main(String[] args) {
//1.write conten to file
File f = new File("/home/dingfeng/java_study/test.txt");
OutputStream out = null;
try{
out = new FileOutputStream(f);
}catch(FileNotFoundException e){}
String test = "Hello World!";
//string change to bytes
byte b[] = test.getBytes();
try{
out.write(b);
}catch(IOException e){}
try{
out.close();
}catch(IOException e){}
//2.next read file
InputStream in = null;
try{
in = new FileInputStream(f);
}catch(FileNotFoundException e){}
byte b1[] = new byte[512];
int i = 0;
try{
i = in.read(b1);
}catch(IOException e){}
try{
in.close();
}catch(IOException e){}
System.out.println(new String(b1,0,i));
}
}
运行结果:
Hello World!
2、字符流
尽管字节流提供了除了任何类型输入/输出操作的足够的功能,但他们不能直接操作Unicode字符。Java的一个主要目标是支持“一次编写,出处运行”,那么包含直接的字符输入/输出的支持就是必要的。如前所述,字符流层次结构的顶层是Reader和Writer抽象类,从他们开始介绍。
(1)Reader
该类是定义Java的流式字符输入模式的抽象类,出错会报IOException异常(Writer一样)。
abstract void | close() 关闭该流并释放与之关联的所有资源。 |
void | mark(int readAheadLimit) 标记流中的当前位置。 |
boolean | markSupported() 判断此流是否支持 mark() 操作。 |
int | read() 读取单个字符。 |
int | read(char[] cbuf) 将字符读入数组。 |
abstract int | read(char[] cbuf, int off, int len) 将字符读入数组的某一部分。 |
int | read(CharBuffer target) 试图将字符读入指定的字符缓冲区。 |
boolean | ready() 判断是否准备读取此流。 |
void | reset() 重置该流。 |
long | skip(long n) 跳过字符。 |
(2)Writer
定义流式字符输出的抽象类。
Writer | append(char c) 将指定字符添加到此 writer。 |
Writer | append(CharSequence csq) 将指定字符序列添加到此 writer。 |
Writer | append(CharSequence csq, int start, int end) 将指定字符序列的子序列添加到此 writer. Appendable 。 |
abstract void | close() 关闭此流,但要先刷新它。 |
abstract void | flush() 刷新该流的缓冲。 |
void | write(char[] cbuf) 写入字符数组。 |
abstract void | write(char[] cbuf, int off, int len) 写入字符数组的某一部分。 |
void | write(int c) 写入单个字符。 |
void | write(String str) 写入字符串。 |
void | write(String str, int off, int len) 写入字符串的某一部分。 |
(3)FileReader
创建了一个可以读取文件内容的Reader类,构造方法
FileReader(File file) 在给定从中读取数据的 File 的情况下创建一个新 FileReader 。 |
FileReader(FileDescriptor fd) 在给定从中读取数据的 FileDescriptor 的情况下创建一个新 FileReader 。 |
FileReader(String fileName) 在给定从中读取数据的文件名的情况下创建一个新 FileReader 。 |
(4)FileWriter
创建了一个可以写文件的Writer类,构造方法
FileWriter(File file) 根据给定的 File 对象构造一个 FileWriter 对象。 |
FileWriter(File file, boolean append) 根据给定的 File 对象构造一个 FileWriter 对象。 |
FileWriter(FileDescriptor fd) 构造与某个文件描述符相关联的 FileWriter 对象。 |
FileWriter(String fileName) 根据给定的文件名构造一个 FileWriter 对象。 |
FileWriter(String fileName, boolean append) 根据给定的文件名以及指示是否附加写入数据的 boolean 值来构造 FileWriter 对象。 |
下例对范例3进行修改,如下
范例4:
import java.io.*;
public class Test {
public static void main(String[] args) {
//1.write conten to file
File f = new File("/home/dingfeng/java_study/test.txt");
Writer out = null;
try{
out = new FileWriter(f);
}catch(IOException e){}
String test = "Hello World!";
//string change to bytes
//byte b[] = test.getBytes();
try{
out.write(test);
}catch(IOException e){}
try{
out.close();
}catch(IOException e){}
//2.next read file
Reader in = null;
try{
in = new FileReader(f);
}catch(FileNotFoundException e){}
//byte b1[] = new byte[512];
char c[] = new char[512];
int i = 0;
try{
i = in.read(c);
}catch(IOException e){}
try{
in.close();
}catch(IOException e){}
System.out.println(new String(c,0,i));
}
}
运行结果:
Hello World!
注意:如果我们把上面一个out.close();代码注释掉,也就是说在向文件中写入内容后不关闭文件,然后打开文件,可以发现文件中没有任何内容,why?
从继承类图可以看到,FileWriter不是直接继承自Writer类,而是继承了Writer的子类,此类为字节流和字符流的转换类。也就是说正在从文件中读取进来的数据还是字节,只是在内存中将字节转换成了字符。所有可以得出结论:字符流用到了缓冲区,而字节流没有用到缓冲区。
3、管道流
管道流主要用于连接两个线程间的通信,也分为字节流(PipedInputStream和PipedOutputStream)和字符流(PipedReader和PipedWriter)两种类型,这边以字节型为例介绍。
一个PipedInputStream对象必须和一个PipedOutputStream对象进行连接而产生一个通信管道,PipedOutputStream可以向管道中写入数据,PipedInputStream可以从管道中读取PipedOutputStream写入的数据。
范例5:
import java.io.*;
import java.io.*;
public class Test {
public static void main(String[] args) {
try{
Sender sender = new Sender();
Receiver receiver = new Receiver();
PipedOutputStream out = sender.getOutputStream();
PipedInputStream in = receiver.getInputStream();
out.connect(in);
sender.start();
receiver.start();
}catch(IOException e){}
}
}
class Sender extends Thread{
private PipedOutputStream out = new PipedOutputStream();
public PipedOutputStream getOutputStream(){
return out;
}
public void run(){
String s = new String("Hello,Receiver!");
try{
out.write(s.getBytes());
out.close();
}catch(IOException e){}
}
}
class Receiver extends Thread{
private PipedInputStream in = new PipedInputStream();
public PipedInputStream getInputStream(){
return in;
}
public void run(){
String s = null;
byte []buf = new byte[512];
try{
int len = in.read(buf);
s = new String(buf,0,len);
System.out.println("Receive info:"+s);
in.close();
}catch(IOException e){}
}
}
运行结果:
Receive info:Hello,Receiver!
管道流用起来比较方便,但也有一些缺点:
1)管道流只能在两个线程间传递数据
1)管道流只能在两个线程间传递数据
2)管道流只能实现单向发送,如果要两个线程间互通讯,则需要两个管道流
4、ByteArrayInputStream与ByteArrayOutputStream
ByteArrayInputStream是输入流的一种实现,他有两个构造方法,每个构造方法都需要一个字节数组来作为其数据源。
ByteArrayInputStream(byte[] buf) 创建一个 ByteArrayInputStream ,使用 buf 作为其缓冲区数组。 |
ByteArrayInputStream(byte[] buf, int offset, int length) 创建 ByteArrayInputStream ,使用 buf 作为其缓冲区数组。 |
ByteArrayOutputStream() 创建一个新的 byte 数组输出流。 |
ByteArrayOutputStream(int size) 创建一个新的 byte 数组输出流,它具有指定大小的缓冲区容量(以字节为单位)。 |
如程序在运行过程中要产生一些临时文件,可以采用虚拟文件方式实现,这两个类可以实现类似于内存虚拟文件的功能。
范例6:
import java.io.*;
import java.io.*;
public class Test {
public static void main(String[] args) throws Exception{
String tmp = "abcdefghijklmnopqrstubwxyz";
byte[] src = tmp.getBytes();
ByteArrayInputStream input = new ByteArrayInputStream(src);
ByteArrayOutputStream output = new ByteArrayOutputStream();
new Test().transform(input,output);
byte[] result = output.toByteArray();
System.out.println(new String(result));
}
public void transform(InputStream in,OutputStream out){
int c = 0;
try{
while((c=in.read())!=-1){
int C = (int)Character.toUpperCase((char)c);
out.write(C);
}
}catch(Exception e){}
}
}
运行结果:
ABCDEFGHIJKLMNOPQRSTUBWXYZ
ABCDEFGHIJKLMNOPQRSTUBWXYZ
5、System.in和System.out
为了至此标准输入输出设备,Java定义了两个特殊的流对象:System.in和System.out。System.in对应键盘,属于InputStream类型,程序使用System.in可以读取从键盘上输入的数据。System.out对于显示器,属于PrintStream类型,PrintStream是OutputStream的一个子类,程序使用System.out可以将数据输出到显示器上。键盘可以被当做一个特殊的输入流,显示器可以被当作一个特殊的输出流。
6、打印流
1)字节打印流
PrintStream类提供了一系列的print和println方法,可以实现将基本数据类型的格式转换成字符串输出。在前面的范例中大量用到的“System.out.println”语句中的System.out,就是PrintStream类的一个实例对象。
构造函数
PrintStream(File file) 创建具有指定文件且不带自动行刷新的新打印流。 |
PrintStream(File file, String csn) 创建具有指定文件名称和字符集且不带自动行刷新的新打印流。 |
PrintStream(OutputStream out) 创建新的打印流。 |
PrintStream(OutputStream out, boolean autoFlush) 创建新的打印流。 |
PrintStream(OutputStream out, boolean autoFlush, String encoding) 创建新的打印流。 |
PrintStream(String fileName) 创建具有指定文件名称且不带自动行刷新的新打印流。 |
PrintStream(String fileName, String csn) 创建具有指定文件名称和字符集且不带自动行刷新的新打印流。 |
其中,autoFlush控制在Java中遇到换行符(\n)时是否自动清空缓冲区,encoding是指定编码方式。
println方法与print方法的区别是:前者会在打印完的内容后面再多打印一个换行符(\n),所以println等于print("\n")。
该对象有多个重载的print和println方法,可输出各种类型的数据,包括Object对象。对于基本的数据类型,先转换成字符串的形式,然后再输出,而不是输出原始的字节内容,如整数221的打印结果是字符"2","2","1"所组成的一个字符串,而不是整数221在内存中的原始数据。对于非基本数据类型的对象,先调用对象的toString方法,然后输出toString方法所返回的字符串。
2)字符打印流
与PrintStream对应的字符打印流是PrintWriter,构造方法
PrintWriter(File file) 使用指定文件创建不具有自动行刷新的新 PrintWriter。 |
PrintWriter(File file, String csn) 创建具有指定文件和字符集且不带自动刷行新的新 PrintWriter。 |
PrintWriter(OutputStream out) 根据现有的 OutputStream 创建不带自动行刷新的新 PrintWriter。 |
PrintWriter(OutputStream out, boolean autoFlush) 通过现有的 OutputStream 创建新的 PrintWriter。 |
PrintWriter(String fileName) 创建具有指定文件名称且不带自动行刷新的新 PrintWriter。 |
PrintWriter(String fileName, String csn) 创建具有指定文件名称和字符集且不带自动行刷新的新 PrintWriter。 |
PrintWriter(Writer out) 创建不带自动行刷新的新 PrintWriter。 |
PrintWriter(Writer out, boolean autoFlush) 创建新 PrintWriter。 |
PrintWriter即使遇到换行符也不会自动清空缓冲区,只在设置了autoflush模式下使用了println方法才会自动清空缓冲区。
范例7:通过PrintWriter向屏幕打印信息
import java.io.*;
public class Test {
public static void main(String[] args) throws Exception{
PrintWriter out = null;
out = new PrintWriter(System.out);
out.println("Hello,World!");
out.close();
}
}
运行结果:
Hello,World!
范例8:通过PrintWriter向文件中打印信息
import java.io.*;
public class Test {
public static void main(String[] args) throws Exception{
PrintWriter out = null;
File f = new File("/home/dingfeng/java_study/test.txt");
try{
out = new PrintWriter(new FileWriter(f));
}catch(IOException e){}
out.println("Hello,World!");
out.close();
}
}
运行结果:
打开创建的txt文件,文件内容为Hello,World!
打开创建的txt文件,文件内容为Hello,World!
7、DataInputStream和DataOutputStream
这两个类提供了与平台无关的数据操作,通常会先通过DataOutputStream按照一定的格式输出,再通过DataInputStream按照一定格式读入。由于可以得到java的各种类型甚至字符串,这样对得到的数据便可以方便的进行处理这在通过协议传输的信息的网络上是非常适用的。
范例9:
import java.io.*;
import java.io.*;
public class Test{
public static void main(String args[]) throws IOException{
// write data to txt file
DataOutputStream out = new DataOutputStream(new FileOutputStream("/home/dingfeng/java_study/test.txt"));
double []prices = {1.0, 2.0, 3.0, 4.0, 5.0};
int []units = {10, 20, 30, 40, 50};
String []descs = {"Apple", "Banana", "Orange", "Watermelon", "Grapes"};
for(int i=0;i<prices.length;i++){
out.writeDouble(prices[i]);
out.writeChar('\t');
out.writeInt(units[i]);
out.writeChar('\t');
out.writeChars(descs[i]);
out.writeChar('\n');
}
out.close();
// read data
DataInputStream in = new DataInputStream(new FileInputStream("/home/dingfeng/java_study/test.txt"));
double price;
int unit;
StringBuffer desc;
double total = 0;
try{
while(true){
price = in.readDouble();
in.readChar();
unit = in.readInt();
in.readChar();
char chr;
desc = new StringBuffer();
while((chr = in.readChar())!='\n'){
desc.append(chr);
}
System.out.println("Info: "+"fruit name: "+desc+"\tcounts:"+unit+"\tprice:"+price);
total = total + unit*price;
}
}catch(EOFException e){
System.out.println("\ntotal money: "+total+" RMB");
}
in.close();
}
}
运行结果:
Info: fruit name: Apple counts:10 price:1.0
Info: fruit name: Banana counts:20 price:2.0
Info: fruit name: Orange counts:30 price:3.0
Info: fruit name: Watermelon counts:40 price:4.0
Info: fruit name: Grapes counts:50 price:5.0
8、合并流
采用SeqpuenceInputStream类,可以实现两个文件的合并操作。如下图:
范例9:
import java.io.*;
public class Test{
public static void main(String args[]) throws IOException{
FileInputStream in1 = null;
FileInputStream in2 = null;
SequenceInputStream s = null;
FileOutputStream out = null;
try{
File inputFile1 = new File("/home/dingfeng/java_study/1.txt");
File inputFile2 = new File("/home/dingfeng/java_study/2.txt");
File outputFile = new File("/home/dingfeng/java_study/12.txt");
in1 = new FileInputStream(inputFile1);
in2 = new FileInputStream(inputFile2);
s = new SequenceInputStream(in1,in2);
out = new FileOutputStream(outputFile);
int c;
while((c = s.read())!= -1) out.write(c);
in1.close();
in2.close();
s.close();
out.close();
System.out.println("OK............");
}catch(IOException e){
e.printStackTrace();
}finally{
if(in1 != null)
try{
in1.close();
}catch(IOException e){}
if(in2 != null)
try{
in2.close();
}catch(IOException e){}
if(s != null)
try{
s.close();
}catch(IOException e){}
if(out != null)
try{
out.close();
}catch(IOException e){}
}
}
}
运行结果:
9、字节流与字符流的转换
Java支持字节流和字符流,但有时需要在字节流和字符流之间转换。InputStreamReader和OutputStreamWriter,这两个类是字节流和字符流之间相互转换的类,前者用于将一个字节流中的字节解码成字符,后者用于将写入的字符编码成字节后写入一个字节流。
InputStreamReader 构造方法
InputStreamReader(InputStream in) 创建一个使用默认字符集的 InputStreamReader。 |
InputStreamReader(InputStream in, Charset cs) 创建使用给定字符集的 InputStreamReader。 |
InputStreamReader(InputStream in, CharsetDecoder dec) 创建使用给定字符集解码器的 InputStreamReader。 |
InputStreamReader(InputStream in, String charsetName) 创建使用指定字符集的 InputStreamReader。 |
OutputStreamWriter(OutputStream out) 创建使用默认字符编码的 OutputStreamWriter。 |
OutputStreamWriter(OutputStream out, Charset cs) 创建使用给定字符集的 OutputStreamWriter。 |
OutputStreamWriter(OutputStream out, CharsetEncoder enc) 创建使用给定字符集编码器的 OutputStreamWriter。 |
OutputStreamWriter(OutputStream out, String charsetName) 创建使用指定字符集的 OutputStreamWriter。 |
BufferedReader in = new BufferedReader(new InputStreamReader(System.in))
BufferedWriter out = new BufferedWriter(new OutputStreamWriter(System.out))
范例10:
import java.io.*;
public class Test{
public static void main(String args[]) {
BufferedReader buf = null;
buf = new BufferedReader(new InputStreamReader(System.in));
String str = null;
while(true){
System.out.print("Please input number:");
try{
str = buf.readLine();
}catch(IOException e){}
int i = -1;
try{
i = Integer.parseInt(str);
i++;
System.out.println("Afrer modify:"+i);
break;
}catch(Exception e){}
}
}
}
运行结果:
Please input number:2
Afrer modify:3
10、IO类层次关系图
下面列出IO保重的各类层次关系图。
字节输入流InputStream类
字节输出流OutputStream
字符输入流Reader
字符输出流
四、对象序列化
所谓的对象序列化,是指将对象转换成二进制数据流的一种实现手段。通过将对象序列化,可以方便的实现对象的传输及保存。
在Java中,ObjectOutputStream和ObjectInputStream这两个类用于序列化对象的操作,用于存储和读取对象的输入输出流类,不难想象,只要把对象中的所有成员变量都存储起来,就等于保存了这个对象,之后从保存的对象中再将对象读取出来就可以继续使用此对象。对象必须实现Serializable接口,才能进行序列化。
范例11:
import java.io.*;
public class Test{
public static void main(String args[]) throws Exception{
File f = new File("/home/dingfeng/java_study/test.txt");
serialize(f);
deserialize(f);
}
public static void serialize(File f) throws Exception{
OutputStream outputFile = new FileOutputStream(f);
ObjectOutputStream cout = new ObjectOutputStream(outputFile);
cout.writeObject(new Person("zhangsan",20));
cout.close();
}
public static void deserialize(File f) throws Exception{
InputStream inputFile = new FileInputStream(f);
ObjectInputStream cin = new ObjectInputStream(inputFile);
Person p = (Person)cin.readObject();
System.out.println(p);
}
}
class Person implements Serializable{
private String name;
private int age;
public Person(String name,int age){
this.name = name;
this.age = age;
}
public String toString(){
return "name:"+this.name+" age:"+this.age;
}
}
运行结果:
name:zhangsan age:20
name:zhangsan age:20
如果不希望类中的某个属性被序列化,可以在声明属性之前加上transient关键字,例如:
private transient String name;
private transient int age;
结果:name:null age:0
另外:
a)当一个父类实现序列化,子类自动实现序列化,不需要显式实现Serializable接口;
b)当一个对象的实例变量引用其他对象,序列化该对象时也把引用对象进行序列化;
c)并非所有的对象都可以序列化。
b)当一个对象的实例变量引用其他对象,序列化该对象时也把引用对象进行序列化;
c)并非所有的对象都可以序列化。