zoukankan      html  css  js  c++  java
  • JavaSE 学习笔记07丨IO流

    Chapter 13. IO流

    13.1 File类

    java.io.File类是文件(file)和目录(文件夹)(directory)路径名(path)的抽象表示,主要用于文件和目录的创建、查找和删除等操作。

    一个File对象,代表硬盘中实际存在的一个文件或者目录(文件夹)。

    • 绝对路径:从盘符开始的路径,是完整的路径。eg: D:\aaa\233.txt
    • 相对路径:相对于项目目录的路径,是便携的路径(开发常用)。eg: 233.txt

    13.1.1 构造方法

    无论该路径下是否存在文件或者目录,都不影响File对象的创建。

    • public File(String pathname):通过将给定的路径名字符串pathname转换为抽象路径名,以创建新的File实例。

      String pathname = "D:\aaa\233.txt";
      File myfile = new File(pathname); 
      
    • public File(String parent, String child):从父路径名字字符串和子路径名字符串,创建新的File实例。

      String parent = "D:\aaa";
      String name = "233.txt";
      File myfile = new File(parent, child);
      
    • public File(File parent, String child):从父抽象路径名和子路径名字符串,创建新的File实例。

      File parentDir = new File("D:\aaa");
      String child = "233.txt";
      File myfile = new File(parentDir, child);
      

    13.1.2 关于获取信息的方法

    • public String getAbsolutePath():返回此File绝对路径名的字符串。

      File f = new File("E:\素材\灵感\GVLOGO2333332.png");
      System.out.println("文件的绝对路径为:" + f.getAbsolutePath());
      //输出结果为:文件的绝对路径为:E:素材灵感GVLOGO2333332.png
      
      File f_2 = new File("灵感\GVLOGO2333332.png"); //传入相对路径
      System.out.println("文件的绝对路径为:" + f_2.getAbsolutePath()); //自动找到绝对路径,但不一定与实际相符。
      //输出结果为:E:IDEA-Projectsasic-codes灵感GVLOGO2333332.png
      
    • public String getPath():将此File转换为路径名的字符串。

      File f = new File("E:\素材\灵感\GVLOGO2333332.png");
      System.out.println("文件的构造路径为:" + f.getPath());
      //输出结果为:文件的构造路径为:E:素材灵感GVLOGO2333332.png
          
      File f_2 = new File("灵感\GVLOGO2333332.png");
      System.out.println("文件的构造路径为:" + f_2.getPath());
      //输出结果为:文件的构造路径为:灵感GVLOGO2333332.png
      
    • public String getName():返回由此File表示的文件或目录的名称字符串。

    • public long length():返回由此File表示的文件的大小(以字节为单位)。

    13.1.3 关于判断的方法

    • public boolean exists():此File表示的文件或目录是否实际存在。
    • public boolean isDirectory():此File表示的是否为目录
    • public boolean isFile():此File表示的是否为文件

    13.1.4 关于创建或删除的方法

    • public boolean createNewFile():当且仅当具有该名称的文件尚不存在时,创建一个新的空文件。当该文件在实际当中并不存在时,返回true(表示创建文件);否则,返回false(不会再创建)。

      File f = new File("233.txt");
      System.out.println("是否存在:" + f.exists()); //false
      System.out.println("是否创建:" + f.createNewFile()); //注意,该方法需要对IO异常进行处理 
      System.out.println("是否存在:" + f.exists()); //true
      

      注意,此方法只能创建文件,不能创建文件夹。且要保证创建文件的路径必须存在,否则抛出异常。

    • public boolean mkdir():创建由此File表示的单级空目录。

      File f = new File("newDir2333");
      System.out.println("是否存在:" + f.exists());
      System.out.println("是否创建:" + f.mkdir());
      System.out.println("是否存在:" + f.exists());
      

      public boolean mkdirs():创建由此File表示的目录,包括任何必需但不存在的父目录。也就说,既可创建单级空文件夹,也可创建多级文件夹。

      其中创建文件夹的路径和名称在构造方法中给出(即构造方法的参数)。当文件夹不存在时,方法会创建文件夹并返回true;当文件夹在实际中已经存在,则不会创建并返回false;当构造方法中给出的路径不存在,返回false

      File f = new File("newDir666\newDir668");
      System.out.println("是否创建:" + f.mkdirs()); //true
      File f2 = new File("newDir666\newDir668");
      System.out.println("是否存在:" + f2.mkdirs()); //false, 无需创建
      
    • public boolean delete():删除由此File表示的文件或目录。当文件或目录删除成功则返回true;当文件夹中有内容,则不会删除,且返回false;当构造方法中路径在实际中并不存在,返回false

      System.out.println(f.delete()); //true
      System.out.println(f2.delete()); //false
      

    13.1.5 目录的遍历

    • public String[] list():返回一个String数组,表示该File目录中所有子文件或目录的名称字符串。

    • public File[] listFiles():返回一个File数组,表示该File目录中所有子文件或目录的File对象(具有更多信息)。

      File dir = new File("E:\素材\灵感");
      //获取当前目录下的文件/文件夹名称
      String[] names = dir.list(); 
      for(String name : names) System.out.println(name);
      //获取当前目录下的文件/文件夹对象
      File[] files = dir.listFiles(); 
      for(File file : files) System.out.println(file);
      

      注意,调用上面两种方法的File对象,表示的必须是实际存在的目录,否则会抛出空指针异常。

    文件搜索的案例:

    搜索D:aaa目录中的.java文件。

    public class JustTest {
        public static void main(String[] args)  {
            String pathName = new Scanner(System.in).next();
            File fir = new File(pathName);
            findDir(fir);
        }
        public static void findDir(File dir){
            File[] files = dir.listFiles();
            for(File file : files){
                if(file.isFile()){ //是文件,到达递归基
                    if(file.getName().toLowerCase().endsWith(".java")){
                        System.out.println("文件:" + file.getAbsolutePath());
                    }
                }
                else{ //是目录,继续深搜
                    findDir(file);
                }
            }
        }
    }
    

    13.1.6 文件过滤器的优化

    java.io.FileFilter,是一个接口,是File的过滤器。该接口的对象,只有一个方法,可以传递给File类的listFiles(FileFilter)作为参数:

    • boolean accept(File pathname)

      测试pathname是否应该包含在当前File目录中,保留住则返回true,过滤掉则返回false。其保留规则:要么是.java文件;要么是目录,用于继续遍历。

      接口作为参数,则需传递子类对象,并覆写其中方法,我们选择匿名内部类方式较为方便。

      通过过滤器作业,listFiles(FileFilter)返回的数组元素中,子文件对象都是符合条件的,可直接打印。

    public class JustTest {
        public static void main(String[] args)  {
            String pathName = new Scanner(System.in).next();
            File fir = new File(pathName);
            findDir(fir);
        }
        public static void findDir(File dir){
            File[] files = dir.listFiles(new FileFilter() {
                @Override
                public boolean accept(File pathname) {
                    return pathname.getName().endsWith(".java") || pathname.isDirectory();
                }
            });
            for(File file : files){
                if(file.isFile()){ //是文件,到达递归基
                    if(file.getName().endsWith(".java")){
                        System.out.println("文件:" + file.getAbsolutePath());
                    }
                }
                else{ //是目录,继续深搜
                    findDir(file);
                }
            }
        }
    }
    

    13.2 I/O流概述

    Java中I/O操作主要指java.io包下的内容,进行输入、输出操作。

    13.2.1 IO的分类

    根据数据的流向分为:输入流和输出流。

    • 输入流:将数据从其他设备上读取到内存中的流。
    • 输出流:将数据从内存中写出到其他设备上的流。

    根据数据的类型分为:字节流和字符流。

    • 字节流:以字节为单位,读写数据的流。
    • 字符流:以字符为单位,读写数据的流。
    输入流 输出流
    字节流 字节输入流(InputStream 字节输出流(OutputStream
    字符流 字符输入流(Reader 字符输出流(Writer

    由于简单演示,在介绍字节流、字符流时,我们暂且将IO异常抛出。实际开发中,针对IO异常,应使用try...catch代码块进行处理。

    13.3 字节流

    13.3.1 字节输出流(OutputStream)

    java.io.OutputStream抽象类是表示字节输出流的所有类的超类,将指定的字节信息写出到目的地。它定义了字节输出流的几个基本方法:

    • public void close():关闭此输出流并释放与此流相关联的任何系统资源。
    • public void flush():刷新此输出流并强制写出所有缓冲的输出字节。
    • public void write(byte[] b):将b.length个字节从指定的b字节数组写出此输出流。
    • public void write(byte[] b, int off, int len):将指定的b字节数组中从偏移量off开始的len个字节写出此输出流。
    • public abstract void write(int b):写出一个字节的数据

    13.3.2 FileOutputStream 类

    java.io.FileOutputStream 类 是 文件输出流,它作为OutputStream中的一个子类,用于将数据写出到文件。

    一、构造方法

    • public FileOutputStream(File file):创建文件输出流以写入由指定的File对象表示的文件。
    • public FileOutputStream(String name):创建文件输出流写入以指定名称name的文件。

    当你创建一个流对象时,必须传入一个文件路径。若该路径下没有这个文件,则会自动创建;若该文件已存在,则会清空该文件中数据。举例如下:

    public class JustTest {
        public static void main(String[] args) throws FileNotFoundException {
            File f = new File("233.txt"); //使用File对象
            FileOutputStream fos = new FileOutputStream(f);
            FileOutputStream fos_2 = new FileOutputStream("b.txt"); //使用文件名称创建流对象
        }
    }
    

    二、写出字节数据

    1. 写出字节:调用write(int b)方法,每次可以写出一个字节数据。注意,尽管参数为int类型的四个字节,但只会保留一个字节的信息写出。

      public class JustTest {
          public static void main(String[] args) throws IOException {
              FileOutputStream fos = new FileOutputStream("233.txt");
              fos.write(97); //写入第一个字节,转换为字符即为'a'
              fos.write(98); //写入第二个字节,转换为字符即为'b'
              fos.write(99);
              fos.close(); //流操作完毕后必须调用close方法以释放系统资源
          }
      }
      // 233.txt文件中打印为:abc
      
    2. 写出字节数组:调用write(byte[] b)方法,每次可以写出数组中的数据。

      public class JustTest {
          public static void main(String[] args) throws IOException {
              FileOutputStream fos = new FileOutputStream("233.txt");
              byte[] b = "国奖我来了".getBytes(); //字符串转化为字节数组
              fos.write(b); //写出字节数组数据
              fos.close(); //关闭资源
          }
      }
      // 233.txt文件中打印为:国奖我来了
      
    3. 写出指定长度字节数组:调用write(byte[] b, int off, int len),每次写出时从off索引开始,以len个字节。

      public class JustTest {
          public static void main(String[] args) throws IOException {
              FileOutputStream fos = new FileOutputStream("233.txt");
              byte[] b = "abcdefgh".getBytes(); //字符串转化为字节数组
              fos.write(b, 3, 4); //写出字节数组数据
              fos.close(); //关闭资源
          }
      }
      // 233.txt文件中打印为:defg
      

    三、数据的追加续写

    对于第二点中的方法,每次程序运行,创建输出流对象时,都会清空目标文件中的数据。为了保留目标文件中的数据,可以使用下面的构造方法(相比于第一点的构造方法,需要多传入一个append参数):

    • public FileOutputStream(File file, boolean append)
    • public FileOutputStream(File file, boolean append)

    当传入的append参数为true时表示追加数据,false表示清空原有的数据。

    public class JustTest {
        public static void main(String[] args) throws IOException {
            FileOutputStream fos = new FileOutputStream("233.txt", true);
            byte[] b = "abcde".getBytes(); //字符串转化为字节数组
            fos.write(b);
            fos.close(); //关闭资源
        }
    }
    /* 对于 233.txt 文件
    操作前:ddd
    操作后:dddabcde
    */
    

    【附】写出换行

    • 回车符 :回到一行的开头(return)
    • 换行符 :下一行(newline)

    对于系统中的换行:

    • (Windows)系统,每行结尾为: (回车+换行)
    • (Unix)系统,每行结尾为: (换行)
    • (Mac)系统,每行结尾为: (回车),从(Mac OS X)开始与(Linux)统一。

    13.3.3 字节输入流(InputStream)

    java.io.InputStream 抽象类 是表示字节输入流所有类的超类,可以读取字节信息至内存中。它定义了字节输入流的几个基本方法:

    • public void close():关闭此输入流并释放与此流相关联的任何系统资源。
    • public abstract int read():从数据中读入一个字节,并返回该字节,该方法在碰到流的结尾时返回-1
    • public int read(byte[] b):读入一个字节数组,并返回实际读入的字节数,或者在碰到流的结尾时返回-1。注意,该方法最多读入b.length个字节。
    • public int read(byte[] b, int off, int len)

    13.3.4 FileInputStream 类

    java.io.FileInpurStream 类 是文件输入流,从文件中读入字节。

    一、构造方法

    • FileInputStream(File file):通过打开与实际文件的连接来创建一个FileInputStream,该文件由文件系统中的File对象file命名。
    • FileInputStream(String name):通过打开与实际文件的连接来创建一个 FileInputStream ,该文件由文件系统中的路径名 name 命名。

    注意,在创建一个流对象时,必须传入一个文件路径,该路径下如果没有该文件,会抛出FileNotFoundException

    public class JustTest {
        public static void main(String[] args) throws IOException {
            File file = new File("a.txt");
            FileInputStream fos = new FileInputStream(file); //使用File对象创建流对象
            FileInputStream fos_2 = new FileInputStream("233.txt"); //使用文件名称创建该流对象。
        }
    }
    

    二、读取字节数据

    1. 读取字节:调用read()方法,每次可读取一个字节数据,并提升为int类型,读取到文件末尾时,返回-1。(注意,文件中含有中文时,读入后会变成乱码!)

      public class JustTest {
          public static void main(String[] args) throws IOException {
              FileInputStream fis = new FileInputStream("read.txt");
              int b; //临时变量,保存读入一个字节数据转化为的int数据
              while( (b = fis.read()) != -1)
                  System.out.println((char)b);
              fis.close();
          }
      }
      
    2. 使用字节数组读取:调用read(byte[] b),每次读入b.length个字节数至数组中,并返回读取到的有效字节个数。同样地,当读取到末尾时,返回-1

      使用数组读入,使得每次读入多个字节,从而减少了系统间的IO操作次数,从而提高了读写的效率,建议开发中使用。

      public class JustTest {
          public static void main(String[] args) throws IOException {
              FileInputStream fis = new FileInputStream("read.txt");
              int len;
              byte[] b = new byte[2]; //定义2个字节长度的字节数组
              while((len = fis.read(b)) != -1){ //每次读取后,将数组的有效字节部分,转换为字符串并打印
                  System.out.println(new String(b, 0, len)); //len表示每次读取的有效字节个数!(想想边界条件)
              }
              fis.close();//关闭资源
          }
      }
      /*
      输出结果为
      ab
      cd
      e
      */
      

    13.3.5 字节流应用:图片复制

    复制原理:从已有文件中读入字节,将该字节写出到另一个文件中。

    public class JustTest {
        public static void main(String[] args) throws IOException {
            FileInputStream fis = new FileInputStream("E:\素材\灵感\GVLOGO23.png"); //字节输入流对象,构造方法绑定要读入的数据域
            FileOutputStream fos = new FileOutputStream("test_copy.jpg"); //字节输出流对象,绑定要写入的目的地
            byte[] b = new byte[1024];
            int cur;
            while ((cur = fis.read(b)) != -1){
                fos.write(b, 0, cur);
            }
            fos.close(); //先关闭输出流
            fis.close(); //后关闭输入流
        } //先开后关,后开先关。
    }
    

    13.4 字符流

    在存储文本字符串时,需要考虑字符编码(character encoding)方式,Java提供字符流类,以字符为单位读写数据,专门用于处理文本文件(如中文字符)。

    字符流,只能操作文本文件,不能操作图片、视频等非文本文件。当我们只希望 读或写 文本文件时,使用字符流;其他情况使用字节流。

    13.4.1 字符输入类(Reader)

    java.io.Reader 抽象类是表示用于读取字符流的所有类的超类,可读入字符信息至内存中。它定义了字符输入流的几个基本方法:

    与字节输入类InputStream的基本方法一致,只不过传入的是字符数组而不是字节数组

    • public void close():关闭此流并释放与此流相关联的任何系统资源。
    • public int read():从输入流读入一个字符。
    • public int read(char[] cbuf):从输入流中读取一些字符,并将他们存储到字符数组cbuf中。

    13.4.2 FileReader 类

    java.io.FileReader 类是读取字符文件的便利类,构造时使用系统默认的字符编码和默认字节缓冲区。其构造方法的实现就是使用了 FileInputStream (文件字节输入流)来实现

    字符编码:字节与字符的对应规则,(Windows)系统的中文编码默认为(GBK)编码表(一个汉字占用2个字节,简体中文),而IDEA中为(UTF-8)(一个汉字占用3个字节,8位UniCode转换格式)

    字节缓冲区:一个字节数组,用于临时存储字节数据。

    一、构造方法:

    • FileReader(File file):给定要读取的File对象以创建一个新的FileReader

    • FileReader(String fileName):给定要读取的文件的名称fileName以创建一个新的FileReader

      public class JustTest {
          public static void main(String[] args) throws IOException {
              File file = new File("233.txt");
              FileReader fr = new FileReader(file); //使用File对象创建流对象
              FileReader fr_2 = new FileReader("666.txt"); //使用文件名称创建流对象
          }
      }
      

    二、读取字符数据

    1. 读取字符:调用read方法,每次可读入一个字符的数据,并提升为int类型,读取到文件末尾时返回-1

      public class JustTest {
          public static void main(String[] args) throws IOException {
              FileReader fr = new FileReader("233.txt");
              int b;
              while((b = fr.read()) != -1){
                  System.out.println((char)b);
              }
              fr.close();
          }
      }
      
    2. 使用字符数组读入:调用read(char[] cbuf)方法,每次读入cbuf.length字符数至数组中,并返回读取到的有效字符个数。同样地,当读取到末尾时,返回-1

      public class JustTest {
          public static void main(String[] args) throws IOException {
              FileReader fr = new FileReader("233.txt");
              int len;
              char[] cbuf = new char[2];
              while((len = fr.read(cbuf)) != -1){
                  System.out.println(new String(cbuf, 0, len)); //要获取有效字符
              }
              fr.close();
          }
      }
      

    13.4.3 字符输出流(Writer)

    java.io.Writer 抽象类 是表示用于写出字符流的所有类的超类,将指定的字符信息写出到目的地,它定义了字符输出流的几个基本方法:

    • void write(int c):写入单个字符。
    • void write(char[] cbuf):写入字符数组。
    • abstract void write(char[] cbuf, int off, int len):写入字符数组的某一部分,以off作为开始索引,len个需写入的字符个数。
    • void write(String str):写入字符串。
    • void write(String str, int off, int len):写入字符串的某一部分,以off作为开始索引,len个需写入的字符个数。
    • void flush():刷新该流的缓冲。
    • void close():关闭此输出流但需要先刷新它。

    13.4.4 FileWriter 类

    java.io.FileWriter类是写出字符至文件的便利类,构造时使用默认的字符编码和默认字节缓冲区。

    一、构造方法

    • FileWriter(File file):给定要读入的File对象,创建一个新的FileWriter

    • FileWriter(String fileName):给定要读入的文件名称,创建一个新的FileWriter

      public class JustTest {
          public static void main(String[] args) throws IOException {
              File file = new File("233.txt");
              FileWriter fw = new FileWriter(file); //使用File对象创建流对象
              FileWriter fw_2 = new FileWriter("666.txt"); //使用文件名称创建流对象
          }
      }
      

    二、写出字符数据

    1. 写出字符:调用write(int b)方法,每次可写出一个字符数据(至内存缓冲区,是字符转换为字节的过程)。

      关于关闭和刷新:关闭一个输出流的同时,也会冲刷用于该输出流的缓冲区:所有被临时置于缓冲区中,以使用更大的包的形式传递的字符在关闭输出流时都将被送出。特别地,如果不关闭文件,那么写出字节的最后一个包可能将永远得不到传递。

      如果既想写出数据,又想继续使用流,则需要flush方法。

      • flush:刷新缓冲区,但流对象仍可继续使用。
      • close:先刷新缓冲区,然后通知系统释放资源,此时流对象不可再被使用。
      public class JustTest {
          public static void main(String[] args) throws IOException {
              FileWriter fw = new FileWriter("233.txt");
              fw.write('刷');
              fw.flush();
              fw.write('新');
              fw.write("
      "); //写出换行!
              fw.flush();
              fw.write('关');
              fw.close();
              /*输出结果为:
              刷新
              关
              */
              //fw.write('闭'); //关闭后再写入会抛出异常
          }
      }
      
    2. 写出字符数组:调用write(char[] cbuf)write(char[] cbuf, int off, int len),每次可以写出字符数组中的数据。

      public class JustTest {
          public static void main(String[] args) throws IOException {
              FileWriter fw = new FileWriter("233.txt");
              char[] arr = "你好打工人".toCharArray();
              fw.write(arr); //你好打工人
              fw.write(arr, 2, 3); //打工人
              fw.close();
          }
      }
      
    3. 写出字符串:调用write(String str)write(String str, int off, int len),每次可写出字符串中的数据。

      public class JustTest {
          public static void main(String[] args) throws IOException {
              FileWriter fw = new FileWriter("233.txt");
              String msg = "你好打工人";
              fw.write(msg); //你好打工人
              fw.write(msg, 2, 3); //打工人
              fw.close();
          }
      }
      

    13.5 IO 异常的处理

    13.5.1 JDK7前的处理

    public class JustTest {
        public static void main(String[] args) {
            FileWriter fw = null; //注意,应在外面定义这个file对象(别忘了赋初值)
            try {
                fw = new FileWriter("233.txt");
                fw.write("早安打工人!");
            } catch (IOException e){
                e.printStackTrace();
            } finally { //关闭流资源
                try{
                    if(fw != null) //fw有可能为空,为空则不能再调用其方法。
                        fw.close(); //然而,close也有可能抛出异常
                } catch (IOException e){
                    e.printStackTrace();
                }
            }
        }
    }
    

    13.5.2 JDK7的处理

    可以使用JDK7优化后的try-with-resource语句,该语句确保了每个资源在语句结束时关闭。所谓的资源(resource),指在程序完成后必须关闭的对象。

    格式为:

    try(创建流对象语句){ //可以使用多个,若有多个则使用';'隔开
        //读写数据
    } catch(IOException e){
        e.printStackTrace();
    }
    

    使用举例:

    public class JustTest {
        public static void main(String[] args) {
            try (FileWriter fw = new FileWriter("233.txt")){
                fw.write("早安打工人!");
            } catch (IOException e){
                e.printStackTrace();
            }
        }
    }
    

    13.6 属性集

    java.util.Properties继承于Hashtable,以表示一个持久的属性集,它使用键-值结果存储数据,每个键及其对应值默认是字符串.

    Properties类 被许多Java类使用,如获取系统属性时,System.getProperties方法即为返回一个Properties对象。

    一、构造方法

    • public Properties():创建一个空的属性列表。

    二、基本的存储方法

    • public Object setProperty(String key, String value):保存一对属性。

    • public String getProperty(String key):使用此属性列表中指定的键key搜索属性值。

    • public Set<String> stringPropertyNames():获取由所有键的名称组成的集合

      public class JustTest {
          public static void main(String[] args) {
              Properties mypro = new Properties();
              //添加键值对元素
              mypro.setProperty("filename", "233.txt");
              mypro.setProperty("length", "998244353");
              mypro.setProperty("location", "D:\233.txt");
              System.out.println(mypro); //打印属性集对象
              //通过键获取属性值
              System.out.println(mypro.getProperty("filename"));
              System.out.println(mypro.getProperty("length"));
              //遍历属性值,获取由所有键组成的集合
              Set<String> mystr = mypro.stringPropertyNames();
              for(String key : mystr){
                  System.out.println(key + " : " + mypro.getProperty(key));
              }
          }
      }
      

    三、与流相关的方法

    • public void load(InputStream inStream):从字节输入流inStream中读入键值对。通过流对象,可关联至硬盘中保存的某文件中,读取到集合中使用。

    • public void load(Reader reader) :与上同理,传入的是字符输入流。

      注意:

      1. 文本中的数据,必须是键值对形式,可以使用空格、等号、冒号(英文)等符号作为分隔符。
      2. 可以使用#进行注释,被注释的键值对不会被读入。
      /* 233.txt文件中 */
      #save data
      #Sun Nov 15 11:26:58 CST 2020
      姓名:打工人
      年龄=19岁
      居住地 广州
      #学校:TSH
          
      /* JustTest.java文件中 */
      public class JustTest {
          public static void main(String[] args) throws IOException {
              Properties mypro = new Properties();
              mypro.load(new FileReader("233.txt")); //字符输入流
              Set<String> myset = mypro.stringPropertyNames();
              for(String key : myset){
                  System.out.println(key + "----" + mypro.getProperty(key));
              }
          }
      }
      
      /* 输出结果 */
      姓名----打工人
      居住地----广州
      年龄----20岁
      

    13.7 缓冲流

    缓冲流,也叫高效流,是对4个基本的FileXxx流的增强。其基本原理即是,在创建流对象时,创建一个内置的默认大小的缓冲区数组,通过缓冲区的读写,减少系统IO次数,从而提高读写效率。

    按数据类型分类有:

    • 字节缓冲流:BufferedInputStreamBufferedOutputStream
    • 字符缓冲流:BufferedReaderBufferedWriter

    13.7.1 字节缓冲流

    构造方法:

    • public BufferedInputStream(InputStream in):创建一个新的缓冲输入流。
    • public BufferedOutputStream(OutputStream out):创建一个新的缓冲输出流。

    使用举例:

    public class JustTest {
        public static void main(String[] args) throws IOException {
            long st = System.currentTimeMillis();
            try( //创建流对象
                BufferedInputStream bis = new BufferedInputStream(new FileInputStream("E:\素材\灵感\GVLOGO233.png"));
                BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("E:\素材\copy.png"));
            ){  //读写数据
                int b;
                while((b = bis.read()) != -1){
                    bos.write(b);
                }
            } catch (IOException e){
                e.printStackTrace();
            }
            long ed = System.currentTimeMillis();
            System.out.println("缓冲流复制时间:" + (ed - st) + "ms!");
        }
    }
    /*输出结果:
    缓冲流复制时间:82ms!
    */
    

    13.7.2 字符缓冲流

    构造方法:

    • public BufferedReader(Reader in):创建一个新的缓冲输入流。
    • public BufferedWriter(Writer out) :创建一个新的缓冲输出流。

    字符缓冲流的基本方法与普通字符流调用方式一致,但也具备特有的方法:

    • 对于 BufferedReaderpublic String readLine():读一行文字。

      public class JustTest {
          public static void main(String[] args) throws IOException {
              BufferedReader br = new BufferedReader(new FileReader("E:\IDEA-Projects\basic-codes\233.txt"));
              String myline = null; //保存读入的一行字符
              while((myline = br.readLine()) != null){ //读取【每】一行的字符
                  System.out.println(myline); 
                  System.out.println("------------");
              }
              br.close();
          }
      }
      
    • 对于 BufferedWriterpublic void newLine():写出一行 行分隔符,由系统属性定义符号。

      public class JustTest {
          public static void main(String[] args) throws IOException {
              BufferedWriter bw = new BufferedWriter(new FileWriter("666.txt"));
              bw.write("早安");
              bw.newLine(); //换行
              bw.write("打工人");
              bw.write("好起来了!");
              bw.close();
          }
      }
      /*输出结果:
      早安
      打工人好起来了!
      */
      

    13.7.3 缓冲流应用:文本排序

    将下列文本信息按段编号恢复顺序:

    public class JustTest {
        public static void main(String[] args) throws IOException {
            HashMap<String, String> lineMap = new HashMap<>(); //Map集合,序号作为key,段落文字内容作为value
            BufferedReader br = new BufferedReader(new FileReader("in.txt"));
            BufferedWriter bw = new BufferedWriter(new FileWriter("out.txt"));
            String myline = null;
            while((myline = br.readLine()) != null){
                String[] twoStrs = myline.split("\."); //将一行文字以.作为分隔符,分隔为两个字符串。
                lineMap.put(twoStrs[0], twoStrs[1]);
            }
            br.close();
            for(int i = 1; i <= lineMap.size(); i++){
                String keyStr = String.valueOf(i); //将数字key转换为字符串类型
                String valStr = lineMap.get(keyStr); //查找对应的value字符串
                bw.write(keyStr + "." + valStr); //写出段落
                bw.newLine(); //换行!
            }
            bw.close();
        }
    }
    

    13.8 转换流

    13.8.1 字符编码和字符集

    字符编码(Character Encoding):一套自然语言的字符与二进制数之间的对应规则。按其相应规则,将字符存储到计算机中,称为编码;将存储在计算机中的二进制数按照相应规则解析显示出来,称为解码。

    字符集(Charset):也称编码表,是一个系统支持的所有字符的集合,包括各国文字、标点符号、图形符号、数字等。当指定其常见的字符集有:ASCII字符集、GBK字符集、Unicode字符集等。

    当指定了编码,它所对应的字符集也即指定下来。

    • ASCII 字符集 :用于显示现代英文,主要包括控制字符(回车键、退格、换行键等)和可显示字符(英文大小写字符、阿拉伯数字、西文符号)。

      • 基本ASCII字符集使用7位表示一个字符(共128个)
      • 拓展字符集使用8位表示一个字符(共256个)。
    • ISO-8859-1 字符集:用于显示欧洲使用的语言。

    • GBxxx 字符集:用于显示中文而设计的一套字符集

      • GBK:最常用的中文码表。
      • GB18030:最新的中文码表。每个字可由1个、2个或4个字节组成。
    • Unicode 字符集:也称统一码、标准万国码,为表达任意语言的任意字符而设计。最多使用4个字节的数字来表达每个字母、符号或者文字。

      • UTF-8编码:最常用,可以用来表示Unicode标准中任何字符,它是电子邮件、网页及其他存储或传送文字的应用 中,优先采用的编码。其编码规则有:128个US-ASCII字符,只需一个字节编码;拉丁文等字符,需要二个字节编码;大部分常用字(含中文),使用三个字节编码;其他极少使用的Unicode辅助字符,使用四字节编码。
      • UTF-16
      • UTF-32

    转换流即是字节与字符间的桥梁。

    13.8.2 InputStreamReader 类

    转换流java.io.InputStreamReader,是Reader子类,是从字节流到字符流的桥梁,利用它读入字节,并使用指定的字符集(由名称指定,可接受平台默认字符集),将其字节解码为字符

    构造方法:

    • InputStreamReader(InputStream in, String charsetName):创建一个指定字符集的字符流

    使用举例:

    public class JustTest {
        public static void main(String[] args) throws IOException {
            String myfile = "E:\IDEA-Projects\666.txt";
            InputStreamReader isr = new InputStreamReader(new FileInputStream(myfile)); //创建流对象,默认UTF-8编码
            InputStreamReader isr2 = new InputStreamReader(new FileInputStream(myfile)); //创建流对象,指定GBK编码
            int b; 
            while((b = isr.read()) != -1){ //使用默认编码字符流读入,乱码
                System.out.println((char)b); //有可能出现乱码
            }
            isr.close();
            while((b = isr2.read()) != -1){ //使用指定编码字符流读入,正常解析
                System.out.println((char)b); //正常显示
            }
            isr.close();
        }
    }
    

    13.8.3 OutputStreamWriter 类

    转换流java.io.OutputStreamWriter,是Writer子类,是从字符流至字节流的桥梁,使用指定的字符集,将字符编码为字节

    构造方法:

    • OutputStreamWriter(OutputStream in, String charsetName):创建一个指定字符集的字节流。

    使用举例:

    public class JustTest {
        public static void main(String[] args) throws IOException {
            String myfile = "E:\IDEA-Projects\666.txt";
            OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(myfile)); //默认UTF-8编码
            osw.write("早安"); //保存6个字节
            osw.close();
            String myfile_2 = "E:\IDEA-Projects\123.txt";
            OutputStreamWriter osw2 = new OutputStreamWriter(new FileOutputStream(myfile_2), "GBK"); //指定GBK编码
            osw2.write("打工人"); //保存为4个字节
            osw2.close();
        }
    }
    

    13.8.4 转换流应用:转换文件编码

    将GBK编码的文本文件,转换为UTF-8编码的文本文件。

    public class JustTest {
        public static void main(String[] args) throws IOException {
            String srcFile = "E:\IDEA-Projects\666.txt";
            InputStreamReader isr = new InputStreamReader(new FileInputStream(srcFile), "GBK"); //转换输入流,指定GBK编码
            String destFile = "E:\IDEA-Projects\233.txt";
            OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(destFile));//转换输出流,默认UTF-8编码
    
            char[] cbuf = new char[1024];
            int len;
            while((len = isr.read(cbuf)) != -1){ //读入GBK编码的文本文件
                osw.write(cbuf, 0, len); //写出UTF-8编码的文本文件
            }
            osw.close();
            isr.close();
        }
    }
    

    13.9 序列化

    Java 提供了一种对象序列化的机制,用一个字节序列表示一个对象,该字节序列包含该「对象的数据」、「对象的类型」和「对象中存储的数据」信息。当字节序列写出到文件之后,相当于文件中持久保存了一个对象的信息。

    该字节序列还可从文件中读入,重构对象,对其进行反序列化

    13.9.1 ObjectOutputStream 类

    java.io.ObjectOutputStream 类,将Java对象的原始数据类型写出至文件中,实现对象的持久存储。

    构造方法:

    • public ObjectOutputStream(OutputStream out):创建一个指定OutputStreamObjectOutputStream

    序列化操作:

    1. 一个对象若想要序列化,必须同时满足两个条件:

      • 该类必须实现java.io.Serializable 接口(该接口是一个标记接口)。若不实现此接口的类,则不会使任何状态序列化或反序列化,抛出NotSerializableException

      • 该类所有属性必须是可序列化的,若有一个属性不需要可序列化,则该属性必须注明是瞬态的,使用transient关键字修饰。

        public class Student implements  java.io.Serializable{ //实现Serializable的接口
            public String name;
            public String No;
            public transient String address; //transient瞬态修饰成员,不会被序列化
            public void nameCheck(){
                System.out.println("学号为" + No + "的学生姓名为:" + name);
            }
        } //为演示方便,部分方法暂忽略
        
    2. 写出对象的方法

      • public final void writeObject(Object obj):将指定对象写出。
      public class JustTest {
          public static void main(String[] args) {
              Student a = new Student();
              a.name = "Luffy"; a.No = "23333"; a.address = "Brazil";
              try{
                  ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("E:\IDEA-Projects\666.txt"));
                  oos.writeObject(a); //写出对象,但注意该方法会抛出IO异常
                  oos.close();
                  System.out.println("Serialized data is saved successfully!");
              } catch (IOException i){ //捕获IO异常
                  i.printStackTrace();
              }
          }
      }
      

    13.9.2 ObjectInputStream 类

    ObjectInputStream反序列流,就之前使用的ObjectOutputStream序列化的原始数据恢复为对象。

    构造方法:

    • public ObjectInputStream(InputStream in):创建一个指定InputStreamObjectInputStream

    反序列化操作:

    若能找到一个对象的.class文件,即可进行反序列化操作,调用ObjectInputStream 读入对象的方法:

    • public final Object readObject():读入一个对象。注意,JVM反序列化对象时,必须能够找到传入的.class文件的类,否则抛出ClassNotFoundException 异常。

      public class JustTest {
          public static void main(String[] args) {
              Student a = null;
              try{
                  FileInputStream fileIn = new FileInputStream("E:\IDEA-Projects\666.txt");
                  ObjectInputStream ois = new ObjectInputStream(fileIn); //传入字节输入流参数
                  a = (Student) ois.readObject(); //读入对象,返回Object类需强转一下
                  ois.close();
                  fileIn.close();
              } catch (IOException i){
                  i.printStackTrace();
                  return;
              } catch (ClassNotFoundException c){ //readObject会抛出这类异常
                  System.out.println("Student class can't be found!");
                  c.printStackTrace();
                  return;
              }
              System.out.println("Name:" + a.name + " Address:" + a.address); //若无异常则直接打印输出。
          }
      }
      

      尽管JVM反序列化对象时能够找到.class,但注意.class文件在序列化对象之后发生了修改,则反序列化操作有可能失败,抛出个 InvalidClassException 异常。发生该异常的原因如下:

      • 该类的序列版本号与从流中读入的类描述符的版本号不匹配
      • 该类包含未知数据类型。
      • 该类没有可访问的无参构造方法。

      Serializable 接口给需要序列化的类提供一个默认的序列版本号serialVersionUID,该版本号目的在于验证序列化的对象和对应类是否版本匹配。当然,也可以自定义版本号,从而避免序列化后再修改.class文件所发生的冲突异常。

      public class Student implements java.io.Serializable{ //实现Serializable的接口
          public String name;
          public String No;
          public transient String address; //transient瞬态修饰成员,不会被序列化
          private static final long serialVersionUID = 1L; //加入序列版本号
          public int eid; //假定我序列化之后再添加该属性再编译,是能够反序列化的(前提是你不能使用默认的序列版本号),该属性赋为默认值。
          public void nameCheck(){
              System.out.println("学号为" + No + "的学生姓名为:" + name);
          }
      }
      

    13.9.3 序列化集合

    练习:将存有多个自定义对象的集合进行序列化,并保存至文件中。然后再反序列化该文件,遍历集合打印对象信息。

    public class JustTest {
        public static void main(String[] args) throws Exception {
            Student a = new Student("Luffy", "001");
            Student b = new Student("Ben", "002");
            Student c = new Student("Nami", "003");
            ArrayList<Student> arr = new ArrayList<>();
            arr.add(a); arr.add(b); arr.add(c);
            serialise(arr); //序列化
    
            //反序列化
            ObjectInputStream ois = new ObjectInputStream(new FileInputStream("E:\IDEA-Projects\666.txt"));
            ArrayList<Student> mylist = (ArrayList<Student>) ois.readObject();
            for(int i = 0; i < mylist.size(); i++){
                Student cur = mylist.get(i);
                System.out.println(cur.getName() + "---" + cur.getNo());
            }
        }
        private static void serialise(ArrayList<Student> arr) throws IOException {
            ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("E:\IDEA-Projects\666.txt"));
            oos.writeObject(arr);
            oos.close();
        }
    }
    

    执行结果:

    13.10 打印流

    控制台打印输出即调用了print()/println()方法等,这些方法均来自于java.io.PrintStream 类,该类能够方便地打印各种数据类型的值。

    13.10.1 PrintStream 类

    java.io.PrintStream继承自父类OutputStream,故也有close()write()flush()等共性方法。

    特点:

    1. 只负责数据的输出,不负责数据的读取。
    2. 与其他输出流不同,它永远不会抛出IOException。(但还是能够抛出FileNotFoundException 异常)

    构造方法:

    创建打印流PrintStream对象,其构造方法需传入 要输出的目的地(必须存在) 作为参数。

    • public PrintStream(File file):输出目的地为一个文件。
    • public PrintStream(OutputStream out):输出目的地为一个字节输出流。

    特有方法:

    • void print(任意类型)

    • void println(任意类型):输出包括换行

      若使用继承自父类的write方法写出数据,它会查询对应的编码表(如97->'a')并将其解码输出;若使用自己特有的方法print()println(),写出的数据原样输出(如97->97)

    另外,通过调用System.setOut()方法,可以将输出语句的目的地,改变为参数中传递的打印流的目的地(默认情况下,是控制台输出的)

    • static void setOut(PrintStream out):重新分配“标准”输出流。

      public class Hello {
          public static void main(String[] args) throws FileNotFoundException {
              System.out.println("我在控制台输出!");
              PrintStream ps = new PrintStream("C:\Users\admin\IdeaProjects\MyIdeaProjects\666.txt");
              System.out.println(ps);
              System.out.println("我在打印流的目的地中输出!");
              ps.close();
          }
      }
      
  • 相关阅读:
    python应用之文件属性浏览
    python进阶之路之文件处理
    magento安装时的数据库访问错误
    magento麦进斗客户地址属性不保存在sales_flat_order_address
    自动填写麦进斗Magento进货地址字段
    麦进斗magentoRequireJs回调失败
    如何在麦进斗magento2中调用站外的JS?
    在magento1.9结账地址中删除验证
    麦进斗:在windows系统里面刷新magento2的缓存
    如何安装麦进斗Magento2
  • 原文地址:https://www.cnblogs.com/J-StrawHat/p/13967219.html
Copyright © 2011-2022 走看看