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

黑马程序员-->Java基础-->IO流

2014-11-04 23:15 357 查看
-------android培训java培训、期待与您交流!
----------

一、概述

1、IO流是用来处理设备之间的数据传输,Java对数据的操作是通过流的方式。

流:可以理解数据的流动,就是一个数据流。IO流最终要以对象来体现,Java用于操作流的对象都在IO包中。

2、流按操作数据分为两种:字节流与字符流。 

流按流向分为:输入流(读),输出流(写)。

字节流:处理字节数据的流对象。设备上的数据无论是图片或者DVD,文字,它们都以二进制存储的。

二进制的最终都是以一个8位为数据单元进行体现,所以计算机中的最小数据单元就是字节。

意味着,字节流可以处理设备上的所有数据,所以字节流一样可以处理字符数据。

那么为什么要有字符流呢?因为每个国家的字符都不一样,所以涉及到了字符编码问题,

那么GBK编码的中文用unicode编码解析是有问题的,所以需要获取中文字节数据的同时指定的编码表才可以解析正确数据。

为了方便于文字的解析,所以将字节流和编码表封装成对象,这个对象就是字符流。只要操作字符数据,优先考虑使用字符流体系。

字节流和字符流的区别:

(1)字符流处理的单元为2个字节的Unicode字符,分别操作字符、字符数组或字符串,而字节流处理处理的单元为1个字节,

操作字节和字节数组。所以字符流是由java虚拟机将字节转化为2个字节的Unicode字符为单位的字符而成的,所以它对多国语言支持性比较好;

(2)如果是音频文件、图片、歌曲,就用字节流好点,如果是关系的到中文文本的,用字符流好点。所有文件的存储都是字节的存储,

在磁盘上保留的并不是文件的字符而是先把字符编码成字节,再存储这些字节到磁盘。字节流可用于任何类型的对象,包括二进制对象,

而字符流只能处理字符或者字符串。字节流提供了可以处理任何类型的IO操作的功能,但它不能直接处理Unicode字符,而字符流就可以。

注意:流的操作只有两种:读和写。

3、流的体系因为功能不同,但是有共性内容,不断抽取,形成继承体系。该体系一共有四个基类,而且都是抽象类。

字节流的抽象基类:InputStream ,OutputStream。

字符流的抽象基类:Reader , Writer。

注:由这四个类派生出来的子类名称都是以其父类名作为子类名的后缀。

如:InputStream的子类FileInputStream。

如:Reader的子类FileReader。

 

二、字符流

1、FileWriter

既然IO流是用于操作数据的,那么数据的最常见体现形式是:文件。

需求:在磁盘上,创建一个文件并写入一些文字数据,找到一个专门用于操作文件的Writer子类对象,

FileWriter后缀名是父类名,前缀名是该流对象的功能。

代码如下:



总结:1、创建一个字符输出流对象,用于操作文件。该对象一建立,就必须明确数据存储位置,是一个文件。

 2、对象产生后,会在堆内存中有一个实体,同时也调用了系统底层资源,在指定的位置创建了一个存储数据的文件。

 3、如果指定位置,出现了同名文件,文件会被覆盖。

 4、调用Writer类中的write方法写入字符串。字符串并未直接写入到目的地中,而是写入到了流中,(其实是写入到内存缓冲区中)。

 5、fw.flush(); // 刷新缓冲区,将缓冲区中的数据刷到目的地文件中。

 6、fw.close(); // 关闭流,其实关闭的就是java调用的系统底层资源。在关闭前,会先刷新该流。

 

2、IO异常的处理方式和文件的续写

代码演示:



 

3、FileReader

读取数据第一种方式:使用Reader体系,读取一个文本文件中的数据。返回 -1 ,标志读到结尾。

代码示例:



 

读取数据第二种方式:通过字符数组进行读取,较为高效,自定义缓冲区buffer。



 

4、拷贝文本文件

例子:将C盘一个文本文件复制到D盘

复制原理:

其实就是将C盘下的文件数据存储到D盘的一个文件中。

步骤:

1、在D盘创建一个文件。用于存储C盘文件中的数据。

2、定义读取流和C盘文件关联。

3、通过不断的读写完成数据储存。

4、关闭资源

代码如下:



 

5、字符流缓冲区

缓冲区的出现提高了对数据的读写效率

对应类:BufferedWriter BufferedReader

缓冲区要结合流才可以使用,在流的基础上对流的功能进行了增强

(1)字符写入缓冲区(BufferedWriter)代码演示:



该缓冲区提供了一个跨平台的换行符newLine()。

 

(2)字符读取流缓冲区(BufferedReader)代码演示:



 

(3)通过缓冲区复制一个.java文件



注意:readLine方法返回的时候只返回回车符之前的数据内容,并不返回回车符。

readLine方法的原理:

无论是读一行或者读取多个字符,其实最终都是在硬盘上一个一个读取,所以最终使用的还是read方法读一个的方法。

三、装饰设计模式

1、当想要对已有的类进行功能增强时,可以定义类,将已有的类对象传入,基于已有的功能来加强功能,那么自定义的该类称为装饰类。

装饰类通常会通过构造方法接收被装饰的类的对象,并基于被装饰的对象的功能,提供更强的功能。

代码示例:



 

2、装饰模式和继承的区别



 

3、BufferedReader的子类LineNumberReader(装饰类)

代码示例:



四、字节流

1、字节流的读写操作以及图片拷贝,代码如下:







 

2、通过缓冲区演示MP3的复制,代码如下:



 

3、键盘录入

代码示例:通过键盘录取一行数据并打印其大写



总结:键盘录入对应的是字节输入流(InputStream)对象;需要加入临时容器StringBuilder类对象,操作数据;

编程思想:只要没有读到数据末尾,即判断到结束标记,就一直往容器里面增加数据;如果读到结束标记,

做出相应的动作,包括判断容器内容是不是“over”,是就结束不是就变大写字母打印出来并且删除本次容器中数据。

其实,上面代码演示就是读一行的原理,即readLine方法。

五、转换流

字节流通向字符流的桥梁 InputStreamReader

字符流通向字节流的桥梁 OutputStreamWriter

1、读取转换流代码示例:



 

2、写入转换流代码示例(内含读取转换流代码):



 

3、流操作的基本规律:

对象很多,不知道用哪个,通过两个明确来完成

(1)明确源和目的

源:输入流  InputStream Reader

目的:输出流  outputstream writer

 

(2)操作的数据是否是纯文本

是:字符流

不是:字节流

 

(3)当体系明确后,再明确要具体使用哪个对象,通过设备来进行区分:

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

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

举例:



 

4、使用转换流的真正好处--可以指定编码表



 

5、改变标准输入输出设备(在不修改原有的源和目的设备代码上调整)



 

六、File类

File类:文件或目录的抽象表现形式;用来将文件或者文件夹封装成对象;

方便对文件与文件夹的属性信息进行操作;File对象可以作为参数传递给流的构造函数。

1、创建File对象的三种方式:

(1)File f1 = new File("c:\\abc\\a.txt");

(2)File f2 = new File("c:\\abc","b.txt");

(3)File d = new File("c:\\abc");

     File f3 = new File(d,"c.txt");

File.separator是具有跨平台性的分隔符。如:

File f = new File("c:"+File.separator+"abc"+File.separator+"zzz"+File.separator+"a.txt");

2、File类常见方法:

(1)创建:

 boolean createNewFile():在指定位置创建文件,如果该文件已经存在,则不创建,返回false.

     和输出流不一样,输出流对象一建立就创建文件,而且文件已经存在,回覆盖。

(2)删除

 boolean delete():删除失败返回false.如果文件正在被使用,则删除不了返回false.

 void deleteOnExit():在程序退出时删除指定文件。

(3)判断

 boolean exists():文件或者文件夹是否存在

 isFile():是否是文件

 isDirectory():是否是文件夹

 isHidden():是否是隐藏文件

 isAbsolute():是否是绝对路径

(4)获取信息

 getName():获取文件/文件夹名字

 getPath():获取文件的相对路径

 getParent():获取文件的父目录

 getAbsolutePath():获取绝对路径

 lastModified():最后一次修改文件的时间

 length():获取文件大小

 

代码示例:





 

3、File对象(功能)的文件列表

获取系统根目录和目录内容,代码如下:



 

4、过滤文件



 

5、递归



 

(1)用递归删除带内容的文件目录。代码如下:



 

(2)使用递归,创建java文件列表

思路:a、将指定目录进行递归(看指定目录下的是否有子目录,且子目录中是否有java文件);

 b、获取递归过程中所有java文件;

 c、将所有java文件存储在一个集合中 ;

 d、将集合中的java文件路径名取出存放在一个文本中。

代码如下:





 

七、Properties集合

1、Properties是hashtable的子类,也就是说它具备map集合的特点。

2、Properties集合特点:

(1)集合中的键和值都是字符串对象。

(2)集合中的数据可保存在流中,或从流中获取数据。

3、通常该集合用于操作以键值对形式存在的配置文件。

代码示例:





 

八、IO流中的其他流

1、打印流(PrintStream,PrintWriter)

该流提供了打印方法,可以将各种数据类型的数据原样打印。

PrintStream(字节打印流)

构造函数可以接受的参数类型

a、file对象 File

b、字符串路径  String    

c、字节输出流   OutputStream

PrintWriter(字符打印流)

a、file对象 File

b、字符串路径  String    

c、字节输出流   OutputStream

d、字符输出流 Writer

代码示例:



 

2、序列流(SequenceInputStream)

表示其他输入流的逻辑串联,它从输入流的有序集合开始读取,直到到达文件末尾,接着从第二个输入流读取,

以此类推,直到到达包含的最后一个输入流的文件末尾为止;主要是对多个流进行合并。

多个读取流合并为一个读取流,代码示例:



 

3、切割流(split)

需求:切割一张照片并且合并它,代码如下:







 

4、对象的序列化(结合字节流)

(1)对象的序列化,对象中的数据不做改变,永久保存在文件中。

(2)操作对象流:ObjectInputStream    ObjectOutputStream

(3)Serializable :没有方法的接口,也称为标记接口。是对类中的成员进行序列化,可以把堆内存中的数据序列化,静态不能实现序列化。

(4)transient:被transient修饰的类中的非静态成员不能被序列化,依旧存在于堆内存中。

(5)在类中定义UID的作用:给类定义固定标识,为了方便序列化。代码:public static final long serialVersionUID=42L

代码演示:





 

5、管道流

代码示例:





 

6、RandomAccessFile类

该类不算是io体系中的子类,而是直接继承自Object,但是它是io包中的成员,因为它具备读和写的功能;

内部封装了一个数组,而且通过指针对数组的元素进行操作,可以通过getFilePointer获取指针位置,同时可以通过seek方法改变只针对的位置;

其实完成读写的原理就是内部封装了字节输入流和输出流。

通过构造函数可以看出,该类只能操作文件,而且操作文件还有模式,模式的四种取值:r、rw、rwd、rws;

如果模式为rw,操作的文件不存在,会自动创建。如果存在则不会覆盖。如果模式为只读r,不会创建文件。会去读取一个已经存在的文件,如果该文件不存在,则会出现异常。

应用方面:多线程分段下载文件

代码示例:





 

7、操作基本数据流(DataInputStream, DataoutputStream)

与字节流相结合,可以用于操作基本数据类型的数据流对象。

代码演示:



 

8、操作字节数组的流(ByteArrayInputStream  ByteArrayOutputStream )

ByteArrayInputStream:在构造的时候,需要接受数据源,而且数据源是一个字节数组。

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

因为这两个流对象都操作的是数组,并没有使用系统资源,所以不用进行close关闭,也不必抛异常。

在流操作规律讲解时:

 源设备:键盘 system.in ;硬盘 FileStream ;内存 ArrayStream ;

 目的设备:控制台 system.out ;硬盘 FileStream ;内存 ArrayStream

用流的读写思想来操作数组。

代码演示:



 

九、字符编码

1、编码表的由来:

计算机只能识别二进制数据,早期由来是电信号,为了方便应用计算机,让它可以识别各个国家的文字,

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

 

2、常见编码表:

ASSCII:美国标准信息交换码,用一个字节的7位来表示;

ISO8859-1:拉丁码表,欧洲码表,用一个子节的8位来表示;

GB2312:中国的中文编码表,用两个字节表示一个字符;

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

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

UTF-8:最多使用3个字节来表示一个字符

3、编码和解码:

编码:字符串变成字节数组;String---Byte[]  str.getBytes();

 

解码:字节数组变成字符串。Byte[]---String  new String(byte[]);

 

代码示例:



注意:当编码和解码对应的编码表不同时,易造成乱码现象。

如果编码是GBK,解码是ISO8859-1,会出现乱码,解决方法:再将乱码字符串按照ISO8859-1编码,最后再按GBK解码即可。

如果是编码是GBK,解码是UTF-8,如果按UTF-8再编码再按GBK解码还是乱码,无法解决问题。

因为GBK和UTF-8都是识别中文,而ISO8859-1不识别中文,都是按一个字节去编码解码。

 

 

-------android培训java培训、期待与您交流!
----------
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  IO流