java中的BIO基础-4

本文主要讲解Java中的BIO( Blocking I/O)基础,探讨如何在Java中进行数据的读取操作,适合Java初学者和进阶开发者了解BIO的基本用法。
摘要由CSDN通过智能技术生成
节点流
文件节点流
FileInputStream FileOutputStream 是文件字节流,是一种节点流
文件字节输入流的构造方法:
FileInputStream(" 文件名称 ") ,如果文件不存在则 FileNotFoundException
FileInputStream(File)
文件字节输出流的构造方法 :
FileOutputStream(“ 文件名称 ”) 如果文件不存在则新建文件,如果文件存在则覆盖文件内容
FileOutputStream(String name 文件名称 , boolean append 是否采用追加方式 )
FileReader FileWriter 类似
FileInputStream FileOutputStream 两个类属于结点流,第一个类的源端和第二个类的目的端都是磁盘文件,它们的构造方法允许通过文件的路径名来构造相应的流。
如:
FileInputStream infile = new FileInputStream("myfile.dat");
FileOutputStream outfile = new FileOutputStream("results.dat");
要注意的是,构造 FileInputStream, 对应的文件必须存在并且是可读的,而构造 FileOutputStream 时,如输出文件已存在,则必须是可覆盖的。
要求:将 d 盘上的 TestFile.java 拷贝到 e 盘,并命名为 dd.txt
try ( InputStream is = new FileInputStream ( "d:/TestFile.java" );
        OutputStream os = new FileOutputStream ( "e:\\dd.txt" ); ) {
                byte [] buffer = new byte [ 8192 ];
                int len = is . read ( buffer );
                while ( len > 0 ){
                        os . write ( buffer , 0 , len );
                        len = is . read ( buffer );
                }
           } catch ( IOException e ){
                e . printStackTrace ();
        }
构造输出文件流时可以使用两种不同的方式
OutputStream os = new FileOutputStream ( "e:\\dd.txt" ); 如果文件不存在则自动创建;如
果文件存在则进行覆盖
OutputStream os = new FileOutputStream ( "e:\\dd.txt" , true ), 这里的 boolean 类型参数
表示是否采用追加的方式写入文件
内存数组节点
如果文本则使用 char[] ,如果二进制则使用 byte[]
构造器方法
CharArrayReader(char[] buf) 其中 char[] 就是数据的来源,也就是说 Reader 就是从 char[] 中读取数
CharArrayRead(char[] buf, int offset, int length)
CharArrayWriter 用于实现向一个字符数组中写入数据,这个数组可以自动调整大小
ByteArrayInputStream ByteArrayOutputStream CharArrayReader 以及 CharArrayWriter 类似,支持操作的内容不同而已,操作byte[] char[]
内存字串流
StringReader 用于从一个字串 String 中读取数据
String str = " 亚洲说: 我爱小黑 ’" ;
StringReader sr = new StringReader ( str );
int cc ;
while (( cc = sr . read ()) !=- 1 )
        System . out . print (( char ) cc );
sr . close ();
StringWriter 用于给一个 StringBuffer 中写入数据,实现一个可边长的字串
Scanner sc = new Scanner ( System . in );
try (
        StringWriter sw = new StringWriter ();
        Writer fw = new FileWriter ( "c:/console.txt" ) ){
                String temp = sc . nextLine ();
                while ( ! temp . equals ( "quit" )){
                        if ( temp != null && temp . trim (). length () > 0 )
                                sw . write ( temp + "\n" );
                                temp = sc . nextLine ();
                }
                                fw . write ( sw . toString ());
} 
总结
读写文件使用节点流 FileInputStream/FileOutputStream FileReader/FileWriter ,如果操作文本
文件,建议使用 FileReader/FileWriter ,如果操作二进制文件建议使用
FileInputStream/FileOutputStream
需要建立缓冲区 ( 建立临时文件的方式效率低 ), 可以考虑使用内存节点,例如
CharArrayReader/CharArrayWriter StringReader/StringWriter
ByteArrayInputStream/ByteArrayOutputStream
如果需要一个二进制缓冲区可以使用 ByteArrayInputStream/ByteArrayOutputStream ,如果需要
一个字符缓存可以使用 CharArrayReader/CharArrayWriter StringReader/StringWriter
如果数据量不是特别大使用 CharArrayReader/CharArrayWriter 更为方便,如果数据量大而且可能
需要直接操作缓冲区则使用 StringReader/StringWriter
StringWriter 中提供了方法 getBuffer():StringBuffer
过滤流类型
过滤流就是在节点流的基础上附加功能
过滤流
就是 decorate 模式中的抽象装饰角色
FilterInputStream/FilterOutputStream FilterReader/FilterWriter
public class FilterInputStream extends InputStream { // 典型的装饰模式
        protected volatile InputStream in ; // 被装饰目标
        protected FilterInputStream ( InputStream in ) { // 通过构造器组装被装饰对象
                this . in = in ;
        }
        public int read () throws IOException { // 调用 Filter 中的 read 方法时实际操作是由
被装饰对象实现的
                return in . read ();
        }
}
所谓的过滤流实际上就是类似上面的加密处理,在输入之后(后置处理,被装饰对象先执行)或者输出之前(前置处理,先处理然后被装饰对象执行)进行一下额外的处理,最终实际操作是调用被装饰对象的方法完成工作,依靠这种装饰模式实现在节点流的基础上附加额外功能. 当然也允许多个过滤流嵌套从而达到功能累加的目的
FilterInputStream 实际上就是一个装饰抽象角色
自定义流实现循环 13 加密 :
读取数据不变 :FileReader---BufferedReader
写出数据自定义过滤流 SecurityWriter(FilterWriter)
​​​​​​
public class SecurityWriter extends FilterWriter {
        protected SecurityWriter ( Writer out ) {
                super ( out );
        }
        public void write ( int c ) throws IOException {
                if ( c >= 'a' && c <= 'z' ) {
                c = ( c - 'a' + 13 ) % 26 + 'a' ;
                } else if ( c >= 'A' && c <= 'Z' ) {
                        c = ( c - 'A' + 13 ) % 26 + 'A' ;
                }
                super . write ( c );
        }
}

public class SecurityReader extends FilterReader {
        protected SecurityReader ( Reader in ) {
                super ( in );
        }
        public int read () throws IOException {
                int c = super . read ();
                if ( c >= 'a' && c <= 'z' ) {
                        c = ( c - 'a' + 13 ) % 26 + 'a' ;
                } else if ( c >= 'A' && c <= 'Z' ) {
                        c = ( c - 'A' + 13 ) % 26 + 'A' ;
                }
                return c ;
        }
}
桥接转换流
InputStreamReader OutputStreamWriter java.io 包中用于处理字符流的最基本的类,用来在字节流和字符流之间作为中介:从字节输入流读入字节,并按编码规范转换为字符;往字节输出流写字符时先将字符按编码规范转换为字节。使用这两者进行字符处理时,在构造方法中应指定一定的平台规范,以便把以字节方式表示的流转换为特定平台上的字符表示。
转换流可以在构造时指定其编码字符集
InputStreamReader 用于将一个 InputStream 类型的输入流自动转换为 Reader 字符流
OutputStreamWriter 用于将一个 Writer 字符输出流转换为 OutputStream 字节输出流
InputStreamReader 构造器
InputStreamReader(InputStream)
InputStreamReader(InputStream, String)
InputStreamReader(InputStream, Charset)
InputStreamReader(InputStream, CharsetDecorder)
Reader r = new InputStreamReader ( System . in );
int kk = r . read (); // 例如输入的是 中国 ,这里实际读取的是 " "
// 因为这里读取的是一个字节,所以输入 " 中国 " ,实际读取的是 " " 的一个字节,输出显示为 ?
kk = System . in . read ();
System . out . println ( " 输入的是: " + ( char ) kk );
InputSteram is=new InputStreamReader(System.in,”iso8859-1”);
Reader r = new InputStreamReader ( System . in , "gbk" );
int kk = r . read (); // 例如输入的是 " 中国 " ,实际读取的是 " "
System . out . println ( " 输入的是: " + ( char ) kk );
注意:一般不建议自行设置编码字符集,除非是必须的

缓冲流
缓冲流是套接在响应的节点流之上,对续写的数据提供缓冲的功能,提高读写的效率,同时增加了一些新方法
以介质是硬盘为例,字节流和字符流的弊端:在每一次读写的时候,都会访问硬盘。 如果读写的频率比较高的时候,其性能表现不佳。为了解决以上弊端,采用缓存流。
缓存流在读取的时候,会一次性读较多的数据到缓存中,以后每一次的读取,都是在缓存中访问,直到缓存中的数据读取完毕,再到硬盘中读取。
InputStream is = new FileInputStream("d:\\FileTest.java");
        long start=System.currentTimeMillis();//获取从 1970-1-1 0:0:0 到当前的毫米值
        int con = is.read();
        while (con != -1) {
                System.out.write(con);
                con = is.read();
        }
        long end=System.currentTimeMillis();
        is.close();
        System.out.println("统计时间为 :"+(end-start)+"ms");
60ms
构造方法
BufferedReader(Reader) 不定义缓存大小,默认 8192
BufferedReader(Reader in, int size)size 为自定义缓冲区的大小
BufferedWriter(Writer)
BufferedWriter(Writer out, int size)size 为自定义缓冲区的大小
BufferedInputStream(InputStream)
BufferedInputStream(InputStream in, int size)size 为自定义缓冲区的大小
BufferedOutputStream(OutputStream)
BufferedOutputStream(OuputStream out, int size)size 为自定义缓冲区的大小
InputStream is = new BufferedInputStream(new
        FileInputStream("d:\\FileTest.java"));
        long start=System.currentTimeMillis();
        int con = is.read();//这里并不是直接从文件中进行读取,而是从缓存中进行读
        while (con != -1) {
                System.out.write(con);
                con = is.read();
        }
        long end=System.currentTimeMillis();
        is.close();
        System.out.println("统计时间为 :"+(end-start)+"ms");
        is.close();
10ms
缓冲输入流的方法
BuffedReader 提供了一个方法 readLine():String ,但是 BufferedInputStream 中并没有这个
BufferedReader 提供了 readLine 方法用于读取一行字符串,以 \r \n 分割(换行符)
如果读取内容为 null ,则表示读取到了流的末尾
readLine 方法会自动剔除本行内容末尾的换行符
BufferedWriter 提供了 newLine 方法用于写入一个行分隔符
对于输出的缓冲流,写入的数据会先在内存中缓存,使用 flush 方法会使内存中的数据立即写出
键盘录入
System.in:InputStream 用于指代系统默认的输入设备 键盘
方法 read():int 可以实现代码执行到这里则会阻塞等待,只要输入数据为止
BufferedReader br = new BufferedReader ( new InputStreamReader ( System . in ));
        System . out . println ( " 输入数据 " )
        String temp = "" ;
        while (( temp = br . readLine ()). trim (). length () > 0 ){
                if ( "quit" . equals ( temp )) break ;
                        System . out . println ( temp );
        }
br . close ();

BufferedWriter bw = new BufferedWriter ( new OutputStreamWriter ( System . out ));
bw . write ( " 只有缓冲区满才自动进行输出显示 " );
bw . flush (); // 刷新缓冲区,否则看不到输出内容
System . in . read ();
bw . close (); // 关闭输出时会首先自动进行刷新缓冲区
数据流
DataInputStream DataOutputStream 两个类创建的对象分别被称为数据输入流和数据输出流。这是很有用的两个流,它们允许程序按与机器无关的风格读写Java 数据。所以比较适合于网络上的数据传输。这两个流也是过滤器流,常以其它流如InputStream OutputStream 作为它们的输入或输出DataInputStram和 DataOutputStream 分别继承自 InputStream OuputStream ,属于过滤流,需要分别套接在InputStream OutputStream 类型的节点流上
只有字节流,没有对应的字符流
DataInputStream DataOutputStream 提供了可以存取与机器无关的 Java 原始类型数据的方法
DataInputSteram DataOutputStream 构造方法为
DataInputStream(InputStream)
DataOutputStream(OutputStream)
1 、用数据输出流将菲波那契级数的前二十项存放在 fei.dat 文件中。
2 、从 fei.dat 文件中读出数列,显示输出,并计算累加和。
读取、写出一个 double 数据到文件中
// 使用数据流就可以直接操作简单类型数据
double dd = 123.4567 ;
FileOutputStream fos = new FileOutputStream ( "d:\\a.data" );
fos . write (( dd + "" ). getBytes ());
fos . close ();
// 如果不使用数据流,则需要额外编码进行数据类型转换
FileInputStream fis = new FileInputStream ( "d:/a.data" );
byte [] buffer = new byte [ 8192 ];
int len = fis . read ( buffer );
fis . close ();
String str = new String ( buffer , 0 , len );
double dd = Double . parseDouble ( str );
System . out . println ( dd );
加入需要写一个 double ,然后一个 String ,然后再一个 int
需要将输入内容转换为 String ,并且为了区分数据需要引入特殊符号,例如 @@ ,输入数据为
123.456@@shi ya zhou@@12 。从功能角度上说没问题,但是编码太复杂了,所以引入 Data 类型的输入输出流
// 这里不使用 OutputStream 定义变量的原因是:需要使用 DataOutputStream 中定义的特殊方法,而不是父类中定义的通用方法
DataOutputStream dos = new DataOutputStream ( new
FileOutputStream ( "d:\\a.data" ));
dos . writeDouble ( 123.456 );
dos . writeChars ( "刘爱斌 !" );
dos . writeInt ( 12 );
dos . close ();
由于读取出现问题,针对中间的 String 数据引入一个额外的数据,其中存储 String char 个数
写出数据
double salary = 123.456 ;
String ss = "刘 爱斌,孙爱文,文爱瑞 " ;
int age = 12 ;
DataOutputStream dos = new DataOutputStream ( new BufferedOutputStream ( new
FileOutputStream ( "d:\\aa.data" )));
dos . writeDouble ( salary );
dos . writeInt ( ss . length ());
dos . writeChars ( ss );
dos . writeInt ( age );
dos . close ();

 读取数据

double salary = 0 ;
String str = "" ;
int age = 0 ;
// 读取数据的前提是必须知道数据的结构
DataInputStream dis = new DataInputStream ( new BufferedInputStream ( new
FileInputStream ( "d:\\aa.data" )));
salary = dis . readDouble ();
StringBuilder sb = new StringBuilder ();
int len = dis . readInt ();
for ( int i = 0 ; i < len ; i ++ ) sb . append ( dis . readChar ());
str = sb . toString ();
age = dis . readInt ();
System . out . println ( salary + "---" + str + "---" + age );
注意:读取数据判断文件结束 EOFException ,这里没有 -1
在具体应用中建议针对字串使用 readUTF writeUTF
DataOutputStream dos = new DataOutputStream ( new FileOutputStream ( "data2.txt" ));
dos . writeDouble ( 1234.56 );
String str = " 猴子愛小終,小終愛信心 " ;
dos . writeUTF ( str );
dos . writeInt ( 99 );
dos . close ();

DataInputStream dis = new DataInputStream ( new FileInputStream ( "data2.txt" ));
double d1 = dis . readDouble ();
String ss = dis . readUTF ();
int kk = dis . readInt ();
System . out . println ( d1 + "\t" + ss + "\t" + kk );
dis . close ();
打印流
PrintStream PrintWriter 都属于输出流,分别针对字节和字符
PrintWriter PrintStream 都提供了重载的 print println 方法用于输出多种类型数据
print(Object):void
输出引用类型,实际上是调用对象的 toString 方法转换为 String 进行输出
public void println ( Object x ) {
        String s = String . valueOf ( x ); // 调用 String 类中的静态方法将 object 类型的数据转换为字符串
        synchronized ( this ) {
                print ( s );
                newLine (); //print('\n')
        }
}
//String 中的 valueOf 方法的定义
public static String valueOf ( Object obj ) {
        return ( obj == null ) ? "null" : obj . toString (); // 如果输出对象非空,则调用对象的toString 方法
}
println 表示输出后自动换行
PrintWriter PrintStream 的输出操作不会抛出异常,用户通过检测错误状态获取错误信息
PrintWriter PrintStream 有自动的 flush 功能 textOut.flushBuffer() ;
PrintWriter(Writer)
PrintWriter(Writer out, boolean autoFlush) 自动刷新 ----println
PrintWriter ( OutputStream out ) // 参数是一个字节流,但是不需要通过桥接处理
PrintWriter ( OutputStream out , boolean autoFlush )
PrintStream ( OutputStream )
PrintStream ( OutputStream out , boolean autoFlush )
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值