Day13:IO流

IO流主要涉及到Java中的数据是怎样传送的,是一什么方式传送的,又是以什么方式阅读的

IO流:
    IO:用于处理设备上的数据的技术。设备:内存,硬盘,光盘。
    流:系统资源,windows系统本身就可以操作设备。各种语言只是使用系统平台上的这个资源。
        并对外提供了各种语言自己的操作功能,这些功能最终调用的是系统资源。
        使用完资源一定要记住:释放。
        
IO:java中所涉及的功能对象都存储到java.io包中。    
设备上数据最常见的存储表现形式文件file.
小知识点
* 递归:函数自身调用自身。函数内部又使用到了该函数功能。
* 什么时候使用?
* 功能被重复使用,但是每次该功能使用参与运算的数据不同时,可以考虑递归方式解决。
* 使用时,一定要定义条件。
* 注意递归次数过多,会出现栈内存溢出。
* String LINE_SEPARATOR = System.getProperty("line.separator");(任何系统都可以的换行符)
/*
 * 将数据写入到文件中。
 * 使用字节输出流。
 * FileOutputStream。
 */
File dir = new File("tempfile");
if(!dir.exists()){
    dir.mkdir();
}
//1,创建字节输出流对象。用于操作文件,在对象初始化时,必须明确数据存储的目的地。
//输出流所关联的目的地,如果不存在,会自动创建。如果存在,则覆盖。
FileOutputStream fos = new FileOutputStream("tempfile\fos.txt");
        
//2,调用输出流的写功能。
fos.write("abcde".getBytes());
                
//3,释放资源。
fos.close();
    public static void main(String[] args) {

   //         需求获取一个想要的指定文件的集合。获取javase_code下(包含子目录)的所有的.java的文件对象。并存储到集合中。
        /*
         * 思路:
         * 1,既然包含子目录,就需要递归。
         * 2,在递归过程中需要过滤器。
         * 3,满足条件,都添加到集合中。
         */ 
        File dir = new File("e:\javase_code");
        
        List<File> list = fileList(dir,".java");
        
        for(File file : list){
            System.out.println(file);
        }   
    }
    /**
     * 对指定目录进行递归。
     * 
     * 多级目录下都要用到相同的集合和过滤器。那么不要在递归方法中定义,而是不断的进行传递。
     * 
     * @param dir 需要遍历的目录。
     * @param list 用于存储符合条件的File对象。
     * @param filter 接收指定的过滤器。
     */
    public static void getFileList(File  dir,List<File> list,FileFilter filter){
 
        //1,通过listFiles方法,获取dir当前下的所有的文件和文件夹对象。
        File[] files = dir.listFiles();
        
        //2,遍历该数组。
        for(File file : files){
            
            //3,判断是否是文件夹。如果是,递归。如果不是,那就是文件,就需要对文件进行过滤。
            if(file.isDirectory()){
                getFileList(file, list, filter);
            }else{
                //4,通过过滤器对文件进行过滤
                if(filter.accept(file)){
                    list.add(file);
                }
            }
            
        }
    }

  
   /**
     * 定义一个获取指定过滤器条件的文件的集合。
     */
    public static List<File> fileList(File dir,String suffix){
        
        //1,定义集合。
        List<File> list = new ArrayList<File>();
        
        //2,定义过滤器。
        FileFilter filter = new FileFilterBySuffix(suffix);//自定义的文件拦截器
        
        getFileList(dir, list, filter);
        
        return list;  
    }
File:IO技术用于操作设备上数据的,而数据最常见的体现方式是文件。
先了解了文件的操作。
    创建,删除,存在,隐藏,获取......;


需求:怎么操作文件的数据呢?
    使用IO流对象。而且文件数据都是字节存在。
学习了可以操作文件的字节流。    
    InputStream
        |--FileInputStream
    OutputStream
        |--FileOutputStream
        
为了提高了操作效率。引入缓冲区。
    InputStream
        |--FileInputStream
        |--FilterInputStream
            |--BufferedInputStream
    OutputStream
        |--FileOutputStream
        |--FilterOutputStream    
            |--BufferedOutputStream

发现,文件数据,媒体文件字节流没问题。
但是对于文本文件,想要操作文件中的中文数据时,字节流只能操作字节,需要我们字节解码成字符。麻烦。
所以就到API找对象,就发现字符流中有字节和字符的桥梁,传说中的转换流。
    Reader
        |--InputStreamReader:字节-->字符。
    Writer
        |--OutputStreamWriter:字符--->字节。
        
它们出现了解决了中文的编码转换问题。

为了便捷操作字符文件。找到了转换流的子类,但是它有局限性,只能操作文件,而且是默认编码。
如果不操作文件,而且编码不是默认的,需要使用转换流。
    Reader
        |--InputStreamReader:字节-->字符。
            |--FileReader
    Writer
        |--OutputStreamWriter:字符--->字节。
            |--FileWriter

    
为了提高了字符流的操作效率。引入字符串的缓冲区。
字符流=字节流+编码
Reader
|--InputStreamReader:字节-->字符。 |--FileReader=FileInputStream+InputStreamReader |--BufferedReader:String readLine() Writer |--OutputStreamWriter:字符--->字节。 |--FileWriter=FileOutputStream+OutputStreamWritter |--BufferedWriter:String newLine();

        /*
         * 需求:把目录中的1.jpg复制一次并把文件名改为2.jpg
         */
        //1、获取文件
        File file = new File("1.jpg");
        //2、创建具体流对象,明确数据源
        FileInputStream fis = new FileInputStream(file);
        BufferedInputStream bis = new BufferedInputStream(fis);
        //3、创建对冲流,明确目的文件
        FileOutputStream fos = new FileOutputStream("2.jpg");
        BufferedOutputStream bos = new BufferedOutputStream(fos);
        //4、进行频繁读写
        int len = 0;
        byte[] buf = new byte[1024];
        while((len=bis.read(buf))!=-1){
            //写入流
            bos.write(buf,0,len);
            //刷新流
            bos.flush();
        }
        //5、释放资源
        fis.close();
        fos.close();

接着,来一个综合性的流的练习题

        /*
         * 作业:键盘录入多名学生的信息:格式:姓名,数学成绩,语文成绩,英文成绩.
         * 按总分由高到低,将学生的信息进行排列到文件中。
         * 
         * 思路:
         * 1,使用键盘录入技术。
         * 2,操作的学生信息,信息很多,需要将信息封装成学生对象。
         * 3,总分由高到低,需要排序,需要对学生对象中的总分排序。需要将多个学生对象进行容器存储。
         * 哪个容器呢?TreeSet集合。
         * 4,将容器中学生对象的信息写入到文件中。
         * 
         * 
         */
        
        
        //创建一个逆序的比较器。
        Comparator<Student> comp = Collections.reverseOrder();
        
        //使用操作学生信息的工具类。
        Set<Student> set = GetInfoTool.getStudents(comp);
        
        File destFile = new File("tempfile\info.txt");
        GetInfoTool.write2File(set, destFile);
//学生类
public
class Student implements Comparable<Student> { private String name; private int ma,cn,en; private int sum; public Student() { super(); } public Student(String name, int ma, int cn, int en) { super(); this.name = name; this.ma = ma; this.cn = cn; this.en = en; this.sum = ma+cn+en; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((name == null) ? 0 : name.hashCode()); result = prime * result + sum; return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Student other = (Student) obj; if (name == null) { if (other.name != null) return false; } else if (!name.equals(other.name)) return false; if (sum != other.sum) return false; return true; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getMa() { return ma; } public void setMa(int ma) { this.ma = ma; } public int getCn() { return cn; } public void setCn(int cn) { this.cn = cn; } public int getEn() { return en; } public void setEn(int en) { this.en = en; } public int getSum() { return sum; } public void setSum(int sum) { this.sum = sum; } @Override public int compareTo(Student o) { int temp = this.sum - o.sum; return temp==0?this.name.compareTo(o.name):temp; } }
//对学生信息进行操作
public
class GetInfoTool { /** * 获取所有学生对象集合,按照学生对象的自然排序。 * @throws IOException */ public static Set<Student> getStudents() throws IOException{ return getStudents(null); } /** * 获取学生对象集合,按照指定的比较器排序。 * @param comp * @return * @throws IOException */ public static Set<Student> getStudents(Comparator<Student> comp) throws IOException{ //1,键盘输入。 BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in)); //创建一个容器存储学生对象。 Set<Student> set = null; //如果比较器存在,就创建带有比较器的对象。 if(comp!=null){ set = new TreeSet<Student>(comp); }else{ set = new TreeSet<Student>(); } //2,获取键盘录入的信息。 String line = null; while((line=bufr.readLine())!=null){ //键盘录入结束标记。 if("over".equals(line)){ break; } //因为录入的数据是有规律的。可以通过指定的规则进行分割。 String[] strs = line.split(","); //将数组中的元素封装成对象。 Student stu = new Student(strs[0],Integer.parseInt(strs[1]) ,Integer.parseInt(strs[2]) ,Integer.parseInt(strs[3])); //将学生对象存储到集合中。 set.add(stu); } //关闭键盘录入须知:如果后面不在使用键盘录入是可以关闭的,如果后面还要使用,就不要关闭,继续通过System.in就可以获取。 // bufr.close(); return set; } /** * 将集合中的学生信息写入到文件中。 * @throws IOException */ public static void write2File(Set<Student> set,File destFile) throws IOException{ BufferedWriter bufw = null; try{ bufw = new BufferedWriter(new FileWriter(destFile)); //遍历集合。 for(Student stu : set){ bufw.write(stu.getName()+" "+stu.getSum()); bufw.newLine(); bufw.flush(); } }finally{ if(bufw!=null){ try { bufw.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
缓冲区原理:
    临时存储数据的方法。减少对设备操作的频率,提高了效率。其实就是将数据临时缓存到了内存(数组)中。
    
模拟一个BufferedReader。
    
    
Writer
    |--TextWriter
    |--MediaWriter

在对数据写入操作过程中,希望提升效率。
要对操作文本的对象提升效率,使用缓冲区技术。

Writer
    |--TextWriter
        |--BufferedTextWriter
    |--MediaWriter    
        |--BufferedMediaWirter
    |--AudioWriter
        |--BufferedAudioWriter
        
这样的体系,为了增加一些功能,而通过产生子类来完成,会导致继承体系变得很臃肿。
重新思考体系的设计问题。都是在写的方法进行效率的提升。
为什么不将该功能进行单独的封装呢?要提升哪个具体对象,将哪个具体对象交给该功能不就可以了吗?

class BufferedWriter extends Writer
{
    BufferedWriter(Writer w)
    {
    }
    /*
    BufferedWriter(TextWriter w)
    {}
    
    BufferedWriter(MediaWriter w)
    {
    }
    */
}        
Writer
    |--TextWriter
    |--MediaWriter
    |--AudioWriter
    |--BufferedWriter
        
TextWriter tw = new TextWriter();
BufferedWriter bufw = new BufferedWriter(tw);
//tw.write();
bufw.write();
    
解决:可以给对象提供额外的功能(职责)。比继承这种方式更为灵活。
起个名字,装饰设计模式(Wrapper,Decorator)。
装饰类与被装饰类都所属于同一个体系。
同时装饰类中持有被装饰类的引用。
原文地址:https://www.cnblogs.com/vijay/p/3508066.html