黑马程序员--Java IO流

来源:互联网 发布:长远汽配软件 编辑:程序博客网 时间:2024/04/28 12:54

---------------------- android培训、java培训、期待与您交流! -----------------------------------------------------------------------------------------------------------------------------------------------

Java中的IO流部分

注意:字节流什么都可以操作,但是字符流只能是操作文本数据。例如:键盘输入和控制台输出。虽然标准输入和输出对应的是System.in和System.out但是,操作的是纯文本的文件,可以转换为字符流来操作。但是如果是MP3,视频等,这些不是纯文本的文件,所以不能转换为字符流来操作。 

IO流用来处理设备之间的数据传输。Java对数据的操作是通过流的方式。
流按操作的数据分为两类:字节流和字符流。
按照流向分为:输入流和输出流。
字节流的抽象基类:InputStream,OutputStream.
字符流的抽象基类:Reader,Writer.
由这四个类派生出来的子类名称都是以其父类名称作为子类名的后缀。
例如 :InputStream 的子类FileInputStream.
            Reader 的子类是FileReader.
据我所知,目前FileOutputStream是直接将内存中的数据写入到了指定的硬盘上。也就是直接和硬盘打交道的流。
另外,多数的Stream,都是将内存中的字节写到目的地。
对于FileWriter中的:
因为流是在内存中的,对于字符流读取的数据会临时存放到流内部中位于内存中的缓冲区,并不是直接写到目的(一般是位于硬盘上的文件,即外存)中。所以会需要刷新。

1.字符流

 

文件操作的流

FileReader和FileWriter

 

 

int

read() 
 
读取单个字符。

int

read(char[]  cbuf)
 
将字符读入数组。

 

假如在该目录下已经存在了一个demo.txt文件,则希望在该已有文件中内容的末尾处继续写入数据。
而不是覆盖这个相同的文件。FileWriter(String fileName, boolean append)
用这个构造函数就可以了。
在window中,\r\n代表的是换行,在linux中\n代表的是换行。
文件的读取:
read()读取单个字符,每次返回int型的数据,是读到的字符,如果返回的是-1.则代表着读取结束。
Read(char []buf)此返回的也是int型的数据,意思是读到的字符的个数。

 

原理是:在外层看来是调用了read(buf),但是内部还是相当于调用read()方法,每次读取单个字符,调用read()方法,将磁盘中的a读到了内存数组中的第一个位置中,同时磁头和指针都向前移动一个单位,则依次类推,如果是数组的指针位于第三个位置的右边,则就不再读取,返回读的个数,打印一下,数组的指针又回到第一个位置处。第二次开始读取,和第一次一样,同时将内存数组中之前每个位置上的数据都覆盖掉了。在第三次的时候,因为只读到了一个字符,所以就返回了,只覆盖掉了数组中的第一个数据,数组中第二次装入的后两个数据没有被覆盖,所以就会出现上述结果的第三行。
当第四次读的时候,因为指针所指没有数据,所以就返回了-1.

 

void

write(char[]  cbuf)
 
写入字符数组。

void

write(char[]  cbuf, int off, int len)
 
写入字符数组的某一部分。

void

write(int  c)
 
写入单个字符。

void

write(String str)
 
写入字符串。

void

write(String str, int off, int len)
 
写入字符串的某一部分。

 

 在输出流中有缓冲区的作用是:因为输出的是字符,所以要查询编码表,所以就要由临时存储数据的缓冲区。

字符缓冲流

BufferedReader和BufferedWriter


BufferedReader与BufferedWriter类 各拥有8192个字符的缓冲区,当读入或写出字符数据时,会先尽量从缓冲区读取。例如BufferedReader在读取文本文件时,会先将字符数据读入缓冲区,而之后若使用read()方法时,会先从缓冲区中进行读取,如果缓冲区数据不足,才会再从档案中读取,藉由缓冲区,可以减少对磁盘的I/O动作,藉以提高程序的效率。

而使用BufferedWriter时,写出的数据并不会先输出至目的地,而是先储存至缓冲区中,如果缓冲区中的数据满了,才会一次对目的地进行写出,例如一个目标档案,藉由缓冲区,可以减少对磁盘的I/O动作,藉以提高程序的效率
都是用来提高流的读写效率的,这是因为他们内部都封装了数组。
这两个类的对象调用close方法关闭后,就不用再调用传入参数(例如FileReader和FileWriter对象)的close方法。因为这是用到类组合的方式,这个流缓冲类的对象的close方法实际上内部调用的就是参数(例如FileReader和FileWriter对象)的close方法。

1. BufferedWriter
中提供了一个newLine()方法。可以跨平台。因为根据不平台的JDK,类如是windows中的则底层是\r\n。如果是linux则是\n.
2. BufferedReader
提供了一次读取一行的方法readLine,方便于对文本数据的获取。当返回为null的时候,表示对到了文件的末尾。但是这个readLine()返回一行数据不包含行终止符(例如回车符)。只返回有效的数据。
拷贝文件就要将输入流和输出流关联起来,在上个文件流中是通过一个字符数组来关联的。
   而在BufferedReader和BufferedWriter中,则是通过一个String字符串来作为中间的桥梁来关联缓冲输入流和缓冲输出

 

 

2.字节流

String中的方法。

byte[]

getBytes() 
 
使用默认的字符集将此 String编码为 byte序列,并将结果存储到一个新的 byte数组中

byte[]

getBytes(String charsetName)
  使用指定的字符集将此 String编码为 byte序列,并将结果存储到一个新的 byte数组中

 

String的一些常用构造函数:

 

String(byte[] bytes)
 
通过使用平台的默认字符集解码指定的 byte数组,构造一个新的 String

String(byte[] bytes,String charsetname)
 
通过使用指定的  charset 解码指定的 byte数组,构造一个新的 String

String(byte[] bytes, int offset, int  length)
 
通过使用平台的默认字符集解码指定的 byte子数组,构造一个新的 String

String(byte[] bytes, int offset, int  length,String charsetname)
 
通过使用指定的  charset 解码指定的 byte子数组,构造一个新的 String

 

String(char[]  value)
 
分配一个新的 String,使其表示字符数组参数中当前包含的字符序列

String(char[]  value, int offset, int count)
 
分配一个新的 String,它包含取自字符数组参数一个子数组的字符

 字节输出流是不用刷新的,因为磁盘上存储就是以字节为单位的,而字节输出流就是直接操作这个基本数据单位的,所以不用刷新。
而字符流会先缓存起来,然后刷新到硬盘上。

文件操作的字节流

FileInputStream和FileOutputStream


字节文件输入流中特有的方法available(),作用是获取输入流关联的文件中字节的个数。

这个available()方法仅适用于小文件的读操作,因为假设读1个2G大小的电影,因为如果用这个avaiable()方法,因为java虚拟机启动默认是64M的空间,虽然虚拟机空间是可以调整的,但是假设物理内存只有1.5G,怎么调整虚拟机也无法达到2G。而程序一定要new出一个2G大小的数组,这就导致内存的崩溃。所以适用于小文件的读操作。
对于缓冲字节流的bufis的int  read()方法和bufos的void write(int b) 方法的一点总结。


为什么read()方法读取的是字节,而返回的却要用int类型呢?
因为read()方法读取的是字节,在计算机中是8位二进制,假如读到的一个字节是
11111111,也就是-1,

假如该read()方法返回的结果是byte的话。因为读到的-1返回了,正好和流结尾符(-1)是相同的。这样就会导致数据还没读完,程序就停止了,不再读取了。

如果将byte提升为int的话,返回的还是-1(无论是int还是byte,表示的逻辑数据总是相同的,不过就是二者在计算机中的表现形式不同而已,一个8位表示,一个32位表示),这时-1的表示形式就变为四个字节了

111111111-111111111-11111111-11111111.

为了让其保证byte的数据,所以就应将

111111111-111111111-11111111-11111111.

000000000-000000000-00000000-11111111

--------------------------------------------------------------------------

000000000-000000000-00000000-11111111

这样就得到了正确的数据。

调用write(int b)方法的时候,实际上是强制转换,是int数据内部的最后8位也就是11111111取走,

read()write()就保证了数据的正确性。

 

int

read() 
 
从输入流中读取数据的下一个字节。

 int

read(byte[]  b)
 
从输入流中读取一定数量的字节,并将其存储在缓冲区数组 b中。

 

 

void

write(byte[]  b)
 
b.length个字节从指定的 byte数组写入此输出流

void

write(byte[]  b, int off, int len)
 
将指定 byte 数组中从偏移量 off 开始的 len个字节写入此输出流

void

write(int  b)
 
将指定的字节写入此输出流。

 

3.标准的输入和输出:

System.in:

 

对应的标准输入设备:键盘

 

public static final InputStreamin

是字节输入流对象。

 

System.out:对应的是标准的输出设备:

控制台。

 

public static final PrintStreamout

PrintStream OutputStream的子类。

System中提供了修改且设置标准输入和输出设备的方法

分别是

标准输入设备的设置:System.setIn(InputStream  in).

标准输入设备的设置:System.setOut(PrintStream out).

4.转换流

InputStreamReader读取转换流。是字节流通向字符流的桥梁,在构造的时候要接受一个字节流。

 

OutputStreamWriter写入转换流。是字符通向字节的桥梁。在构造函数中需要传递一个字节流对象。

 

转换流还具有指定编码表的作用。通常涉及到编码转换的时候会用到转换流。转换流是FileReaderFileWriter的父类,在FileReaderFileWriter封装的过程中将编码表写固定了。

和编码相关的例子:

 

 

字节缓冲流:

1.BufferedInputStream

2.BufferedOutputStream

5.流操作的基本规律:

 

 

 

 6.编码以及编码表

编码:将字符变为字节。String-->byte[] 

1.str.getBytes().

使用平台默认的字符集(GBK)str编码为byte序列。

2.str.getBytes(String charsetName).

    使用指定的字符集将str编码为byte序列。

解码:将字节变为字符。byte[]-àString 

1.  new String(byte[] bytes).

使用平台默认的字符集(GBK)解码bytes数组。

2.  new String(byte[]bytes,String charsetName)

使用指定的字符集解码指定bytes数组。

运行后的结果为:

 

 

用UTF-8编码,用GBK解码,会出现乱码问题。

 

 

 

用ISO8859-1来编码和解码中文字符。

 

如果是编码用ISO8859-1,解码用ISO8859-1解码。似乎是正确的

但是ISO8859-1是欧洲码表,不识别中文,所以编码的时候,根据

拉丁文中哪个像汉字中的哪个,表示为硬盘上的字节数据,而解码

的时候,最后得到的都是这些拉丁文中的字符。

 

用GBK或者UTF-8来编码中文,用ISO8859-1来解码中文,会出现错误。

该情况常见的情景:例如是开发WEB工程。用到的服务器(用来部署web工程)TomcatTomcat用到的编码表是:ISO8859-1

假如是新浪的web网站,网页指定的都是charset=GBK(UTF-8),这样在访问新浪网站的时候,如果提交一个“你好”后,经服务器端的解析,就会得到结果为“????”,从而出错。

 如果在请求方式是Get,那么就只能是在web工程中先用ISO8859-1编码一次,再用GBK(UTF-8)解码一次就好了

运行结果是:

 

用GBK(UTF-8)编码,再用UTF-8(GBK)解码,再用UTF-8(GBK)编码,在用GBK(UTF-8)解码,会出现问题

 

运行结果是:

这个出错的原因是UTF-8编码集合GBK编码集都识别中文。所以不能用ISO8859-1那种方案来解决。

 

1.      ASCII:美国标准信息交换码

用一个字节的7位来表示一个字符。

2.      ISO8859-1:拉丁码表。欧洲码表。

用一个字节的8位来表示一个字符

3.      GB2312:中国的中文编码表。

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

用两个字节表示一个字符。

5.      Unicode:国际标准码,融合了多种文字。

所有字符都用两个字节来表示。(Java中的char类型就是用的Unicode类型的)

6.      UTF-8(Unicode TransfromFormat,8就是最小以8为单位):最多用三个字节来表示一个字符。

一个字节够用就用一个字节,两个字节够用就用两个,三个字节够用就用三个。最多三个字节。

一个byte8位,一个char16位,一个int32位。位是二进制的表示形式。

对于GBK的一些解释:GBK编码中,如果所用到的输入法是半角的,则所有的英文字符只占用1个字节,如果是输入法是全角的,则英文字符也占用两个字节。

例如A,如果是半角,则占用一个字节,如果是全角,则占用两个字节。

我们一般输入法用到的都是半角的。一般的情况下汉字都大于127的。(不太确定)

汉字都是两个字节表示的。为了和半角的英文区别,汉字的两个字节中,每个字节中最高位是1,而英文字符的最高位是0.所以每个汉字的两个字节所代表的整数都是负数,而英文字符所代表的整数是正数。

例如:我A

机内存储的可能是:

我:1000-0101  1100-0111

                -78              -68

A 0010-1111

         43

但如果是全角的A的话,则存储的是规则和汉字字符的存储规则是相同的。

 

7.File文件操作

File是将文件和文件夹封装成对象。
是文件或目录路径名的抽象表示。
可以用File对象的方法对文件或者文件夹的属性信息进行操作。
File类可以将已经存在或是还未存在的文件或是目录创建为File类的对象。
如果文件或是目录尚未存在,要调用createNewFile()方法去在硬盘上创建一个具体硬盘上的file对象
注意:
只要是有构造函数构造了一个File类的对象,那么File类的各种方法都可以调用,
File file=new File("F:\\hello.txt");
如果hello.txt在硬盘上不存在的话,那么file对象照样可以调用File类的各种方法,例如:file.length(),file.isFile();但是这些方法没有作用到具体的hello.txt上,因为这个文件不存在,所以调用的方法返回的都是默认的值。
但是如果hello.txt存在的话,或者hello.txt本来不存在,但是调用了createNewFile方法在硬盘上创建出了hello.txt。那么在调用方法的时候,就会作用hello.txt.

File中的separator字段具有跨平台性。

 

 

File(File parent,String child)
          根据 parent 抽象路径名和 child 路径名字符串创建一个新 File 实例。File(String pathname)
          通过将给定路径名字符串转换为抽象路径名来创建一个新 File 实例。File(String parent,String child)
根据 parent 路径名字符串和 child 路径名字符串创建一个新 File 实例。

 

 

文件对象的操作:

一、文件目录创建

 (1)创建文件:

boolean createNewFile();

在指定位置创建文件,如果该文件存在了,就不再创建,返回false

和输出流不一样,输出流对象已建立就会创建文件,如果有则覆盖。

(2)创建文件夹:mkdir创建单级文件夹。Mkdirs()创建多级文件夹。

二、文件的删除:

(1) boolean delete()  删除失败返回false

(2)void delteOnExit() 在程序退出时删除文件。

.、判断。

1. 判断文件或路径是否存在。Boolean exists().

2. 判断是否文件还是文件夹。Boolean isFile(). Boolean isDirectory().

              在判断是否为文件还是文件夹的时候,一定要判断该File对象是否在硬盘上存在

                   通过exists判断

是否为绝对的路径。IsAbsolute();

四、获取信息。

String     getName()                        返回file对象的名称

String     getPath(),                         返回file对象创建时传入的路径

String     getParent(),                     返回file对象的父路径

String     getAbsolutePath()          返回file对象的绝对路径

File         getAbsoluteFile()             返回file对象的绝对路径封装成的file对象

long       lastModified()                  返回file对象最后被修改的时间

long       length()                             返回file对象所表示的文件的长度

注意:

getPath()返回的结果是:file对象封装的是什么路径,返回的就是什么路径。

getAbsolutgePath()返回的结果是:file对象的绝对路径,也就是封装的路径加上父路径的结果。

getParent()方法,如果file类在构造时,仅是传递了该file的名称,那么返回的结果是null

但是如果名称前有目录的话,则会返回名称之前的目录。

例如:

File f=newFile(“file.txt”)

f.createNewfile();

f.getParent()返回的是null

File f=newFile(“abc\\file.txt”)

f.getParent()返回的结果是abc

 

File file=new File("D:\\abc\\hh");

file.mkdirs();

System.out.println(file.getParent());

System.out.println(file.getAbsolutePath());

System.out.println(file.getPath());

D:\abc

D:\abc\hh

D:\abc\hh

 

 

 

 

File[]   listRoots():列出可用的文件系统的根目录。

 

boolean

endsWith(String suffix)
 
测试此字符串是否以指定的后缀结束。

 

 

String[]

list()  

 

返回一个目录下的所有的文件和目录的名称

 

  File[]

   listFiles()   

返回当前目录下所有文件和目录封装成的File对象。

文件的过滤:

list():

 

String[]

list(FilenameFilterfilter)
 
返回一个字符串数组,这些字符串指定当前目录中满足指定过滤器的文件和目录的名称。

listFiles():

 

File[]

listFiles(FileFilter filter)
 
返回File类型的数组,这些File对象表示当前目录中满足指定过滤器的文件和目录。

File[]

listFiles(FilenameFilterfilter)
 
返回File数组,这些File对象表示当前目录中满足指定过滤器的文件和目录。

 

 

 

 

8.Properties对象

Propertieshashtable的子类。它具备map集合的特点,而且它里边存储的键值对都是字符串。

是集合中和IO技术相结合的集合容器。

该对象的特点:可以用于键值对形式的配置文件。

具有的优点:不仅仅是操作键值对数据(集合对象具备的功能),还能操作位于硬盘上的键值对数据,通过IO流。

集合对象操作的都是存储在内存中的数据。

Properties对象都是通过IO流将数据位于硬盘上的数据读入内存,然后操作,操作完后,可以放回到硬盘上。这就是properties的优点所在。

 

 

9.PrintStream和PrintWriter

这两个流最重要的是它们的打印方法,能将各种基本数据类型保持原样性打印到控制台上。

printStrem的构造函数:

PrintStream(File file)

PrintStream(OutputStream out)

PrintStream(OutputStream out, booleanautoFlush)

autoFlush boolean 变量;如果为 true,则 printlnprintfformat方法将刷新输出缓冲区.

PrintStream(String fileName)

总的来说构造方法接收的参数是:

1.File对象。

2.路径名Strng字符串。

3.字节输出流对象。OutputStream.

 

printWriter的构造函数:

PrintWriter(File file)

 

PrintWriter(OutputStream out)

PrintWriter(OutputStream out, booleanautoFlush)

autoFlush boolean 变量;如果为 true,则 printlnprintfformat方法将刷新输出缓冲区.

 

PrintWriter(Writer out)

PrintWriter(Writer out, boolean autoFlush)

 

PrintWriter(String fileName)

总的来说构造方法接收的参数是:

1.File对象。

2.路径名Strng字符串。

3.字节输出流对象。OutputStream.

4.字符输出流对象。Writer

10.详细的功能性流对象:

1.     ObjcetInputStream

2.    ObjectOutputStream.

功能:将对象序列化。但是要被序列化的该对象所对应的类必须实现Serializable接口

才可以。否则会抛异常。

3.    DataInputStream

4.    DataOutputStream(使用频率较高)

用于操作基本数据类型的数据的流对象。

5.    ByteArrayInputStream
6.    ByteArrayOutputStream

此类实现了一个输出流,其中的数据被写入一个 byte缓冲区数组中。缓冲区会随着数据的不断写入而自动增长。可使用toByteArray() toString()获取数据。

 

7.pipedInputStream

8.pipedOutputStream

 

 

 

9.RandomAccessFile

 

该类不是算是IO体系中子类。
而是直接继承自Object。

但是它是IO包中成员。因为它具备读和写功能。
内部封装了一个数组,而且通过指针对数组的元素进行操作。
可以通过getFilePointer获取指针位置,
同时可以通过seek改变指针的位置。


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

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

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


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

 

10.SequenceInputStream合并流

可以将多个文本等合并为一个

也可以将文件分割为若干个:

 

 

----------------------android培训、java培训、期待与您交流! --------------------------------------------------------------------------------------------------------------------------------------------------

详细请查看:http://edu.csdn.net/heima