黑马程序员_IO流

来源:互联网 发布:华为t8300数据恢复 编辑:程序博客网 时间:2024/05/20 21:44

一、流的概念和作用

   IO流用来处理设备之间的数据传输。java对数据的操作时通过流的方式,java用于操作流的对象都在Io包中。流的本质就是数据传输,根据数据传输特性将流抽象为各个类,方便直观的操作数据。
流按操作数据分为:字节流和字符流。
流按流向分为:输入流(读),输出流(写)。
字符流和字节流 
   字符流的由来:其实就是,字节流读取文字字节数据后,不直接操作而是先查指定的编码变。获取对于的文字,再对这个文字进行操作。简单说就是:字节流+编码表。
输入流和输出流相对于内存设备而言。
将外设中的数据读取到内存中:输入;
将内存中的数据写入到外设中:输出;


二、IO流常用的基类
字节流的抽象基类(顶层父类):InputStream,OutputStream。
字符流的抽象基类(顶层父类):Reader,Writer。
PS:有这四个类派生出来的子类名称都以父类名作为子类名后缀。 例如:InputStream的子类FileInputStream。Reader的子类FileReder。前缀也表示该对象的一个作用功能。
字符流——FileWriter
如果要操作文字数据,建议优先考虑字符流,而且要将数据从内存写到硬盘上,要使用字符流中的输出流:Writer。
PS:创建一个可以往文件中写入字符数据的字符流输出对象。既然是往一个文件中写入文字数据。那么在创建对象时,就必须明确该文件,也就是用于存储数据的目的地。
如果文件不存在,则会自动创建。如果文件存在,则会被覆盖。如果构造函数中加入true,可以实现文件的续写。
FileWriter fw = new FileWrite(“demo,txt”,true);
//调用Writer对象中的writer(String)方法,写入数据.其实数据写入到的是临时存储缓冲区中.
fw.write("哈哈哈");
//需要刷新,将数据直接写到目的地中.关闭流,关闭资源,在关闭前会先调用flush刷新方法.
三、FileWriter-------IO异常处理
FileWriter fw = null;try{  fw = new FileWriter("dome.txt");  fw.writer("abcd");}catch(IOException e){         System.out.println(e.toString());}finally{        if(fw!=null){   //必须要判断不等于空,否则close找不到,会出现异常        try{                    fw.close();   }catch(IOException e){      throw new RuntimeException("关闭失败");    }         }    }


四、字符流缓冲区
缓冲区的出现提高了对数据的读写效率.对应类: BufferedWriter,BufferedReader,缓冲区要结合流才使用.
作用:在流的基础上对流的功能进行了增强.它本身就是封装了一个数组的对象,用数组在缓存流所操作的数据,所以缓冲区必须有流对象。
BufferedWriter:newLine();写的时候可换行. BufferedReader:readLine();读的时候按行获取.使用了读取缓冲区的read方法,将读取到的字符进行缓冲并判断换行标记,将标记前的缓存数据变成字符串返回。
缓冲区----装饰设计模式
装饰设计模式:对一组对象的功能进行增强时,就可以使用该模式进行问题的解决。进行了相当于外观的改变,但实质没变。
装饰和继承都能实现对功能的扩展为什么还要出现这样的一个设计模式呢?有什么区别呢?
继承只为提高效率,有多个流对象,对导致继承体系臃肿,不够灵活。装饰设计,将缓冲单独封装,哪个对象用就将对象与缓冲关联。特点:装饰类和被装饰类都必须属同一个接口或父类。
缓冲区----LineNumberReader 根据行号来读取文本文件。
自定义缓冲区
public class MyBufferedReaderDemo {FileReader r;//定义一个数组作为缓冲区private char[] buf = new char[1024];//定义一个指针用于操作这个数组中的元素.当操作到最后一个元素后,指针应该归零;private int pos =0;//定义一个计数器用于 记录缓冲区中的数据个数,当数据个数减到0,就从源中继续获取数据到缓冲区中.private int count = 0;MyBufferedReaderDemo(FileReader r){this.r = r;}public int myRead() throws IOException{//从源中获取一批数据到缓冲区中.只有计数器为0,才需要从源中获取数据.if(count==0){count = r.read(buf);pos = 0;//每次数据获取后角标归零;if(count==-1){return -1;     }}char ch = buf[pos];pos++;count--;return ch;}public String myReanLine() throws IOException{StringBuffer sb = new StringBuffer();int ch = 0;while((ch= myRead())!=-1){if(ch=='\r'){continue;}if(ch=='\n'){return sb.toString();}sb.append((char)ch);}//健壮性判断,读到末尾时,但没有出现换行的标记,仍要将最后的数据获取到if(sb.length()!=0){return sb.toString();}return null;}public void myClose() throws IOException{r.close();}}


字节流
基本操作与字符流类相同。但它不仅可以操作字符,还可以其他媒体文件。如:图片,音乐。
五、转换流
将字节流转换为字符流:InputStreamReader。在字节与字符之间搭建一个桥梁,它使用指定的charset(字符集)读取字节并将其解码为字符。
将字符流转换为字节流:OutputStreamReader。字符通向字节之间的一个桥梁,可使用指定的charset(字符集)将要写入流中字符编码成字节。
//1.第一种情况OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("gbk_1.txt"),"GBK");//2.第二种情况FileWriter fw = new FileWrite("gbk_1.txt");
这两句代码功能等同。FileWriter:其实就是转换流指定了本机默认的码表的体现,而且这个转换流的子类对象,可方便操作文本文件。简单的说:操作文件字节流+本机默认编码表。这是按默认码表来操作文件便携类。
如果操作文本文件需要明确具体编码,FileWriter就不行了,必须使用转换流来指定。(UTF-8对一个中文是3个字节,GBK是2个字节)
什么时候用转换流?
(1) 源或目的对应的设备是字节流,但操作的是文本数据,可使用转换流为桥梁。
(2) 一旦操作文本涉及到具体的编码时,就必须用到转换流来指定。
六、File类
用来将文件或文件夹封装成对象,方便对文件与文件夹的属性信息进行操作,File对象可以作为参数传递给流的构造函数。
File对象可以获取,创建和删除,判断,重命名(如果目录不同,会有剪切功能),系统根目录和容量获取(ListRoot和getFreeSpace()),获取目录内容(list());
list();获取当前目录下的文件以及文件夹名,含隐藏文件。调用list方法的File对象中封装的必须是目录。否则会发生NullPointerException,如果访问的是系统级目录也会发生空指针异常。如果目录存在但是没有内容,会返回一个数组,长度为0.
如果有需求,只想得到自己指定的内容,可以用到过滤器FilenameFilter接口。
其中实现accept(File dir,String name)方法。只要满足条件,返回true就取出来。

深度遍历文件夹()递归
public class Test_深度遍历文件夹_递归 {/** * @param args */public static void main(String[] args) {File dir = new File("E:\\1");listAll(dir,0);}private static void listAll(File dir,int lever) {System.out.println(getSpase(lever)+dir.getName());lever++;File []file = dir.listFiles();for (int i = 0; i < file.length; i++) {if(file[i].isDirectory()){listAll(file[i],lever);  //当是文件夹时,继续调用自己,并将当前这个文件夹作为参数传给方法}else{System.out.println(getSpase(lever)+file[i].getName());}}}private static String getSpase(int lever) {StringBuffer sb = new StringBuffer();sb.append("|--");for (int i = 0; i < lever; i++) {sb.insert(0,"|  ");}return sb.toString();}}
七、IO打印流
PrintSteam:字节打印流
(1) 提供了打印方法,可对多种数据类型值进行打印,并保持数据的表示形式.
它不抛IOexception,构造函数,接收三种类型的值:1.字符串路径;2.File对象;3.字节输出流.
自动刷新,可以创建一个PrintStream;这意味着可在写入 byte 数组之后自动调用 flush 方法,可调用其中一个 println 方法,或写入一个换行符或字节 ('\n')。 
PrintWriter:字符打印流
(1)提供了打印方法,可对多种数据类型值进行打印,并保持数据的表示形式.
它不抛IOexception,构造函数,接收三种类型的值:1.字符串路径;2.File对象;3.字节输出流.4.字符输出流
PrintSteam类不同,如果启用了自动刷新,则只有在调用printlnprintfformat 的其中一个方法时才可能完成此操作,而不是每当正好输出换行符时才完成。这些方法使用平台自有的行分隔符概念,而不是换行符。 
八、序列流
SequenceInputStream:对多个流进行合并.将流按有序的顺序排列.主要应用与文件的切割,文件合并(+配置文件)
class FileNamefilter implements FilenameFilter{private String filename;public FileNamefilter(String filename) {this.filename = filename;}public boolean accept(File dir, String name) {return name.endsWith(filename);}}public class SequenceInputStream {private static final int size = 1048576;public static void main(String[] args) throws IOException {File file2  = new File("e:\\partfiles");mergeFile(file2);}/** * 文件合并 * @throws IOException  */private static void mergeFile(File dir) throws IOException {/** * 1.通过过滤器读取到该配置文件================================================= */File [] file = dir.listFiles(new FileNamefilter(".properties"));if(file.length!=1){throw new RuntimeException(dir+",该目录下没有properties扩展名的文件或者不唯一!");}//记录这个文件,因为就这一个文件,索引为0.File confile = file[0];/** * 2.获取文件中的信息============================================================ *///合并文件时,必须获取被切割文件的信息.文件名称,个数,用到properties对象Properties prop = new Properties();//用流读取到FileInputStream fis = new FileInputStream(confile);//再将读取到的信息给properties对象,获取相关信息prop.load(fis);String filename = prop.getProperty("filename");int partCount = Integer.parseInt(prop.getProperty("partCount"));/** * 3.通过过滤器读取到碎片文件文件==================================================== */File[] partFile = dir.listFiles(new FileNamefilter(".part"));//判断碎片个数对不对if(partFile.length!=(partCount-1)){throw new RuntimeException("碎片文件不完整,无法合并!");}/** * 4.将碎片文件和流对象关联,并存储到集合中.======================================= *///完整后通过序列流SequenceInputStream合并碎片ArrayList<FileInputStream> all = new ArrayList<FileInputStream>();for (int i = 0; i < partFile.length; i++) {all.add(new FileInputStream(partFile[i]));}/** * 5.将多个流合并成一个序列流 */Enumeration<FileInputStream> en = Collections.enumeration(all);java.io.SequenceInputStream sis = new java.io.SequenceInputStream(en);/** * 6.读写过程 */FileOutputStream fos = new FileOutputStream(new File(dir,filename));byte[] buf = new byte[1024*1024];int len = 0;while((len=sis.read(buf))!=-1){fos.write(buf,0,len);}fos.close();fis.close();}/** * 文件的切割 * @param file * @throws IOException */private static void splitFile(File file) throws IOException {//读取流关联的文件FileInputStream fis = new FileInputStream(file);//定义一个切割空间大小byte [] buf = new byte[size];FileOutputStream fos = null;int len = 0;int count = 1;//切割文件时,必须记录被切割文件的信息.文件名称,个数,便于合并.使用键值对的方式,用到properties对象Properties prop = new Properties();//定义生成碎片文件的目录File dir = new File("e:\\partfiles");if(!dir.exists()){dir.mkdirs();}while((len=fis.read(buf))!=-1){//将其封装成File对象,目录就是dirfos = new FileOutputStream(new File(dir,(count++)+".part"));fos.write(buf, 0, len);fos.close();}//将被切割文件的信息保存在properties文件中prop.setProperty("filename",file.getName());prop.setProperty("partCount",String.valueOf(count));fos = new FileOutputStream(new File(dir,count+".properties"));prop.store(fos, "save file info");fos.close();fis.close();}}

对象的序列化ObjectInputStream与ObjectOutputStream
实体对象必须实现序列化接口Serializable.
ObjectInputStream:对象序列化;ObjectOutputStream:对象反序列化
它的作用:(1)对象某些特定的对象,如:数据库连接对象。存储特点数据的对象,想让其生命周期延长,可存储起来。
(2)用于给被序列化的类加入ID号.来判断类和对象是不是同一个版本。

   八、随机访问文件—RandomAccessFile
随机访问文件,自身具备读写方法。通过skipBytes(int x)以及seek(int x);来达到随机访问.
skipBytes(int x): 尝试跳过输入的 n 个字节以丢弃跳过的字节。
seek(long pos):设置到此文件开头测量到的文件指针偏移量,在该位置发生下一个读取或写入操作。
它不是IO体系的子类,父类是Object
特点:(1)该对象既能读又能写;
(2)读对象内部维护了一个Byte数组,并通过指针可以操作数组中的元素.
(3)可通过getFilePointe读取指针位置和通过seek方法设置指针的位置;
(4)其实该对象就是将字节输入流和输出流进行了封装;
(5)通过构造函数可以看出,该对象的源或者目的只能是文件.
如果文件不存在会创建,如果存在,不会覆盖,seek方法操作指针,实现随机读和写.

0 0
原创粉丝点击