I/O流可谓是博大精深呀,今天总结一下今天所学到的东西吧:

首先理解一下I/O流的概念吧,Java的I/O流是实现输入、输出的基础,它可以方便实现数据的输入、输出操作,Java中把不同的输入、输出流抽象地表述为“流”,通过流的方式允许Java程序使用相同的方式来访问不同的输入、输出流。InputStream和OutputStream是两个处理字节流的基础的类(且为抽象类),而用于处理字符流的两个基础的类是Reader和Writer,所有其他的流类都是以这4个类为基础的。

按不同的方式可以把流分为不同的类:

1.按照流的方向分为输入流和输出流

输入流:只能从中读取数据,而不能向其中写入数据(如InputStream、FileInputStream);

输出流:只能向其中写入数据,而不能向其中读取数据(如OutputStream、FileOutputStream等);

2.按照流的角色分为节点流和处理流

节点流:可以向一个特定的IO设备读/写数据的流,也称作基础流、低级流(FileInputStream、FileOutputStream)

处理流:实现对一个已存在的流的连接和封装,通过所封装的流的功能调用实现数据读/写功能的流,也称作高级流、包装流(如BufferedInputStream/BufferedOutputStream缓冲流DataInputStream/DataOutputStream数据流);它同时也是IO的精华—>基础流中构造中参数是介质,而处理流中构造中参数是流。

下面看几个实例吧:

第一部分:先看看基础流吧!

实例一:InputStream和OutputStream

这两个流是抽象类,提供了输入/输出处理的基本接口,并且实现了其中的一些方法,他们读/写流的方式是以字节为单位进行的。

InputStream用于从源按照字节读取数据,它定义了一下一些方法:

int read():从输入流中读取数据的下一个字节,并将它返回。如果遇到源的末尾,则返回-1,可以利用这一点判断是否已经到了流的末尾;

int read(byte[] buffer):将数据读入一个字节数组中,同时返回读取的字节数。同样如果遇到源的末尾,则返回-1;

int read(byte[] buffer,int offset,int length):将数据读入一个字节数组,放到数组的offset指定的位置开始,并用length来指定读取的最大字节数同样如果遇到源的末尾,则返回-1;

void close():用于关闭该流,在使用完流以后,一定要记得使用此方法关闭,否则数据可能不会写入文件;

void close():用于冲刷数据流,将数据写入文件,close方法中会先执行此方法。

*注意一点:缓冲区是采用数据覆盖的形式进行传递数据的。

下面看一个代码:

package day11.io;
import java.io.*;
public class TestTestInputStream {
public static void main(String[] args) throws Exception {
int size;
InputStream is = new FileInputStream("E:\\test.txt");//input流需要先创建文件
System.out.println("可读取 的字节:"+(size=is.available()));
byte[] t = new byte[200];
for(int i=0;i<size;i++){
t[i] = ((byte)is.read());
System.out.println(t[i]);
}
System.out.println();
OutputStream os = new FileOutputStream("E:\\test.txt");
byte[] c = new byte[200];
for(int j = 0;j<200;j++){
c[j] =(byte)(j);
os.write(c[j]);
}
os.close();
is.close();
}
}

    

上述代码中用了try、catch直接处理了IO异常,调用read方法时,文件必须存在,调用write方法时会自动创建一个文件。

实例二:FileInputStream和FileOutputStream

他们分别是InputStream和OutputStream的子类,在生成FileInputStream和FileOutputStream类的对象时,如果指定的文件找不到,都会产生FileNotFoundException异常,因此必须捕获或抛出异常。

下面看一个代码:

package day11.io;
import java.io.*;
public class TestIo {
public static void main(String[] args) {
InputStream is = null;
OutputStream os = null;
try {
//使用read方法时,文件必须存在
//write方法会自动创建一个文件(如果没有这个文件)
os = new FileOutputStream("D:\\2.txt");
os.write(98);
byte[] c = new byte[]{1,2,3};
os.write(c);
System.out.println();
is = new FileInputStream("D:\\2.txt");//会报错java.io.FileNotFoundException:
int i = is.read();
System.out.println(i);
System.out.println(c.length);
} catch (IOException e) {
e.printStackTrace();
}finally{
try {
is.close();
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}

    实例三:FileInputStream和FileOutputStream进行文件内容复制,利用write是可以创建文件的特点,复制出另一个内容一模一样的文件,为了提高复制效率,引进了数组,如果不写flush() 和close(),那么复制出的文件会变小,如果write中参数不加后面两个,那么复制出的文件会变大。下面看一下例子

    

package blog;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
public class TestCopy {
public static void main(String[] args) throws Exception {
File file1 = new File("E:\\test.txt");
File file2 = new File("E:\\tt.txt");
FileInputStream fis = new FileInputStream(file1);
FileOutputStream fos = new FileOutputStream(file2);
int i;
byte[] buff = new byte[8*1024];
while((i=fis.read(buff))!=-1){
fos.write(buff,0,i);
}
System.out.println("succees!");
System.out.println("file1的大小为:"+file1.getUsableSpace()+" ");
System.out.println("file2的大小为:"+file2.getUsableSpace()+" ");
//      file1.delete();完成了剪切功能
fis.close();
fos.close();
}
}

    输出结果如下所示:

    

succees!
file1的大小为:73930264576
file2的大小为:73930264576

   程序结合了InputStream和OutputStream的用法,通过一个FileInputStream从一个文件中读出文件内容,然后将它作为FileOutputStream的输出,写入另一个文本文件,这样就完成了复制。

第二部分:接下来看看高级数据流^_^

高级流是对基础流进行了封装,并有许多功能的扩展,下面介绍几个常用的高级数据流:

1.BufferedInputStream和BufferedOutputStream

  实现了带缓冲的过滤流,在读写的同事对数据进行缓存,这样可以避免每次读写或发送数据时都要进行实际的物理读写操作,因此在输入输出时经常用到这两个类。在用BufferOutputStream输出时,数据先输入缓冲区,缓冲区大小默认为8K,当缓冲区满时再写入连接的输出流,可以调用flush()方法来清空缓冲区。

看一个代码理解一下:

实例四:

Buffered...有很多方法,在这里就不一一列举了,读者可以查相应的api。

下面的方法复制了一个相同的文件:

package day11.io;
import java.io.*;
public class TestFileCopy {
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream("D:\\1.txt");
FileOutputStream fos = new FileOutputStream("D:\\2.txt");
BufferedInputStream bis = new BufferedInputStream(fis);
BufferedOutputStream bos = new BufferedOutputStream(fos);
int i = 0;
//默认读的是8K,要是缓冲取满了会自动写入文件里的
while((i=bis.read())!=-1){
bos.write(i);
}
//      bos.flush();//冲刷缓冲区,强制将缓冲区中的内容写入文件
//      bos.close();//关闭缓冲区在close之前会先flush的
}
}

    下面说一下System.setOut();方法,它可以控制输出方式,即在控制台输出还是在文件中输出。

package day11.io;
import java.io.*;
public class TestSISO {
public static void main(String[] args) throws IOException {
// 从控制台读一行
// Scanner是5.0之后提供的类。
InputStream is = System.in;
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
String s = br.readLine();
//需要在控制台路数数据然后直接在控制带显示;
System.out.println(s);
//PrintStream 中的println方法经常和BufferedReader的readLine()配合使用。
PrintStream ps = new PrintStream(new File("D:\\a.txt"));
System.out.println("控制台输出!");
System.setOut(ps);
System.out.println("文件中输出!");
ps.print("I'm coming!");
ps.print(s);
}
}

    控制台输出如下:

wo
wo
控制台输出!

    文件中内容如下:

文件中输出!
I'm coming!

    

2.DataInputStream和DataOutputStream

  它们同样分别继承自FilterInputStream和FilterOutputStream,可以用与计算机无关的格式读写Java的基本数据类型以及String对象,DataInputStream中对应的以read开头,如readByte(),readDouble等等,write也同样。

在上一个代码的基础上增加了此数据流的功能:

实例五:

package day11.io;
import java.io.*;
public class TestDataFlow {
public static void main(String[] args) throws IOException {
//Data数据流中包含了8种基本数据流的重载,可输入8种,另外还可查看api还有别的
File file = new File("D:\\a.txt");
FileOutputStream fos = new FileOutputStream(file);
DataOutputStream dos = new DataOutputStream(fos);
BufferedOutputStream bos = new BufferedOutputStream(dos);
dos.writeChar('M');
dos.writeDouble(1.34154);
dos.writeUTF("家");
FileInputStream fis = new FileInputStream(file);
DataInputStream dis = new DataInputStream(fis);
BufferedInputStream bis = new BufferedInputStream(dis);
char a = dis.readChar();
double d = dis.readDouble();
String s = dis.readUTF();
System.out.println(a+" "+s+" "+d);
//只需要关闭外层的就可以啦,它会自动关闭内层的流
dis.close();
dos.close();
}
}
输出如下:
M 家 1.34154

    

Data...数据流则比较人性化,多了很多方法,提供更多数据类型,writeUTF也提供了汉字的输入~

3.ObjectInputStream和ObjectOutputStream

万物皆OBJ,同样数据流也有Object类型的,见闻知其意,即数据流中传递的是对象的类型,下面看个小例子:

实例六:

首先实体类如下:

package day11.io;
import java.io.Serializable;
public class Pet implements Serializable{
private String name;
private int age;
public Pet() {
super();
// TODO Auto-generated constructor stub
}
public Pet(String name, int age) {
super();
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Pet [name=" + name + ", age=" + age + "]";
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Pet other = (Pet) obj;
if (age != other.age)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}

    实体类代码中均未自动生成的方法,在写这个的时候明确了一点概念:Collection和Serializable接口均无字段和方法,Compatable只有一个方法即compareTo(),然后写一下为其测试类,将对象的数据存入D:\\Pet.content的文件中,然后从中读取Pet类的数据:

package day11.io;
import java.io.*;
public class TestPetOOSIOS {
public static void main(String[] args) throws Exception {
File file = new File("D:\\Student.content");
FileOutputStream fos = new FileOutputStream(file);
ObjectOutputStream oos = new ObjectOutputStream(fos);
Pet p1 = new Pet("七仔",5);
oos.writeObject(p1);
FileInputStream fis = new FileInputStream(file);
ObjectInputStream ois = new ObjectInputStream(fis);
Pet p2 = (Pet) ois.readObject();
System.out.println("MY Dog'name is "+p2.getName()+"! \r\nAnd his age is "+p2.getAge()+" years old!");
oos.close();
ois.close();
}
测试结果如下:
MY Dog'name is 七仔!
And his age is 5 years old!

    同样还可定义另一个类让其成为本实体类的属性,然后同样也就可以传递这个类的数据流了

4.Reader和Writer

它处理的是字符类型的数据流,它也分高级流和低级流;

Reader与InputStream相同用于从流中读取数据,它的方法如下:

int read():用于从流中读出一个字符,并将其返回。

int read():将从流中读出的字符放到字符数组buffer中,返回读出的字符数。

Int read(char[] buffer,int offset,int length):将读出的字符放到字符数组的指定offset开始的空间,每次最多读出length个字符。

Void close():关闭Reader流,在使用完Reader流后,一定记得将其关闭。

Boolean ready():判断流是否已经准备好被读取。

其他等方法可查看api文档。

Writer与OutputStream类似,用于向流中写入数据,它的方法如下:

void write(int c)

void write(char[] buffer)

void write(char[] buffer,int offset,int length)

void write(String string)

void write(String string,int offset,int length)

均为write方法的重载,意义同以上的read方法差不多

void close(),void flush()方法与以前的相同。

实例七:

package day11.io;
import java.io.*;
public class TestReader {
public static void main(String[] args) throws IOException {
File file = new File("D:\\a.txt");
FileReader fr = new FileReader(file);//同一下两句的效果相同,但前提是保证是gbk编码
//      FileInputStream fis = new FileInputStream(file);
//      InputStreamReader isr = new InputStreamReader(fis,"gbk");
char c = (char)fr.read();
System.out.println(c);
//注意输入编码格式要一致
FileOutputStream fos = new FileOutputStream(file);
OutputStreamWriter osw = new OutputStreamWriter(fos,"UTF-8");
osw.write("中国");
for(int i=0;i<65535;i++){
osw.write(i);
}   fr.close();
osw.close();
}
}

    下面一个是用了Readre和Writer的方法

package day11.io;
import java.io.*;
public class TestBuffered {
public static void main(String[] args) throws IOException {
File file = new File("D:\\a.txt");//得先有这个文件
FileInputStream fis = new FileInputStream(file);
InputStreamReader isr = new InputStreamReader(fis);
BufferedReader br = new BufferedReader(isr);
String str = null;
while((str = br.readLine())!=null){
System.out.println(str);
}
  FileOutputStream fos = new FileOutputStream(file);
OutputStreamWriter osw = new OutputStreamWriter(fos);
BufferedWriter bos = new BufferedWriter(osw);
bos.write("一二三四五");
bos.newLine();// 新起一行。
bos.write("六七八九十");
bos.flush();// 注意冲刷
bos.close();
br.close();
}
}

    在a.txt中输出了几乎所有的编码。在这个学习中有明确了一点编码小问题:对于汉字来说,gbk/gb2312占两个字节,utf-8占三个字节。

I/O果然是个不同凡响的东西,内容好多,以后再慢慢聊吧,今天就到这样了~晚安!