File与IO流

Maven依赖

<!--使用properties设置自定义标签可以统一版本号-->
<properties>
    <java.version>4.11</java.version>
</properties>
<!--junit测试依赖-->
<dependencies>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>${java.version}</version>
        <!--<scope>test</scope>-->
    </dependency>
</dependencies>

static String pathSeparator 与系统有关的路径分隔符,为了方便,它表示为一个字符串
static char pathSeparatorChar

String pathSeparator = File.pathSeparator;
System.out.println(pathSeparator);//路径分隔符 Windows:分号;   linux:冒号;

static String separator 与系统有关的默认名称分隔符,为了方便,它表示为一个字符串。
static char separatorChar 与系统有关的默认名称分隔符。

String separator = File.separator;
System.out.println(separator);//文件名称分隔符 Windows:反斜杠\    linux:正斜杠/
/*
        static  String pathSeparator 与系统有关的路径分隔符,为了方便,它表示为一个字符串
        static  char pathSeparatorChar

        static  String separator    与系统有关的默认名称分隔符,为了方便,它表示为一个字符串。
        static  char    separatorChar   与系统有关的默认名称分隔符。

        操作系统:路径不能写死了
        C:\develop\a\a.txt  windows
        C:/develop/a/a.txt  linux
        在Windows中和linux中都可以用
        "C:"+File.pathSeparator+"develop"+File.pathSeparator+"a"+File.pathSeparator+"a.txt"
         */
        String pathSeparator = File.pathSeparator;
        System.out.println(pathSeparator);//路径分隔符 Windows:分号;   linux:冒号;
        String separator = File.separator;
        System.out.println(separator);//文件名称分隔符 Windows:反斜杠\    linux:正斜杠/

路径:

绝对路径:是一个完整的路径
以盘符(C:,D:)开始的路径
C:\a.txt
D:\demo\b.txt
相对路径:是一个简化的路径
相对指的是相对于当前项目的跟目录(C:\Program Files\Application Verifier)
如果当前项目的跟目录,路径可以简化书写
C:\Program Files\Application Verifier\123.txt-->简化为:123.txt(可以省略项目的根目录)

注意:

1.路径是不区分大小写
2.路径中的文件名称分隔符Windows使用反斜杠,反斜杠是转义字符,两个反斜杠代表一个普通的反斜杠

File(String pathname)通过将给定路径名 字符串转换为抽象路径名来创建一个新 File 类

/*
     * File(String pathname)通过将给定路径名 字符串转换为抽象路径名来创建一个新 File 类
     * 参数:
     *   String pathname:字符串的路径名称
     *   路径可以是以文件结尾,也可以是以文件夹结尾
     *   路径可以是相对路径也可以是绝对路劲
     *   路劲是可以存在的也可以是不存在的
     *   创建File对象,只是把字符串路劲封装为File对象,不考虑路劲的真假情况
     * */
private static void show01() {
        File file1 = new File("D:\\Work\\Word\\a.txt");
        System.out.println(file1);//重写了Object类的toString方法 D:\Work\Word\a.txt
        File file2 = new File("D:\\Work\\Word");
        System.out.println(file2);//文件夹 D:\Work\Word
        File file3 = new File("b.txt");
        System.out.println(file3);//相对路径 b.txt
    }

File(String parent,String child) 根据parent路径名 字符串和child路径名 字符串创建一个新的File实例。

 /*
        File(String parent,String child) 根据parent路径名 字符串和child路径名 字符串创建一个新的File实例。
        参数:把路径分成了两部份
            String parent:父路径
            String child:子路径
        好处:父路径和子路径,可以单独书写,使用起来非常灵活,父路径和子路径都可以变化。
     */
    private static void show02(String parent, String child) {
        File file = new File(parent, child);
        System.out.println(file);
    }

File(File parent,String child) 根据parent路径名 字符串和child路径名 字符串创建一个新的File实例。

/*
        File(File parent,String child) 根据parent路径名 字符串和child路径名 字符串创建一个新的File实例。
        参数:把路径分成了两部份
            File parent:父路径
            String child:子路径
        好处:
            父路径和子路径,可以单独书写,使用起来非常灵活,父路径和子路径都可以变化。
            父路径是File类型,可以使用File的方法对路径进行一些操作,再使用路径创建对象。
     */
    private static void show03() {
        File fi=new File("D:","aa");
        File file = new File(fi, "aa.txt");
        System.out.println(file);
    }

public String getAbsolutePath();返回此File的绝对路径名字字符串
public String getPath();将此File转换为路径名 字符串
public long length():返回由此File表示的文件的长度。

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

public String getAbsolutePath();返回此File的绝对路径名字字符串
获取的构造方法中传递的路径
无论路径是绝对的还是相对的,getAbsolutePath()方法返回的都是绝对路径

private static void show01() {
        File file = new File("C:\\Users\\小熊打倒的饭团\\IdeaProjects\\first\\a.txt");
        String absolutePath = file.getAbsolutePath();
        System.out.println(absolutePath);
        File fi = new File("b.txt");
        String absolutePath2 = fi.getAbsolutePath();
        System.out.println(absolutePath2);
    }

public String getPath()

public String getPath();将此File转换为路径名 字符串
获取的构造方法中传递的路径
toString方法调用的就是getPath方法
源码:
public String toString() {
return getPath();
}

private static void show03() {
        File file1 = new File("C:\\Users\\小熊打倒的饭团\\IdeaProjects\\first\\a.txt");//是一个文件
        String name1 = file1.getName();
        System.out.println(name1);//a.txt
        File file2=new File("C:\\Users\\小熊打倒的饭团\\IdeaProjects\\first");//是一个文件夹
        String name2=file2.getName();
        System.out.println(name2);//first
    }

public long length()

public long length():返回由此File表示的文件的长度。
获取的是构造方法指定的文件大小,以字节为单位
注意:
文件如果没有大小概念的,不能获取文件夹的大小
如果构造方法中给出的路径它不存在,那么length返回0

private static void show04(){
        //返回文件大小,以字节为单位:313626
        File file1=new File("D:\\aaa.jpg");
        long length1=file1.length();
        System.out.println(length1);
        //文件不存在返回0
        File file2=new File("D:\\aa.jpg");
        long length2=file2.length();
        System.out.println(length2);
        //文件夹也返回0:8192
        File file3=new File("D:\\Software");
        long length3=file3.length();
        System.out.println(length3);
    }

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

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

public boolean exists():此File表示文件或目录是否实际存在。
用于判断构造方法中的路径是否存在
存在返回:true
不存在返回:false

    @Test
    public  void show01() {
        //绝对路径
        File file1 = new File("D:\\aaa.txt");
        //判断文件是否存在
        System.out.println(file1.exists());//true
        //相对路径
        //D:\workPaper\firstdemo\b.txt
        File file2 = new File("b.txt");
        //获取文件的路径
        System.out.println(file2.getAbsolutePath());
        //判断文件是否存在
        System.out.println(file2.exists());//false
    }

public boolean isDirectory():此File表示是否为目录

public boolean isDirectory():此File表示是否为目录
用于判断构造方法中给定的路径是否为以文件夹结尾
是的话返回:true
不是的话返回:false
注意:
电脑硬盘中只有文件/文件夹,两个方法是互斥的
这两个方法使用前提,路径必须是存在的,否则都返回false

    @Test
    public void show02() {
        //绝对路径
        File file1 = new File("D:\\aaa.txt");
        //判断文件是否存在
        System.out.println(file1.exists());//true
        //判断构造方法中给定的路径是否为以文件夹结尾
        System.out.println(file1.isDirectory());//false
        //判断构造方法中给定的路径是否为以文件结尾
        System.out.println(file1.isFile());//true
    }

public boolean createNewFile():当且仅当该文件不存在时,创建一个新的文件
public boolean delete():删除由此File表示的文件或目录
public boolean mkdir():创建由此File表示的目录
public boolean mkdirs():创建由此File表示的目录,包括任何必需但不存在的父目录

public boolean createNewFile():当且仅当该文件不存在时,创建一个新的文件创建文件的路径和名称在构造方法中给出(构造方法的参数)

public boolean createNewFile():当且仅当该文件不存在时,创建一个新的文件
创建文件的路径和名称在构造方法中给出(构造方法的参数)
返回值:布尔值
true:文件不存在,创建文件,返回true
false:文件存在,不创建文件,返回false
注意:
1.此方法只能用于创建文件,不能用于创建文件夹
2.创建文件的路径必须存在,否则会抛出异常
public boolean createNewFile() IOException
createNewFile声明抛出了IOException,我们调用这个方法,就必须处理异常,要么throws,要么try、catch

    @Test
    public void show01() {
        //文件创建
        File file1=new File("aaa.txt");
        File file2=new File("aaa\\aaa.txt");
        System.out.println("是否存在相对目录下的aaa.txt:"+file1.exists());//false
        try {
            System.out.println("是否创建相对目录下aaa.txt:"+file1.createNewFile());//true
            System.out.println("是否创建相对目录下aaa\\aaa.txt:"+file2.createNewFile());//路径不存在会抛出异常,创建失败
        } catch (IOException e) {
            e.printStackTrace();
            System.err.println("路径不存在,创建失败");
        }
        System.out.println("是否存在相对目录下aaa.txt:"+file1.exists());//true
        System.out.println("是否存在相对目录下aaa\\aaa.txt:"+file2.exists());//false
    }

public boolean mkdir():创建由此File表示的目录

public boolean mkdirs():创建由此File表示的目录,包括任何必需但不存在的父目录

public boolean mkdir():创建由此File表示的目录
public boolean mkdirs():创建由此File表示的目录,包括任何必需但不存在的父目录
创建文件夹的路径和名称在构造方法中给出(构造方法的参数)
返回:布尔值
true:文件夹不存在,创建文件夹,返回true
false:文件夹存在,不创建文件夹,返回false;构造方法中给出的路径不存在返回false
注意:
1.此方法只能用来创建文件夹,不能用来创建文件
2.创建文件夹的路径必须存在,否则会抛出异常

    @Test
    public void show02(){
        //目录创建
        File file1=new File("newDir");
        File file2=new File("111\\222\\333");//文件夹上级目录不存在
        System.out.println("是否存在相对目录下的newDir文件夹:"+file1.exists());//false
        System.out.println("是否创建相对目录下的newDir文件夹:"+file1.mkdir());//true
        System.out.println("是否存在相对目录下的newDir文件夹:"+file1.exists());//true
        System.out.println("是否存在相对目录下的111\\222\\333文件夹:"+file2.exists());//false
        System.out.println("是否创建相对目录下的111\\222\\333文件夹:"+file2.mkdir());//false
        System.out.println("是否存在相对目录下的111\\222\\333文件夹:"+file2.exists());//false
    }
     @Test
    public void show03(){
        //创建多级目录
        File file=new File("111\\222\\333");//文件夹上级目录不存在
        System.out.println("是否存在相对目录下的111\\222\\333文件夹:"+file.exists());//false
        System.out.println("是否创建相对目录下的111\\222\\333文件夹:"+file.mkdirs());//false
        System.out.println("是否存在相对目录下的111\\222\\333文件夹:"+file.exists());//false
    }

public boolean delete():删除由此File表示的文件或目录

public boolean delete():删除由此File表示的文件或目录
此方法,可以删除构造方法路径中给出的文件/文件夹
返回值:布尔值
true:文件/文件夹删除成功,返回true
false:文件夹中有内容(文件有内容直接删除),不会删除放回false;构造方法中给出的路径不存在返回false
注意:
delete方法是直接在硬盘中删除文件/文件夹,不走回收站,删除要谨慎。
多级目录不能直接全部删除

    @Test
    public void show04(){
        File file1=new File("aaa.txt");
        File file2=new File("newDir");
        File file3=new File("111\\222\\333");
        System.out.println("是否删除相对路径下的aaa.txt:"+file1.delete());//true
        System.out.println("是否删除相对路径下的newDir:"+file2.delete());//true
        System.out.println("是否删除相对路径下的111\\222\\333:"+file3.delete());//false
    }

File类遍历(文件夹)目录功能

public String[] list():返回一个String数组,表示该File目录中所有子文件或目录
public File[] listFiles():返回一个File数组,表示该File目录中的所有子文件或目录
注意:
list方法和listFiles方法遍历的是构造方法中给出的目录
如果构造方法中给出的目录不存在则会抛出空指针异常
如果构造方法中给出的路径它不是一个目录,也会抛出空指针异常java.lang.NullPointerException

public String[] list():返回一个String数组,表示该File目录中所有子文件或目录.

public String[] list():返回一个String数组,表示该File目录中所有子文件或目录.
遍历构造方法中给出的目录,会获取目录中所有文件/文件夹的名称,把获取到的多个名称存储到一个String数组中
能够获取到隐藏的文件夹

@Test
    public void show01(){
        File file=new File("D:\\workPaper\\firstdemo");
        String[] array=file.list();
        //遍历
        for(String str:array){
            System.out.println(str);
        }
    }

public File[] listFiles():返回一个File数组,表示该File目录中的所有子文件或目录

public File[] listFiles():返回一个File数组,表示该File目录中的所有子文件或目录
遍历构造方法给出的目录,它会获取目录中的所有文件/文件夹,把文件/文件夹封装为File对象,多个File对象存储到数组中

@Test
    public void show02(){
        File file=new File("D:\\workPaper\\firstdemo");
        File[] array=file.listFiles();
        //遍历
        for(File str:array){
            System.out.println(str);
        }
    }

递归

递归:方法自己调用自己
递归的分类:
递归分为两种,直接递归和间接递归
直接递归称为方法自身调用自己。
间接递归可以A方法调用B方法,B方法调用A方法
注意事项:
递归一定要有条件限制,保证能够停下来,否则会发生栈内存溢出
在递归中虽然有限定条件,但是递归的次数不能太多。否则也会发生栈内存溢出。
在递归方法中禁用构造方法。
递归使用前提:
当调用方法的时候,方法的主体不变,每次调用的方法次数不同,可以使用递归。

递归一定要有条件限制,保证能够停下来,否则会发生栈内存溢出java.lang.StackOverflowError
a方法一直调用a方法,就会导致栈内有无数个a方法,方法太多了,超出了栈内存的大小,就会导致内存的溢出错误
注意:
当一个方法调用其他方法的时候,被调用的方法没有执行完毕之前,当前方法会一直等待调用的方法执行完毕,才会继续执行。

@Test
    public void a() {
        b(1);
    }



    public void b(int i) {
        System.out.println(i);
        //java.lang.StackOverflowError,方法次数也不能太多
        if (i == 10000) {
            //结束方法
            return;
        }
        b(++i);
    }
     /*
        构造方法,禁止递归
            编译报错:构造方法是创建对象使用的,一直递归会导致内存中有无数的个对象,直接编译报错
     */
    public Recurison(){
        Recurison();
    }

递归计算1~n之间的和

package com.ft.first.recurison;

import org.junit.Test;

public class RecurisonAdd {
    /*
        使用递归计算1~n之间的和
    */
    @Test
    public void add() {
        int s = sum(10);
        System.out.println(s);
    }

    /*
        定义一个方法,使用递归计算1~n之间的和。
        1+2+3+……+n
        n+(n-1)+(n-2)+……+1
        已知:
            最大值:n
            最小值:1
        使用递归必须明确:
            1.递归的结束条件:
                获取到1的时候结束
            2.递归的目的:
                获取下一个被加的数字(n-1)
    */
    public static int sum(int n) {
        //获取到1的时候结束
        if (n == 1) {
            return 1;
        }
        //获取下一个被加的数字
        return n + sum(n - 1);
    }
}

n的阶乘

package com.ft.first.recurison;

import org.junit.Test;

public class RecurisonAdd {
    /*
        阶乘:所有小于及等于该数的正整数的积
        n的阶乘:n!=n*(n-1)*(n-2)*……*3*2*1
        推理得出:
            n!=n(n-1)!
    */
    @Test
    public void multiplication(){
        int end=jc(10);
        System.out.println(end);
    }
    /*
        定义一个方法,使用递归计算n的阶乘。
        n的阶乘:n!=n*(n-1)*(n-2)*……*3*2*1
        使用递归必须明确:
            1.递归的结束条件:
                获取到1的时候结束
            2.递归的目的:
                获取下一个被加的数字(n-1)
    */
    public static int jc(int n){
        //获取到1的时候结束
        if(n==1){
            return 1;
        }
        //获取下一个被乘的数字
        return n*jc(n-1);
    }
}

递归打印多级目录

package com.ft.first.recurison;

import org.junit.Test;

import java.io.File;

public class RecurisonFile {
    /*
        递归打印多级目录
        遍历:D:\qwer文件夹及子文件夹
            D:\qwer\a
            D:\qwer\a\aaa.jpg
            D:\qwer\a\aaa.txt
            D:\qwer\b
            D:\qwer\b\bbb.java
            D:\qwer\b\bbb.txt
            D:\qwer\abc.java
            D:\qwer\abc.txt
    */
    @Test
    public void look() {
        File file=new File("D:\\qwer");
        getAllFile(file);
    }

    /*
        定义一个方法,参数传递File类型的目录
        方法中对目录进行遍历
    */

    public void getAllFile(File dir) {
        //打印被遍历的目录名称
        System.out.println(dir);
        File[] files = dir.listFiles();
        for (File file : files) {
            //对遍历的file进行判断是否是文件夹
            if(file.isDirectory()){
                //file是文件夹,则继续比遍历这个文件夹
                //我们发现getAllFile方法就是传递一个文件夹,遍历文件夹的方法
                //所以直接调用getAllFile方法即可、自己调用自己(递归)
                getAllFile(file);
            }else{
                //如果就是一个文件,直接打印即可
                System.out.println(file);
            }
        }
    }
}

获取文件后缀名忽略大小写


                //获取文件名
                //String name = file.getName();
                //String name=file.getPath();
                String name=file.toString();
                //把字符串转为小写
                name=name.toLowerCase();
                //2.调用String类中的方法endsWhit判断字符串是否是已.java结尾的
                Boolean b=name.endsWith(".java");
                //3.如果是以.Java结尾的直接输出即可
                if(b){
                    System.out.println(file);
                }
                */
                if(file.getName().toLowerCase().endsWith(".java")){
                    System.out.println(file);
                }

我们使用过滤器来实现递归打印多级目录

File[] listFiles(FileFilter filter)

File[] listFiles(FileFilter filter)
返回抽象路径名数组,这些路径名表示此抽象路径名表示的目录中满足指定过滤器的文件和目录
java.io.FileFilter接口:用于抽象路径名(File对象)的过滤器。
作用:用来过滤文件的。
抽象方法:用来过滤文件的方法
boolean accept(File pathname)
测试指定抽象路径名是否应该包含在某个路径名列表中。
参数:File pathname:使用的就是listFiles方法遍历目录,得到的每一个文件对象

static File[] listFiles(FilenameFilter filter)

static File[] listFiles(FilenameFilter filter)
返回抽象路径名数组,这些路径名表示此抽象路径名表示的目录中满足指定过滤器的文件和目录。
java.io.FileFilter接口:用于抽象路径名(File对象)的过滤器。
作用:用于过滤文件名称。
抽象方法:用来过滤文件的方法。
boolean accept(File dir, String name)
测试指定文件是否应该包含在某一文件列表中。
参数:
File dir:构造方法中传递的被遍历的目录。
String name:使用listFiles方法遍历目录,获取的每一个文件/文件夹名称。
注意:
两个过滤器接口都没有实现类,需要我们自己写实习类,重写过滤器的方法accept,在方法中自己定义过滤的规则

实现类

过滤器的规则
在accept方法中,判断File对象是否是以.java结尾的,返回布尔值
是的话返回true,保存到File数组中
不是的话返回false,不保存到File数组中

package com.ft.first.filter;

import java.io.File;
import java.io.FileFilter;

/*
    创建过滤器FileFilter的实现类,重写过滤器中的accept方法,定义过滤的规则
*/
public class FileFilterImpl implements FileFilter {
    /*
    必须明确两件事:
        1.过滤器中的accept方法是谁调用的
        2.accept方法的参数pathname是什么?
    */
    @Override
    public boolean accept(File pathname) {
        //过滤器的规则
        //在accept方法中,判断File对象是否是以.java结尾的
        //是的话返回true,保存到File数组中
        //不是的话返回false,不保存到File数组中


        //accept返回值是布尔值
        //true:就会把传递过来的FIle对象保存到File数组中
        //false:就不会把传递过来的FIle对象保存到File数组中

        //如果是文件夹也返回true,继续遍历文件夹
        if(pathname.isDirectory()){
            return true;
        }
        return pathname.getName().toLowerCase().endsWith(".java");
    }
}

具体操作

listFiles方法一共做了3件事
1.listFiles方法会对构造方法中传递的目录进行遍历,获取目录中的每一个文件/文件夹-->封装为File对象
2.listFiles方法会调用参数传递的过滤器中的方法
3.listFiles方法会把遍历得到的每一个File对象,传递过accept方法的参数叫pathname

package com.ft.frist.filter;

import org.junit.Test;

import java.io.File;

public class Filter01 {
    /*
        递归打印多级目录
        遍历:D:\qwer文件夹及子文件夹
            D:\qwer\a
            D:\qwer\a\aaa.jpg
            D:\qwer\a\aaa.txt
            D:\qwer\a\abc.JAVA
            D:\qwer\b
            D:\qwer\b\bbb.java
            D:\qwer\b\bbb.txt
            D:\qwer\abc.java
            D:\qwer\abc.txt
        我们使用过滤器来实现
        在File类中有两个和listFiles重载的方法,方法的参数传递的就是过滤器。

        File[] listFiles(FileFilter filter)
        返回抽象路径名数组,这些路径名表示此抽象路径名表示的目录中满足指定过滤器的文件和目录
        java.io.FileFilter接口:用于抽象路径名(File对象)的过滤器。
            作用:用来过滤文件的。
            抽象方法:用来过滤文件的方法
                boolean accept(File pathname)
                测试指定抽象路径名是否应该包含在某个路径名列表中。
                参数:File pathname:使用的就是listFiles方法遍历目录,得到的每一个文件对象

        static File[] listFiles(FilenameFilter filter)
        返回抽象路径名数组,这些路径名表示此抽象路径名表示的目录中满足指定过滤器的文件和目录。
        java.io.FileFilter接口:用于抽象路径名(File对象)的过滤器。
            作用:用于过滤文件名称。
            抽象方法:用来过滤文件的方法。
            boolean accept(File dir, String name)
            测试指定文件是否应该包含在某一文件列表中。
            参数:
                File dir:构造方法中传递的被遍历的目录。
                String name:使用listFiles方法遍历目录,获取的每一个文件/文件夹名称。

        注意:
            两个过滤器接口都没有实现类,需要我们自己写实习类,重写过滤器的方法accept,在方法中自己定义过滤的规则
    */
    @Test
    public void look() {
        File file = new File("D:\\qwer");
        getAllFile(file);
    }

    public void getAllFile(File dir) {
        //打印被遍历的目录名称
        System.out.println(dir);
        File[] files = dir.listFiles(new FileFilterImpl());//传递过滤器对象
        //listFiles方法一共做了3件事
        //1.listFiles方法会对构造方法中传递的目录进行遍历,获取目录中的每一个文件/文件夹-->封装为File对象
        //2.listFiles方法会调用参数传递的过滤器中的方法
        //3.listFiles方法会把遍历得到的每一个File对象,传递过accept方法的参数叫pathname
        for (File file : files) {
            //对遍历的file进行判断是否是文件夹
            if (file.isDirectory()) {
                getAllFile(file);
            } else {
                if (file.getName().toLowerCase().endsWith(".java")) {
                    System.out.println(file);
                }
            }
        }
    }

}

使用匿名内部类

package com.ft.first.filter;

import org.junit.Test;

import java.io.File;
import java.io.FileFilter;
import java.io.FilenameFilter;

public class Filter02 {

    @Test
    public void look() {
        File file = new File("D:\\qwer");
        getAllFile(file);
    }

    public void getAllFile(File dir) {
        //打印被遍历的目录名称
        //System.out.println(dir);

        File[] files = dir.listFiles(new FileFilter() {
            @Override
            public boolean accept(File pathname) {
                //过滤规则
                return pathname.isDirectory() || pathname.getName().toLowerCase().endsWith(".java");
            }
        });
        File[] files = dir.listFiles(new FilenameFilter() {
            @Override
            public boolean accept(File dir, String name) {
                //过滤规则
                return new File(dir, name).isDirectory() || name.toLowerCase().endsWith(".java");
            }
        });

        //使用lambda表达式来优化匿名内部类(接口中只有一个抽象方法)
        File[] files=dir.listFiles((d,name)->new File(d, name).isDirectory() || name.toLowerCase().endsWith(".java"));
        File[] files=dir.listFiles((File d,String name)->{
            //过滤规则
            return new File(d, name).isDirectory() || name.toLowerCase().endsWith(".java");
        });
        for (File file : files) {
            //对遍历的file进行判断是否是文件夹
            if (file.isDirectory()) {
                getAllFile(file);
            } else {
                System.out.println(file);
            }
        }
    }

}

IO概述:

什么是IO

生活中,你肯定经历过这样的场景,当你编辑了一个文本,忘记了ctrl+s,可能文件就白白编辑了。当你的电脑上插入了一个U盘,可以把视频或者图片,拷贝到你的电脑硬盘里面,那么数据都是在设备上的呢?键盘、内存、硬盘、外接设备等等。
我们把这种的数据传输,可以看做是一种数据的流动,按照流动的方向,以内存为基准,分为输入input输出output,即流向内存是输入流,流出内存是输出流。
java中I/O操作主要是指使用java.io包下的内容,进行输入、输出操作。输入也叫做读取数据输出也叫做写出数据

IO的分类

根据数据的流向分为:输入流输出流
输入流:把数据从其他设备上读取到内存中。
输出流:把数据从内存中写出到其他设备上的流。
格局数据的类型分为:字节流字符流

字节流:
字节输入流:inputStream
字节输出流:outputStream
字符流:
字符输入流:Reader
字符输出流:Writer

字节流:

一切皆为字节

一切文件数据(文本、图片、视频等)在存储时,都是二进制数字的形式保存的,都是一个一个的字节,那么传输时一样如此。所以,字节流可以传输任意文件的数据。在操作流的时候,我们要时刻明确,无论使用什么样的流对象,底层传输的始终为二进制数据。

字节输出流[outputStream]

java.io.OutputStream抽象类是表示字节输出流的所有类的超类(父类),将指定的字节信息写出到目的地。它定义了字节输出流的基本共性功能方法。

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

基础使用
package com.ft.first.outputStream;

import org.junit.Test;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;

/*
    java.io.OutputStream
    此抽象类是表示输出字节流的所有类的超类。输出流接受输出字节并将这些字节发送到某个接收器。
    定义了一些子类共性的成员方法。(子类都可以使用)
    void close():关闭此输出流并释放与此流有关的所有系统资源。
    void flush():刷新此输出流并强制写出所有缓冲的输出字节。
    void write(byte[] b):将 b.length 个字节从指定的 byte 数组写入此输出流。
    void write(byte[] b, int off, int len):将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此输出流。
    abstract  void write(int b):将指定的字节写入此输出流。

    java.io.FileOutputStream:继承了java.io.OutputStream
    FileOutputStream:文件字节输出流
    作用:把内存中的数据写入到硬盘的文件中。

    构造方法:
        FileOutputStream(String name):创建一个向具有指定名称的文件中写入数据的输出文件流。
        FileOutputStream(File file):创建一个向指定 File 对象表示的文件中写入数据的文件输出流。
        参数:
            String name:目的地是一个文件的路径。
            File file:目的地是一个文件。
        构造方法的作用:
            1.创建一个FileOutputStream对象
            2.会根据构造方法中传递的文件/文件路径创建一个空的文件
            3.会把FileOutputStream对象指向创建好的文件
    写入数据的原理:(内存-->硬盘)
        java程序-->JVM(java虚拟机)-->os(操作系统)-->os调用写数据的方法-->把数据写入到文件中(硬盘)
    字节输出流的步骤(重点):
        1.创建一个FileOutputStream对象,构造方法中传递写入数据的目的地
        2.调用FileOutputStream对象中的方法write,把数据写入到文件中(硬盘)
        3.释放资源(流使用会占用一定的内存,使用完毕后要把内存清空,提高程序的效率)
*/
public class OutputStreamDemo01 {

    @Test
    public void FileOutputStream01() {
        try {
            //创建一个FileOutputStream对象,构造方法中传递写入数据的目的地
            FileOutputStream fos = new FileOutputStream("aaa.txt");
            //调用FileOutputStream对象中的方法write,把数据写入到文件中(硬盘)
            //void write(byte[] b):将 b.length 个字节从指定的 byte 数组写入此输出流。
            fos.write(97);
            //写数据的时候会把10进制转为二进制,即fos.write(97); 97--->1100001
            //硬盘中存储的数据都是字节
            //1个字节=8个比特位(10101010)
            /*
            任意的文本编辑器(记事本,notepad++,……)
            在打开的时候,都会查询编码表,把字节转换为字符表表示:
            0-127:查询ASCII表
            其他值查询系统默认的编码表(中文系统GBK)
             */
            //释放资源(流使用会占用一定的内存,使用完毕后要把内存清空,提高程序的效率)
            fos.close();
        } catch (java.io.IOException e) {
            e.printStackTrace();
        }
    }
}

硬盘中存储的数据都是字节:
1个字节=8个比特位(10101010)
任意的文本编辑器(记事本,notepad++,……)
在打开的时候,都会查询编码表,把字节转换为字符表表示:
0-127:查询ASCII表
其他值查询系统默认的编码表(中文系统GBK)

一次写多个字节

void write(byte[] b):将 b.length 个字节从指定的 byte 数组写入此输出流。
void write(byte[] b, int off, int len):将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此输出流。
int off:数组的开始索引
int len:写几个字节
byte[] bytes = {65, 66, 67, 68, 69};索引从0开始
fos.write(bytes, 1, 2);即{66,67}

UTF-8中3个字节是一个中文,GBK中2个字节是一个中文

写入字符的方法:可以使用String类中的方法把字符串,转换成字节数组
byte[] getBytes():把字符串转换为字符数组
UTF-8中3个字节是一个中文,GBK中2个字节是一个中文
byte[] byte2="你好".getBytes();//你好==[-28, -67, -96, -27, -91, -67]
System.out.println(Arrays.toString(byte2));
fos.write(byte2);

package com.ft.first.outputStream;

import org.junit.Test;

import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.Array;
import java.util.Arrays;

public class OutputStreamDemo02 {
    /*
    void write(byte[] b):将 b.length 个字节从指定的 byte 数组写入此输出流。
    void write(byte[] b, int off, int len):将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此输出流。
    */
    @Test
    public void FileOutputStream01() {
        try {
            //创建FileOutputStream对象,构造方法中绑定要写入数据的目的地
            FileOutputStream fos = new FileOutputStream("b.txt");
            //调用FileOutputStream对象中的方法write,把数据写入到文件中
            //在文件中显示100,写几个字节?

            fos.write(49);
            fos.write(48);
            fos.write(48);
            /*
            void write(byte[] b):将 b.length 个字节从指定的 byte 数组写入此输出流。
            一次写多个字节:
                如果写的第一个字节是正数(0-127),那么显示的时候会查询ASCII表
                如果写的第一个字节是负数,那第一个字节会和第二个字节,两个字节组成一个中文显示,系统查询默认编码表(GBK)
            */
            byte[] bytes = {65, 66, 67, 68, 69};
            //byte[] bytes={-65,-66,-67,68,69};
            fos.write(bytes);
            /*
            void write(byte[] b, int off, int len):将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此输出流。
                int off:数组的开始索引
                int len:写几个字节
            */
            //索引从0开始
            fos.write(bytes, 1, 2);

            /*
            写入字符的方法:可以使用String类中的方法把字符串,转换成字节数组
            byte[] getBytes():把字符串转换为字符数组
            */
            //UTF-8中3个字节是一个中文,GBK中2个字节是一个中文
            byte[] byte2="你好".getBytes();//你好==[-28, -67, -96, -27, -91, -67]
            System.out.println(Arrays.toString(byte2));
            fos.write(byte2);
            //释放资源
            fos.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

数据追加续写

经过以上的演示,每次程序运行时,就会创建输出流对象,都会清空原目标文件的内容。但往往我们需要给文件追加内容。那如何保留文件的原本内容呢?再给它追加内容呢?

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

这两个构造方法,参数中都可以传入一个boolean类型的值,true代表追加数据,false表示清空原有的数据。这样创建的输出流对象,就可以指定是否要追加到指定文件的数据了。代码演示:

import org.junit.Test;

import java.io.FileOutputStream;
import java.io.IOException;

public class OutputStreamDemo03 {
    /**
     * FileOutputStream(String name, boolean append):
     *      创建一个向具有指定 name 的文件中写入数据的输出文件流。
     * FileOutputStream(File file, boolean append):
     *      创建一个向指定 File 对象表示的文件中写入数据的文件输出流。
     * 参数:
     *      String name,File file:写入数据的目的地
     *      boolean append:追加写入的开关
     *          true:创建对象不会覆盖原文件,继续再后面追加写入
     *          false:创建对象时会覆盖原文件,即创建一个新文件覆盖原文件
     * 写换行符号:
     *      windows:\r\n
     *      Linux:\n
     *      mac:\r
     */
    @Test
    public void FileAppend() throws IOException {
        FileOutputStream fileOutputStream=new FileOutputStream("a.txt",true);
        fileOutputStream.write("你好棒呀!".getBytes());
        fileOutputStream.close();
    }
    @Test
    public void FileWritern() throws IOException {
        FileOutputStream fileOutputStream=new FileOutputStream("a.txt",true);
        for (int i = 0; i < 10; i++) {
            fileOutputStream.write("我换行啦".getBytes());
            fileOutputStream.write("\r\n".getBytes());
        }
        fileOutputStream.close();
    }
}

FileOytputStream中写入换行符:

fileOutputStream.write("\r\n".getBytes());

字节输入流(InputStream)

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

构造方法:

FileInputStream(File file):通过打开与实际文件的连接桥梁,来创建一个FileInputStream,该文件由文件系统中的File对象file命名。
FileInputStream(FileDescriptor fdobj):通过使用文件描述符 fdObj 创建一个 FileInputStream,该文件描述符表示到文件系统中某个实际文件的现有连接。
FileInputStream(String name):通过打开与实际文件的连接来创建一个FileInputStream,该文件由文件系统中的路径名name命名。

当你创建一个流对象时,必选传入一个文件路径,该路径下,如果没有该文件,会抛出FileNotFoundException

构造方法

FileInputStream(String name):
FileInputStream(File file):
参数:就是读取文件的数据原
String name:文件的路径。
File file:读取的文件

构造方法的作用:

1.创建一个FileInputStream对象。
2.会把FileInputStream对象指向构造方法中要读取的文件。

读取字节数据

读取字节:read方法,每次可以读取一个字节的数据,提示为int类型,读取到文件末尾,返回-1,代码使用演示:

import org.junit.Test;

import java.io.FileInputStream;
import java.io.IOException;

/**
 * java.io.InputStream:字节输入流
 * 此抽象类是表示字节输入流的所有超类
 * <p>
 * 定义了所有子类的共性方法
 * int read():从输入流中读取数据的下一个字节
 * int read(byte[] b):从此输入流中将最多 b.length 个字节的数据读入一个 byte 数组中。
 * long skip(long n): 从输入流中跳过并丢弃 n 个字节的数据。
 * int read(byte[] b, int off, int len):从此输入流中将最多 len 个字节的数据读入一个 byte 数组中。
 * void close():关闭此文件输入流并释放与此流有关的所有系统资源。
 * <p>
 * java.io.FileInputStream extends InputStream
 * FileInputStream:文件字节输入流
 * 作用:把硬盘文件中的数据读取到内存中
 * 构造方法:
 * FileInputStream(String name):
 * FileInputStream(File file):
 * 参数:就是读取文件的数据原
 * String name:文件的路径。
 * File file:读取的文件
 * 构造方法的作用:
 * 1.创建一个FileInputStream对象。
 * 2.会把FileInputStream对象指向构造方法中要读取的文件。
 * 读取数据的原理(硬盘-->内存):
 * java程序-->JVM-->OS-->OS读取方法-->读取文件
 * 字节输入流的使用步骤(重点):
 * 1.创建FileInputStream对象,构造方法中绑定要读取的数据源
 * 2.使用FIleInputStream对象中的read,读取文件
 * 3.释放资源
 */

public class InputStreamDemo01 {

    @Test
    public void inputOne() throws IOException {
        //创建FileInputStream对象,构造方法中绑定要读取的数据源
        FileInputStream fileInputStream = new FileInputStream("b.txt");//b.txt文件内容为  abc
        //使用FIleInputStream对象中的read,读取文件
        //int read()读取文件中的一个字节并返回,读取到文件的末尾它会返回-1
        int read = fileInputStream.read();
        System.out.println(read);//返回字节对应的编码数字      97
        int read1 = fileInputStream.read();
        System.out.println(read1);//98
        int read2 = fileInputStream.read();
        System.out.println(read2);//99
        int read3 = fileInputStream.read();
        System.out.println(read3);//-1

        /**
         * 发现以上读取文件是一个重复的过程,所以可以使用循环
         * 不知道文件中有多少个字节,可以使用while循环
         * while循环结束条件,读取到-1的时候结束,及没有字节可以读取了
         *
         * 布尔表达式:(len=fileInputStream.read())!=-1
         *  1.fileInputStream.read():读取字节。
         *  2.len=fileInputStream.read():把读取到的字节赋值给len。
         *  3.(len=fileInputStream.read())!=-1:判断变量len是否不等于-1.
         */
        //定义一个int数据,记录读取到的字节
        int len;
        while ((len = fileInputStream.read()) != -1) {
            System.out.print((char) len);
        }
        //释放资源
        fileInputStream.close();
    }
}

读取多个字节的流程.png

一次读取多个字节使用byte[] bytes1=new byte[1024];//1kb 存储读取到的多个字节

import org.junit.Test;

import java.io.FileInputStream;
import java.io.IOException;
import java.util.Arrays;

/**
 * 字节输入流一次可以读取多个字节的方法。
 *      int read(byte[] b):从此输入流中将最多 b.length 个字节的数据读入一个 byte 数组中。
 * 明确两件事情:
 *      1.方法的参数byte[]的作用?
 *          起到缓冲作用,存储每次读取到的多个字节
 *          数组长度一般定义为1024(1kb)或者是1024的整数倍
 *      2.方法的返回值int是什么?
 *          每次读取到的有效字节个数
 *String类的构造方法:
 *  String(byte[] bytes):通过使用平台的默认字符集解码指定的 byte 数组,构造一个新的 String。
 *  把字节数组转为字符串。
 *  String(byte[] bytes, int offset, int length):通过使用平台的默认字符集解码指定的 byte 子数组,构造一个新的 String
 *  把字节数组的一部分转为字符串。
 *  offset:表示数组的开始位置、length:表示转换的字节个数
 */

public class InputStreamDemo02 {
    @Test
    public void TestDemo01() throws IOException {
        //创建FileInputStream对象,构造方法中绑定要读取的数据源
        FileInputStream fileInputStream=new FileInputStream("a.txt");//内容为ABCDE
        //使用FileInputStream对象中的方法read读取文件
        //int read(byte[] b):从此输入流中将最多 b.length 个字节的数据读入一个 byte 数组中。
        byte[] bytes=new byte[2];
        //byte[] bytes=new byte[1024];
        //数组:起缓冲作用,存储读取到的多个字节
        int read = fileInputStream.read(bytes);//read:读取的有效字节个数
        System.out.println(read);//2,每次读取字节的个数
        System.out.println(Arrays.toString(bytes));//[65,66]
        System.out.println(new String(bytes));//AB

        read = fileInputStream.read(bytes);
        System.out.println(read);//2
        System.out.println(new String(bytes));//CD

        read = fileInputStream.read(bytes);
        System.out.println(read);//1
        System.out.println(new String(bytes));//ED
        read = fileInputStream.read(bytes);
        System.out.println(read);//-1
        System.out.println(new String(bytes));//ED


        /**
         * 发现以上读取的是一个重复的过程,可以使用循环优化
         * 不知道文件中有多少字节,所以使用while循环
         * while循环的结束的条件:读取到-1结束
         */
        byte[] bytes1=new byte[1024];//1kb 存储读取到的多个字节
        int len=0;//记录每次读取的有效字节个数
        while ((len=fileInputStream.read(bytes1))!=-1){
            //0,len代表读到了好多个字节就输出好多,或者会有多余的空格
            //把字节数组的一部分转为字符串。
            System.out.println(new String(bytes1,0,len));
        }

        //释放资源
        fileInputStream.close();

    }
}

文件复制:

原理:从已有文件中读取字节,将该字节写出到另一个文件。
一读一写使用InputStream读取文件再使用OutputStream写出文件

定义一个毫秒值记录程序消耗的时间
long start = System.currentTimeMillis();
long end = System.currentTimeMillis();
System.out.println("文件复制共耗时:" + (end - start) + "ms");

import org.junit.Test;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

/**
 * 文件复制练习:一读一写
 * 明确:
 * 数据源:D:\\aaa.jpg
 * 数据的目的地:D:\\workPaper\\aaa.jpg
 * 文件复制的步骤:
 * 1.创建一个字节输入流对象,构造方法中绑定要读取的数据源
 * 2.创建一个字节输出流对象,构造方法中绑定要写入的目的地
 * 3.使用字节输入流对象中的方法read读取文件
 * 4.使用字节输出流对象中的方法write,把读取到的字节写入到目的地的文件中
 * 5.释放资源
 */

public class IntOutCopyFile {

    @Test
    public void copyFile() throws IOException {
        //定义一个毫秒值记录程序消耗的时间
        long start = System.currentTimeMillis();

        //创建一个字节输入流对象,构造方法中绑定要读取的数据源
        FileInputStream fileInputStream = new FileInputStream("D:\\aaa.jpg");
        //创建一个字节输出流对象,构造方法中绑定要写入的目的地
        FileOutputStream fileOutputStream = new FileOutputStream("D:\\workPaper\\aaa.jpg");
        //一次读取一个字节、一次写入一个字节

        //使用字节输入流对象中的方法read读取文件
        int len = 0;
        while ((len = fileInputStream.read()) != -1) {
            //使用字节输出流对象中的方法write,把读取到的字节写入到目的地的文件中
            fileOutputStream.write(len);
        }

        //使用数组缓冲读取多个字节,写入多个字节
        byte[] bytes = new byte[1024];
        //一读一写
        int rest = 0;//每次读取的有效字节个数
        while ((rest = fileInputStream.read()) != -1) {
            //使用字节输出流对象中的方法write,把读取到的字节写入到目的地的文件中
            fileOutputStream.write(bytes, 0, rest);//写也写入有效的个数
        }
        //释放资源(先关闭写的流,再关闭读的流) 写完了一定读完了,但读完了不一定写完了
        fileOutputStream.close();
        fileInputStream.close();
        long end = System.currentTimeMillis();
        System.out.println("文件复制共耗时:" + (end - start) + "ms");
    }
}

先开后关,后开先关

字符流

当使用字节流读取文本文件时,可能会有一个小问题。就是遇到中文字符时,可能不会显示完整的字符,那是因为一个中文字符可能占用多个字节存储。GBK编码格式的中文,占2个字节,UTF-8编码格式的中文,占3个字节。所以java提供一些字符流类,以字符为单位读写数据,专门用于处理文本文件。

字符输入流Reader

java.io.Reader抽象类是表示用于读取字符流的所有类的超类,可以读取字符信息到内存中。它定义了字符输入流的基本共性功能方法。

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

FileReader类

java.io.FileReader类是读取字符文件的便利类。构造时使用系统默认的字符编码和默认字节缓冲区。
小帖士:
1.字符编码:字节与字符的对应规则。Windows系统的中文编码默认是GBK编码表。

package com.ft.first.reader;

import org.junit.Test;

import java.io.FileReader;
import java.io.IOException;

/*
    java.io.Reader:字符输入流,是字符输入流的最顶层的父类,定义了一些共性的成员方法。是一个抽象类

    共性的成员方法:
        int read():读取单个字符并返回读取到的字符。
        int reade(char[] a):一次读取多个字符,将字符读入到char数组中。
        void close():关闭该流并释放与之关联的所有资源。

    java.io.FileReader  extends InputStreamReader   extend  Reader
    FileReader:文件字符输入流
    作用:把硬盘文件中的数据,以字符的方式读取到内存中。
    构造方法:
        FileReader(String fileName):
        FileReader(File file):
        参数:读取文件的数据源
            String fileName:文件的路径。
            File file:就是一个文件。
    FileReader构造方法的作用:
        1.会创建一个FileReader对象。
        2.会把FileReader对象指向一个要读取的文件。

    字符输入流的使用步骤:
        1.创建FileReader对象,构造方法中绑定要读取的数据源。
        2.使用FIleReader对象中的read读取文件。
        3.释放资源。
 */
public class ReaderDemo01 {

    @Test
    public void ReaderDemo01() throws IOException {
        FileReader fileReader = new FileReader("a.txt");
        int rest = 0;
        while ((rest = fileReader.read()) != -1) {
            System.out.println((char)rest);
        }
        
        char[] chars=new char[1024];//用来存储读取到的多个字符
        int rest=0;//记录的每次读取到的有效字符个数
        while ((rest=fileReader.read(chars))!=-1){
            /*
                String类的构造方法
                String(char[] value) 把字符数组转换成字符串
                String(char[] value,int offset,int count) 把字符数组中一部份转换成字符串,offset数组的开始索引,count转换的字符串个数。
            */
            System.out.println(new String(chars,0,rest));
        }
        //释放资源
        fileReader.close();
    }
}

字符输出流 Writer

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

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

FileWriter类

java.io.FileWriter extends OutputStreamWriter extends Writer
FileWriter:文件字符输出流。
作用:把内存中的字符数据写入到文件中

构造方法:
    FileWriter(File file):根据给定的 File 对象构造一个 FileWriter 对象。
    FileWriter(String fileName):根据给定的文件名构造一个 FileWriter 对象。
    参数:写入数据的目的地
        File file:是一个文件
        String fileName:写入文件的路径
    构造方法的作用:
        1.会创建一个FileWriter对象。
        2.会根据构造方法中传递的文件/文件路径,创建文件。
        3.会把FileWriter对象指向创建好的文件。

基本写出数据

import org.junit.Test;

import java.io.FileWriter;
import java.io.IOException;

/*
    java.io.Writer:字符输出流,是所有的字符输出流的最顶层的父类,是一个抽象类

    共性成员方法:
        void write(int c):写入单个字符
        void write(char[] a):写入字符数组
        abstract  void  write(char[]  a,int  off,int  len):写入字符数组的某一部分,off数组的开始索引,len写的字符个数。
        void  write(String  str):写入字符串。
        void  write(String  str,int  off,int  len):写入字符串的某一部分,off表示字符串的开始索引,len写的字符串的字符个数。
        void  flush():刷新该流的缓冲。
        void  close():关闭此流,但是要先刷新它。

    java.io.FileWriter extends OutputStreamWriter extends Writer
    FileWriter:文件字符输出流。
    作用:把内存中的字符数据写入到文件中

    构造方法:
        FileWriter(File file):根据给定的 File 对象构造一个 FileWriter 对象。
        FileWriter(String fileName):根据给定的文件名构造一个 FileWriter 对象。
        参数:写入数据的目的地
            File file:是一个文件
            String fileName:写入文件的路径
        构造方法的作用:
            1.会创建一个FileWriter对象。
            2.会根据构造方法中传递的文件/文件路径,创建文件。
            3.会把FileWriter对象指向创建好的文件。
    字符输出流的使用步骤:
        1.创建一个FileWriter对象,构造方法中绑定要写入数据的目的地
        2.使用FileWriter方法中的write,把数据写入内存缓冲区中(字符转换为字节的过程)
        3.使用FileWriter中的方法flush,把内存缓冲区中的数据,刷新到文件中。
        4.释放资源(会先把内存缓冲区的数据刷新到文件中,即第三步flush可以不用写)。
 */
public class WriterDemo01 {
    @Test
    public void writerFile01() throws IOException {
        //创建一个FileWriter对象,构造方法中绑定要写入数据的目的地,后面的ture代表是否追加写入数据
        FileWriter fileWriter = new FileWriter("b.txt",true);
        //使用FileWriter方法中的write,把数据写入内存缓冲区中(字符转换为字节的过程)
        fileWriter.write(97);
        //如果不调用flush或者close方法,那么数据不会被写入到文件中,数据保存到缓冲区中。只有调用了这两方法中的一个才会刷新缓冲区。

        /*
            flush方法与close方法的区别:
                flush():刷新缓冲区,流对象可以继续使用。
                close():先刷新缓冲区,然后通知系统释放资源。流对象不可以再被使用
         */

        //使用FileWriter中的方法flush,把内存缓冲区中的数据,刷新到文件中。
        fileWriter.flush();
        //释放资源(会先把内存缓冲区的数据刷新到文件中,即第三步flush可以不用写)。
        fileWriter.close();
    }
}

写出其他数据

写出字符数组:write(char[] a)write(char[] a,int off,int len),每次可以写出字符数组中的数据,用法类似于FileOutputStream。
写出字符串:write(String str)write(String str,int off,int len),每次可以写出字符串中的数据,更为方便,代码演示:

/*
    字符输出流写数据的其他方法
        void write(char[] a):写入字符数组
        abstract  void  write(char[]  a,int  off,int  len):写入字符数组的某一部分,off数组的开始索引,len写的字符个数。
        void  write(String  str):写入字符串。
        void  write(String  str,int  off,int  len):写入字符串的某一部分,off表示字符串的开始索引,len写的字符串的字符个数。

 */

import org.junit.Test;

import java.io.FileWriter;
import java.io.IOException;

public class WriterDemo02 {
    @Test
    public void fileWrite() throws IOException {
        FileWriter fileWriter = new FileWriter("b.txt");
        char[] chars = {'a', 'b', 'c', 'd', 'e'};
        //void write(char[] a):写入字符数组
        fileWriter.write(chars);//abcde
        //abstract  void  write(char[]  a,int  off,int  len):写入字符数组的某一部分,off数组的开始索引,len写的字符个数。
        //从第一个索引开始写,写入3个字符
        fileWriter.write(chars, 1, 3);
        //void  write(String  str):写入字符串。
        fileWriter.write("你好");
        //void  write(String  str,int  off,int  len):写入字符串的某一部分,off表示字符串的开始索引,len写的字符串的字符个数。
        fileWriter.write("今天又是掉头发的一天",4,3);
        //属性缓冲区
        fileWriter.flush();
        //关闭流对象
        fileWriter.close();
    }
}

续写和换行:

续写和换行使用两个参数的构造方法:
FileWriter(String fileName,boolean append)
FileWriter(File file,boolean append)

参数:String fileName,File file表示写入数据的目的地。
boolean append:续写的开关 true:不会创建新的文件覆盖原来的文件,可以续写。false:创建新的文件覆盖原文件。

换行:换行符号:

windows:\r\n
linux:\n
mac:\r

/*
    续写和换行:
    续写和换行使用两个参数的构造方法:
        FileWriter(String fileName,boolean append)
        FileWriter(File file,boolean append)
        参数:String fileName,File file表示写入数据的目的地。
        boolean append:续写的开关  true:不会创建新的文件覆盖原来的文件,可以续写。false:创建新的文件覆盖原文件。
    换行:换行符号
        windows:\r\n
        linux:\n
        mac:\r
 */

import org.junit.Test;

import java.io.FileWriter;
import java.io.IOException;

public class WriterDemo03 {
    @Test
    public void writerFileTest() throws IOException {
        FileWriter fileWriter=new FileWriter("b.txt",true);
        for (int i = 0; i < 10; i++) {
            fileWriter.write("Hello,Word"+i);
            //写入换行符号
            fileWriter.write("\r\n");
        }
        fileWriter.close();
    }
}

IO异常的处理

之前练习,我们一直把异常抛出去的,而实际开发中我们并不这样处理,建议使用try……cath……finally代码块,处理异常部分,代码演示:

/*
    在JDK1.7之前我们使用try catch finally处理流中的异常
    格式:
        try{
            可能产生异常的代码块
        }catch(异常类的变量 变量名){
            异常的处理原则
        }finally{
            一定会执行的代码
            资源释放
        }
 */

import org.junit.Test;

import java.io.FileWriter;
import java.io.IOException;

public class DemoTryCath {
    @Test
    public void TryTest() {
        //提高fileWriter的作用域,让finally可以使用
        //变量在定义的时候可以没有值,但是在使用的时候它必须有值
        //fileWriter=new FileWriter("b.txt",true);执行失败的话fileWriter就没有值,fileWriter。close()就会报错。
        FileWriter fileWriter = null;
        try {
            //可能产生异常的代码块
            fileWriter = new FileWriter("b.txt", true);
            for (int i = 0; i < 10; i++) {
                fileWriter.write("你好" + i + "\r\n");
            }
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println(e);
        } finally {
            //一定会执行的代码
            //创建对象失败了,fileWriter的默认值是null,null是不能调用方法的,会抛出NullPointerException,需要增加一个判断,不是null再把资源释放。
            if (fileWriter != null) {
                try {
                    //fileWriter.close();方法声明抛出了IOException异常对象,所有我们就处理这个异常对象,要么throws,要么try……catch
                    fileWriter.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

JDk7的新特性

/*
    JDk7的新特性
    再try的后边可以增加一个(),在括号中可以定义流对象
    那么这个流对象的作用域就在try中有效
    try中的代码执行完毕,会自动把流对象释放,不用写finally
    格式:
        try(定义流对象;定义流对象……){
            可能产生异常的代码块
        }catch(异常类变量 变量名){
            异常处理的逻辑
        }
 */

import org.junit.Test;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class DemoTryCath01 {
    @Test
    public void TryTest() {
        //这样流对象就不用关闭,它会自动关闭,作用域只在try的大括号中
        try (FileInputStream fileInputStream = new FileInputStream("D:\\aaa.jpg");
             FileOutputStream fileOutputStream = new FileOutputStream("D:\\workPaper\\aaa.jpg")) {
            byte[] bytes = new byte[1024 * 1024];
            //一读一写
            int rest = 0;//每次读取的有效字节个数
            while ((rest = fileInputStream.read()) != -1) {
                fileOutputStream.write(bytes, 0, rest);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

JDK9的新特性

/*
    JDK9的新特性
    try的前边我们可以定义流对象
    在try后边的()中可以直接引用流对象(变量名)
    在try代码执行完毕之后,流对象也可以释放掉,不用写finally
    格式:
        A a=new A();
        B b=new B();
        try(a,b){
            可能产生异常的代码
        }catch(异常类变量 变量名){
            异常处理逻辑
        }
 */

import org.junit.Test;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

public class DemoTryCath02 {
    @Test
    public void TestDemoTry() throws FileNotFoundException {
        FileInputStream fileInputStream = new FileInputStream("D:\\aaa.jpg");
        FileOutputStream fileOutputStream = new FileOutputStream("D:\\workPaper\\aaa.jpg");
        try(fileInputStream;fileOutputStream){
            byte[] bytes = new byte[1024 * 1024];
            //一读一写
            int rest = 0;//每次读取的有效字节个数
            while ((rest = fileInputStream.read()) != -1) {
                fileOutputStream.write(bytes, 0, rest);
            }
        }catch (IOException e){
            System.out.println(e);
        }
    }
}

属性集

java.util.Properties集合 extends Hashtable<Object,Object> implements Map<K,V>
Properties 类表示了一个持久的属性集。Properties 可保存在流中或从流中加载。属性列表中每个键及其对应值都是一个字符串。
Properties集合是一个双列集合,key和value默认都是字符串。
Properties集合是一个唯一和IO流相结合的集合

使用Properties集合存储数据,遍历取出Properties集合中的数据。

/*
    java.util.Properties集合 extends Hashtable<Object,Object> implements Map<K,V>
    Properties 类表示了一个持久的属性集。Properties 可保存在流中或从流中加载。属性列表中每个键及其对应值都是一个字符串。
    Properties集合是一个唯一和IO流相结合的集合
        可以使用Properties集合中的方法store,把集合中的临时数据,持久化写入到硬盘中存储
        可以使用Properties集合中的方法load,把硬盘中保存的文件(键值对),读取到集合中使用
    Properties集合是一个双列集合,key和value默认都是字符串
 */

import org.junit.Test;

import java.util.Properties;
import java.util.Set;

public class TestProperties01 {
    /*
        使用Properties集合存储数据,遍历取出Properties集合中的数据。
        Properties集合是一个双列集合,key和value默认都是字符串。
        Properties集合中有一些操作字符串的特有方法。
            Object setProperty(String key, String value):调用 Hashtable 的方法 put。
            String getProperty(String key):用指定的键在此属性列表中搜索属性。通过key找value,相当于Map中的get方法
            Set<String> stringPropertyNames():返回此属性列表中的键集,其中该键及其对应值是字符串,如果在主属性列表中未找到同名的键,则还包括默认属性列表中不同的键。相当于Map中的keySet方法
     */
    @Test
    public void TestProp() {
        //创建Properties集合对象
        Properties properties = new Properties();
        //使用setProperty往集合中添加数据
        properties.setProperty("IE", "10086");
        properties.setProperty("Goog", "10001");
        properties.setProperty("Houfu", "10010");
        //properties.put(1,true);
        //使用stringPropertyNames()把Properties集合中的键取出,存储到set集合中
        Set<String> set = properties.stringPropertyNames();
        //遍历Set集合,取出的properties集合中的每一个键
        for (String key : set) {
            //使用getProperty(String key)方法通过kay获取value
            System.out.println("properties中的键值对" + key + ":" + properties.getProperty(key));
        }
    }
}

可以使用Properties集合中的方法store,把集合中的临时数据,持久化写入到硬盘中存储。

 /*
        可以使用Properties集合中的方法store,把集合中的临时数据,持久化写入到硬盘中存储。
        void store(OutputStream out,String comments)
        void store(Writer writer,String comments)
        参数:
            OutputStream out:字节输出流,不能写中文。
            Writer writer:字符输出流,可以写中文。
            String comments:用来解释说明保存的文件是干什么用的。
                不能使用中文,会产生乱码,默认是Unicode编码。
                一般使用""空字符串。
        使用步骤:
            1.创建Properties集合对象,添加数据
            2.创建字节输出流/字符输出流对象,构造方法中绑定要输出的目的地
            3.使用Properties集合中的方法store,把集合中的临时数据,持久化到硬盘中。
            4.释放资源。
     */
    @Test
    public void TestProp02() throws IOException {
        //创建Properties集合对象
        Properties properties = new Properties();
        //使用setProperty往集合中添加数据
        properties.setProperty("IE", "10086");
        properties.setProperty("Goog", "10001");
        properties.setProperty("Houfu", "10010");
        //创建字节输出流/字符输出流对象,构造方法中绑定要输出的目的地

        //字符输出流
        FileWriter fileWriter=new FileWriter("prop.txt");
        //使用Properties集合中的方法store,把集合中的临时数据,持久化到硬盘中。
        properties.store(fileWriter,"");
        //释放资源
        fileWriter.close();

        //字节输出流
        properties.store(new FileOutputStream("prop.txt"),"");
    }

可以使用Properties集合中的方法load,把硬盘中保存的文件(键值对),读取到集合中使用

 /*
        可以使用Properties集合中的方法load,把硬盘中保存的文件(键值对),读取到集合中使用
        void load(InputStream inStream)
        void load(Reader reader)
        参数:
            InputStream inStream:字节输入流,不能读取含有中文的键值对。
            Reader reader:字符输入流,能读取含有中文的键值对。
        使用步骤:
            1.创建Properties集合对象
            2.使用Properties集合对象中的方法load读取保存键值对的文件
            3.遍历properties集合
        注意:
            1.存储键值对的文件中,键与值的连接符号可以使用=,空格(其他符号)。
            2.存储键值对的文件中,可以使用#来进行注释,被注释的键值对把会被读取。
            3.存储键值对的文件中,键与值默认都是字符串,不用加引号。
     */
    @Test
    public void TestProp03() throws IOException {
        //创建Properties集合对象
        Properties properties = new Properties();
        //使用Properties集合对象中的方法load读取保存键值对的文件
        //字符输入流可以读取中文
        properties.load(new FileReader("prop.txt"));
        //字节输入流不能读取中文
        properties.load(new FileInputStream("prop.txt"));
        //遍历properties集合
        Set<String> set = properties.stringPropertyNames();
        for (String key : set) {
            String value = properties.getProperty(key);
            System.out.println(key + "=" + value);
        }
    }

缓冲流

概述

缓冲流,也叫做高效流,是对4个基本的Filexxx流的增强,所以也是4个流,按照数据类型分类:

字节缓冲流:BufferedInputStream,BuffereOutputStream
字符缓冲流:BufferedReader,BufferedWriter

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

字节缓冲流:

构造方法

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

BufferedInputStream缓冲输入流

/*
    java.io.BufferedInputStream extends InputStream
    BufferedInputStream:字节缓冲输出流
    继承自父类的成员方法:
        int read():从输入流中读取数据的下一个字节
        int read(byte[] b):从此输入流中将最多 b.length 个字节的数据读入一个 byte 数组中。
        long skip(long n): 从输入流中跳过并丢弃 n 个字节的数据。
        int read(byte[] b, int off, int len):从此输入流中将最多 len 个字节的数据读入一个 byte 数组中。
        void close():关闭此文件输入流并释放与此流有关的所有系统资源。
    构造方法:
        BufferedInputStream(InputStream in):创建一个BufferedInputStream并保存其参数,即输入流in,以便将来使用。
        BufferedInputStream(InputStream in,int size):创建具有指定缓冲区大小的BufferedInputStream并保存其参数,即输入流in,以便将来使用。
        参数:
            InputStream in:我们可以传递FileInputStream对象,缓冲流会给FileInputStream增加一个缓冲区,提高FileInputStream的读取效率
            int size:指定缓冲流的内部缓冲区大小,不指定就是默认大小
    使用步骤:
        1.创建一个FileInputStream对象,构造方法中绑定要读取的数据源
        2.创建BufferedInputStream对象,构造方法中传递FileInputStream对象,提高FileInputStream的读取效率
        3.使用BufferedInputStream对象中的方法read,读取文件
        4。释放资源
 */
import org.junit.Test;

import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;

public class BufferedInputStreamTest {
    @Test
    public void TestBufferedInputStream01() throws IOException {
        //创建一个FileInputStream对象,构造方法中绑定要读取的数据源
        FileInputStream fileInputStream = new FileInputStream("c.txt");
        //创建BufferedInputStream对象,构造方法中传递FileInputStream对象,提高InputStream的读取效率
        BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream);
        //使用BufferedInputStream对象中的方法read,读取文件
        int len = 0;
        while ((len = bufferedInputStream.read()) != -1) {
            System.out.println(len);
        }
        //存储每次读取的数据
        byte[] bytes = new byte[1024];
        //记录每次读取的有效字节个数
        int aaa = 0;
        while ((aaa = bufferedInputStream.read(bytes)) != -1) {
            //把数组转换为字符串,从0开始到有效的字节个数
            System.out.println(new String(bytes, 0, aaa));
        }
        //关闭缓冲流,基本流就会跟着关闭
        bufferedInputStream.close();
    }
}

BufferedOutputStream缓冲输出流

/*
    java.io.BufferedOutputStream extends OutputStream
    BufferedOutputStream:字节缓冲输出流
    继承自父类的共性成员方法:
        void close():关闭此输出流并释放与此流有关的所有系统资源。
        void flush():刷新此输出流并强制写出所有缓冲的输出字节。
        void write(byte[] b):将 b.length 个字节从指定的 byte 数组写入此输出流。
        void write(byte[] b, int off, int len):将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此输出流。
        abstract  void write(int b):将指定的字节写入此输出流。
    构造方法:
        BufferedOutputStream(OutputStream out):创建一个新的缓冲输出流,以将数据写入指定的底层输出流
        BufferedOutputStream(OutputStream out,int size):创建一个新的缓冲输出流,以将具有指定缓冲区大小的数据写入指定的底层输出流
        参数:
            OutputStream out:字节输出流
                我们可以传递FileOutputStream,缓冲流会给FileOutputStream增加一个缓冲区,提高FileOutputStream的写入操作效率。
            int size:指定缓冲流的内部缓冲区大小,不指定就是默认大小
    使用步骤:
        1.创建一个字节输出流FileOutputStream,在构造方法中绑定要输出的目的地。
        2.创建BufferedOutputStream对象,在构造方法中传递FileOutputStream对象,提高FileOutputStream对象的效率
        3.使用BufferedOutputStream对象中的方法write,把数据写入到内部的缓冲区中
        4.使用BufferedOutputStream对象中的方法flush,把内部缓冲区的数据刷新到文件中
        5.释放资源,先调用flush刷新数据,所以第四步可以省略
 */

import org.junit.Test;

import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class BufferedOutputStream01 {
    @Test
    public void TestBufferedOutputStream01() throws IOException {
        //创建一个字节输出流FileOutputStream,在构造方法中绑定要输出的目的地。
        FileOutputStream fileOutputStream = new FileOutputStream("c.txt");
        //创建BufferedOutputStream对象,在构造方法中传递FileOutputStream对象,提高FileOutputStream对象的效率
        BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fileOutputStream);
        //使用BufferedOutputStream对象中的方法write,把数据写入到内部的缓冲区中
        bufferedOutputStream.write("我把数据写入到内部缓冲区中".getBytes());
        bufferedOutputStream.flush();
        bufferedOutputStream.close();
    }
}

使用缓冲流进行文件复制

import org.junit.Test;

import java.io.*;

/**
 * 文件复制练习:一读一写
 * 明确:
 * 数据源:D:\\aaa.jpg
 * 数据的目的地:D:\\workPaper\\aaa.jpg
 * 文件复制的步骤:
 * 1.创建字节缓冲输入流对象,构造方法中传递字节输入流
 * 2.创建字节缓冲输出流对象,构造方法中传递字节输出流
 * 3.使用字节缓冲输出流对象中的方法read,读取文件
 * 4.使用字节缓冲输出流中的方法write,把读取的数据写入到内部缓冲区中
 * 5.释放资源(会把缓冲区的数据,刷新到文件中)
 */

public class BufferedCopyFile {
    @Test
    public void CopyFile() throws IOException {
        //获取开始的毫秒值
        long start = System.currentTimeMillis();
        //创建字节缓冲输入流对象,构造方法中传递字节输入流
        BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream("D:\\aaa.jpg"));
        //创建字节缓冲输出流对象,构造方法中传递字节输出流
        BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(new FileOutputStream("D:\\workPaper\\aaa.jpg"));
        //使用字节缓冲输出流对象中的方法read,读取文件
        //一次读取一个字节写入一个字节
        int len = 0;
        while ((len = bufferedInputStream.read()) != -1) {
            //一次写入一个字节
            bufferedOutputStream.write(len);
        }
        //使用数组缓冲区读取多个字节,写入多个字节
        byte[] bytes = new byte[1024];
        int size = 0;
        while ((size = bufferedInputStream.read(bytes)) != -1) {
            //一次写入一个数组的字节
            bufferedOutputStream.write(bytes, 0, size);
        }
        //释放资源
        bufferedOutputStream.close();
        bufferedInputStream.close();
        //获取结束时的毫秒值
        long end = System.currentTimeMillis();
        System.out.println("所花费的时间:" + (end - start) + "ms");
    }
}

字符缓冲流

构造方法

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

字符缓冲输出流

/*
    java.io.BufferedWriter extends Writer
    BufferedWriter:字符缓冲输出流
    继承自父类的共性成员方法
        void write(int c):写入单个字符
        void write(char[] a):写入字符数组
        abstract  void  write(char[]  a,int  off,int  len):写入字符数组的某一部分,off数组的开始索引,len写的字符个数。
        void  write(String  str):写入字符串。
        void  write(String  str,int  off,int  len):写入字符串的某一部分,off表示字符串的开始索引,len写的字符串的字符个数。
        void  flush():刷新该流的缓冲。
        void  close():关闭此流,但是要先刷新它。
    构造方法:
        BufferedWriter(Writer out):创建一个使用默认大小输出缓冲区的缓冲字符输出流。
        BufferedWriter(Writer out,int size):创建一个使用给定大小输出缓冲区的新缓冲字符输出流。
        参数:
            Writer out:字符输出流,我们可以传递FileWriter,缓冲流会给FileWriter增加一个缓冲区,提高写入效率。
            int size:指定缓冲区的大小,不写即默认的大小。
    特有的一个成员方法:
        void newline():写入一个行分隔符,会根据不同的操作系统来写入对应的分隔符。
    使用步骤:
        1.创建一个字符缓冲输出流,构造方法中传递字符输出流。
        2.调用字符输出流中的方法writer把数据写入到内存缓冲区中。
        3.调用字符缓冲输出流中的方法flush,把内存缓冲区中的数据刷新到文件中。
        4.释放资源。
 */

import org.junit.Test;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;

public class BufferedWriterDemo01 {
    @Test
    public void TestBufferedWriter() throws IOException {
        //创建一个字符缓冲输出流,构造方法中传递字符输出流。
        BufferedWriter bufferedWriter=new BufferedWriter(new FileWriter("c.txt"));
        //调用字符输出流中的方法writer把数据写入到内存缓冲区中。
        for (int i = 0; i < 10; i++) {
            bufferedWriter.write("你好呀!");
            //写入换行符号
            //bufferedWriter.write("\r\n");
            bufferedWriter.newLine();
        }
        //调用字符缓冲输出流中的方法flush,把内存缓冲区中的数据刷新到文件中。
        bufferedWriter.flush();
        //释放资源
        bufferedWriter.close();
    }
}

字符缓冲输入流

/*
    java.io.BufferedReader extends Reader
    继承自父类的共性成员方法:
        int read():读取单个字符并返回读取到的字符。
        int reade(char[] a):一次读取多个字符,将字符读入到char数组中。
        void close():关闭该流并释放与之关联的所有资源。
    构造方法:
        BufferedReader(Reader in):创建一个使用默认大小输入缓冲区的缓冲字符输入流。
        BufferedReader(Reader in,int size):创建一个使用指定大小输入缓冲区的字符输入流。
        参数:
            Reader in:字符输入流,我们可以传递FileReader,缓冲流会给FileReader增加一个缓冲区,会提高FileReader的读取效率。
        特有的成员方法:
            public String readLine():读取一行文本。读取一行数据。
            行的终止符号:通过下列字符之一即可认为某行已经结束:换行('\n')、回车('\r')或回车后面跟着换行('\r\n')
        返回值:包含该行内容,不包含任何终止符号,如果已经到达流末尾,则返回null。

        使用步骤:
            1.创建一个字符缓冲输入流,构造方法中传递字符输入流。
            2.使用字符缓冲输入流对象中的方法read/readLine读取文本。
            3.释放资源。
 */

import org.junit.Test;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;

public class BufferedReaderDemo01 {
    @Test
    public void TestBufferedReader() throws IOException {
        //创建一个字符缓冲输入流,构造方法中传递字符输入流。
        BufferedReader bufferedReader = new BufferedReader(new FileReader("c.txt"));
        //使用字符缓冲输入流对象中的方法read/readLine读取文本。
        String line = null;
        line = bufferedReader.readLine();
        System.out.println(line);
        line = bufferedReader.readLine();
        System.out.println(line);
        line = bufferedReader.readLine();
        System.out.println(line);
        line = bufferedReader.readLine();
        System.out.println(line);
        
        //发现以上读取是一个重复的过程,可以使用while循环,不知道读取到那代表读取完毕?
        //while的结束判断为,读取到null代表读取完了。
        while ((line = bufferedReader.readLine()) != null) {
            System.out.println(line);
        }

        //释放资源。
        bufferedReader.close();
    }
}

文本排序

import org.junit.Test;

import java.io.*;
import java.util.HashMap;

/*
    练习:
        对文本的内容进行排序
        按照1,2,3,……
    分析:
        1.创建一个HashMap集合对象,可以存储每行文本的序号(1,2,3,……);value:存储每行文本。
        2.创建字符缓冲输入流对象,构造方法中绑定字符输入流。
        3.创建字符缓冲输出流,构造方法中绑定字符输出流。
        4.使用字符缓冲输入流中的方法readLine,逐行读取文本。
        5.对读取到的文本进行切割,获取行中的序号和文本的内容。
        6.把切割好的序号和文本的内容存储HashMap集合中(key序号是有序的,会自动排序1,2,3,4,……)。
        7.遍历HashMap集合,获取每一个键值对。
        8.把每个键值对,拼接到文本行。
        9.把拼接好的文本,使用字符缓冲输出流中方法write,写入到文件中。
        10.释放资源。
 */
public class DemoText {
    @Test
    public void TestDemo() throws IOException {
        //创建一个HashMap集合对象,可以存储每行文本的序号(1,2,3,……);value:存储每行文本。
        HashMap<String,String>map=new HashMap<>();
        //创建字符缓冲输入流对象,构造方法中绑定字符输入流。
        BufferedReader bufferedReader=new BufferedReader(new FileReader("in.txt"));
        //创建字符缓冲输出流,构造方法中绑定字符输出流。
        BufferedWriter bufferedWriter=new BufferedWriter(new FileWriter("out.txt"));
        //使用字符缓冲输入流中的方法readLine,逐行读取文本。
        String line;
        while ((line=bufferedReader.readLine())!=null){
            //对读取到的文本进行切割,获取行中的序号和文本的内容。
            String[] array = line.split("\\.");
            //把切割好的序号和文本的内容存储HashMap集合中(key序号是有序的,会自动排序1,2,3,4,……)。
            map.put(array[0],array[1]);
        }
        //遍历HashMap集合,获取每一个键值对。
        for(String key:map.keySet()){
            String value=map.get(key);
            //把每个键值对,拼接到文本行。
            line=key+"."+ value;
            //把拼接好的文本,使用字符缓冲输出流中方法write,写入到文件中。
            bufferedWriter.write(line);
            //写一个换行
            bufferedWriter.newLine();
        }
        //释放资源。
        bufferedWriter.close();
        bufferedReader.close();
    }
}

转换流

字符编码和字符集

字符编码

计算机中存储的信息都是用二进制数表示的,而我们在屏幕上看到数字、英文、标点符号、汉字等字符是二进制数转换之后的结果。按照某种规则,将字符存储到计算机中,称为编码。反之,将存储在计算机中的二进制数按照某种规则解析显示,称为解码。比如说:按照A规则存储,同样按照A规则解析,那么就能显示正确的文本符号。反正,按照A规则存储,再按照B规则解析,就会导致乱码现象。

编码:字符(看得懂的)-->字节(看不懂的)
解码:字节(看不懂的)-->字符(看德懂的)

字符编码:就是一套自然语言的字符与二进制数之间的对应规则。
编码表:生活中文字和计算机中二进制的对应规则。

字符集

字符集:也叫编码表。是一个系统支持的所有字符集合、包括各国家文字、标点符号、图形符号、数字等。
计算机要准确的存储和识别各种字符集符号,需要进行符号编码,一套字符集必然至少有一套字符编码。常见字符集有ASCII字符集、GBK字符集、Unicode字符集等。
可见,当指定了编码,它所对应的字符集自然就指定了,所以编码才是我么最关心的。

ASCII字符集:

ASCII是基于拉丁字母的一套电脑编码系统,用于显示现代英语,主要包括控制字符(回车键、退格、换行键等)和可显示字符(英文大小写字符、阿拉伯数字和西文符号)。
基本的ASCII字符集,使用7为(bits)表示一个字符,共128个字符。ASCII的扩展字符集使用8位(bits)表示一个字符,共256个字符,方便支持欧洲常用字符。

ISO-8859-1字符集:

拉丁码表,别名Latin-1,用于显示欧洲使用的语言,包括荷兰、丹麦、德语、意大利语、西班牙语等。
ISO-5559-1使用单字节编码,兼容ASCII编码。

GBxxx字符集:

GB就是国标的意思,是为了显示中文而设计的一套字符集。
GB2312:简体中文码表。一个小于127的字符的意思与原来相同。但两个大于127的字符连在一起时,就表示一个汉字,这样大约可以组合了包含7000多个简体汉字,此外数学符号、罗马希腊的字母、日文的假名们都编进去了,连在ASCII里本来就有的数字、标点、字母都统统重新编了两个字节长的编码,这就是常说的"全角"字符,而原来在127号以下的哪些就叫"半角"字符了。
GBK:最常用的中文编码表,是在GB2312标准基础上扩展规范,使用了双字节编码方案,共记录了21003个汉字,完全兼容GB2312标准,同时支持繁体汉字及日韩汉字等。
GB18030:最新的中文编码表。收录了70244个,采用多字节编码,每个字可以由1个、2个或4个字节组成。支持中国国内少数民族的汉字,同时支持繁体汉字及日韩汉字等。

Unicode字符集:

Unicode编码系统为表达任意字符而设计,是业界的一种标准,也称为统一码、标准万国码。
它最多使用4个字节的数字来表示每个字母、符号或者文字。有三种编码方案,UTF-8、UTF-16和UTF-32.最常用的是UTF-8编码。
UTF-8编码,可以用来表示Unicode标准中任何字符,它是电子邮件,网页及其它存储或传送文字的应用中,优先采用UTF-8编码。互联网工程工作小组(IETF)要求所有互联网协议都必须支持UTF-8编码。所以,我们开发WEB应用,也要使用UTF-8编码。它使用一至四个字节为每个字符编码,编码规则:

128个US-ASCII字符,只需要一个字节编码。
拉丁文等字符,需要两个字节编码。
大部分常用之(含中文),使用三个字节编码。
其他极少是哟Unicode辅助字符,使用四字节编码。

编码引起的问题

在IDEA中,使用FileReader读取项目文件中的文本文件。由于IDEA的设置,默认都是UTF-8编码。所以没有如何问题。但是,当读取Windows系统中创建的文本文件时,由于Windows系统默认使用的时GBK编码,就会出现乱码。

/**
 * FileReader可以读取默认编码格式(UTF-8)的文件
 * FileReader读取系统,默认编码(中文GBK)会产生乱码
 */
 @Test
    public void Test() throws IOException {
        FileReader fileReader=new FileReader("helloGBK.txt");
        int len=0;
        while ((len=fileReader.read())!=-1){
            System.out.println((char) len);
        }
        fileReader.close();
    }

那么如何来读取GBK编码格式的文件呢?

InputStreamReader类(转换流)

转换流java.io.InputStreamReader,是Reader类的子类,是从字节流到字符流的桥梁。它读取字节,并使用指定的字符集将其解码。它的字符集可以以由名称指定,也可以接受平台的默认字符集。
构造方法:

  • InputStreamReader(InputStream in):创建一个使用默认字符集的字符流。
  • InputStreamReader(InputStream in,String charsetName):创建一个指定字符集的字符流
import org.apache.coyote.http11.InputFilter;
import org.junit.Test;

import java.io.*;

/**
 * java.io.InputStreamReader extends Reader
 * InputStreamReader:是字节流通向字符流的桥梁,它使用指定charset 读取字节并转为字符(解码)
 * 共性的成员方法:
 * int read():读取单个字符并返回读取到的字符。
 * int reade(char[] a):一次读取多个字符,将字符读入到char数组中。
 * void close():关闭该流并释放与之关联的所有资源。
 * 构造方法:
 * InputStreamReader(InputStream in)创建一个使用默认字符集的InputStream
 * InputStreamReader(InputStream in,String charName)创建一个使用指定字符集的InputStream
 * 参数:
 * InputStream in:字节输入流,用来读取文件中保存的字节
 * String charName:指定的编码表名称,不区分大小写。我们可以是utf-8/UTF-8,……不指定默认使用UFT-8
 * 使用步骤:
 * 1.创建一个 InputStreamReader对象,构造方法中传递字节输入流和指定编码表的名称
 * 2.使用 InputStreamReader对象中的reader读取文件
 * 3.关闭资源
 * 注意事项:
 * 构造方法中指定的编码表的名称要和文件的编码相同,否则会发送乱码
 */
public class DemoInputStreamReader {
    @Test
    public void Test() throws IOException {
        //reader_utf_8();
        reader_GBK();
    }

    /**
     * 使用转换流inputStreamReader读取UTF-8格式的文件
     */
    public void reader_utf_8() throws IOException {
        //创建一个 InputStreamReader对象,构造方法中传递字节输入流和指定编码表的名称
        InputStreamReader inputStreamReader = new InputStreamReader(new FileInputStream("utf-8.txt"), "utf-8");
        //使用 InputStreamReader对象中的reader读取文件
        int len=0;
        while ((len=inputStreamReader.read())!=-1){
            System.out.println((char) len);
        }
        //是释放资源
        inputStreamReader.close();
    }

    private void reader_GBK() throws IOException {
        //创建一个 InputStreamReader对象,构造方法中传递字节输入流和指定编码表的名称
        InputStreamReader inputStreamReader = new InputStreamReader(new FileInputStream("utf-8.txt"), "GBK");
        //使用 InputStreamReader对象中的reader读取文件
        int len=0;
        while ((len=inputStreamReader.read())!=-1){
            System.out.println((char) len);
        }
        //是释放资源
        inputStreamReader.close();
    }
}

转换流java.io.OutputStreamWriter,是Writer类的子类,是字符通向字节的桥梁,可以使用指定的charName 将要写入的流中的字符编码转为字节(编码)
构造方法:

  • OutputStreamWriter(OutputStream out)创建使用默认编码的OutputStreamWriter
  • OutputStreamWriter(OutputStream out,String charName)创建使用指定的字符集的OutputStreamWriter字符流
import org.junit.Test;

import java.io.*;

/**
 * java.io.OutputStreamWriter extends Writer
 * OutputStreamWriter:是字符通向字节的桥梁,可以使用指定的charName 将要写入的流中的字符编码转为字节(编码)
 * 继承自父类的共性方法:
 *         void write(int c):写入单个字符
 *         void write(char[] a):写入字符数组
 *         abstract  void  write(char[]  a,int  off,int  len):写入字符数组的某一部分,off数组的开始索引,len写的字符个数。
 *         void  write(String  str):写入字符串。
 *         void  write(String  str,int  off,int  len):写入字符串的某一部分,off表示字符串的开始索引,len写的字符串的字符个数。
 *         void  flush():刷新该流的缓冲。
 *         void  close():关闭此流,但是要先刷新它。
 * 构造方法:
 *      OutputStreamWriter(OutputStream out)创建使用默认编码的OutputStreamWriter
 *      OutputStreamWriter(OutputStream out,String charName)创建使用指定的字符集的OutputStreamWriter
 *      参数:
 *          OutputStream out:字节输出流,可以用来些转换后的字节到文件中。
 *          String charName:指定的编码表名称,不区分大小写。我们可以是utf-8/UTF-8,……不指定默认使用UFT-8
 *      使用步骤:
 *          1.创建一个 OutputStreamWriter对象,构造方法中传递字节输出流和指定编码表的名称
 *          2.使用 OutputStreamWriter对象中的writer,把字符转换为字节存储到缓冲区中(编码)
 *          3.使用 OutputStreamWriter对象中的方法flush,把内存缓冲区中的字节刷新到文件中(使用字节流写字节的过程)
 *          4.关闭资源
 */
public class DemoOutputStreamWriter {
    @Test
    public void Test() throws IOException {
        //writer_utf_8();
        writer_GBK();
    }

    /**
     * 使用转换流OutputStreamWriter写UTF-8格式的文件
     */
    public void writer_utf_8() throws IOException {
        //创建一个 OutputStreamWriter对象,构造方法中传递字节输出流和指定编码表的名称
        OutputStreamWriter outputStreamWriter=new OutputStreamWriter(new FileOutputStream("utf-8.txt"),"utf-8");
        //使用 OutputStreamWriter对象中的writer,把字符转换为字节存储到缓冲区中(编码)
        outputStreamWriter.write("你好");
        //使用 OutputStreamWriter对象中的方法flush,把内存缓冲区中的字节刷新到文件中(使用字节流写字节的过程)
        outputStreamWriter.flush();
        //是释放资源
        outputStreamWriter.close();
    }

    private void writer_GBK() throws IOException {
        //创建一个 OutputStreamWriter对象,构造方法中传递字节输出流和指定编码表的名称
        OutputStreamWriter outputStreamWriter=new OutputStreamWriter(new FileOutputStream("utf-8.txt"),"GBK");
        //使用 OutputStreamWriter对象中的writer,把字符转换为字节存储到缓冲区中(编码)
        outputStreamWriter.write("你好");
        //使用 OutputStreamWriter对象中的方法flush,把内存缓冲区中的字节刷新到文件中(使用字节流写字节的过程)
        outputStreamWriter.flush();
        //是释放资源
        outputStreamWriter.close();
    }
}

转换文件的编码练习:

  1. 指定GBK编码的转换流,读取文件
  2. 使用UTF-8编码的转换流,写出文件

案例实现:

import org.junit.Test;

import java.io.*;

public class TestFileCharName {
    @Test
    public void textFile() throws IOException {
        //创建InputStreamReader对象,构造方法中传递字节输入流和指定的编码表名称
        InputStreamReader inputStreamReader=new InputStreamReader(new FileInputStream("GBK.txt"),"GBK");
        //创建OutputStreamWriter对象,构造方法中传递字节输出流和指定的编码表名称UTF-8
        OutputStreamWriter outputStreamWriter=new OutputStreamWriter(new FileOutputStream("UTF_8.txt"),"UTF-8");
        //使用InputStreamReader对象的reader方法读取文件
        int len=0;
        while ((len=inputStreamReader.read())!=-1){
            //使用OutputStreamWriter对象中的writer方法,把读取到的数据写入到文件中
            outputStreamWriter.write(len);
        }
        //释放资源
        outputStreamWriter.close();
        inputStreamReader.close();
    }
}

移步于:序列化与打印流

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,837评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,551评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,417评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,448评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,524评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,554评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,569评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,316评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,766评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,077评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,240评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,912评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,560评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,176评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,425评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,114评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,114评论 2 352