1 PrintStream、PrintWriter打印流
打印流:
该流提供了print打印方法,可以将各种数据类型的数据都原样打印。
字节打印流:PrintStream
构造函数可以接收的参数类型:
1,file对象。 File
2,字符串路径。 String
3,字节输出流。 OutputStream
字符打印流:PrintWriter
构造函数可以接收的参数类型:
1,file对象。 File
2,字符串路径。 String
3,字节输出流。 OutputStream
4,字符输出流。 Writer
代码示例:
import java.io.*;
class PrintWriterDemo{
public static void main(String[] args) throws IOException{
BufferedReader bufr =
new BufferedReader(new InputStreamReader(System.in));
PrintWriter out = new PrintWriter(System.out,true); //true表示,println将自动刷新缓冲区。
//PrintWriter out = new PrintWriter(new FileWriter("a.txt"),true);
String line = null;
while((line=bufr.readLine())!=null) {
if(line.equals("over"))
break;
out.println(line.toUpperCase()); //把line转成大写后写入打印流。
//PrintWriter的构造函数中传递了参数true,println会自动刷新缓冲区。
//out.flush();
}
out.close();
bufr.close();
}
}
2 SequenceInputStream合并流
SequenceInputStream:表示其他输入流的逻辑串联。
它从输入流的有序集合开始,并从第一个输
入流开始读取,直到到达文件末尾,
接着从第二个输入流读取,依次类推,直到到达包含的最后一个输入流的文件末尾为止。
构造方法:
SequenceInputStream( Enumeration<? extends InputStream> e);
SequenceInputStream( InputStream s1, InputStream s2);
使用到了Vector类的elements方法,返回一个枚举Enumeration。
代码示例:
import java.io.*;
import java.util.*;
class SequenceDemo{
public static void main(String[] args) throws IOException{
Vector<FileInputStream> v = new Vector<FileInputStream>(); //Vector集合,元素是输入流
v.add(new FileInputStream("e:\\1.txt")); //添加一个流到集合中
v.add(new FileInputStream("e:\\2.txt"));
v.add(new FileInputStream("e:\\3.txt"));
Enumeration<FileInputStream> en = v.elements(); //返回枚举,Enumeration接口
SequenceInputStream sis = new SequenceInputStream(en); //把枚举集合传递到合并读取流
FileOutputStream fos = new FileOutputStream("e:\\4.txt");
byte[] buf = new byte[1024];
int len = 0;
while((len=sis.read(buf))!=-1){
fos.write(buf,0,len);
}
fos.close();
sis.close();
}
}
3 合并流扩展:切割文件然后合并
把一个文件切割成多个碎片文件。
然后,再把这些碎片文件合并,得到原文件。
思路:
1,切割文件:定义一个1KB大的数组,读取一个文件,每一次读取1KB大小的数据,把这一次读取的数据单独定义一个输出流,输出为一个碎片文件。最终有多少个碎片文件,就定义了多少个输出流。
2,合并文件:假如有N个碎片文件,就定义N个输入流,这些输入流对象存储到一个集合中。获取这个集合的迭代器,用匿名内部类方式创建一个枚举对象,并在实现中调用迭代器的方法,实现枚举对象的判断和取出。用合并流实现从多个输入流中连续的读取数据,再连续的写入到一个输出流中,实现碎片文件的合并。创建合并流时,要把枚举对象作为参数,传递给合并流的构造函数。因为要把枚举中的多个输入流,合并为一个输入流。枚举的元素就是输入流。
代码示例:
import java.io.*;
import java.util.*;
class SplitFile{
public static void main(String[] args) throws IOException{
splitFile(); //切割文件
merge(); //合并文件
}
//切割文件,把文件切割成碎片文件
public static void splitFile() throws IOException{
FileInputStream fis = new FileInputStream("e:\\EVE.jpg"); //源
FileOutputStream fos = null; //目的
byte[] buf = new byte[1024]; //1KB
int len = 0;
int count = 1;
while((len=fis.read(buf))!=-1){
fos = new FileOutputStream("e:\\Split Files\\"+(count++)+".part");
fos.write(buf,0,len);
fos.close();
}
fis.close();
}
//合并碎片文件,复原
public static void merge() throws IOException{
ArrayList<FileInputStream> al = new ArrayList<FileInputStream>();
for(int x=1; x<=6; x++){
al.add(new FileInputStream("E:\\Split Files\\"+x+".part"));
}
final Iterator<FileInputStream> it = al.iterator(); //集合的迭代器
//新建一个枚举,枚举中调用迭代器的方法。迭代器通过集合al获得。
//枚举一般通过Vector获得,这里没有Vector。
Enumeration<FileInputStream> en = new Enumeration<FileInputStream>()
{
public boolean hasMoreElements(){ //内部类
return it.hasNext();
}
public FileInputStream nextElement(){
return it.next();
}
};
SequenceInputStream sis = new SequenceInputStream(en); //源,枚举en作为参数传递给合并流
FileOutputStream fos = new FileOutputStream("E:\\Split Files\\EVE.jpg"); //目的
byte[] buf = new byte[1024];
int len = 0;
while((len=sis.read(buf))!=-1){
fos.write(buf,0,len);
}
fos.close();
sis.close();
}
}
4 ObjectOutputStream、ObjectInputStream及对象的序列化
ObjectOutputStream和ObjectInputStream,这两个流对象可以操作对象。
序列化:序列化就是此对象的所属类实现Serializable接口,实现了Serializable接口以后,为每个对象分配了一个UID,这个UID即 serialVersionUID。
可以在对象的所属类中自定义serialVersionUID的值,这样就永远一个对象。
静态不能被序列化。
代码示例:
import java.io.*;
class ObjectStreamDemo {
public static void main(String[] args) throws Exception{
writeObj();
readObj();
}
//创建一个Person对象,并把这个对象存到obj.txt文件中。
public static void writeObj() throws IOException{
ObjectOutputStream oos =
new ObjectOutputStream(new FileOutputStream("e:\\obj.txt"));
oos.writeObject(new Person("lisi",39)); //把对象写入流中
oos.close();
}
//从obj.txt文件中读取那个对象。
public static void readObj() throws Exception{
ObjectInputStream ois =
new ObjectInputStream(new FileInputStream("e:\\obj.txt"));
Person p = (Person)ois.readObject(); //从流中读取对象
System.out.println(p);
ois.close();
}
}
//Serializable标记接口,想要序列化必须实现此接口,起标记作用。
class Person implements Serializable{ //被标记的可以序列化
private String name = null;
private int age = 0;
Person(String name,int age){
this.name = name;
this.age = age;
}
public String toString(){
return name+" : "+age;
}
}
5 管道流
IO包中的管道流:
PipedInputStream
PipedOutputStream
PipedReader
PipedWriter
管道读取流流和管道写入流可以像管道一样对接上,即管道读取流可以读取管道写入流写入的数据。
可以认为有一个虚拟的“管道”,PipedInputStream从这个管道中读取数据,而PipedOutputStream把数据写入到这个管道中。写入之后,PipedInputStream就可以读取到数据了。如果管道中没有数据,PipedInputStream会线程阻塞。
管道流需要加入多线程技术。
代码示例和注释:
import java.io.*;
class Read implements Runnable{ //实现Runnable接口并覆盖run,多线程
private PipedInputStream in;
Read(PipedInputStream in){ //管道输入流
this.in = in;
}
public void run(){
try{
byte[] buf = new byte[1024];
int len = in.read(buf); //in中没有数据时,线程阻塞。
String s = new String(buf ,0,len);
System.out.println(s);
in.close();
}
catch(IOException e){
System.out.println("管道读取流失败");
}
}
}
class Write implements Runnable {
private PipedOutputStream out;
Write(PipedOutputStream out){ //管道输出流
this.out = out;
}
public void run(){
try{
//将指定的字节数组写入管道输出流,write是把指定数据写入流中,
//往管道流中写入数据,Read中阻塞的线程可以读到数据,继续执行。
out.write("piped lai la".getBytes());
out.close();
}
catch(IOException e){
System.out.println("管道输出流失败");
}
}
}
class PipedStreamDemo {
public static void main(String[] args) throws IOException{
PipedInputStream in = new PipedInputStream(); //负责读取管道中的数据到流中。
PipedOutputStream out = new PipedOutputStream(); //负责向管道中写入数据,以便读取
in.connect(out); //connect方法把管道流的输入流和输出流关联起来,形成一条管道。
Read r = new Read(in);
Write w = new Write(out);
new Thread(r).start();
new Thread(w).start();
}
}
6 RandomAccessFile随机访问文件
随机访问文件,RandomAccessFile自身具备读写的方法。
通过skipBytes(int x), seek(int x)来达到随机访问的目的。
该类不是IO体系中的子类,而是直接继承自Object。
但是它是IO包中的成员,因为它具备读和写功能。
内部封装了一个数组,而且通过指针对数组的元素进行操作。
可以通过getFilePointer获取指针位置,同时可以通过seek方法改变指针的位置。
其完成读写的原理就是内部封装了字节输入流和输出流。
通过构造函数可以看出,该类只能操作文件,而且操作文件还有模式。
RandomAccessFile(File file, String mode)
RandomAccessFile(String name, String mode)
mode: r——以只读方式打开。调用结果对象的任何 write 方法都将导致抛出 IOException。
rw——打开以便读取和写入。如果该文件尚不存在,则尝试创建该文件。
如果模式为只读r,不会创建文件,会去读取一个已存在文件,如果该文件不存在,则会出现异常。
如果模式为读写rw,而且该对象的构造函数要操作的文件不存在,则会自动创建,若存在则不会覆盖。
代码示例:
import java.io.*;
class RandomAccessFileDemo{
public static void main(String[] args) throws IOException{
//writeFile();
readFile();
writeFile_2();
}
public static void writeFile() throws IOException{
RandomAccessFile raf = new RandomAccessFile("e:\\ran.txt","rw"); //读写模式
raf.write("李四".getBytes()); //基于字节流,写入的是字节数组。
raf.writeInt(97); //writeInt写四个字节,97是“a”的ASCII码,即写“000a”;int类型32位即四个字节。
raf.write("王五".getBytes());
raf.writeInt(98);
raf.close();
}
public static void readFile() throws IOException{
RandomAccessFile raf = new RandomAccessFile("e:\\ran.txt","r"); //只读模式。
//通过seek和skipBytes方法,达到随机访问的目的。
//调整对象中开始读写位置,调整文件指针的位置。
raf.seek(8*1); //从第8脚标开始取,即调整指针到第9个字节。
//跳过指定的字节数。
//raf.skipBytes(8); //跳过8个字节,从第9个字节开始取。
byte[] buf = new byte[4];
raf.read(buf);
String name = new String(buf);
int age = raf.readInt();
System.out.println("name="+name);
System.out.println("age="+age);
raf.close();
}
public static void writeFile_2() throws IOException{
RandomAccessFile raf = new RandomAccessFile("e:\\ran.txt","rw");
raf.seek(8*3); //从第25个字节处开始写入数据。
//也可以通过seek方法指定指针位置,来修改数据。
raf.write("周七".getBytes());
raf.writeInt(100);
}
}
7 DataStream操作基本数据类型的流对象
DataInputStream与DataOutputStream,可以用于操作基本数据类型的数据的流对象。
DataInputStream( InputStream in)
DataOutputStream( OutputStream out)
八大基本数据类型:byte、int、short、long、float、double、char、boolean。
DataInputStream可以直接读取基本数据类型的数据,比如readInt可以读取Int类型。
DataOutputStream可以直接写入基本数据类型的数据,比如writeInt可以写入Int类型。
转换流的构造函数可以传递字符编码,指定所需的是哪种字符编码。
代码示例和注释:
import java.io.*;
class DataStreamDemo {
public static void main(String[] args) throws IOException{
writeData();
readData();
writeUTFDemo();
readUTFDemo();
//存取转换流
OutputStreamWriter osw =
new OutputStreamWriter(new FileOutputStream("e:\\utf.txt"),"UTF-8");
osw.write("你好");
osw.close();
}
//DataOutputStream中写基本数据类型的方法。
public static void writeData() throws IOException{
DataOutputStream dos =
new DataOutputStream(new FileOutputStream("e:\\data.txt"));
dos.writeInt(234); //写入int类型。
dos.writeBoolean(true); //写入boolean类型。
dos.writeDouble(9883.421); //写入double类型。
dos.close();
}
//DataInputStream中读基本数据类型的方法。
public static void readData() throws IOException{
DataInputStream dis =
new DataInputStream(new FileInputStream("e:\\data.txt"));
int num = dis.readInt(); //一次读四个字节,以int形式返回。
boolean b = dis.readBoolean(); //一次读两个字节,以Boolean形式返回。
double d = dis.readDouble(); //读取double类型
System.out.println("num="+num);
System.out.println("b="+b);
System.out.println("d="+d);
dis.close();
}
//writeUTF()演示,操作中文。
public static void writeUTFDemo() throws IOException{
DataOutputStream dos =
new DataOutputStream(new FileOutputStream("e:\\UTFdata.txt"));
dos.writeUTF("你好"); //以UTF编码写入
dos.close();
}
//readUTF()演示,操作中文
public static void readUTFDemo() throws IOException{
DataInputStream dis =
new DataInputStream(new FileInputStream("e:\\UTFdata.txt"));
String s = dis.readUTF(); //以UTF编码读取,返回String形式。
System.out.println(s);
dis.close();
}
}
8 ByteArrayStream用于操作字节数组的流对象
ByteArrayInputStream,ByteArrayOutputStream:用于操作字节数组的流对象。
ByteArrayInputStream:在构造的时候,需要接收数据源,而数据源是一个字节数组。
ByteArrayInputStream(byte[] buf)
ByteArrayInputStream(byte[] buf, int offset, int length)
ByteArrayOutputStream:在构造的时候,不用定义数据目的,
因为该对象中已经内部封装了可变长度的字节数组,这就是数据目的地。
ByteArrayOutputStream()
ByteArrayOutputStream(int size)
因为这两个流对象都是操作数组,并没有使用file,即没有使用系统资源,所以,不用进行close关闭。
ByteArrayInputStream 包含一个内部缓冲区,该缓冲区包含从流中读取的字节。
关闭 ByteArrayInputStream 无效。此类中的方法在关闭此流后仍可被调用,而不会产生任何 IOException。
ByteArrayOutputStream 此类实现了一个输出流,其中的数据被写入一个 byte 数组(内部封装)。
缓冲区会随着数据的不断写入而自动增长。可使用 toByteArray() 和 toString() 获取数据。
关闭 ByteArrayOutputStream 无效。此类中的方法在关闭此流后仍可被调用,而不会产生任何 IOException。
在流操作规律讲解时:
源设备:
键盘 System.in,硬盘 FileStream,内存 ArrayStream。 //数组流,即ByteArrayStream
目的设备:
控制台 System.out,硬盘 FileStream,内存 ArrayStream。
代码示例:
import java.io.*;
class ByteArrayStreamDemo{
public static void main(String[] args){
//数据源。
ByteArrayInputStream bis =
new ByteArrayInputStream("ABCDEFG".getBytes());
//数据目的。
ByteArrayOutputStream bos = new ByteArrayOutputStream();
int by = 0;
while((by=bis.read())!=-1) {
bos.write(by);
}
System.out.println(bos.size()); //打印缓冲区大小。
System.out.println(bos.toString()); //以字符串形式打印bos的内容。
//writeTo(outputStream)方法把byte数组输出流中的全部内容,写入到指定输出流中。
//bos.writeTo(new FileOutputStream("e:\\ArrayStream.txt"));
}
}