转载

Java输入/输出流基础(IO流)

  • 输入流:将外设中的数据读取到内存中。
  • 输出流:将内存中的数据写到外设中。

按字节,字符分类

  • 字节流:传输过程中,传输数据的最基本单位是字节的流。
  • 字符流:传输过程中,传输数据的最基本单位是字符的流。

二、字符流

由来

字节流读取字节数据后,先不操作数据,而是去查找指定的编码表,然后再对文字进行操作。简单来说,字符流=字节流+编码表。

操作文本的字符流

向文件中写数据

FileWriter

主要构造方法:

  • new FileWriter(String path):如果此文件不存在,则会自动创建此文件;如果,此文件已经存在,则会覆盖此文件;下同。
  • new FileWriter(String path,boolean append):如果append为true,那么表示可以追加数据(也就是多次使用write方法,不会把以前写入文本的数据覆盖掉,可以在文本后面追加数据),下同。
  • new FileWriter(File file)
  • new FileWriter(File file,boolean append)

实用方法:

  • write(String str):把字符串str写入到缓冲区中
  • write(char []ch):把字符数组ch写入到缓冲区中
  • write(char ch[],int start,int offset):把字符数组ch中从start开始的长度为offset的字符写入到缓冲区中
  • write(int c):把字符c写入缓冲区中
  • flush():把缓冲区中的数据刷新到磁盘中
  • close():关闭流,关闭资源。在关闭之前要先刷新数据(close会自动调用flush方法)

换行:

  • 因为在不同的操作系统中,换行符是不一样的,所以需要适配操作系统。Java中已经为我们解决了该问题。
  • 换行符的获取:final String line=System.getProperty("line.separator");

IO异常的处理:

  • 标准写法:
FileWriter fw = null;
        try {
            fw = new FileWriter("G:/xxx.txt", true);
            fw.write("窗外的麻雀,在电线杆上多嘴,你说这一句,很有夏天的感觉");
        } catch (IOException e) {
            System.out.println(e.getMessage());
        } finally {
            // 只有当fw不为空的时候,才能关闭资源,否则会有空指针异常
            if (fw != null) {
                try {
                    fw.close();
                } catch (IOException e) {
                    throw new RuntimeException("老铁,关闭资源失败!");
                }
            }
        }
复制代码

读取文件中的数据

FileReader

构造方法:

  • new FileReader(String path)
  • new FileReader(File file)

实用方法:

  • int read():每次读取一个字符,返回读到的字符(int类型);如果返回的是-1,那么就表示已经读到了流的末尾。
  • int read(char ch[]):每次返回读取到的字符数,即读到了几个字符

该方法原理图:

Java输入/输出流基础(IO流)
  • int read(char ch[],int start,int len)
  • close():关闭资源。

小练习(复制并粘贴一个文本文件)

  • 思路:使用FileReader不断地把文本从源文件中读取出来,并把读到的字符(或字符数组)用FileWriter写入到另一个文件。
  • 注意点:复制的过程中可能会产生中文乱码,这是因为编码方式不一致导致的:FileReader读取字符以及FileWriter写字符时的编码方式是根据操作系统的编码方式来的,而txt文本的编码方式可能会与上述编码方式不一致,所以会产生乱码。
  • 文本文件复制原理图:
Java输入/输出流基础(IO流)
  • 实现代码:
public void test01() {
        FileReader fr = null;
        FileWriter fw = null;
        try {
            fr = new FileReader("G://系统属性.txt");
            fw = new FileWriter("F://系统属性.txt", true);
            char ch[] = new char[1024];
            int len = -1;
            while ((len = fr.read(ch)) != -1) {
                fw.write(ch, 0, len);
                fw.flush();
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (fr != null) {
                try {
                    fr.close();
                } catch (IOException e) {
                    throw new RuntimeException("异常......");
                }
            }
            if (fw != null) {
                try {
                    fw.close();
                } catch (IOException e) {
                    throw new RuntimeException("异常......");
                }
            }
        }
    }
复制代码

缓冲区

BufferedWriter

构造方法:

  • new BufferedWriter(Writer writer):创建BufferedWriter时必须把字符输出流传入,这相当于把资源传入。缓冲区大小为默认。
  • new BufferedWriter(Writer writer,int size):指定缓冲区大小为size。

实用方法:

  • write(String str)
  • write(int c)
  • write(char ch[])
  • write(char ch[],int start,int len)
  • newLine():换行。
  • flush():刷新,最好每使用缓冲流写一次数据就刷新一次。
  • close():关闭资源。 注意: 关闭BufferedWriter后,传入的字符输出流也已经被关闭了

BufferedReader

构造方法:

  • new BufferedReader(Reader reader):创建BufferedReader,并把Reader传入。缓冲区大小为默认。
  • new BufferedReader(Reader reader,int size):指定缓冲区大小为size。

实用方法:

  • int read()
  • int read(char ch[]):读取字符,并存入字符数组,读取规律同Reader。
  • int read(char ch[],int start,int len)。
  • String readlLine():读取一行文本,返回读取到的字符串,如果读取到流的末尾,则返回空。

read()以及readLine()方法原理图:

Java输入/输出流基础(IO流)

LineNumberReader

介绍:LineNumberReader是BufferedReader的子类,LineNumberReader记录了当前行号,也可以获取/设置行号。

构造方法:

  • new LineNumberReader(Reader reader)

实用方法:

  • String readLine()
  • read(char ch[])
  • read(char[],int start,int len)
  • int getLineNumber():获取读取的当前行(是第几行)
  • void setLineNumber():行数默认是0,此时就从第一行开始读取;如果自己设置行为n,那么就从第n+1行开始读取。

自定义缓冲区

代码如下:

//自定义BufferedReader
public class MyBufferedReader {
    private FileReader fr;
    private char ch[] = new char[10]; //缓冲区
    private int pos = -1; //缓冲区中指针的位置
    private int count = 0; //缓冲区中元素个数

    //构造方法
    public MyBufferedReader(FileReader fr) {
        this.fr = fr;
    }

    //自定义read方法
    //br中的read方法读取数据时从缓冲区中读取
    public int myRead() throws IOException {
        //缓冲区中元素个数为0
        if (this.count == 0) {
            pos = -1; //角标赋值为-1
            //从数据源中读取数据存入缓冲区
            int len = fr.read(this.ch);
            if (len == -1)
                return -1;
            this.count = len;
            //从缓冲区中读取一个字符
            --this.count; //缓冲区中的字符减少一个
            return (int) ch[++pos];
        } else {
            //缓冲区有数据
            --this.count;
            return (int) ch[++pos];
        }
    }

    //读取一行文本
    public String myReadLine() throws IOException {
        StringBuffer sb = new StringBuffer();
        int len = 0;
        while ((len = this.myRead()) != -1) {
            char c = (char) len;
            if (c == '/r')
                continue; //遇到'/r'继续读取
            if (c == '/n')
                //直接就return,
                // 因为如果跳出了循环,那么就没法分清是读取到了流末尾出的循环,
                // 还是用break跳出的循环
                return sb.toString();
            sb.append(c);
        }
        return null; //返回null表示上面的循环结束,即为读到了流末尾
    }

    public void close() throws IOException {
        fr.close();
    }
}
复制代码

装帧设计模式

  • 介绍:装帧设计模式是为了使类的功能更加强大。
  • 实现原理: -请看如下代码:
public class Person {

    public void eat() {
        System.out.println("恰饭!!!");
    }
}

public class NewPerson {
    private Person p;

    public NewPerson(Person p) {
        this.p = p;
    }

    public void eat() {
        System.out.println("饭前喝汤");
        p.eat();
        System.out.println("饭后甜点");
    }
}
复制代码
  • 装帧设计模式与继承的区别:
    • 装饰类比继承更加精炼

三、字符流

FileInputStream

构造方法:

  • new FileInputStream(String path)
  • new FileInputStream(File file)

实用方法:

  • int read():读取一个字节的数据。若返回-1表示读取到了流的末尾。
  • int read(byte b[]):读取数组b的长度的字节数据进入b数组中,返回读取到的字节数,返回-1表示读到了流的末尾。详细的读取数据到数组b中的过程同上,此处不再赘述。
  • int read(byte b[],int off,int len)
  • int available():返回文件中可以读取的数据的估计值(返回字节大小);本方法可以在创建字节数组时定义数组的大小,不过,当文件很大的时候,不能用此方法。

FileOutputStream

构造方法:

  • new FileOutputStream(String path)
  • new FileOutputStream(File file)
  • new FileOutputStream(String path,boolean flag)
  • new FileOutputStream(File file,boolean flag)

实用方法

  • void write(byte b[])
  • void write(byte b[],int off,int len)
  • void write(int b):写入一个字符(char),只不过是以int来表示。

BufferedInputStream

构造方法:

  • new BufferedInputStream(InputStream fis)
  • new BufferedInputStream(InputStream fis, int size)

实用方法:

  • int read()
  • int read(byte []b)
  • int read(byte []b,int off,int len)
  • close()

BufferedOutputStream

构造方法:

  • new BufferedOutputStream(OutputStream fos)
  • new BufferedOutputStream(OutputStream fos,int size)

实用方法:

  • void write(byte []b)
  • void write(byte []b,int off,int len)
  • void write(int ch)

四、转换流

解释

  • 看不懂:磁盘中的存储的数据都是“看不懂”的,也就是以字节形式存储的。
  • 看得懂:内存中存储的数据都是“看得懂”的,也就是以字符形式存储的。

InputStreamReader

  • new InputStreamReader(InputStream in):
  • new InputStreamReader(InputStream in,String charsetName):需要传入一个InputStream对象,采用当前平台(操作系统)的解码方式来解码。

OutputStreamWriter:使用指定的解码方式来解码。

  • 把“看得懂”的数据转换成"看不懂"的数据,即把字符流编码成字节流。
  • new OutputStreamWriter(OutputStream out):需要传入一个OutputStream对象,使用当前平台(操作系统)的编码方式来编码。
  • new OutputStreamWriter(OutputStream out,String charsetName):使用指定的编码方式来编码。

转换流原理图

Java输入/输出流基础(IO流)

以指定编码方式来读取某文本

遇到这种需求时,就需要用到转换流,因为只有转换流才能指定编解码格式。GBK把一个汉字解析成两个字节,UTF-8把一个汉字解析成三个字节。所以,如果写入文本用的编码格式与读取文本用的解码格式不一样的话,就会读取到乱码(也可能是问号?)。 代码如下:

@Test
    //使用指定编码方式写入一个字符串到文本文件中
    public void test03() throws IOException {
        OutputStreamWriter outputStreamWriter =
                new OutputStreamWriter(new FileOutputStream("G://转换流.txt"), "GBK");
        outputStreamWriter.write("你好,大兄弟!!");
        outputStreamWriter.flush();
    }

    @Test
    //使用指定的解码格式读取文本文件中的字符串
    public void test04() throws IOException {
        InputStreamReader inputStreamReader =
                new InputStreamReader(new FileInputStream("G://转换流.txt"), "UTF-8");
        char[] ch = new char[10];
        int len = -1;
        while ((len = inputStreamReader.read(ch)) != -1) {
            System.out.println(new String(ch, 0, len));
        }
    }
复制代码

五、File

构造方法:

  • new File(String path):根据路径path把该文件(夹)封装成一个对象。
  • new File(String parentPath,String childPath):根据两个字符串的路径,把该文件(夹)封装成对象。这样做的好处是可以灵活填写路径。
  • new File(File f,String path):根据文件对象f以及路径path把对应的文件(夹)封装成对象。

File里面的字段(final类型):

  • 与系统有关的默认名称分隔符
    • File.separator:String类型。Windows下是"",Linux下是"/"。
    • File.separatorChar:char类型。
  • 与系统有关的路径分隔符
    • File.pathSeparator: 此字符用于分隔以路径列表形式给定的文件序列中的文件名,在 UNIX 系统上此字段为:,在 Microsoft Windows 系统上,它为;。
    • File.pathSeparatorChar:char类型。

常用方法:

  • 获取
    • 文件(夹)名字:String getName()
    • 文件(夹)路径
      • 相对路径:String getPath();是相对于项目所处的路径,File构造方法里面传入什么路径,getPath就获取什么路径。因为路径里面不写磁盘的话,那就一定是相对路径。写了磁盘就是绝对路径。
      • 绝对路径:String getAbsolutePath()
    • 文件(夹)大小:long length();返回字节大小。
    • 文件(夹)最后修改时间:long lastModified(),返回距离1970年1月1日的毫秒数。
    • 系统目录及其容量
      • static File [] listRoots():获取系统目录(可以理解为磁盘目录),返回一个File数组。
      • long getFreeSpace():获取该目录(磁盘)的可用空间,返回字节数(long类型)。
      • long getTotalSpace():获取该目录(磁盘)的总空间,返回字节数(long类型)。
      • long getUsableSpace():获取该目录(磁盘)可以被虚拟机使用的空间,返回字节数(long类型)。
    • 当前文件夹里面的文件或文件夹名字
      • String [] list()
      • String [] list(FilenameFilter filter):传入一个过滤器(通过文件名字来过滤),过滤器的解释请看下文。
      • 注意:File封装的必须是一个文件夹(目录),如果封装的是一个文件,则会抛空指针异常。
    • 当前文件夹里面的文件(夹)
      • File [] listFiles():获取某个文件夹里面的文件(夹),返回File数组,可以用来操作文件,而不仅仅是获取文件名字。
      • File [] listFiles(FilenameFilter filter):传入一个过滤器(通过文件名字来过滤)。
      • File [] listFiles(FileFilter filter):传入一个过滤器(通过文件来过滤)。
    • 过滤器解释
      • 过滤器分为两种,一种是FilenameFilter(通过文件名字来过滤,比如可以过滤掉非.txt后缀的文件),另一种是FileFilter(通过文件来过滤,比如可以过滤掉隐藏文件)。
      • FilenameFilter:FilenameFilter是一个接口,它里面有一个抽象方法boolean accept(File f,String name),文件的过滤就是通过该方法来过滤的。自定义过滤器实现该接口后,实现accept()方法,如果该方法返回true,那么就留下当前遍历到的文件,否则就不留下。
      • FileFilter:FileFilter过滤器的原理与FilenameFilter的原理是一样的,只不过该接口里面的抽象方法是boolean accept(File f),方法参数里面只有一个File对象,并没有文件名。因为该过滤器是通过File来过滤的,与文件名无关。
      • 过滤器原理图
        Java输入/输出流基础(IO流)
  • 创建与删除
    • 文件
      • 创建:boolean createNewFile();根据文件的路径创建一个文件,如果该文件存在,则不创建并返回false;否则创建并返回true。下同,自己体会。(与IO流那边是不同的,那边每一次都是直接覆盖)。
      • 删除:boolean delete();删除该文件。删除成功返回true,删除失败返回false。下同,自己体会。如果某个文件里面有内容,比如File f=new File("G:/a"),而a文件里面有其他的文件或文件夹,那么f.delete()就会失败。Windows删除文件是从里面往外面删的。
    • 文件
      • 创建:
        • boolean mkdir():创建单个文件夹,例如只创建一个a文件夹。
        • boolean mkdirs():创建多层文件夹,例如创建a/b/c/d/e/f。
      • 删除:boolean delete();删除该文件。如果File f=new File("G:/a/b/c/d/e/f"),那么此时定位的文件夹是f,故f.delete()删除的是f文件夹,其余文件夹不会被删除。
  • 判断
    • boolean exists():判断文件(夹)是否存在,存在则返回true,否则返回false。
    • boolean isFile():判断是否为文件。
    • boolean isHidden():判断文件(夹)是否为隐藏文件。
    • boolean isDirectory():判断是否为文件夹。
    • boolean canRead():判断该文件(夹)是否可读。
    • boolean canWrite():判断该文件(夹)是否可写。
    • boolean canExecute():判断该文件(夹)是否可运行。
  • 重命名
    • boolean renameTo(File des):如果源文件src和目标文件des在同一个磁盘下,那么src.renameTo(des)则会以des的名字来重命名src。如果src和des不在同一个磁盘里,那么src.renameTo(des)则会把src文件剪切到des所在的磁盘,并以des的名字重命名src。
原文  https://juejin.im/post/5ebf9375e51d453914498bd0
正文到此结束
Loading...