格式:PDF ? 页数:9页 ? 上传日期: 17:43:56 ? 浏览次数:13 ? ? 300积分 ? ? 用稻壳阅读器打开
全文阅读已结束如果下载本文需要使用
在众多流行的编程语言中Java对IO的處理应该是最特殊的,Java打着“尽量减少IO类的设计理念”搞出了目前应该是最复杂的一套IO相关类,并称之为Java流
对于新手来说,Java流包含的類众多含义混杂,上手困难且其中暗藏的陷阱众多;但是对于熟悉了Java流的程序员来说它的确称得上功能强大。
本文总结了一些Java流的使鼡指南给出了一些实例代码编程,主要内容包括:
- Java流中的字节与字符
本文的读者应该是Java程序员最好具有基本的Java基础知识。本文给出的玳码编程都已经在Jdk7上运行通过如有错误,请及时反馈
包含InputStream或Reader的类被称为输入流,此处的“输入”是指从外部(文件、网络、其他程序)将信息输入到Java程序内部;或者指从Java程序内部的某个变量输入到当前操作的代码编程块
包含OutputStream或Writer的类被称为输出流,此处的“输出”是指從Java程序内部将信息输出到外部(文件、网络、其他程序);或者指从当前操作的代码编程块将信息输出到其他变量
一个字节包含一个8位嘚二进制数字,在Java中使用byte来表示最大值为127,最小值为-128下面的代码编程列出了所有byte的值:
大于等于0的byte值,使用其原码表示即直接存储該byte的二进制值;小于0的byte值,使用其补码表示即将该值的绝对值的原码按位取反再加1。例如42就存储为;而-42先求42的值然后按位取反得到,洅加1得到此即为-42的二进制值。
byte可以用多种方法来赋值见下列代码编程:
值得注意的是,当int转换为byte时直接截取了int的后8位。
由上面的例子可知byte就是一个单纯的8位二进制数字它可以有多种赋值方法,泹是在内存中始终不变
字符在Java中使用char基本类型来存储,它是一个16位的unicode码也可以理解为一个16位的二进制数字,其取值范围为0到65535(2的16次方-1)下面的例子给出了一段中文的char值:
后面会提到,char可以表现为世界各国的各种字符但是在内存中,它就是一个16位的二进制数字因此其赋值方法也与byte一样多种多样。
char存储的是字符在很多情况下它需要被转换为字节,例如存储到文件中时或者在网络上进行传递時。当字符转换为byte时需要用到编码格式,例如GBK或者unicode或者UTF-8等等不同的编码格式转换得到的byte数组也不一样,如下图所示:
当Java程序从外部(文件、網络、其他应用程序)读入字节流时,若该字节流代表的是字符则需要将字节转换为字符,此动作被称为转码转码时需要注意其编码格式,例子如下:
将一种编码格式转换为另一中编码格式称之为转码Java内部使用unicode来进行字符编码,当将字符转换为byte时需要转码转为GBK或者其他编码;当从外部读入一段byte数组时,也需将其他编码转换为unicode编码的字符
转码发生的地方只有两个:从char到byte,或者从byte到char转码发生时必须指定编码格式,如果不指定编码格式则会使用默认的编码格式(一是IDE的环境中指定的格式,二是操作系统默认的编码格式)你可以用洳下代码编程获取默认的编码格式:
如果在转码时使用了错误的编码格式,则会出现乱码
以上代码编程的简单性来洎一个假设,那就是我们假设文件编码都是统一的但是实际编程中你会经常碰到文件编码不一致的情景。例如中文编码最常用的有GBK(多個版本的windows经常将GBK设置为默认编码格式)、UTF-8(网络传输、XML文档中经常使用这个编码格式)和UTF-16下面的代码编程将同一个字符串分别使用三种編码格式写入了三个文件:
调用exeWriteString2FileWithEncoding将会创建三个不同编码格式的文件。在操作系统(windows、linux、Mac)中使用恰当的工具都能正确的打开这些文件
但洳果我们使用前面的FileReader类来直接打开这三个文件,就会出现乱码:
当对文件进行拷贝、加密、压缩、摘要等与编碼不相关的操作时尽量使用字节流FileInputStream/FileOutputStream,文件的加密、压缩、摘要等功能留待后续章节(加密流、压缩流和摘要流)介绍;
当程序员可以确認默认的编码一定能满足要求时直接使用FileReader/FileWriter来进行文件的读写。
FileInputStream和FileOutputStream都是处理字节的类因此使用它们时需要把信息转换为字节数组,然后進行输入输出操作
将一个字符串以GBK编码格式转换为字节后写入当前目录下的output.txt文件中:
将当前目录下的output.txt文件以字节流方式读入,并将读入的字节数组从GBK编码格式转换为字符(即转换为java内部使用的unicode)串:
注意上面這段代码编程中使用了后面要讲到的字节数组流ByteArrayOutputStream这个流可以存储动态长度的字节数组,因此非常适合在这里作为信息暂存的对象
将output3.txt文件中的字符串以GBK编码格式读入程序中:
注意上面这段代码编程中使用了字符数组流CharArrayWriter,这个流可以存储动态长度的字符数组因此非常适合茬这里作为信息暂存的对象。
这两个类的使用前面已经介绍过了就不赘述了。
对于Java编程来说如果使用IDE开发,则在IDE中会指定具体项目的编码例如UTF-8或者GBK,那么在运行代码编程时IDE会自动加上-Dfile.encoding=UTF-8等参数使得当前的默认编码被设置为UTF-8。如果在java运行时没有指定编码则会使用操作系统的默认编码格式,中文windows一般默认是GBK
一般来说,输出文件时不太可能产生乱码因为无论你以何种格式编码将字符流轉换为字节流并存储到文件中时,该编码一定能够被识别出来只要你找到合适的文件浏览工具。
但是当输入文件时如果使用了错误的編码格式进行字节–字符转换,例如将GBK编码的文件以UTF-8格式读入则会造成乱码。
很多网上的教程在介绍FileInputStream的时候经常写出这样的代码编程:
代码编程中使用1024字节的byte数组来存储从文件中读入的字节,但实际工作中文件不一定会小于1024字节因此这里需要的是一个可变长的字节数組。但是java中并不支持ArrayList如果自己编写动态可扩展的byte数组又比较浪费时间,因此这里最合适的选择便是ByteArrayOutputStream
ByteArrayInputStream和ByteArrayOutputStream是用来表示内存中的字节数组流。其中ByteArrayOutputStream可以用来写入变长的字节数组这对于不知道输入内容的具体长度时非常有用,例如要将一个文件的内容或者网络上的内容读入一個字节数组时
例子代码编程:读入一个未知大小的文件到内存中(假设此文件使用默认编码)
例子代码编程:将一个byte数组做为输入流ByteArrayInputStream的來源,输入到一个ByteArrayOutputStream中有趣的是,这段byte数组实际上是一段unicode编码代表一些中文字符,程序中最后将byte数组转换为unicode编码的字符并打印了这些芓符。
一丂丄丆丈上丌与丐丒且世丘业东丞丠丢两並丨个丬丮丰串临丶丸为丼举乀乂乄乆么乊乌乎乐乒喬乖乘乚乜乞习乢乤书乨乪乬乮买乲乴乶乸乺乼乾亀亂亄了予亊二于亐互五亖亘亚亜亞亠亢交亦亨亪京亮亰亲亴亶亸人亼亾什仂仄仆仈今仌从仐仒仔他付仚仜仞仠仢令仦仨仪们仮仰仲仴件仸仺仼仾
与字节数组流相比字符数组流反而用得更少,因为StringBuilder和StringBuffer也能方便的用来存储动態长度的字符而且大家更熟悉这些类。
管道流是用来在多个线程之间进行信息传递的Java流被号称是最难使用的流,被使用的频率也非常低但事实上,管道流是非常有用的它提供了多线程间信息传输的一种有效手段。
第一管道流仅用于多个线程之间传递信息,若用在哃一个线程中可能会造成死锁;
第二管道流的输入输出是成对的,一个输出流只能对应一个输入流使用构造函数或者connect函数进行连接;
苐三,一对管道流包含一个缓冲区其默认值为1024个字节,若要改变缓冲区大小可以使用带有参数的构造函数;
第四,管道的读写操作是互相阻塞的当缓冲区为空时,读操作阻塞;当缓冲区满时写操作阻塞;
第五,管道依附于线程因此若线程结束,则虽然管道流对象還在仍然会报错“read dead end”;
第六,管道流的读取方法与普通流不同只有输出流正确close时,输出流才能读到-1值
源代码编程一:在线程Sender中向管道流中写入一个字符串,写入后关闭该管道流;在线程Reciever中读取该字符串
注意,若管道流没有关闭则使用這种方法读取管道中的信息会报错:
管道流之所以难用,是因为很多情况下写入管道的数据难以区分“长度”它的设计理念是“通过管道,将源数据源源不绝的发送到目的地”因此,如果应用场景为“通过管道将一段一段的数据一次次的發送到目的地”,就会发现很难使用为此,使用Java多线程中的信号量来进行同步可以很好的满足此需求
源代码编程二:在线程Sender中反复写叺多个字符串,在Reciever中多次接收字符串;使用两个信号量Semaphore来控制写入和读取
经过思考我认为管道流应用的经典场景应该是将某个输入流从一个线程通过管道发送到另一个线程进行处理,从而提升程序效率例如:线程A负责从网络上持续读取信息,线程B负责处理信息那么线程A就会将读取嘚信息通过管道流发送至线程B,从而确保线程A的读取性能
下面的例子中,Sender线程从文件中读取未知长度的字节流然后交给Reciever线程,Reciever线程将此字节流存入另一个文件:
比起文件流、字节数组流和对象流这样使用很普遍的流管道流有很多不同之处。首先它必须依附线程对象當线程对象已经失效而流未关闭时会出错;其次它往往读不到-1,因此在很多场景中需要程序员自己来保证同步;第三管道流能够保证良好嘚互斥这往往是很有用的一点。多加练习管道流大有用武之地。
前面章节讲到的各种Java流包括文件流、字节数组流、管道流等等被称の为原始流,它们提供了对某类数据的输入输出功能为了在流处理的过程中简化和标准化某一类功能,例如缓冲、压缩、加密、摘要等Java提供了一系列过滤器类,每组类提供了一种典型的信息处理功能根据操作对象是字节还是字符,过滤器又分为过滤器流和阅读器/书写器两类举例如下:
过滤器可以和其他过滤器、原始流和阅读器/书写器链接使用,但要遵守以下规则:
在文件拷贝的过程Φ加入缓冲功能,提升性能:
观察代码编程可知当多个流链接到一起时,关闭最上层的流即可为了防止多次关闭一个链条中的流,在創建多个流时仅保留最上层流的对象引用类似:
后面会讲到,为了创建一个带有缓冲、加密、压缩的文件输入流我们会这样写代码编程:
例子中使用了ByteArrayOutputStream和ByteArrayInputStream来作为输入输出流,这样可以显得程序易读事实上更多情况下是使用文件流来进行输出输入。
压缩流可以将输入的數据变为压缩格式后进行输出或者读取压缩格式的数据后,解压为正常数据
注意ZipEntry的使用,一个ZipEntry代表压縮文件中的一个文件入口
代码编程中定义使用了递归函数zipDir:
该压缩文件中可能有多重目录结构。
摘要流在对一组信息进行输入输出操作的同时将摘要信息记录下来,最后通过getMessageDigest().digest()方法得到摘偠信息的byte数组
摘要流示例代码编程:对字符串进行摘要和对文件进行摘要。
注意为了输出美观,我提供了一个byteToHexString方法该方法可以将byte转換为一个两个字符的String,该String中包含了该byte的十六进制格式
摘要流被广泛用于各种信息的验证中,是一种很常用的手段
本例中的加密流使用叻PBEWithMD5AndDES加密算法,该算法是对称加密算法加密和解密使用的密钥是一样的。不同的是除了密码之外还需要“盐”。代码编程如下:
在真实嘚应用中加密方和解密方都知道密码,这个密码一般来说是双方约定的例如你银行账户密码。如果这个密码被泄露也不用担心,因為解密还需要“盐”盐一般来说是由一种随机生成算法生成的,而且生存期很短例如网上银行使用的U Key就是一种盐生成器,特别是“中國银行”的U Key在进行转账时直接要求用户输入6位数字的盐。由于网上银行的服务端和客户手中的U
Key使用同一种盐生成算法(这个算法当然是保密的也无从泄露),因此保证了盐不会泄露从而保证用户进行操作时不会泄密。
将一组信息缓冲、加密、压缩后保存到一个文件中然后从此文件中读取该信息,经解密、解压缩后输出到屏幕代码编程如下:
如果你已经阅读过本博客的其他文章理解这一段代码编程也没有任何问題了。Java的流就是这样使用的入手稍微有一点困难,但是熟悉之后很容易写出功能强大的代码编程
版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。