黑马程序员——java基础——IO流
来源:互联网 发布:波士顿矩阵法案例 编辑:程序博客网 时间:2024/05/17 04:09
一, IO流
A:IO流是用来处理设备之间的数据传输
B:Java对数据的操作是通过流的方式
C:Java用来操作流的对象在io包中
D:流按操作的数据分为 字节流和字符流
E:流按流向分为 输入流和输出流
二 , 常用IO流的基类
A:字节流的基类
InputStream OutputStream
B:字符流的基类
Reader Writer
注意:这个四个基类派生出来的子类都是都是以其父类名作为其子类名的后缀名 ,这个四个基类都是抽象类不能实例化。
三 ,io包中的字符流
A:字符流的由来,其实就是字节流读取文本的字节数据后,不直接进行操作而是先查指定的编码集获取对应的文字。简单是就是字节流+编码表。(字符流的特点就是读完数据之后 我没有立即写到目的地 而是去查编码表 )不要拿字符流操作媒体文件 也有可能有字符流赋值的文件和原文件一模一样 但是这种概率非常的底因为字符流在操作数据的时候 每一个数据 都会在编码表去找对应的值 比如:用字符流copy一张图片的时候 这个图片中一些二进制数据时编码表没有的 所以就会返回值不正确虽然字符流的read方法 是去读取一个字符 但是实际上 字符流在从硬盘上读取数据的时候 也是一个一个字节的读
字符流的两个顶层基类是 Reader Writer,这两个基类中定义类对字符数据进行读写的基本操作 。
Rader类中方法
注意:read()方法是读取单个字符 它的返回值的返回范围是0 到 65535 之间 当读取流的末尾就返回-1 该类中的所有read方法都是阻塞式方法
Writer类中方法
注意:write(int c)方法接收的参数虽然是int类型的,但是实际上只会写入参数值的低位16 高位16位被忽略。 还有在写入数据的时候记得要调用flush方法。
B:继承自字符流基类的常用 流对象
(a) , 字符读取流
1, FileRader
这是用操作文本文件的便捷类,该类使用的是平台默认的编码表。
//创建FileReader对象比并且明确数据源FileReader re=new FileReader("C:\\Users\\Enhon\\Desktop\\Demo.txt");//定义临时变量int temp;//进行频繁的读取操作并输出(每次读取一个字符)while((temp=re.read())!=-1){System.out.print((char)temp);}//关闭流re.close();
上面的方式显然效率不高 因为每次读取一个字符再操作一个字符,我们可以用read(char[] arr)来提高效率
//创建FileReader对象比并且明确数据源FileReader re=new FileReader("C:\\Users\\Enhon\\Desktop\\Demo.txt");//定义字符数组char[] arr=new char[1024];//定义临时变量int temp;//进行频繁的读取操作并输出,先将读取到的字符都放到arr数组中 然在从数组中取出数据while((temp=re.read(arr))!=-1){System.out.print(new String(arr,0,temp));}//关闭流re.close();
2,BufferedReader
字符读取流缓冲区,该类采用了装饰设计模式用来提高字符读取流的读取效率。创建该类的对象的时候要和一个字符读取流关联,因为该类是增强读取流的性能的所以在创建对象的时候必须要明确是对哪个读取流的功能进行增强。该类实现操作字符 ,数组,行的高效读取。该类中定义了可以操作行的方法readLine();
BufferedReader:能实现提高效率的底层原理:
BufferedReader 内部定义一个 Reader类型的成员,并在构造函数中为这个变量赋值,同时内部封装了一个字符数组。先会调用通过构造函数传进来的字符读取流的read(char[] buf)方法将从源中读取到的字符存入到字符数组中,然后该类的reade()方法实际上是在从字符数组中读取字符,而不是从源中读取数据所以效率高。然后,该类的readLine(),readLine()方法里面定义一个StringBuffer类型的局部变量 ,接着调用该类的reade()方法从字符数组中一个一个读取字符然后存到StringBuffer中,当读到 行的结束标记之后就将这个StringBuffer中的数据返回,这样就可以实现一行一行的读取数据。
在知道了BufferedReader的底层实现,我们可以自己定义一个BufferedReader (MyBufferedReader)
public class MyBufferedReader {//字符数组的长度private static final int BUFFER_SIZE = 1024;//表示该类是对Reader进行性能增强public Reader r=null;//构造函数public MyBufferedReader(Reader r) {super();this.r = r;}//数组的指针和记录数组中有效元素的个数public int pos=0,count=0;//创建字符数组public char arr[] =new char[BUFFER_SIZE];//从数组中读取一个字符的方法public int MyReader() throws IOException{if(this.count==0){this.count=this.r.read(arr);this.pos=0;}if(this.count==-1){return -1;}char temp=this.arr[pos++];this.count--;return (int)temp;}//从字符数组中一个一个读取字符,存入StringBuilder中 然后判断行的技术标记返回一行的数据public String MyReaderLine() throws IOException{StringBuilder strtB=new StringBuilder();int temp=0;while((temp=this.MyReader())!=-1){if((char)temp=='\r'){continue;}if((char)temp=='\n'){return strtB.toString();}strtB.append((char)temp);}if(strtB.length()!=0){return strtB.toString();}return null;}}
3, LineNumberReader 该类是BufferedReader的所以说该类也是一个字符读取流缓冲区,该类的特点是在可以读取文本数据的时候加入行号或者是按照行号读取
//创建LineNumberReader 对象LineNumberReader lineReader=new LineNumberReader(new FileReader("C:\\Users\\Enhon\\Desktop\\Demo.txt"));String temp=null;//从文本中读取数据 然后加上行号数一起输出while((temp=lineReader.readLine())!=null){System.out.println(lineReader.getLineNumber()+". "+temp);}//关闭流lineReader.close();
//创建LineNumberReader 对象LineNumberReader lineReader=new LineNumberReader(new FileReader("C:\\Users\\Enhon\\Desktop\\Demo.txt"));//设置行号lineReader.setLineNumber(2);String temp=null;//从文本中读取数据 然后加上行号数一起输出while((temp=lineReader.readLine())!=null){System.out.println(lineReader.getLineNumber()+". "+temp);}//关闭流lineReader.close();
(b) , 字符输出流
1,FileWriter
用来写入字符文件的便捷类,使用的是本地平台的默认的编码表。
<span style="white-space:pre"></span>//创建字符写入流FileWriter writer=new FileWriter("C:\\Users\\Enhon\\Desktop\\Demo.txt");//将数据写入到目的地中writer.write("fafafafadfadfdgfgfg");//刷新缓冲writer.flush();//关闭流writer.close();
//创建字符写入流FileWriter writer=new FileWriter("C:\\Users\\Enhon\\Desktop\\Demo.txt");//定义一个字符数组char[] arr="FAFAFASDFSDF".toCharArray();//将数组中的数组写入到目的地中writer.write(arr);//刷新缓冲writer.flush();//关闭流writer.close();注意:如果在创建该类的对象时指定的文件如果原先就存在的话,就会覆盖原先的文件,也可以做到不覆盖,在创建该的对象的时候我们可以在构造函数中传入true,表示能在与该流关联的文件中续写。
2, BufferedWriter 字符输出流缓冲区 也是采用装饰设计模式用来提高字符输出流的效率。 将文本写入字符输出流,缓冲各个字符,从而提供单个字符、数组和字符串的高效写入。创建该类的对象的时候也要明确是要提高哪个输出流的效率。
//创建BufferedWriter对象 并明确与之关联的字符输出流对象BufferedWriter bufW=new BufferedWriter(new FileWriter("C:\\Users\\Enhon\\Desktop\\Demo.txt"));//将数据写入到缓冲区中bufW.write("fdfdf");//写入一个换行符bufW.newLine(); //刷新缓冲bufW.flush();//关闭流bufW.close();
注意:该类的newLine()方法是想缓冲区中写入本地平台的行分隔符,写入是相当于是 System.getpropertiy("line.separator"), newLine()并不是 可以替代System.getpropertiy("line.separator"),newLine()是往
字符输出流缓冲区中写入本地平台的行分隔符,而System.getpropertiy("line.separator")是返回的本地平台的行分隔符。
四, io包中的字节流
A:字节流基类中的方法
InputStream中的方法
read()的放回值的返回时0 到255 读取到文件的末尾就返回-1 该类中的所有read方法都是阻塞式方法
OutputStream中的方法
write(int b)方法接受的参数虽然是int类型的但是只会讲该参数的最低8位写入输出流中 24高位省略掉
B:继承自字节流基类的常用 流对象
(a)字节读取流
1,FileInputStream 是InputStream的直接子类
从源中读取一个字节处理一个字节
<span style="white-space:pre"></span>//创建字节读取流对象FileInputStream fileR=new FileInputStream("C:\\Users\\Enhon\\Desktop\\Demo.txt");//定义一个变量int ch=0;//读取一个字节并转成该字节对应的字符 输出while((ch=fileR.read())!=-1){System.out.println((char)ch);}//关闭流fileR.close();注意:上面这种方式要是读取的是文本数据 里面包含有中文 我们是看不见中文的(乱码)
从源中读取字节数据存放进字节数组中 然后从字节数组中取出数据 这种效率要高一样
<span style="white-space:pre"></span>//创建字节读取流对象FileInputStream fileR=new FileInputStream("C:\\Users\\Enhon\\Desktop\\Demo.txt");//定义一个字节数组byte[] arr=new byte[1024];//定义一个变量int ch=0;//从源中读取数据存放到字节数组中while((ch=fileR.read(arr))!=-1){System.out.println(new String(arr,0,ch));}//关闭流fileR.close();
这种方式操作的数据要是文本数据的话里面含有中文的话不会乱码(编码和解码所有的码表一致)
//创建字节读取流对象FileInputStream fileR=new FileInputStream("C:\\Users\\Enhon\\Desktop\\Demo.txt");//获取该字节流关联的文件的字节(获取大小)int byteCount=fileR.available();//定义一个字节数组 长度是 byteCountbyte[] arr=new byte[byteCount];//定义一个变量int ch=0;//从源中读取数据存放到字节数组中fileR.read(arr);System.out.println(new String(arr));
上面这种方式没有利用循环, 先是获取文件的大小 ,然后再是创建一个相同大小的字节数组 ,将源中所有的数据存放到数组中,要是源文件很大的话,用这种方式就会出现内存溢出的情况。
2 BufferedInputStream
字节读取流缓冲区,是用来提高字节读取流的读取效率的,创建该类的对象的时候一定要明确是对哪个字节读取流的读取效率进行增强。该类的内部封装一个字节数组,该类的读取数据的方法是从该字节数组中读取所以效率高。
//创建字节读取流缓冲区对象 并与指定的字节读取流相关联BufferedInputStream bufR=new BufferedInputStream(new FileInputStream("C:\\Users\\Enhon\\Desktop\\Demo.txt"));//定义一个临时变量int ch=0;//从缓冲区中读取数据 并输出while((ch=bufR.read())!=-1){System.out.print((char)ch);}//关闭流bufR.close();
(b)字节输出流
1, FileOutputStream 是OutputStream的直接子类
//创建字节输出流对象FileOutputStream filO=new FileOutputStream("C:\\Users\\Enhon\\Desktop\\Demo.txt");//将数据写入到流中filO.write("fasdfsdfds".getBytes());//关闭流filO.close();
//创建字节输出流对象 并且表示可以续写FileOutputStream filO=new FileOutputStream("C:\\Users\\Enhon\\Desktop\\Demo.txt",true);byte[] arr="fasdfsdfds".getBytes();int count=0;//此往目的地中写入一个字节while(true){if(count==arr.length){break;}filO.write(arr[count++]);}//关闭流filO.close();
2,BufferedOutputStream 字节输出流缓冲区,是用来提高字节输出流的写入效率的,创建该类的对象的时候一定要明确是对哪个字节输出流的写入效率进行增强。该类的内部封装一个字节数组,该类的写入数据的方法是将数据写入到字节数组中
//创建字节输出流缓冲区对象BufferedOutputStream out=new BufferedOutputStream(new FileOutputStream("C:\\Users\\Enhon\\Desktop\\Demo.txt"));//将数据写入到缓冲区中out.write("Kobe".getBytes());//刷新缓冲区out.flush();//关闭流out.close();
3,字节流应用例子 copy一个MP3文件
<span style="white-space:pre"></span>//创建字节读取流缓冲区 并明确源BufferedInputStream in=new BufferedInputStream(new FileInputStream("C:\\Users\\Enhon\\Desktop\\Demo.mp3"));//创建字节读取流缓冲区 并明确目的BufferedOutputStream out =new BufferedOutputStream(new FileOutputStream("C:\\Users\\Enhon\\Desktop\\aa\\Demo.mp3"));//临时变量int ch=0;//连续读写while((ch=in.read())!=-1){out.write(ch);}out.close();
五 ,IO流的异常处理
用IO流在操作文件的时候 ,由于多方面原因,很容易抛出异常,所以我们要进行异常处理 以下面的例子展示出来
//这变量一定要定义在try的面外 不然在finally中是访问不到的BufferedInputStream in=null;BufferedOutputStream out =null;try{in=new BufferedInputStream(new FileInputStream("C:\\Users\\Enhon\\Desktop\\Demo.mp3"));out=new BufferedOutputStream(new FileOutputStream("C:\\Users\\Enhon\\Desktop\\aa\\Demo.mp3"));int ch=0;while((ch=in.read())!=-1){out.write(ch);}}finally{//要是在创建流对象的时候就抛出异常 的话 in就是null 下面的in.close()就会抛出NullPointerException 所以这里要判断if(in!=null){try{in.close();}catch(IOException e){throw new RuntimeException(e);}}//要是在创建流对象的时候就抛出异常 的话 out就是null 下面的out.close()就会抛出NullPointerException 所以这里要判断if(out!=null){try{out.close();}catch(IOException e){throw new RuntimeException(e);}}}
六, 标准输入输出流
标准输入流:该流可以通过System.in获取 ,返回的是InputStream类型的对象,是一字节读取流。可以将该流的源看成是键盘(控制台),由于该流是标准输入流,我们最好不要去调用其close()。
标准输出流:该流可以通过System.out获取 ,返回的是PrintStream类型的对象,是一个字节输出流。可以将该流的目的地看成是控制台,由于该流是标准输出流,我们最好不要去调用其close()。
七 ,InputStreamReader OutputStreamWriter
InputStreamReader :
InputStreamReader 是字节流通向字符流的桥梁:它使用指定的 charset 读取字节并将其解码为字符。它使用的字符集可以由名称指定或显式给定,或者可以接受平台默认的字符集.该流是前面我们学习的FilrReader 的直接父类,其实FilrReader这个流对象的底层也是在做字节流到字符流的转换。
录入从键盘上的输入的数据 打印在控制上
//创建装换流对象 将标准输入流转成字符流InputStreamReader inChange=new InputStreamReader(System.in);//为了读取高效加入缓冲区BufferedReader buf=new BufferedReader(inChange);String temp=null;//读取数据 并打印在控制台上while((temp=buf.readLine())!=null){System.out.println(temp);}
使用该转换流是的时候我们可以指定编码表 不指定就是用平台默认的编码表。
OutputStreamWriter :
OutputStreamWriter 是字符流通向字节流的桥梁:可使用指定的 charset 将要写入流中的字符编码成字节。它使用的字符集可以由名称指定或显式给定,否则将接受平台默认的字符集。该流是前面我们学习的FilrWriter 的直接父类,其实FilrWriter 这个流对象的底层也是在做字符流到字节流的转换。
录入从键盘上的输入的数据 打印在控制上(不使用 System.out.pintln()直接打印)
<span style="white-space:pre"></span>//创建装换流对象 将标准输入流转成字符流InputStreamReader isr=new InputStreamReader(System.in);//为了读取高效加入缓冲区BufferedReader bufr=new BufferedReader(inChange);//创建装换流对象 将字符流转换成标准输出流OutputStreamWriter osw=new OutputStreamWriter(System.out);//为了读取高效加入缓冲区BufferedWriter bufw=new BufferedWriter(outChange);String temp=null;//读取数据 并打印在控制台上while((temp=bufr.readLine())!=null){bufr.write(temp);bufr.newLine();bufr.flush();}上面的代码中用到的流是标准输入输出流所以没有调用close方法
上面代码中流之间的相互装换的流程可以用下面的图片来展示
八, io包中的其他类
A:打印流 PrintStream PrintWriter
PrintWriter打印流字符流形式 PrintStream打印流的字节流形式
1,PrintStream
PrintStream 为其他输出流添加了功能,使它们能够方便地打印各种数据值表示形式。该类提供了 打印方法打印各种数据类型的值并保证数据的原有样式。其构造函数可以接受三种类型的值,分别是字符串路径,File对象,字节输出流。还有该类在创建对象的时候在构造函数中传入true可以实现自动刷新。
<span style="white-space:pre"></span>PrintStream pr=new PrintStream("demo.txt"); pr.writ(609); pr.close();最后打开 demo.txt文件发现里面的内容就是a
<span style="white-space:pre"></span> PrintStream pr=new PrintStream("demo.txt"); pr.print(609); pr.close();最后打开 demo.txt文件发现里面的内容就是609
产生这种结果的原因是 因为PrintStream 的write(int i) 是将i的二进制的最后八位 写到了 demo.txt而 print方法是按照原有的形式写入到文件中,这个方法实际上就是把609变成了字符串在写入文件中的这个字节打印流中的print方法可以把数据打印到目的地中并且保持数据的表现形式 print方法的原理就是将要打印的数据先变成字符串然后再将字符数据写到目的地中
2,PrintWriter:
向文本输出流打印对象的格式化表示形式。此类实现在 PrintStream 中的所有 print 方法。它不包含用于写入原始字节的方法,对于这些字节,程序应该使用未编码的字节流进行写入。其构造函数可以接受四种类型的值,分别是字符串路径,File对象,字节输出流,字符输出流。
<span style="white-space:pre"></span>//录入键盘输入BufferedReader bufr=new BufferedReader(new InputStreamReader(System.in));//创建字符打印流对象PrintWriter p=new PrintWriter(System.out);String temp=null;while((temp=bufr.readLine())!=null){if("over".equals(temp)){break;}p.println(temp);} //关闭流bufr.close();p.close();运行上面的代码 在控制台上 输入 很多的数据 但是就是没有打印出来 当在输入over的时候就会打印 原因很简单: out是将数据打印打了缓冲区 没有刷新 ,一旦我们输入over的时候 循环结束 就会执行close方法 close方法会调用 flush 所以就有了输出结果
修改代码
//录入键盘输入BufferedReader bufr=new BufferedReader(new InputStreamReader(System.in));//创建字符打印流对象PrintWriter p=new PrintWriter(System.out);String temp=null;while((temp=bufr.readLine())!=null){if("over".equals(temp)){break;}p.println(temp);p.flush();} //关闭流bufr.close();p.close();
要保证数据的原样输出的时候就用就用上面的两个打印流 但是不保证数据的大小不改变
B:序列流 SequenceInputStream
SequenceInputStream 表示其他输入流的逻辑串联。它从输入流的有序集合开始,并从第一个输入流开始读取,直到到达文件末尾,接着从第二个输入流读取,依次类推,直到到达包含的最后一个输入流的文件末尾为止。
构造函数
从构造函数中可以知道 要是有两个字节输入流 可以直接通过第二个构造函数 传参 要是有多个的话 就要考虑用 第一个 但是第一个的参数是 Enumerartion 类型 这就要想到Vector结合 它的elements方法可以返回一个实现了Enumerartion 接口的对象 还有集合工具类 中的enumeration()方法可以将collection集合转换成Enumeration
使用第一个构造函数的方法放,创建一个Vector<FileInputStream> 集合往集合中添夹元素就OK 比较简单 这里就不做演示。
还有一种使用第一个构造函数的方法就是 自己通过匿名内部类 创建一个Enumration的实例
ArrayList<FileInputStream> arrayList=new ArrayList<FileInputStream>(); for(int i=1;i<4;++i) { arrayList.add(new FileInputStream(i+".txt")); } final Iterator<FileInputStream> it=arrayList.iterator();//這裡一定要用final修飾不然在內部類中訪問不到 就是将内部类的时候 当内部类在局部位置上时 访问局部的变量 该变量要用final修饰 //Enumeration是一個接口我們不知道怎麼new一個對象 就只能用匿名內部類 //重寫這個內部類中的方法 因為枚舉就是取代了迭代器 所在在重寫內部類中的方法的時候 //就要借用迭代器 Enumeration<FileInputStream> en=new Enumeration<FileInputStream>() { @Override public FileInputStream nextElement() { return it.next(); } @Override public boolean hasMoreElements() { return it.hasNext(); } }; SequenceInputStream seInputStream=new SequenceInputStream(en); FileOutputStream fileOutputStream=new FileOutputStream("123.txt"); byte[] byteArray=new byte[1024]; int len=0; while((len=seInputStream.read(byteArray))!=-1) { fileOutputStream.write(byteArray,0,len); } fileOutputStream.close(); seInputStream.close();
我也可以用集合工具类中的enumeration方法来获取一个Enumeration的实例
ArrayList<FileInputStream> arrayList=new ArrayList<FileInputStream>(); for(int i=1;i<4;++i) { arrayList.add(new FileInputStream(i+".txt")); } Enumeration<FileInputStream> en=Collections.enumeration(arrayList); SequenceInputStream seInputStream=new SequenceInputStream(en); FileOutputStream fileOutputStream=new FileOutputStream("123.txt"); byte[] byteArray=new byte[1024]; int len=0; while((len=seInputStream.read(byteArray))!=-1) { fileOutputStream.write(byteArray,0,len); } fileOutputStream.close(); seInputStream.close();
C:操作对象的流 ObjectInputStream ObjectOutputStream
ObjectInputStream 用来序列化对象 ObjectOutputStream用来读取序列化的对象。 被操作的对象必须实现Serializable接口,这个接口中没有定义任何方法,这个接口的作用是启标记作用,这是一个标记接口。 简单是的说就是上面的两个类 就是一个写入对象 到设备上或者从设备上面读取对象中封装的数据
先定义一个Person (要是实现Serializable接口)
public class Person implements Serializable { private String name; private int age; public String getName() { return name; } public void setName(String name) { this. name = name; } public int getAge() { return age; } public void setAge(int age) { this. age = age; } public Person(String name, int age) { super(); this. name = name; this. age = age; }}
//创建ObjectOutputStream对象 ObjectOutputStream obo=new ObjectOutputStream(new FileOutputStream("person.object")); //将数据写入目的地中 obo.writeObject(new Person("张山",12)); //关流 obo.close();
反序列化
//创建ObjectInputStream对象ObjectInputStream obi=new ObjectInputStream(new FileInputStream("person.object"));//读取数据 并打印Person p=(Person)obi.readObject();System.out.println(p.getAge()+" "+p.getName());//关闭流obi.close();
一个类被static和transient 修饰的成员是不会被序列话的。
D:RandomAccessFile
此类的实例支持对随机访问文件的读取和写入。随机访问文件的行为类似存储在文件系统中的一个大型 byte 数组。存在指向该隐含数组的光标或索引,称为文件指针;输入操作从文件指针开始读取字节,并随着对字节的读取而前移此文件指针。如果随机访问文件以读取/写入模式创建,则输出操作也可用;输出操作从文件指针开始写入字节,并随着对字节的写入而前移此文件指针。写入隐含数组的当前末尾之后的输出操作导致该数组扩展。该文件指针可以通过 getFilePointer 方法读取,并通过 seek 方法设置.这个类和前面学习的io类不一样,以前的类都是要么是读取流要么输入流(就是一类只能具有读取或者写入方法,而RandomAccessFile既有读取数据方法,也有写入数据的方法) 。
该类的特点:
该类既可以读也可以写
该类的内部封装一个大型的byte数组 并通过指针可以操作数组中元素
可以通过getFilePointer()方法获取指针的位置,也可以通过seek()方法来设置指针的位置
该实际上就是对字节输入流和字节输出流进行了封装
该类的源和目的只能是文件,从构造函数中可以看出来
该类是Object类的直接子类
在创建该类的对象的时候,会关联一个文件 要是文件先前句存在的话,不会覆盖以前的文件。
该类中定义了很多的写入数据的方法 有一个需要注意的方法writeBytes(String str) 按字节序列将该字符串写入该文件。该字符串中的每个字符均按顺序写出,并丢弃其高八位。写入从文件指针的当前位置开始。 所以我们在用该类写入字符串的时候不要用这个方法可以用write(byte[] b)将字符串先转换成字节数组,再写入字节数组就OK
该类的构造函数
构造函数中的第二个参数String mode的值可以是:
"r" 以只读方式打开。调用结果对象的任何 write 方法都将导致抛出 IOException。
"rw" 打开以便读取和写入。如果该文件尚不存在,则尝试创建该文件。
"rws" 打开以便读取和写入,对于 "rw",还要求对文件的内容或元数据的每个更新都同步写入到底层存储设备。
"rwd" 打开以便读取和写入,对于 "rw",还要求对文件内容的每个更新都同步写入到底层存储设备
用RandomAccessFile +加多线程复制文件
public class ThreadsRandomAccessFile implements Runnable { private static final int INT = 1024*1024*2;static int count=0; @Override public void run() { while( true) { int offset= count*INT; count++; //声明一个长度为100的字节数组 byte[] byteArray= new byte[INT]; //随机读取数据的流 RandomAccessFile randomAFRead= null; //len记录每次读取到数组中的真实的数组的个数 int len=0; //随机写入数据的流 RandomAccessFile randomAFWrite= null; try { //随机读取数据 randomAFRead= new RandomAccessFile("F:\\Dreamweaver cs6中文安装包_12_LS3[www.bokebus.com].rar" ,"rw" ); randomAFRead.seek(offset); len=randomAFRead.read(byteArray); if(len==-1) return;//這一句代碼一定要加上 不然會出現角標越界異常 //随机写入数据 randomAFWrite= new RandomAccessFile("F:\\KOBE.rar","rw"); randomAFWrite.seek(offset); randomAFWrite.write(byteArray,0,len); if(len!=INT) break; } catch (IOException e) { System. out.println(e); } finally{ //关闭读取流 if(randomAFRead!= null) try { randomAFRead.close(); } catch (IOException e) { System. out.println(e); } //关闭写入流 if(randomAFWrite!= null) try { randomAFWrite.close(); } catch (IOException e) { System. out.println(e); } } } }}
public static void main(String[] args) throws IOException, ClassNotFoundException { ThreadsRandomAccessFile threadRadomAF= new ThreadsRandomAccessFile(); Thread t1= new Thread(threadRadomAF); Thread t2= new Thread(threadRadomAF); Thread t3= new Thread(threadRadomAF); t1.start(); t2.start(); t3.start();}
E:管道流 PipedInputStream PipedOutputStream
管道流和前面学习的流不一样 前面流学习的流 输入流和输出流之间没有直接的联系 比如:在copy一个文件的时候 输入流读取数据放入一个字节数组里面 然后字节输出流将字节数组里面的数据写入到目的地中 而两个流之间没有直接的联系 而管道流不同 两个流之间有直接的联系
PipedInputStream :管道输入流应该连接到管道输出流;管道输入流提供要写入管道输出流的所有数据字节。通常,数据由某个线程从 PipedInputStream 对象读取,并由其他线程将其写入到相应的 PipedOutputStream。不建议对这两个对象尝试使用单个线程,因为这样可能死锁线程。管道输入流包含一个缓冲区,可在缓冲区限定的范围内将读操作和写操作分离开。如果向连接管道输出流提供数据字节的线程不再存在,则认为该管道已损坏。
PipedOutputStream:可以将管道输出流连接到管道输入流来创建通信管道。管道输出流是管道的发送端。通常,数据由某个线程写入 PipedOutputStream 对象,并由其他线程从连接的 PipedInputStream 读取。不建议对这两个对象尝试使用单个线程,因为这样可能会造成该线程死锁。如果某个线程正从连接的管道输入流中读取数据字节,但该线程不再处于活动状态,则该管道被视为处于 毁坏 状态。
管道流要结合多线程使用 管道输入流只会读取管道输出流出流的数据 要是管道输出流没有写入数据 管道输入流就会阻塞,所以说要是单线程来使用管道流的话就会产生死锁
结合多线程 使用管道流
public class InputStream implements Runnable { private PipedInputStream in= null; InputStream(PipedInputStream in) { this. in=in; } public void run() { byte[] byteArray= new byte[100]; int len=0; try { len=this.in.read(byteArray); System. out.println( new String(byteArray,0, len)); } catch (IOException e) { System. out.println(e); } finally{ try { this. in.close(); } catch (IOException e) { System. out.println(e); } } }}public class OutStream implements Runnable{ private PipedOutputStream out= null; OutStream(PipedOutputStream out) { this. out=out; } public void run() { try { this. out.write( "张山".getBytes()); } catch (Exception e) { System. out.println(e); } finally{ try { this. out.close(); } catch (IOException e) { System. out.println(e); } } }} public static void main(String[] args) throws IOException { PipedInputStream in= new PipedInputStream(); PipedOutputStream out= new PipedOutputStream(in); new Thread( new InputStream(in)).start(); new Thread( new OutStream(out)).start(); }运行上面的代码胡输出:张山如果我们在OutStream 类的源码中this. out.write( "张山" .getBytes());的上面添上基于Thread.sleep(5000)运行代码发现: 运行程序5秒之后 才会在控制台上输出“张山”
上面的代码将两个管道流连接起来的方式是通过 PipedOutputStream 对象的构造函数将PipedInputStream 对象作为参数传进去,还有一种方式就是 就是用PipedInputStream 类的connect方法将两个类联系起来
io包中还有操作特定数据的流
1, 操作基本类型数据的流 DataInputStream 和DataOutputStream
2, 操作数据的流 ByteArrayInputStream 和 ByteArrayOutputStream (要是源或者目的是内存的话就可以用这个两个流)
3, 操作字符数组的流 CharArrayReader 和 CharArrayWriter
4,操作字符串的流 StringReader 和StringWriter
- 黑马程序员——Java基础---IO
- 黑马程序员——java基础--IO
- 黑马程序员——Java基础---IO
- 黑马程序员 Java基础——IO
- 黑马程序员—Java基础—IO
- 黑马程序员——java基础——IO流
- 黑马程序员——Java基础——IO流
- 黑马程序员——Java基础——IO流
- 黑马程序员——java基础——IO流
- 黑马程序员——Java基础——IO流
- 黑马程序员——JAVA基础——IO流
- 黑马程序员——java基础——IO流
- 黑马程序员——JAVA基础——IO流
- 黑马程序员——Java基础—IO流
- 黑马程序员——Java基础—IO流(File)
- 黑马程序员——Java基础---IO流(字符流)
- 黑马程序员——Java基础---IO流(字节流)
- 黑马程序员——java基础之IO流
- Cglib实现动态代理-解决大对象值传递问题
- Maven学习(二十六)-Maven私服(三) - Nexus的简单配置
- Ajax详解
- C++ limits头文件的用法(numeric_limits)
- iOS多线程编程之NSThread的使用
- 黑马程序员——java基础——IO流
- Android抽象布局——include、merge 、ViewStub
- Android 横竖屏操作
- 打开一个activity时scrollview不显示在最顶部的解决方法
- Yii的getLastInsertID()注意事项
- 获取网站访客手机号码哪个获取率高
- 正则爬取电影天堂电影信息
- 如何判断Socket连接失效
- genymotion 使用技巧