IO流细节

IO流

文件

文件,其实就是保存数据的地方,比如经常使用的Word文档,TXT文件,Excel文件.....都是文件。它既可以保存一张图片,也可以保存视频,声音....

文件流

  • 文件在程序中是以流的形式来操作的

  • 流:数据在数据源(文件)和程序(内存)之间经历的路径

  • 输入流:数据从数据源(文件)到程序(内存)的路径,这种流,就是把文件的数据,读入到了内存

  • 输出流:数据从程序(内存)到数据源(文件)的路径,输出流,则是把程序(内存)中的数据,输出(写入)到文件中

常用的文件操作

  • 创建文件对象相关构造器和方法

  1. new File(String pathname)//根据路径构建一个File对象

  2. new File(File parent,String child)//根据父目录文件+子路径构建

  3. new File(String parent,String child)//根据父目录+子路径构建

 

Serializable接口可以实现串行化,Comparable代表File可以比较

//在F盘下创建,news1.txt、news2.txt、news3.txt,用三种不同的方式创建
public class FileCreate{
   
    //方式1 new File(String pathname)
    @Test
    public void create01(){
        String filePath = "F:\\news1.txt";
        File file = new File(filePath);
        //在这里创建了一个File对象时,其实并没有创建相关的文件,而是在我们的内存中创建了一个news1.txt的文件,它还存在于内存中
        
        //需要处理IO异常
        try{
            //这里的createNewFile()方法才真正的创建了File文件,把在内存中news1.txt文件输出,创建在我们制定的硬盘里
            file.createNewFile();
            System.out.println("创建成功");
        }catch(IOException e){
            e.printStackTrace();
        }
    }
    //方式2 new File(File parent,String child)//根据父目录文件+子路径构建
    //F:\\news2.txt,这时父文件File parent就是F:\\
   @Test
    public void create02(){
        File parentFile = new File("F:\\");
        String fileName = "news2.txt";
​
        File file = new File(parentFile, fileName);
​
        try{
            file.createNewFile();
            System.out.println("创建成功");
        }catch (IOException e){
            e.printStackTrace();
        }
    } 
    
    //方式3 new File(String parent,String child)//根据父目录+子路径构建
    @Test
    public void create03(){
        String parentPath = "F:\\";
        String fileName = "news3.txt";
​
        File file = new File(parentPath, fileName);
​
        try {
            file.createNewFile();
            System.out.println("创建成功");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
  • 获取文件的相关信息

    1. getName:获取文件名

    2. getAbsolutePath:获取文件的绝对路径

    3. getParent:获取文件的父级目录

    4. length:获取文件大小,按字节返回

    5. exists:这个文件是否存在

    6. isFile:这个文件是不是一个文件

    7. isDirectory:它是不是一个目录

  • public class FileInformation{
        @Test
        public void info(){
            //先创建文件对象
            File file = new File("F:\\news1.txt");
            
            //调用相应的方法,得到对应信息
            System.out.println("文件名字=" + file.getName());
            System.out.println("文件的绝度路径=" + file.getAbsolutePath());
            System.out.println("文件的大小(字节)=" + file.length());
            System.out.println("文件父级目录=" + file.getParent());
            System.out.println("文件是否存在=" + file.exists());
            System.out.println("文件是不是一个文件=" + file.isFile());
            System.out.println("是不是一个目录=" + file.isDirectory());
        }
    }

     

    • 目录操作和文件删除:

      1. mkdir:创建一级目录

      2. mkdirs:创建多级目录

      3. delete:删除空目录或文件

      • public class Directory_{
            //判断 F:\\news1.txt 文件是否存在,如果存在,就删除
            @Test
            public void m1(){
                String filePath = "F:\\news1.txt";
                
                File file = new File(filePath);
                
                if(file.exists()){//判断文件是否存在
                    if(file.delete()){//delete()执行方法会返回一个boolean值,删除成功则返回true,失败返回false
                        System.out.println(filePath+"删除成功");
                    }else{
                        System.out.println(filePath+"删除失败")
                    }
                }else{
                    System.out.println(filePath+"文件不存在");
                }
            }
            
            //判断 F:\\demo02 目录是否存在,存在就删除,否则提示不存在
            //这里我们需要体会到,在java中,目录其实也是被当做文件存在的
           @Test
            public void m2(){
                String filePath = "F:\\demo02";
                
                File file = new File(filePath);
                
                if(file.exists()){//判断目录是否存在
                    if(file.delete()){//delete()执行方法会返回一个boolean值,删除成功则返回true,失败返回false
                        System.out.println(filePath+"删除成功");
                    }else{
                        System.out.println(filePath+"删除失败")
                    }
                }else{
                    System.out.println(filePath+"目录不存在");
                }
            }
            
            //判断F:\\demo\\a\\b\\c 目录是否存在,如果存在就提示已经存在,否则就创建
            @Test
            public void m3(){
                String filePath = "F:\\demo\\a\\b\\c";
        ​
                File file = new File(filePath);
        ​
                if(file.exists()){//判断目录是否存在
                    System.out.println("目录已经存在");
                }else{
                    if(file.mkdirs()){//mkdirs()执行方法也会返回一个boolean值,删除成功则返回true,失败返回false,如果只创建1级目录,比如F:\\demo,是可以用mkdir()方法的,否则用mkdir创建多级目录会出错
                        System.out.println(filePath+"创建成功");
                    }else{
                        System.out.println(filePath+"创建失败");
                    }
                }
            }
        }

    IO流原理及流的分类

    Java IO流原理

    1. I/O是Input/Output的缩写,I/O技术是非常实用的技术,用于处理数据传输,如读/写文件,网络通讯等

    2. Java程序中,对于数据的输入/输出操作以 “流(stream)” 的方式进行

    3. java.io包下提供了各种 “流” 类的接口,泳衣获取不同种类的数据,并通过方法输入或输出

    4. 输入input:读取外部数据(磁盘、光盘等存储设备的数据)到程序(内存)中。

    5. 输出output:将程序(内存)数据输出到磁盘、光盘等存储设备中

     

    • 流的分类

      1. 按操作数据单位不同分为:字节流(8 bit,操作二进制文件[音乐,图片,视频]时可以保证无损操作),字符流(按字符,效率要高些,多用于操作文本文件)

      2. 按数据流的流向不同分为:输入流,输出流

      3. 按流的角色的不同分为:节点流,处理流/包装流

        抽象基类字节流字符流
        输入流InputStreamReader
        输出流OutputStreamWriter
        1. Java中IO流共涉及40多个类,实际上非常规则,都是从如上4个抽象基类派生的

        2. 由这四个类派生出来的子类名称都是以其父类名作为子类名后缀。

      • 文件与流、文件与程序的关系

        1. 文件与程序的关系,通过流来形成的,文件中的数据通过流传入到程序中,或者程序中的数据通过流输入到文件中

        2. 文件与流的关系,个人理解则是,数据的携带者,流携带着数据,送入程序中,反之,则是携带着数据,送入到文件中

  • InputStream :字节输入流

    1. InputStream抽象类是所有字节类输入流的超累

      • InputStream 常用子类:

      1. FileIputStream:文件输入流

      2. BufferedInputStream:缓冲字节输入流,直接父类是FileterInputStream类

      3. ObjectInputStream:对象输入流

    • FileInputStream:文件输入流 方法

      1. available() 返回下一次对此输入流调用 的方法可以不受阻塞地从此输入流读取(或跳过)的估计剩余字节数

      2. close() 关闭此文件输入流并释放与此流有关的所有系统资源

      3. finalize() 确保在不再引用文件输入流时调用其close()方法

      4. getChannel() 返回与此文件输入流有关的唯一FileChannel对象

      5. getFD() 返回表示文件系统中实际文件的连接的 FileDescriptor对象,改文件正在被此FileInputStream使用

      6. read()从此输入流中读取一个数据字节

      7. read(byte[] b) 从此输入流中将最多 b.length() 个字节的数据读入一个byte数组中

      8. read(byte[] b,int off, int len) 从此输入流中将最多len个字节的数据读入一个byte数组中

      9. skip(long n) 从输入流中跳过并丢弃n个字节的数据

        public class FileInputStream_  {
        ​
            //使用read()读取文件
            @Test
            public void readFile01(){
                String filePath = "F:\\hello.txt";
                int i=0;
                FileInputStream fileInputStream = null;
                try {
                    //创建FileInputStream 对象,用于读取 文件
                    fileInputStream = new FileInputStream(filePath);
        ​
                    //从该输入流读取一个字节的数据,如果没有输入可用,此方法将阻止
                    //如果返回-1,表示读取完毕
                    //如果用read方法读取汉字,那么会出现乱码,因为一个汉字并不是一个字节,而read方法是一个字节一个字节读取的
                    while ((i=fileInputStream.read())!=-1){
                        System.out.print((char) i);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }finally {
                    try {
                        fileInputStream.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        ​
            //使用read(byte[] b)读取文件
            @Test
            public void readFile02(){
                String filePath = "F:\\hello.txt";
                int i=0;
                byte[] b=new byte[5];
                FileInputStream fileInputStream = null;
                try {
                    //创建FileInputStream 对象,用于读取 文件
                    fileInputStream = new FileInputStream(filePath);
        ​
                    //从此输入流中指的是,每次读取最多b.length个字节到内存中
                    //如果返回-1,表示读取完毕
                    //如果读取正常,返回实际读取的字节数
                    while ((i=fileInputStream.read(b))!=-1){
                        System.out.print(new String(b,0,b.length-1));
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }finally {
                    try {
                        fileInputStream.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
        ​
    • FileOutputStream :文件输出流 方法

      1. close() 关闭此文件输出流并释放与此流有关的所有系统资源

      2. finalize() 清理到文件的连接,并确保不再用此文件输出流是调用此流的close()方法

      3. getChannel() 返回与此文件输出流有关的唯一FileChannel对象

      4. getFD() 返回与此流有关的文件描述符

      5. write(byte[] b) 将b.length个字节从指定byte数组写入此文件输出流中

      6. write(byte[] b,int off, int len) 将指定byte数组中从偏移量off开始的len个字节写入此文件输出流中

      7. write(int b) 将指定字节写入此文件输出流

        注:在使用FileOutputStream的输出的时候,如果文件不存在,会创建文件(前提是目录已经存在)

        public class FileOutputStream_ {
        ​
            @Test
            public void writeFile() throws IOException {
                //创建FIleOutputStream对象
                String filePath="F:\\a.txt";
                FileOutputStream fileOutputStream=null;
                try {
                    //new FileOutputStream(filePath) 创建方式,是覆盖原有内容
                    //new FileOutputStream(filePath, true),如果调用这个构造器,写入的内容就会追加到文件后面
                    fileOutputStream = new FileOutputStream(filePath);
                    //写入一个字节
                    //fileOutputStream.write('H');
                    // 写入一个字符串
                    String s="你好拉大锯";
                    //fileOutputStream.write(s.getBytes(StandardCharsets.UTF_8));
         //写入拉大锯,注意:拿UTF_8举例,这里面一个汉字是3个字节,所以下面0,3则只输出一个汉字,0的位置(off)代表从哪里开始,3的位置上(len)代表写入几个字节               
                    fileOutputStream.write(s.getBytes(StandardCharsets.UTF_8),3,6);
                } catch (IOException e) {
                    e.printStackTrace();
                }finally {
                    fileOutputStream.close();
                }
            }
    • 文件拷贝

      import java.io.FileInputStream;
      import java.io.FileNotFoundException;
      import java.io.FileOutputStream;
      import java.io.IOException;
      ​
      public class copy {
          public static void main(String[] args) {
              //文件拷贝
              //1.创建字节输入,输出流
              //2.通过输入流读取文件到内存中
              //3.通过输出流输出到指定文件中
              String path="F:\\1.png";
              byte[] b=new byte[1024];
              FileInputStream fileInputStream = null;
              FileOutputStream fileOutputStream = null;
              try {
                  int i;
                  fileInputStream = new FileInputStream(path);
                  fileOutputStream = new FileOutputStream("C:\\Users\\Administrator\\Desktop\\笔记\\1.png");
                  while ((i=fileInputStream.read(b))!=-1) {
                      fileOutputStream.write(b,0,i);//一定要是用这个方法,表示每次输入实际读取到的值,否则如果文件大小超出数组大小,那么可能会出现数据损失
                  }
              } catch (Exception e) {
                  e.printStackTrace();
              }finally {
                  try {
                      fileInputStream.close();
                      fileOutputStream.close();
                  } catch (IOException e) {
                      e.printStackTrace();
                  }
              }
          }
      }
      ​
    • FileReader相关

      1. FileReader相关方法:

        1. new FileReader(File/String)

        2. read:每次读取单个字符,返回该字符,如果到文件末尾返回-1

        3. read(char[] c):批量读取多个字符到数组,返回读取到的字符数,如果到文件末尾返回-1

          相关API:

          1. new String(char[] c):将char[] c数组转换成String

          2. new String(char[] c,int off,int len):将char[] 的指定部分转换成String

    • FileWriter相关

      1. FileWriter常用方法:

        1. new FileWriter(File/String):覆盖模式,相当于流的指针在首端

        2. new FileWriter(File/String,true):追加模式,相当于流的指针在尾端

        3. write(int i):写入单个字符

        4. write(char[] c):写入指定数组

        5. write(char[] c, int off, int len):写入指定数组的指定部分

        6. write(String b):写入整个字符串

        7. write(String b,int off, int len):写入整个字符串的指定部分

        相关API:String类:toCharArray:将String转换成char[]

        注意:FileWriter使用后,必须要关闭(close)或刷新(flush),否则数据会一直存在于内存中,不会真正写入到指定文件!!!

    • 节点流和处理流

      1. 节点流可以从一个特定的数据源读写数据,如FileReader、FileWriter

      2. 处理流(也叫包装流)是“连接”在已存在的流(节点流或处理流)之上,为程序提供更为强大的读写功能,如BufferedReader,BufferedWriter

      3.  

      4. 处理流与节点流相比,其实就是对节点流进行包装,提供更多更具体,更强大的方法,来编写程序,如BufferedReder类中就有一个属性,Reader in,可以传入一个Reader的子类,即可以传入一个节点流,如此调用BufferedReader下的方法,来操作,传入节点流中的数据,这也叫修饰器模式

      5. 小结:处理流没有直接与数据源相连,而是调用了与数据源相连的数据流进行操作扩展

    • 节点流和处理流的区别和联系

      1. 节点流是底层流/低级流,直接跟数据源相接

      2. 处理流(包装流)包装节点流,既可以消除不同节点流的实现差异,也可以提供更方便的方法来完成输入输出

      3. 处理流对节点流进行包装,使用了修饰器设计模式,不会直接与数据源相连

        • 处理流功能主要体现在以下两个方面:

          1. 性能的提高,主要以增加缓冲的方式来提高输入输出的效率

          2. 操作的便捷:处理流可能提供了一系列更便捷的方法来一次输入输出大批量的数据,使用更加灵活方便

    • 处理流-BufferedReader和BufferedWriter

      1. BufferedReader和BufferedWriter属于字符流,是按照字符来读取数据的

      2. 关闭处理流,只需关闭外层流即可,因为在处理流内部实际调用的close方法,是内部包装的节点流的close方法

      3. BufferedReader读取文件:

        public class BufferedReader{
            public static void main(String[] args) throws IOException{
                String filePath="F:\\a.java";
                //创建BufferedReader
                BufferedReader bf = new BufferedReader(new FileReader(filePath));
                //读取
                String line;
                //说明
                //1.BufferedReader.readLine()是按行读取文件
                //2.当返回null时,表示文件读取完毕
                while((line=bf.readLine())!=null){
                    System.out.println(line)
                }
                
                //关闭相关流,只需关闭外层流,即BufferedReader流
                bf.close();
            }
        }
      4. BufferWriter输出文件

        public class BufferedReader{
            public static void main(String[] args) throws IOException{
                String filePath="F:\\e.txt";
                //创建BufferedWriter
                //如果是new FileReader(filePath,true)方式写入表示追加,而不是覆盖
                BufferedWriter bw = new BufferedWriter(new FileReader(filePath));
                //输出
                bw.write("hello");
                bw.newLine();//插入一个换行
                bw.write("hello2");
                
                //关闭相关流,只需关闭外层流,即BufferedReader流
                bw.close();
            }
        }
      • BufferedInputStream和BufferedOutputStream,这两个是字节流的处理流(包装流)

    • 对象处理流-ObjectInputStream和ObjectOutputStream

      • 首先看一个需求

      1. 将int num = 100 这个int数据保存到文件中,注意不是100数字,而是int 100,并且,能够从文件中直接回复int 100

      2. 将Dog dog = new Dog(“小黄”,3)这个dog对象保存到文件中,并且能够从文件文件恢复

      3. 上面的要求,就是能够将 基本数据类型 或者 对象 进行 序列化反序列化 操作

      • 序列化和反序列化

      1. 序列化就是在保存数据时,保存数据的值数据类型

      2. 反序列化就是在恢复数据时,恢复数据的值数据类型

      3. 需要让某个对象支持序列化机制,则必须让其类是可序列化的,为了让某个类是可序列化的,该类必须实现如下两个接口之一:

        Serializable //这是一个标记接口

        Externalizable

      4. ObjectOutputStream输出文件:

        package com.hsp.zaxiang;
        ​
        import java.io.FileOutputStream;
        import java.io.IOException;
        import java.io.ObjectOutputStream;
        import java.io.Serializable;
        ​
        public class ObjectOut_ {
            public static void main(String[] args) throws IOException {
                //序列化后,保存的文件格式,不是纯文本格式,而是按照数据自己的格式保存
                String filePath="F:\\data.dat";
        ​
                ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream(filePath));
        ​
                //序列化数据到F:\data.dat
                objectOutputStream.write(100);
                objectOutputStream.writeBoolean(true);
                objectOutputStream.writeChar('a');
                objectOutputStream.writeDouble(9.5);
                objectOutputStream.writeUTF("啦啦啦");
                objectOutputStream.writeObject(new Dog("旺财",10));
            }
        }
        class Dog implements Serializable {
            private String name;
            private int age;
        ​
            public Dog(String name, int age) {
                this.name = name;
                this.age = age;
            }
        }
      5. ObjectInputStream读取文件

        package com.hsp.zaxiang;
        ​
        import java.io.FileInputStream;
        import java.io.IOException;
        import java.io.ObjectInputStream;
        ​
        public class ObjectInputStream_ {
            public static void main(String[] args) throws IOException, ClassNotFoundException {
                //指定反序列化的文件
                String filePath = "F:\\data.dat";
        ​
                ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(filePath));
        ​
                //读取
                //1.读取(反序列化)的顺序需要和保存数据(序列化)的顺序一致
                //  否则会出现异常
                System.out.println(objectInputStream.read());
                System.out.println(objectInputStream.readBoolean());
                System.out.println(objectInputStream.readChar());
                System.out.println(objectInputStream.readDouble());
                System.out.println(objectInputStream.readUTF());
                System.out.println(objectInputStream.readObject());
            }
        }
        ​

         

      6. 注意事项和使用细节

        1. 读写顺序要一致

        2. 要求实现序列化或反序列化对象,需要实现Serializable

        3. 序列化的类中建议添加SerialVersionUID,为了提高版本兼容性

        4. 序列化对象时,默认将里面所有的属性都进行序列化,但除了static或transient修饰的成员

        5. 序列化对象时,要求里面属性的类型也需要实现序列化接口

        6. 序列化具备可继承性,也就是如果某类已经实现了序列化,则它的所有子类也已经默认实现了序列化

    • 标准输入输出流

      • 介绍

        1. System.in 标准输入 类型:InputStream 默认设备:键盘

          编译类型:InputStream 运行类型:BufferedInputStream

        2. System.out 标准输出 类型:PrintStream 默认设备:显示器

          编译类型:PrintStream 运行类型:PrintStream

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值