您的位置:首页 > 职场人生

黑马程序员---IO流

2013-05-23 18:13 211 查看
------- Java、.NetAndroid培训期待与您交流!-------

一、概念:

(1)用来处理设备(硬盘,控制台,内存)间的数据。

(2)java中对数据的操作都是通过流的方式。

(3)java用于操作流的类都在io包中。

(4)按照流操作的数据的类型不同:分为字节流和字符流。字符流是为了方便中文的操作而来的。

(5)按照流的流向不同分为:输入流,输出流

字符流和字节流(四个抽象基类):

字节流两个基类:InputStream(输入流) OutputStream(输出流)

字符流两个基类:Reader(输入流) Writer(输出流)

二、字符流:

1、结构树:

Reader(read:可以读一个字符或者是读一个字符数组,结束标记返回:-1.要关流)

|--StringReader(操作字符串)

|--CharArrayReader(操作字符数组)(同ByteArrayInputStream,但需要抛异常)

|--PipedReader

|--FilterReader

|--InputStreamReader(常用)

读取转换流:将字节文本转换成字符文本,前提必须接收一个输入字节流对象InputStream

|--FileReader(常用)

|--BufferedReader(特有方法:readLine:返回一行,不返回回车符,打印要用println方法。结束标记返回:null)

|--LineNumberReader(很少)(特有方法:setLineNumber、getLineNumber)

Writer(write:可以写入一个字符或者一个字符数组。写入操作需要刷新flush和关流(关流前会自动刷新一次))

|--StringWriter(操作字符串)

|--CharArrayWriter(操作字符数组)(同ByteArrayOutputStream,但需要抛异常)

|--PrintWriter(常用)

其中有方法:write、print、println、close、flush、append,这些方法都不需要进行处理或抛出异常,方法本身已经覆盖父类方法并且进行了处理。其中只是构造函数和一些特定方法要抛出异常或进行处理。构造函数可以接受的参数有(file对象、字符串路径、字符输出流Writer、字节输出流OutputStream/System.out);

|--FilterWriter

|--OutputStreamWriter(常用)

写入转换流:将字符文本转换成字节文本,前提必须接收一个输出字节流对象OutputStream

|--FileWriter(常用)

|--BufferedWriter(常用)(特有方法:newLine:跨平台换行符)

2、输出(写入)流操作步骤:

1>、FileWriter:

第一步:创建一个FileWriter对象,该对象一被初始化就必须要明确被操作的文件,而且该文件会被创建到指定目录下。如果该目录下已有同名文件,将被覆盖。其实该步就是在明确数据要存放的目的地。
Writer fw = new FileWriter("file.txt");

第二步:调用write方法,将字符串写入到流中。
fw.write("Hello java!");

第三部:刷新流对象中的缓冲区中的数据。
fw.flush();

第四部:关闭流数据,但是关闭前会刷新一次内部的缓冲区中的数据,将数据刷到目的地中。
和flush区别:flush刷新后,流可以继续使用,close刷新后,会将流关闭。
fw.close();

续写方式:传递一个true参数,代表不覆盖已有的文件,并在已有文件的末尾处进行数据续写。
FileWriter fw1 = new FileWriter("file.txt",true);
fw1.write("I think I loved you\r\nthanks..."); //\r\n:Windows系统中的换行符,\n是Linux中的换行符。
fw1.close();

2>、BufferedWriter :

第一步:创建一个字符写入流对象。
FileWriter  fw = new FileWriter("buf.txt");
第二步:为了提高效率加入缓冲技术,将字符写入流对象作为参数传递给缓冲对象的构造函数。
BufferedWriter bufw = new BufferedWriter(fw);

for (int i=0; i<5; i++)
{
bufw.write("abcd"+i);
//该写入缓冲区提供了一个跨平台的换行符:newLine()
bufw.newLine();
bufw.flush(); //记住只要用到缓冲区,就要记得刷新
}
第三部:关闭缓冲区,就是在关闭缓冲区中的流对象。
bufw.close();

3、输入(读取)流操作步骤:

1>、FileReader:

第一步:创建一个文件读取流对象,和指定名称的文件相关联,要保证该文件是已经促在的,如果不存在,会发生异常:FileNotFoundException
Reader r = new FileReader("file.txt");

第二步:调用读取流对象的read方法。read是一次读一个字符,而且会自动往下读。
方式一:读取单个字符
int len = 0;
//r.read()读完后返回-1;
while ((len=r.read())!=-1){
System.out.print((char)len);
}

方式二:通过定义的字符数组进行读取。定义一个字符数组,用于存储读取到的字符,read(char[])返回的是读取到的字符个数。
FileReader fr = new FileReader("SystemDemo.java");
char[] chs = new char[1024];
len =0;
while ((len=fr.read(chs))!=-1)
{
//new String(chs,0,len):将字符数组chs中角标0到num之间(含头不含尾)的字符封装成字符串。
System.out.print(new String(chs,0,len)); //print:注意不用prinln,避免换行操作。
}
第三步:关闭流。
r.close();
fr.close();

2>、BufferedReader:包括异常处理

public static void main(String[] args)
{
FileReader fr = null;
BufferedReader bufr = null;
try{
第一步:创建一个读取流对象和文件相关联。
fr = new FileReader("Demo.java");
第二步:为了提高效率加入缓冲技术,将字符读取流对象作为参数传递给缓冲对象的构造函数。
bufr = new BufferedReader(fr);

String line = null;
while ((line=bufr.readLine())!=null){
System.out.println(line);
}
}
catch (IOException e){
Date d =new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String s = sdf.format(d); //按sdf的格式格式化时间d的格式。
try{
PrintStream ps = new PrintStream("IOExeception.log"); //创建日志文件
ps.println(s); //将异常发生的时间s写入到日志文件中。
System.setOut(ps); //重新分配”标准“输出设备。
}
catch (IOException ex){
throw new RuntimeException("日志文件创建失败");
}
e.printStackTrace(System.out); //就异常信息写入到日志文件中
}
finally{
try{
if(bufr!=null)
bufr.close();
}
catch (IOException e){
throw new RuntimeException("读取流关闭失败");
}
}
}

三、字节流:

1、结构树:

InputStream(read:可以读一个字节或者是读一个字节数组,结束标记返回:-1。available:返回读取文本的字节个数。要关流)

|--ByteArrayInputStream(操作字节数组输入流)

在构造的时候,需要接收数据源,而且数据源是一个字节数组。因为该对象操作的是数组,并没有使用系统资源,所以不用进行close关闭。

|--ObjectInputStream(一般)

方法:readObject(Object obj):读取文件中对象的内容(或值)来直接使用。一次读一个对象,其他方法和ObjectOutputStream都互补。

|--PipedInputStream(一般,比较特殊的流对象)

管道流:输入输出流可以直接进行连接,结合线程来使用。

问题:因为出现多线程,在read方法(阻塞方法,有数据才读,否则等待)执行时可能发生死锁。

构造方法:PipedInputStream(PipedOutputStream src):创建输入流连接到src(输出流)

PipedInputStream():创建尚未连接的管道输入流。

方法:connect(PipedOutputStream src):连接到输出流src,仅用于为创建连接的构造函数。

|--StringBufferInputStream

|--SequenceInputStream(一般)

SequenceInputStream(Enumeration<? extends InputStream> en)

枚举Enumeration是Vector特有的方法,已被ArrayList替换。将多个输入流整合成一个输入流来操作。

构造方法可以接收一个枚举类型的引用,该引用所指向的内容都是InputStream或其子类类型的对象。

|--AudioInputStream

|--FileInputStream(常用)

|--FilterInputStream

|--LineNumberInputStream(前提必须接收一个输入字节流对象InputStream)

|--BufferedInputStream(常用)(同BufferedReader,没有readLine方法)

|--DataInputStream(常用)

专门用于操作基本数据类型的输入流,读取文件的内容全部是二进制表现形式,再经过解码变成我们认识的文字。

方法:readUTF():该方法调用的是UTF-8的修改版,一个汉字占4个字节。

注:read方法和DataOutputStream中的write方法必须匹配,否者读出的数据与来输入数据不一样如writeInt方法必须匹配readInt方法。

OutputStream(write:可以写入一个字节或者一个字节数组。关流)

|--ByteArrayOutputStream(操作字节数组输出流)

在构造的时候,不用定义数据目的,因为该对象已经在内部封装了可变长度的字节数组。

因为该对象操作的是数组,并没有使用系统资源,所以不用进行close关闭。

特有方法:writeTo(OutputStream out):抛IOException,将字节数组输出流的全部内容写入到指定的输出流参数中(文件)。

|--ObjectOutputStream(一般)

几个特别的方法:writeInt:写入文件中的是32位数据,即返回int型,不是字节,操作基本数据类型。

write:写入文件中的是数据的最低8位,即字节。writeLong、writeChar、writeFloat等同理。

writeObject(Object obj):写入文件中的是一个对象包括对象中的数据。

注:被操作的对象需要实现序列化接口Serializable(标记接口:接口中没有方法可覆盖),系统会自定给该类成员变量加上一个UID标记作为序列号,保证其唯一性。static final long serialVersionUID = 51L;

该对象中只有一般成员变量才能被序列化(即存储在堆内存中),放在方法区中的方法和static修饰的成员变量不能被序列化。另外被transient修饰的一般成员变量也不能被序列化。

|--PipedOutputStream(一般,比较特殊的流对象)

|--FileOutputStream(常用)

|--FilterOutputStream

|--BufferedOutputStream(常用)(同BufferedWriter,没有方法newLine)

|--PrintStream(常用)

其中有方法:write、print、println、close、flush、append,这些方法都不需要进行处理或抛出异常,方法本身已经覆盖父类方法并且进行了处理。其中只是构造函数和一些特定方法要抛出异常或进行处理。构造函数可以接受的参数有(file对象、字符串路径、字节输出流OutputStream/System.out);

|--DataOutputStream(常用)

专门用于操作基本数据类型的输出流,写入文件的全部是二进制表现形式,文件再经过指定编码表解码变成一些对应的字符显示在文件中(一般都是乱码)。

方法:writeUTF():该方法调用的是UTF-8的修改版,一个汉字占4个字节。

字节流和字符流的操作步骤基本一样。

2、问题:字节流的读一个字节的read方法为什么返回值类型不是byte,而是int。

答:因为有可能会读到连续8个二进制1的情况,8个二进制1对应的十进制是-1。那么就出现数据还没有读完,就结束的情况。因为我们判断读取结束是通过结尾标记-1来确定的。所以,为了避免这种情况将读到的字节进行int类型的提升。并在保留原字节数据的情况前面了补了24个0(通过将读取的原字节数据&255来实现),变成了int类型的数值。

而在写入数据时,只写该int类型数据的最低8位。

四、特殊流:RandomAccessFile(特殊类,重点掌握)

特点:该类不是IO体系中子类,而是直接继承自Object,但是它是IO包中成员,因为它具备读和写功能。内部封装了一个数组,而且通过指针对数组的元素进行操作。getFilePointer获取指针位置,seek改变指针的位置。

RandomAccessFile(File file, String mode)

RandomAccessFile(String name, String mode)

通过构造函数可以看出,该类只能操作文件。而且操作文件还有模式(mode):只读r,读写rw,rws和rwd。

模式只读 r:不会创建文件,会去读取一个已存在文件,如果该文件不存在,则会出现异常。

模式 rw:操作的文件不存在,会自动创建,如果存则不会覆盖。

总结该类三大特点:

1>模式(mode):r、rw、rws、rwd。

2>将读和写操作封装进同一个类中,且可直接读取和写入基本数据类型。writeLong,readLong等。

3>seek(int pointer):同个该方法可以改编指针的位置,实现对指定位置数据的读取和写入操作,但是要对数据分段,即数据要有规律。

如写入:李斯,57

王五,23

张晓轩,32

前2者是有规律(字段大小一样)的数据写入,但是最后一个与前面的不存在规律性。

用法:通过seek设置可以实现多个线程对数据的分段存储而互不干扰(针对同一个文件)。下载文件,就是应用的分段存储原理。

五、字符编码:

编码表:将各个国家的文字用数字来表示,并一一对应,形成一张表。

编码:字符串变成字节数组。OutputStreamWriter

String-->byte[]; str.getBytes(charsetName); charsetName:字符集,即编码表

解码:字节数组变成字符串。InputStreamReader

byte[] -->String: new String(byte[],charsetName);

常见码表:

ASCII:美国标准信息交换码。用一个字节的7位可以表示。

ISO8859-1:拉丁码表,欧洲码表。用一个字节的8位表示。

GB2312:中国的中文编码表。

GBK:中国的中文编码表升级,融合了更多的中文文字符号。

Unicode:国际标准码,融合了多种文字。所有文字都用两个字节来表示,Java语言使用的就是unicode

UTF-8:最多用三个字节来表示一个字符,最少用一个字节表示字符。如ASCII码表中的字符。。

注:GBK或UTF-8和ISO8859-1进行可以进行护编解码,但GBK和UTF-8两者不可逆编解码。如:用记事本写“联通”(编码为GBK),记事本会自动调用UTF-8来解码,出现乱码。

六、IO流需求:

1、流操作的基本规律:通过三个明确来完成(重点)。

1>明确源和目的。

源 :输入流:InputStream Reader

目的:输出流:OutputStream Writer。

2>操作的数据是否是纯文本?

是:字符流。

否:字节流。

注:前两用来明确体系。

3>当体系明确后,再明确要使用哪个具体的对象。

通过设备来进行区分:

源设备 :内存,硬盘,键盘。

目的设备:内存,硬盘,控制台。

注意:有时候源和目的可能只有其一。

2、需求

2.1:将一个图片文件中数据存储到另一个文件中,复制文件。要按照以上格式完成三个明确。

分析:这个需求中有源和目的都存在。

1>源:InputStream Reader。

2>因为图片不是纯文本,选择 InputStream。

这样体系就明确了。

3>明确需要使用的对象。

明确设备:硬盘,一张图片。InputStream体系中可以操作文件的对象是 FileInputStream

是否需要提高效率:是!加入InputStream体系中缓冲区 BufferedInputStream.

FileInputStream fis = new FileInputStream("sex.jpg");

BufferedInputStream bufis = new BufferedInputStream(fis);

1>目的:OutputStream Writer

2>不是纯文本!OutputStream。

3>设备:硬盘,一张图片。OutputStream体系中可以操作文件的对象 FileOutputStream。

是否需要提高效率:是!加入OutputStream体系中缓冲区 BufferedOutputStream

FileOutputStream fos = new FileOutputStream。("sex_copy.gif");

BufferedOutputStream bufos = new BufferedOutputStream(fos);

2.2:将键盘录入的数据保存到一个文件中。要按照以上格式完成三个明确。

分析:这个需求中有源和目的都存在。

1>源:InputStream Reader

2>是纯文本:Reader

3>设备:键盘,对应的对象是 System.in.

疑问:不是选择Reader吗?System.in对应的不是字节流吗?

答案:为了操作键盘的文本数据方便,转成字符流按照字符串操作是最方便的,即将键盘录入的字节流数据转换成字符流来操作。使用了Reader体系中转换流:InputStreamReader

InputStreamReader isr = new InputStreamReader(System.in);

需要提高效率吗?需要! BufferedReader

BufferedReader bufr = new BufferedReader(isr);

1>目的:OutputStream Writer

2>是存文本!Writer。

3>设备:硬盘,一个文件。使用 FileWriter。

FileWriter fw = new FileWriter("GBK.txt");

需要提高效率吗?需要! BufferedWriter

BufferedWriter bufw = new BufferedWriter(fw);

***************************

扩展,想要把录入的数据按照指定的编码表(utf-8),将数据存到文件中。

1>目的:OutputStream Writer

2>是存文本!Writer。

3>设备:硬盘,一个文件。使用 FileWriter,但FileWriter使用的默认编码表是GBK,怎么解决?

答案:指定的编码表只有转换流可以指定,所以要使用的对象是 OutputStreamWriter。该转换流对象要接收一个字节输出流,而且还可以操作的文件的字节输出流按指定的编码表输出。使用FileOutputStream创建文件。

OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("UTF-8.txt"),"UTF-8");

需要高效吗?需要。 BufferedWriter

BufferedWriter bufw = new BufferedWriter(osw);

注:记住转换流什么使用,字符和字节之间的桥梁,通常,涉及到字符编码转换时,需要用到转换流。

2.3:将一个文本数据打印在控制台上。要按照以上格式完成三个明确。

分析:这个需求中有源和目的都存在。

1>源:InputStream Reader

2>是纯文本!Reader

3>设备:硬盘,文本文件。使用 FileReader.

需要提高效率吗?需要! BufferedReader

FileReader fr = new FileReader("Demo.txt");

BufferedReader bufr = new BufferedReader(fr);

1>目的:OutputStream Writer

2>是纯文本!Writer。

3>设备:控制台,对应的对象是System.out。

为了方便操作,其中可能含有中文字等,所以选择Writer中的转换流 OutputStreanWriter

OutputStreanWriter osw = new OutputStreanWriter(System.out);

需要提高效率吗?需要! BufferedWriter

BufferedWriter bufw = new BufferedWriter(osw);

七、示例:

示例1:复制一个图片。

通过缓冲区字节数组临时存储,然后在复制,很快。

public static void copy throws IOException
{
FileInputStream fis = new FileInputStream("m.jpg");
FileOutputStream fos = new FileOutputStream("m_copy.gif");

byte[] by = new byte[1024];
int len = 0; //字节数组的有效长度
while ((len=fis.read(by))!=-1)
{
fos.write(by,0,len);
}
fis.close();
fos.close();
}

示例2:将键盘录入的数据保存到一个文件中。

BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
扩展:按照指定的编码表将录入数据存入文件中,OutputStreamWriter具备此功能
BufferedWriter bufw =
new BufferedWriter(new OutputStreamWriter(new FileOutputStream("UTF-8.txt"),"UTF-8"));

String line = null;
while ((line=bufr.readLine())!=null)
{
if ("over".equals(line)) //输入over为结束符
break;

bufw.write(line); //将键盘录入的数据写入输出流的缓冲区中
bufw.newLine();
bufw.flush(); //刷新缓冲区
}
bufr.close();
bufw.close();

总结:前面提到java中对数据的操作都是通过流的方式,所以这章非常重要,实际开发中都离不开都数据的处理。掌握字节流和字符流的操作步骤,他们之间的转换桥梁:InputStreamReader和OutputStreamWriter。以及字节流和字符流中一些特有的方法,如BufferedReader的特有方法readLine:返回一行,不返回回车符,打印要用println方法,结束标记返回:null。IO流涉及计到的是从内存、硬盘、键盘到内存、硬盘、控制台9种类型操作,一般从硬盘、键盘到硬盘、控制台最寻常。

其次注意RandomAccessFile这个特殊类,是唯一一个同时具备输入输出流的对象的。以及其三大特点,及其应用领域。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: