zoukankan      html  css  js  c++  java
  • Java语言基础13—IO

    I/O(输入/输出)


    参考资料:《Java从入门到精通》/明日科技编著. 4版. 北京:清华大学出版社,2016

    一、流概述

    • 流是一种有序的数据序列,根据操作的类型,可分为输入流输出流
    • I/O(Input/Output,输入/输出)流提供了一条通道程序,可以用这条通道把源中的字节序列送到目的地。
    • 虽然I/O流通常与磁盘有关,但是程序的源和目的地也可以是键盘、鼠标、内存或显示器窗口等。

    Java由数据流处理输入/输出模式,程序从指向源的输入流中读取数据,数据源可以是文件、网络、压缩包或其他数据源,这个过程也称为数据的读取,如下图所示:

    image

    输出流是指向数据要到达的目的地,程序通过向输出流中写入数据,把信息传递到目的地,输出流的目的地可以是文件、网络、压缩包或其他输出目标,该过程也称为数据的写入,如下图所示:

    image

    总的来说,在Java程序中,要想从文件中读取数据或者将数据写入文件中,都需要在程序和文件之间建立一条数据的传输通道,这个通道就是输入/输出流。

    • 当程序创建输入流对象时,Java会自动创建输入流通道;
    • 当程序创建输出流对象时,Java会自动创建输出流通道。

    二、输入/输出流

    Java语言在java.io包中定义了很多处理各种输入/输出的类。

    • 字节流
    1. 抽象类InputStream(字节输入流)是所有输入字节流类的父类
    2. 抽象类OutputStream(字节输出流)是所有输出字节流类的父类
    • 字符流
    1. 抽象类Reader(字符输入流)是所有输入字符流类的父类
    2. 抽象类Writer(字符输出流)是所有输出字符流类的父类

    字节流与字符流的区别
    image

    简而言之,字节流在操作文件时不会用到缓存(内存),而是直接操作文件本身。而字符流在操作的时候使用了缓存区,而不是直接操作文件。例如,使用字符流执行写入数据操作,如果使用close()方法没有关闭输出流,则文件中看不到任何写入的内容,除非使用flush()方法强行将缓存数据写入目标文件。

    本质上来说,一切都是字节流,其实没有字符流这个东西。字符只是根据编码集对字节进行解码的产物。

    字节流:01010100010010100101
    字符流:aah65Khannskm3bhbasv  //本质上还是字节流
    

    技巧】:字节流可以处理所有数据文件,若处理的是纯文本数据,建议使用字符流。

    1、输入流

    (1)字节输入流

    InputStream类是字节输入流的抽象类,是所有字节输入流的父类,其层次结构如下所示:
    image

    该类中所有方法遇到错误时都会引发IOException异常,下面是该类的一些常用方法:

    • read ():从输入流中读取数据的下一个字节。
    • read (byte[] b):从输入流中读入一定长度的字节,并以整数形式返回字节数。
    • mark (int readlimit):在输入流的当前位置放置一个标记,readlimit参数告知此输入流在标记位置失效之前允许读取的字节数。
    • reset ():将输入指针返回到当前所做的标记处。
    • skip (long n):跳过输入流上的n个字符并返回实际跳过的字节数。
    • close() :关闭此输入流,并释放与该流相关的所有系统资源。

    说明:并不是所有的InputStream类的子类都支持InputStream中定义的所有方法,如skip()、mark()、reset()等方法只对某些子类有用。

    (2)字符输入流

    Java中的字符是Unicode编码,是双字节的,即一个字符占用两个字节。InputStream是用来处理字节的,并不适合处理字符文本。Java为字符文本的输入专门提供了一套单独的类Reader,但是Reader类并不是InputStream类的替换者,只是在处理字符串时简化了编程。

    Reader类是字符输入流的抽象类,是所有字符输入流的父类

    2、输出流

    (1)字节输出流

    OutputStream类是字节输出流的抽象类,该类是所有表示字节输出流类的父类,其层次结构如下所示:
    image

    OutputStream类中的所有方法均返回void,在遇到错误时会引发IOException异常,下面对OutputStream类中的方法做简单的介绍:

    • write (int b):将指定的字节写入此输出流。
    • write (byte[] b):将b个字节从指定的byte数组写入此输出流。
    • write (byte[] b, int off, int len):将指定byte数组中从偏移量off开始的len个字节写入此输出流。
    • flush():彻底完成输出并清空缓存区。
    • close():关闭输出流。

    (2)字符输出流

    Writer类是字符输出流的抽象类,此类为所有字符输出流类的父类,其层次结构如下所示:
    image

    三、File类

    • File类是java.io包中唯一代表磁盘文件本身的对象。
    • 可以通过调用File类中的方法,实现创建、删除、重命名文件等操作。
    • File类的对象主要用来获取文件本身的一些信息,如文件所在的目录、文件的长度、文件的读写权限等,但是不能对文件执行读取或写入数据的操作
    • 数据流可以将数据写入到文件中,文件也是数据流最常用的数据媒介。

    1、文件的创建与删除

    可以使用File类创建一个文件对象。通常使用以下3种构造方法来创建文件对象:

    1. File (String pathname)

    该构造方法通过将给定的路径名字符串,并转换为抽象路径名来创建一个新的File实例。语法格式如下:

    new File(String pathname)
    

    其中,pathname指路径名称(包含文件名),例如:

    File file = new File("C:UsersXULIANGabc.txt") 
    
    1. File (String parent, String child)

    该构造方法根据定义的父路径和子路径字符串(包含文件名)创建一个新的File对象。语法格式如下:

    new File (String parent, String child)
    
    • parent:父路径字符串。例如,"C:Users"。
    • child:子路径字符串。例如,"letter.txt"。

    】那何为父路径与子路径呢?

    这里举个例子,关于"C:UsersXULIANGDocuments"这个目录,"C:"是"C:Users"的父目录,"C:Users"是"C:UsersXULIANG"的父目录。简而言之,父目录就是当前目录的上级目录。

    1. File (File f, String child)

    该构造方法根据parent抽象路径名和子路径字符串创建一个新的File对象。语法格式如下:

    new File (File f, String child)
    
    • f:父路径对象。例如,"D:/doc/"。
    • child:子路径字符串。例如,"letter.txt"。
    import java.io.File;
    
    public class FileTest {
        public static void main(String[] args) {
            //创建文件对象,并指定文件名
            File file = new File("test.txt");
            //判断该文件是否存在
            if (file.exists()){
                //若存在,则删除文件
                file.delete();
                System.out.println("文件已删除");
            }else {
                //捕捉可能出现的异常
                try {
                    //若不存在,则创建该文件
                    file.createNewFile();
                    System.out.println("文件已创建");
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
        }
    }
    
    运行结果:
    文件已创建
    

    上述代码执行后,会在该项目所在的路径下创建一个名为"test.txt"的文本文件。

    3、获取文件信息

    File类提供了很多方法用于获取文件的一些信息,常用的方法如下所示:

    方法 返回值 说明
    getName() String 获取文件的名称
    canRead() boolean 判断文件是否可读
    canWrite() boolean 判断文件是否可写
    exits() boolean 判断文件是否存在
    length() long 获取文件的长度(以字节为单位)

    下面通过实例来介绍使用方法来获取文件的信息:

    import java.io.File;
    
    public class FileTest2 {
        public static void main(String[] args) {
            File file = new File("test.txt");
            if(file.exists()){
                String name = file.getName();
                long length = file.length();
                Boolean hidden = file.isHidden();
                System.out.println("文件的名字:" + name);
                System.out.println("文件的长度:" + length);
                System.out.println("是否为隐藏文件:" + hidden);
            }
        }
    }
    
    运行结果:
    文件的名字:test.txt
    文件的长度:0
    是否为隐藏文件:false
    

    四、文件输入/输出流

    在程序的运行期间,大部分数据都保存在内存中进行操作,当程序结束后或关闭时,这些数据将消失。如果需要将数据永久保存,可使用文件输入/输出流与指定的文件建立连接,将需要的数据永久保存到文件中。

    1、FileInputStream和FileOutputStream类

    FileInputStream和FileOutputStream类都是用来操作磁盘文件

    (1)FileInputStream类

    FileInputStream类继承于InputStream类,满足比较简单的文件读取要求。FileInputStream类常用的构造方法有两种:

    • FileInputStream (String name)
    • FileInputStream (File file)

    第一种构造方法使用给定文件名name,创建一个FileInputStream对象。第二种构造方法使用File对象创建FileInputStream对象,该构造方法允许在把文件连接输入流之前对文件作进一步分析。

    (2)FileOutputStream类

    FileOutputStream类与FileInputStream类具有相同的构造方法,创建一个FileOutputStream

    对象时,可以指定不存在的文件名,但此文件不能是一个被其他程序打开的文件。

    import java.io.File;
    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    import java.io.IOException;
    
    public class FileTest3 {
        public static void main(String[] args) {   
            File file = new File("word.txt");
            System.out.println("---------------写入操作----------------");
            try{
                FileOutputStream fileOutputStream = new FileOutputStream(file);
                //字符串转化成字节码形式,采用Java平台默认字符集进行编码
                byte[] bytes = "Java基础班学习".getBytes();
                System.out.println(""Java基础班学习"字符串的字节码形式:");
                //遍历数组
                for (int i = 0; i < bytes.length; i++) {
                    System.out.print(bytes[i] + " ");
                }
                System.out.println("
    总共 " + bytes.length + " 个字节");
                //执行写入文件操作
                fileOutputStream.write(bytes);
                //关闭输出流
                fileOutputStream.close();
            }catch (IOException e){
                e.printStackTrace();
            }
            System.out.println("---------------读取操作----------------");
            try{
                FileInputStream inputStream = new FileInputStream(file);
                byte bytes[] = new byte[100];
                //读取文件的数据内容,然后保存到bytes数组中,返回字节的个数
                int str = inputStream.read(bytes);
                System.out.println("该文件内容所占用的字节个数为:" + str);
                /*            
                * 将数组中的字节进行解码,并转化为字符串
                * 采用Java平台默认字符集进行解码
                * 形参0表示起始索引位置,str表示截取长度
                */
                String content = new String(bytes,0, str);
                System.out.println("文件中的内容为:" + content);
                //关闭输入流
                inputStream.close();
            }catch (IOException e){
                e.printStackTrace();
            }
        }
    }
    
    运行结果:
    ---------------写入操作----------------
    "Java基础班学习"字符串的字节码形式:
    74 97 118 97 -27 -97 -70 -25 -95 -128 -25 -113 -83 -27 -83 -90 -28 -71 -96 
    总共 19 个字节
    ---------------读取操作----------------
    该文件内容所占用的字节个数为:19
    文件中的内容为:Java基础班学习
    

    注意】虽说Java在程序结束时自动关闭所有打开的流,但是当使用完流后,应该显示地关闭所有打开地流仍是一个良好的习惯,一个被打开的流有可能耗尽系统资源。

    2、FileReader和FileWriter类

    使用FileOutputStream类向文件中写入数据与FileInputStream类从文件中读取内容,都存在一点不足,即这两个类都只提供了对字节或字节数组的读取方法。由于汉字在文件中占两个字节,如果使用字节流,读取不好可能会出现乱码现象,此时采用字符流Reader或Writer类即可避免这种现象。

    FileReader和FileWriter字符流对应了FileInputStream和FileOutputStream类。FileReader流顺序地读取文件,只要不关闭流,每次调用read()方法就会顺序地读取源中地内容,直到源的末尾或流被关闭。

    五、带缓存的输入/输出流

    • 缓存是I/O的一种性能优化
    • 缓存流为了I/O流增加了内存缓存区,有了缓存区,使得在流上执行skip()mark()和reset()方法都成为可能。

    1、BufferedInputStream和BufferedOutputStream类

    BufferedInputStream类可以对所有InputStream类进行缓存区的包装以达到性能优化的目的。BufferedInputStream类有两个构造方法:

    • BufferedInputStream (InputStream in)
    • BufferedInputStream (InputStream in, int size)

    第一种形式的构造方法创建了一个带有32个字节的缓存流,第二种形式的构造方法按指定的大小来创建缓存区。BufferedInputStream读取文件的过程如下所示:

           数据流                 数据流                          数据流
    文件  ------->  InputStream  ------->  BufferedInputStream  -------> 目的地
    

    使用BufferedOutputStream输出信息和OutputStream输出信息完全一样,只不过BufferedOutputStream有一个flush()方法将缓存区的数据强制写入文件中。BufferedOutputStream也有两个构造方法:

    • BufferedOutputStream (InputStream in)
    • BufferedOutputStream (InputStream in, int size)

    第一种形式的构造方法创建了一个带有32个字节的缓存流,第二种形式的构造方法按指定的大小来创建缓存区。

    注意】flush()方法用于即使在缓存区没有满的情况下,也将缓存区的内容强制写入到外设,习惯上将这个过程称为刷新。

    2、BufferedReader与BufferedWriter类

    BufferedReader与BufferedWriter类分别继承于Reader和Writer类,这两个类同样具有内部缓存机制,并可以以为单位进行输入/输出。

    BufferedReader类常用的方法:

    • read() 方法:读取单个字符。
    • readLine()方法:读取一个文本,并将其返回为字符串。若无数据可读则返回null。

    BufferedWriter类的方法都返回void,常用的方法:

    • write(String s, int off, int len)方法:写入字符串的某一个部分。
    • flush()方法:刷新该流的缓存。
    • newLine()方法:写入一个行分隔符

    在使用BufferedWriter类的write()方法时,数据并没有立刻被写入输出流,而是首先进入缓存区中。如果想立即将缓存区中的数据写入输出流时,必须调用flush()方法。

    import java.io.*;
    
    public class Student {
        public static void main(String[] args) {
            String content[] = {"你好吗","好久不见","记得常联系"};
            File file = new File("word.txt");
            try{
                FileWriter fw = new FileWriter(file);
                BufferedWriter bw = new BufferedWriter(fw);
                for (int i = 0; i < content.length; i++) {
                    bw.write(content[i]);   //将字符数组的元素写入磁盘文件
                    bw.newLine();        //写入行分隔符
                }
                bw.close();        //关闭BufferedWriter流
                fw.close();        //关闭FileWriter流
            }catch (IOException e){
                e.printStackTrace();
            }
            try {
                FileReader fr = new FileReader(file);
                BufferedReader bufr = new BufferedReader(fr);
                String str = null;
                int i = 0;
                //读取文件的一行,若不为null,则进入循环
    
                while ((str = bufr.readLine()) != null){
                    i++;
                    System.out.println("第" + i + "行:" + str);
                }
                bufr.close();     //关闭BufferedReader流
                fr.close();       //关闭FileReader流
            }catch (IOException e){
                e.printStackTrace();
            }
    
        }
    }
    
    运行结果:
    第1行:你好吗
    第2行:好久不见
    第3行:记得常联系
    
  • 相关阅读:
    redis学习
    Ubuntu命令大全
    关于jquery中attr和prop的用法
    Ubuntu下修改为永久DNS的方法
    Yii2 behaviors中verbs access的一些理解
    vue_ form表单 v-model
    vue-one_demo_music
    ES6
    VUE 入门 01
    Django model.py表单设置默认值允许为空
  • 原文地址:https://www.cnblogs.com/xuliang-daydayup/p/12946038.html
Copyright © 2011-2022 走看看