------- android培训、java培训、期待与您交流! ----------
对IO流的总结
操作数据分为两种:字节流和字符流。
1. 字符流抽象类基类
①.public abstract class Writer
extends Object
implements Appendable, Closeable, Flushable
写入字符流的抽象类。子类必须实现的方法仅有 write(char[], int, int)、flush() 和 close()。但是,多数子类将重写此处定义的一些方法,以提供更高的效率和/或其他功能。
②. public abstract class Reader
extends Object
implements Readable, Closeable
用于读取字符流的抽象类。子类必须实现的方法只有 read(char[], int, int) 和 close()。但是,多数子类将重写此处定义的一些方法,以提供更高的效率和/或其他功能。
2. 基于字符流抽象基类的实现:
①. Reader的直接子类:
public class BufferedReader
extends Reader
从字符输入流中读取文本,缓冲各个字符,从而提供字符、数组和行的高效读取。
可以指定缓冲区的大小,或者可使用默认的大小。大多数情况下,默认值就足够大了。
通常,Reader 所作的每个读取请求都会导致对基础字符或字节流进行相应的读取请求。因此,建议用 BufferedReader 包装所有其 read() 操作可能开销很高的 Reader(如 FileReader 和 InputStreamReader)。例如,
BufferedReader in
= new BufferedReader(new FileReader("foo.in"));
将缓冲指定文件的输入。如果没有缓冲,则每次调用 read() 或 readLine() 都会导致从文件中读取字节,并将其转换为字符后返回,而这是极其低效的。
可以对使用 DataInputStream 进行按原文输入的程序进行本地化,方法是用合适的 BufferedReader 替换每个 DataInputStream。
②. public class BufferedWriter
extends Writer
将文本写入字符输出流,缓冲各个字符,从而提供单个字符、数组和字符串的高效写入。
可以指定缓冲区的大小,或者接受默认的大小。在大多数情况下,默认值就足够大了。
该类提供了 newLine() 方法,它使用平台自己的行分隔符概念,此概念由系统属性 line.separator 定义。并非所有平台都使用新行符 ('\n') 来终止各行。因此调用此方法来终止每个输出行要优于直接写入新行符。
通常 Writer 将其输出立即发送到基础字符或字节流。除非要求提示输出,否则建议用 BufferedWriter 包装所有其 write() 操作可能开销很高的 Writer(如 FileWriters 和 OutputStreamWriters)。例如,
PrintWriter out
= new PrintWriter(new BufferedWriter(newFileWriter("foo.out")));
将缓冲 PrintWriter 对文件的输出。如果没有缓冲,则每次调用 print() 方法会导致将字符转换为字节,然后立即写入到文件,而这是极其低效的。
③. public class FileWriter
extends OutputStreamWriter
用来写入字符文件的便捷类。此类的构造方法假定默认字符编码和默认字节缓冲区大小都是可接受的。要自己指定这些值,可以先在 FileOutputStream 上构造一个 OutputStreamWriter。
文件是否可用或是否可以被创建取决于基础平台。特别是某些平台一次只允许一个 FileWriter(或其他文件写入对象)打开文件进行写入。在这种情况下,如果所涉及的文件已经打开,则此类中的构造方法将失败。
FileWriter 用于写入字符流。要写入原始字节流,请考虑使用 FileOutputStream。
FileWriter与缓冲BufferedWriter直接相关。
Example:
import java.io.*;
public class IoDemo {
public static void main(String[] args) throws IOException
{
FileWriter f = new FileWriter("1.txt");
f.write("abcde");
f.close();
}
}
程序简单分析:
FileWriter是用来写入字符文件的便捷类。FileWriter f = newFileWriter("1.txt")会在当前文件路径下产生文件1.txt。然后调用FileWriter类中Write方法向其写入字符串。
③.public class FileReader
extends InputStreamReader
用来读取字符文件的便捷类。此类的构造方法假定默认字符编码和默认字节缓冲区大小都是适当的。要自己指定这些值,可以先在 FileInputStream 上构造一个 InputStreamReader。
FileReader 用于读取字符流。要读取原始字节流,请考虑使用 FileInputStream
FileReader与BufferedReader直接相关。
Example:
import java.io.*;
public class IoDemo {
public static voidmain(String[] args) throws IOException
{
FileReader f = newFileReader("1.txt");
int s ;
while((s=f.read())!=-1)
{
System.out.println((char)s);
}
f.close();
}
}
对程序简要分析:
FileReader f = newFileReader("1.txt"):读取磁盘文件1.txt。
注意:几个read方法的细节区分:
如果read方法读取单个字节,那么该方法返回的是字节的ASC码值。
如果read方法读取的不是单个字符,而是字符串,或者数组,返回就是读取的字节个数。
把一个文件复制到另一个文件:
Example1:
直接复制:
import java.io.*;
public classIoDemo {
public static voidmain(String[] args) throwsIOException
{
FileReaderf = newFileReader("1.txt");
FileWriteru = newFileWriter("2.txt");
while((s=f.read())!=-1)
{
System.out.println((char)s);
u.write(s);
}
f.close();
u.close();
}
}
分析:这个程序说明是:把1.txt文件中类容写入2.txt。通过read方法实现这一功能。而read无参数方法每次只能读取一个字符,然后没读一个字符,就把字符写入2.txt。
而有参read方法的实现必须定义临时容器,来装入字符。然后再把临时容器中的字符装入2.txt。
形如:char[] c = new char[20];
while((s=f.read(c))!=-1)
{
System.out.println(c);
u.write(c);
}
具体实现:
import java.io.*;
public classIoDemo {
public static voidmain(String[] args) throwsIOException
{
FileReaderf = newFileReader("1.txt");
FileWriteru = newFileWriter("2.txt");
int s;
char[] c = new char[20];
while((s=f.read(c))!=-1)
{
System.out.println(c);
u.write(c);
}
f.close();
u.close();
}
}
Example2:
使用缓冲器复制:
import java.io.*;
public classIoDemo {
public static voidmain(String[] args) throwsIOException
{
FileReaderf = newFileReader("1.txt");
FileWriteru = newFileWriter("2.txt");
BufferedWriterbuf = newBufferedWriter(u);
BufferedReaderbff = newBufferedReader(f);
int line;
while((line=f.read())!=-1)
{
System.out.println((char)line);
u.write(line);
}
f.close();
u.close();
}
}
程序简单分析:
⑴.FileReader f = new FileReader("1.txt");与语句 BufferedReader bff = new BufferedReader(f);对应,在程序执行语句:read方法后,先把字符读到缓冲器BufferedReader中。后面语句建立于文本的关联。
⑵.FileWriter u = new FileWriter("2.txt");与语句 BufferedWriter buf = new BufferedWriter(u);关联。
在执行write方法后,先把字符逐个从磁盘读出来全部放在缓冲中,读完后,在刷新,把缓冲中的数据放在指定文件中。
3. 字节流
字节流抽象基类:
⑴.public abstract class InputStream
extendsObject
implementsCloseable
此抽象类是表示字节输入流的所有类的超类。
需要定义 InputStream 的子类的应用程序必须始终提供返回下一个输入字节的方法。
①.public class FileInputStream
extendsInputStream
FileInputStream 从文件系统中的某个文件中获取输入字节。哪些文件可用取决于主机环境。
FileInputStream 用于读取诸如图像数据之类的原始字节流。要读取字符流,请考虑使用 FileReader。
此类是InputStream基类直接子类。用于创建与文件系统文件相关联对象,用于读取文件数据。
⑵. public abstract class OutputStream
extendsObject
implementsCloseable, Flushable
此抽象类是表示输出字节流的所有类的超类。输出流接受输出字节并将这些字节发送到某个接收器。
需要定义 OutputStream 子类的应用程序必须始终提供至少一种可写入一个输出字节的方法。
①. public class FileOutputStream
extendsOutputStream
文件输出流是用于将数据写入 File 或 FileDescriptor 的输出流。文件是否可用或能否可以被创建取决于基础平台。特别是某些平台一次只允许一个 FileOutputStream(或其他文件写入对象)打开文件进行写入。在这种情况下,如果所涉及的文件已经打开,则此类中的构造方法将失败。
FileOutputStream 用于写入诸如图像数据之类的原始字节的流。
要写入字符流,请考虑使用 FileWriter。
其中FileInputStream和FileOutputStream是基于字节流的,常用于读写二进制文件。
读写字符文件建议使用基于字符的FileReader和FileWriter,省去了字节与字符之间的转换。
但这两个类的构造函数默认使用系统的编码方式,如果文件内容与系统编码方式不一致,可能会出现乱码。
在这种情况下,建议使用FileReader和FileWriter的父类:InputStreamReader/OutputStreamWriter,
它们也是基于字符的,但在构造函数中可以指定编码类型:InputStreamReader(InputStream
in, Charset cs) 和OutputStreamWriter(OutputStreamout, Charset cs)。
Example1:
创建一个MP3文件。
import java.io.*;
public classIoDemo {
publicstatic void main(String[] args) throwsIOException
{
FileOutputStream f = newFileOutputStream("k.mp3");
f.write(3);
f.close();
}
}
②.复制MP3文件TamasWellsvalderfields.mp3
import java.io.*;
public classIoDemo {
publicstatic void main(String[] args) throwsIOException
{
FileInputStream l = newFileInputStream("TamasWellsvalderfields.mp3");
FileOutputStream f = newFileOutputStream("k.mp3");
int line;
while((line=l.read())!=-1)
{
f.write(line);
}
l.close();
f.close();
}
}
程序简单分析:
FileInputStreaml = newFileInputStream("TamasWellsvalderfields.mp3");语句与mp3文件先关联。然后读取字节然后把它转存入k.mp3文件。
⑶.下面说一说转换问题:
即如何把字符转换为字节流,把字节流转换为字符流。以及其意义。
①.public class OutputStreamWriter
extends Writer
OutputStreamWriter是字符流通向字节流的桥梁:使用指定的 charset 将要向其写入的字符编码为字节。它使用的字符集可以由名称指定或显式给定,否则可能接受平台默认的字符集。
每次调用write() 方法都会针对给定的字符(或字符集)调用编码转换器。在写入基础输出流之前,得到的这些字节会在缓冲区累积。可以指定此缓冲区的大小,不过,默认的缓冲区对多数用途来说已足够大。注意,传递到此 write() 方法的字符是未缓冲的。
为了达到最高效率,可考虑将 OutputStreamWriter 包装到 BufferedWriter 中以避免频繁调用转换器。例如:
Writer out
= newBufferedWriter(new OutputStreamWriter(System.out));
当你构造一个InputStreamReader或OutputStreamWriter时,转换规则定义了16位Unicode和其它平台的特定表示之间的转换。
InputStreamReader从一个数据源读取字节,并自动将其转换成Unicode字符。
如果你特别声明,InputStreamReade会将字节流转换成其它种类的字符流。
OutputStreamWriter将字符的Unicode编码写到输出流,如果你的使用的不是Unicode字符,OutputStreamWriter会将你的字符编码转换成Unicode编码。
对于我们常用的GBK中,英文是占用1个字节,中文是2个
对于UTF-8,英文是1个,中文是3个
对于Unicode,英文中文都是2个
Java的流操作分为字节流和字符流两种
使用其它字符转换
如果你需要从一个非本地(例如,从连接到一个不同类型的机器的网络连接读取)的字符编码读取输入,
你可以象下面这个程序那样,使用显式的字符编码构造
ir=new InputStreamReader(System.in, “8859_1″);
注:如果你通过网络连接读取字符,就应该使用这种形式。
否则,你的程序会总是试图将所读取的字符当作本地表示来进行转换,而这并不总是正确的。ISO
8859-1是映射到ASCII的Latin-1编码模式。
②.public class InputStreamReader
extendsReader
InputStreamReader是字节流通向字符流的桥梁:它使用指定的 charset 读取字节并将其解码为字符。它使用的字符集可以由名称指定或显式给定,否则可能接受平台默认的字符集。
每次调用InputStreamReader 中的一个read() 方法都会导致从基础输入流读取一个或多个字节。要启用从字节到字符的有效转换,可以提前从基础流读取更多的字节,使其超过满足当前读取操作所需的字节。
为了达到最高效率,可要考虑在 BufferedReader 内包装 InputStreamReader。例如:
BufferedReader in
= newBufferedReader(new InputStreamReader(System.in));
System.in
从用户控制台读取数据字节。
③. public class BufferedInputStream
extendsFilterInputStream
作为另一种输入流,BufferedInputStream 为添加了功能,即缓冲输入和支持 mark 和 reset 方法的能力。创建 BufferedInputStream 时即创建了一个内部缓冲区数组。读取或跳过流中的各字节时,必要时可根据所包含的输入流再次填充该内部缓冲区,一次填充多个字节。mark 操作记录输入流中的某个点,reset 操作导致在从所包含的输入流中获取新的字节前,再次读取自最后一次 mark 操作以来所读取的所有字节。
InputStream还有一个子类:过滤器流java.io.FilterInputStream。过滤器流即能把基本流包裹起来,提供更多方便的用法。
FilterInputStream类的构造方法为FilterInputStream(InputStream),在指定的输入流之上,创建一个输入流过滤器。
FilterInputStream的常用的子类如下:
过滤器输入流
流的用途
BufferedInputStream
缓冲区对数据的访问,以提高效率
DataInputStream
从输入流中读取基本数据类型,如int、float、double或者甚至一行文本
LineNumberInputStream
在翻译行结束符的基础上,维护一个计数器,该计数器表明正在读取的是哪一行。
PushbackInputStream
允许把数据字节向后推到流的首部
OutputStream(略)
OutputStream的结构基本和InputStream是一样的。
④. public class BufferedOutputStream
extendsFilterOutputStream
该类实现缓冲的输出流。通过设置这种输出流,应用程序就可以将各个字节写入基础输出流中,而不必为每次字节写入调用基础系统。
Example1:
import java.io.*;
class IODemo
{
publicstatic void main(String[] args) throwsIOException
{
//获取键盘录入对象。
//InputStream in = System.in;
//将字节流对象转成字符流对象,使用转换流。InputStreamReader
//InputStreamReader isr =new InputStreamReader(in);
//为了提高效率,将字符串进行缓冲区技术高效操作。使用BufferedReader
//BufferedReader bufr = newBufferedReader(isr);
//键盘的最常见写法。
BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
// OutputStream out = System.out;
// OutputStreamWriter osw = newOutputStreamWriter(out);
// BufferedWriter bufw = newBufferedWriter(osw);
BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(System.out));
String line = null;
while((line=bufr.readLine())!=null)
{
if("over".equals(line))
break;
bufw.write(line.toUpperCase());
bufw.newLine();
bufw.flush();
}
bufr.close();
}
}
程序说明:
BufferedReaderbufr = new BufferedReader(new InputStreamReader(System.in));
BufferedWriterbufw = new BufferedWriter(new OutputStreamWriter(System.out));
这两句语句使用了联合的的方式。那么为什么使用BufferedWriter和BufferedReader来装转换后的字节流呢??
我们的理解:BufferedWriter和BufferedReader不是和Filewriter和FileReader相关联的嘛?怎么能装人字节流??
说明:当你构造一个InputStreamReader或OutputStreamWriter时,转换规则定义了16位Unicode和其它平台的特定表示之间的转换。
InputStreamReader从一个数据源读取字节,并自动将其转换成Unicode字符。
如果你特别声明,InputStreamReade会将字节流转换成其它种类的字符流。
OutputStreamWriter将字符的Unicode编码写到输出流,如果你的使用的不是Unicode字符,OutputStreamWriter会将你的字符编码转换成Unicode编码。
通过上面这句话:我们发现他们是把字节流转换为Unicode字符,而BufferedWriter和BufferedReader中存储的不是正好是Unicode编码值吗?
故而字符不用去转换,如果你非要转换也未尝不可。而读取的字节文件时必须要把其转换为计算机编码文件。
而我们的字节流缓冲里面存储字节流文件,用于提高效率。
最后做一个总结:
归根结底:讨论的是Unicode编码问题。
每次调用write() 方法都会针对给定的字符(或字符集)调用编码转换器。在写入基础输出流之前,得到的这些字节会在缓冲区累积。调用OutputStreamWriter中的方法write时会自动调用编码转换器。
如何把握字节文件与字符文件的区别:
例如图片文件:把它转换为字符文件,那么计算机认为他还是图片吗?显然不是,它变成了字符。
那么这些字符如何再变成图片呢?
答案就是再把它转换为字节流。