IO OF JAVA
想写好一篇关于JAVA的IO的文章不容易,因为它涉及的东西很多难以写得有深度和有思路。我虽不才但也写。这篇文章有我个人不少的见解,虽然涉足计算机不深但我不想用一大堆这个可能那个可能的字眼,因为这就是我的想法。学习知识就是一个探索过程嘛,我写下了。请鉴赏!!!
JAVA字节类型
JAVA字节类型byte表示的是根据一个二进制的8位数得到的整数,具体来说是指-128至127这256个整数,所以任何一个-128至127的个整数都可以定义为byte类型。从System.out.println((byte)'a')显示结果为97,System.out.println((byte)56.8) 显示结果为56,可以证明这一点。另外可以这样定义byte b='a';的原因是字符常量和整数是互相通用的,不能说明JAVA字节类型byte是指一种ASCII码字符或者其他什么的。
InputStream及其子类的read函数
InputStream子类的read()函数都表示以一个byte一个byte的方式(byte指的是JAVA的字节类型而不是一个内存空间,它是由系统根据当前的输入流得到的,我宁愿这样理解)读当前的输入流。当前的输入流指的是什么呢?先不说,不同的类适用于不同的输入流,这也是InputStream存在那么多子类的原因。怎么读呢?read()函数每读完一个byte就会根据读入的那个byte返回一个相应的int型整数。那么读到的数据放在那里啊?read(byte[ ] b)函数比read()函数功能多一点,会把那个返回的int型整数储存到字节数组b里面去。另外,read(byte[ ] b, int off, int len)除了可以把那个返回的int型整数储存在b里面,还可以指明从哪个byte开始读,读到哪个byte结束。
由于InputStream是一个抽象类,它有唯一一个抽象函数即是read()函数,若继承InputStream就必须复写它。不知讨论继承InputStream是否有意义,但事实上我想讨论的是InputStream及其子类的read函数的返回值。当使用read(byte[ ] b)函数时会把你复写的read()函数的返回值储存到字节数组b里面去。所以返回值最好是-128至127这255个整数,否则256就是周期。比如;read(){return 1;} 与 read(){return 257;}效果一样。
BufferedInputStream的一个特殊对象------标准输入流in
in是BufferedInputStream的一个对象(使用getClass()函数可知道),它不放在IO包里面却是类System的一个静态final常量。
(1),in的read()函数
in的read()函数每要读byte即System.in.read();一次就会要求你从键盘上输入一个键值作为输入流。也就是说in操作的是键值形成的输入流,这时的输入流就是指你在键盘上按入键值。注意当你按下回车键表示输入流的结束时,注意回车键也是一个输入流。使用read(byte[ ] b)函数时,系统会不断的读入输入流,直至用返回值把字节数组b填满,所以读入输入流的次数要么是字节数组b的长度要么是把输入流读完的次数。事实上,尽管read()每次只读一个键值或者read(byte[ ] b)每次读的键值个数不超过b的长度,但你也可以一次输入多个,没有读到的数据会储存到内存去,作为下一次读的输入流。
(2),in的available()函数
这个函数的返回值表示还有多少个byte可以供给read函数读,对于对象in而言即是内存还有多少个键值可以供给read函数读。
(3),in的mark(int)与reset()函数
mark(int) 函数里面的int参数对每个类不是都有作用的,难以把握,或许它没有什么用处吧。将mark(int) 函数(里面的参数尽量小吧)置于输入流的某个位置上,那么当使用reset()函数时,输入流的位置就是刚才的位置。具体这样说:比如read()要读入一个ABCDE的输入流,此时你将mark(int) 语句放在了read()语句的上方,在以后的过程中无论这个ABCDE的输入流是否被读完或者只剩下半截,当你使用reset()语句的时候下面的read()语句要读的输入流就是ABCDE。注意:一,reset()函数上方必须要有mark(int) 函数否则抛出异常。二,reset()函数对应的是在它上方离它最近的mark(int) 函数。
(4),in的close()函数
当你写下close()语句就意味着输入流的结束内存的释放。所以如果继续使用read(),available()函数,reset()函数,skip())函数,这些函数就会抛出异常(不是close()函数抛出异常)。
InputStream的子类
(1)File类
在介绍FileInputStream之前必须先介绍File。File类不是InputStream的子类主要用来找查文件,以便此文件能被其他程序调用操作。File的构造函数有三个,但一般使用File(String pathname)就够了。Pathname是一个表示路径的字符串。在WINDOWS操作系统下可以是这样:“C:/WINDOWS/你要找查的文件”或者“D: /你要的显示文件夹”等等。生成的File对象就是代表你想要你要找查的文件或者代表你想要找查的文件夹。如果File表示的是一个文件夹则可以使用File的list()函数,这个函数会返回一个字符串数组,这个字符串数组里面的元素就是你想要找查文件夹里面的文件名称,最终也将达到找查文件的目的。注意:任何字符串数组来接收list()函数返回的字符串数组,原字符串数组的信息将会丢失,这不是一个覆盖过程。
(2)FileInputStream类
FileInputStream用来指定输入流,即指定用什么文件来read。FileInputStream有三个构造函数,构造函数里面都要求输入参数,这些参数表示文件的路径,就象File对象那样。当启用FileInputStream的read()函数时,在准备read之前系统根据当前的文件得到字节类型的byte并存于内存后再读(一个解码过程),注意所得到的byte不一定能够正确反映当前的文件的的信息。
(3),InputStream的其它子类
InputStream存在那么多子类很大的原因就是为了支持不同的输入流。例如:准输入流in支持的输入流是键盘的键值,FileInputStream支持的输入流是你自己要选的东西(事实上并不能算真正的支持因为不能保证能对它产生正确的读码方式),ByteArrayInputStream支持的输入流是一个byte型的数组等等。这些类的函数功能基本相同。
Reader
(1)JAVA字符类型
JAVA字符类型占用2个字节的空间表示的是各国语言的文字以及其他一些计算机符号,所以可以这样定义:char c='b' ; char c='猫' ; char c='\n' ;。字符常量都会有整数和它对应的,char对应的整数是0至65536。
(2)Reader的read函数
Reader的read函数和InputStream的read()函数道理是一样的只不过单位变成了char,在准备read之前系统会根据当前的输入流得到字符char并存于内存,然后读取这些字符最后将结果存于字符数组中。
(3)InputStream转换为Reader
Reader有个子类InputStreamReader,InputStreamReader的构造函数接受一个InputStream对象,生成的InputStreamReader对象表示输入流由传入的InputStream对象指定,但是对此输入流的解码方式和读取方式就要按Reader的char方式。
(4)Reader的一个子类BufferedReader
BufferedReader的构造函数有两个,但一般使用BufferedReader(Reader)就够了。表示生成的BufferedReader对象所读的输入流是Reader对象所引入的输入流,并且使用BufferedReader对象的read函数时就会建立一个输入流的缓冲,不过缓冲流如果没有结束(指没有被BufferedReader对象读完)就不会再建立。
输入流的缓冲指的就是输入流以某种方式储存于BufferedReader对象所指的内存,所以BufferedReader对象的read函数都是在内存中得到数据,避免了JAVA程序与外部输入流频繁打交道。外部输入流指的是JAVA程序以外的输入流,因此FileReader对象和某些引进外部输入流的InputStreamReader对象转换成BufferedReader对象时才显得非常有必要。
我不知道也推论不了缓冲动作大概以什么方式进行,但可以提供以下现象供大家探讨。1, BufferedReader对象所读的数据是缓冲流,传入的Reader对象所读的数据是缓冲流以外的输入流。 2,BufferedReader对象使用close函数后传入的Reader对象不能使用read函数,Reader对象使用close函数后BufferedReader对象可以使用read函数。
BufferedReader的另外一个构造函数BufferedReader(Reader int)用int来指定每个缓冲流的大小,指定每个缓冲流是包含几个字符的。
Reader与Writer由于是支持16位编码的,所以明显比InputStream与OutputStream实用。为了更好理解,上面才着重介绍了InputStream,不过从实用性方面考虑下面就只着重介绍Writer了。
Writer
(1)CharArrayWriter与StringWriter
这两个类基本上没有什么不同,生成的对象表示一个缓冲区,具体就是说对象所指的内存空间作为写入流的储存空间。写入的动作可以用write函数和 writeTo函数。 比如:w1. write(‘a’), 就表示把字符‘a’写到对象w1中,字符‘a’就是写入流而对象w1就是储存空间。write函数还可以写入字符串和整数,但本质上还是写入字符,因为字符串就是字符数组而整数也可以对应字符,当然,将不能和字符对应的整数写进去是没有意义的。w1. writeTo(w2)则相反,表示把w1储存的内容写入到w2中,w1储存的内容为写入流而对象w2是储存空间。另外,他们的close()和fluse()函数是没有用处的,对象存在写入流就存在。 (2)FileWriter类
使用FileWriter构造函数和FileInputStream构造函数的方法差不多,都是用来引入外部文件的,表示写入流先放在FileWriter对象中,当FileWriter对象使用flush()函数时就把这些写入流清空释放在引入的外部文件中。虽然写入的形式是用字符,但以什么形式储存在外部文件中,由JAVA系统决定。比如:write(‘a’)写入的就是一个字符,但储存在在文本中却是一个字节。
(3)PrintWriter类
PrintWriter的构造函数接受一个对象做参数,表示用生成的PrintWriter对象的print,println,write函数把字符写入那个传进构造函数的对象。print与println提供了丰富的写入方式,要打印的是什么写入的就是后什么。
1,当传进构造函数的对象是CharArrayWriter与StringWriter对象时,直接就把写入流存到参数对象中,PrintWriter对象的flush()函数不起什么作用。
2,当传进构造函数的对象是FileWriter对象时,也直接会把写入流存到FileWriter对象中,PrintWriter对象的flush()函数对FileWriter对象也不起什么作用。但是如果使用PrintWriter对象的flush()函数,就会和使用FileWriter对象的flush()函数一样,会把这些写入FileWriter对象的写入流清空释放在引入的外部文件中。
3,当传进构造函数的对象是OutputStream对象时,就要等到PrintWriter对象使用flush()函数时,才会把写入流储存到OutputStream对象中。
4,PrintWriter的构造函数还可以传入一个boolean的参数,当boolean为true时就表示每当PrintWriter对象使用完一次println函数时就会调用一次flush()函数。而不赋予boolean值或者boolean值为false时就不具备这个功能。
PrintStream的两个特殊对象-----标准输出流out和标准错误输出流err
它们是PrintStream的个对象,不放在IO包里面却是System类的个静态final常量。out表示使用本身的print,println,write函数把写入流储存在自己身上,并且储存在它身上的写入流都会在执行期清空释放在控制台上,显示出来。比如:System.out.println("ABCDE");先把“ABCDE\n”写入对象本身out里面,执行期再清空对象out释放“ABCDE\n”到控制台上。对于err,道理应该和out是一样的,只不过是显示出来的字体颜色不一样。
最后,BufferedWriter类
我觉得这个类没什么用处,因为Writer类可以使用flush()函数随时清空释放对象里面的内容,而且如果不使用flush()函数就不会释放。也就是说,何时写到外部文件完全由自己控制。不大相信这种说法吧,呵呵,我也是,一种偷懒的说法!不想再总结IO了,它的东西太多了不值得一下子学完,不过我相信学习了这些知识当需要学习BufferedWriter类或者IO的其他东西的时候就会很快上手