JavaSE基础加强之File类与IO流(六)

文章目录

(一)File类
(二)递归
(三)过滤器
(四)IO字节流
(五)IO字符流
(六)Properties集合
(七)缓冲流
(八)转换流
(九)序列化流
(十)打印流

(一)File类

File类的概述

java.io.File类是文件和目录路径名的抽象表示形式
java把电脑中的文件文件夹(目录)封装为了一个File类
我们可以使用File类对文件和文件夹进行操作

我们可以使用File类的方法:

  1. 创建一个文件/文件夹
  2. 删除文件/文件夹
  3. 获取文件/文件夹
  4. 判断文件/文件夹是否存在
  5. 对文件夹进行遍历
  6. 获取文件的大小

File类是一个与系统无关的类,任何的操作系统都可以使用这个类中的方法

File类的静态成员变量

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

操作路径不能写死:

  • windowsC:\develop\a\a.txt
  • linuxC:/develop/a/a.txt
  • 应该:"C:"+File.separator+"develop"+File.separator+"a"+File.separator+"a.txt"
public class Demo01File {
    public static void main(String[] args) {

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

        String separator = File.separator;
        System.out.println(separator);// 文件名称分隔符 windows:反斜杠\  linux:正斜杠/
    }
}

绝对路径相对路径

绝对路径:是一个完整的路径
以盘符(C:或者D:等等)开始的路径
如:C:\\Users\itcast\\IdeaProjects\\shungyuan\\123.txt

相对路径:是一个简化的路径
相对指的是相对于当前项目的根目录
如果使用当前项目的根目录,路径可以简化书写
C:\\Users\itcast\\IdeaProjects\\shungyuan\\123.txt–>简化为: 123.txt(可以省略项目的根目录)

还可以这么写:
JavaSE基础加强之File类与IO流(六)_第1张图片
注意

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

File类的构造方法

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

      参数:
          String pathname:字符串的路径名称
      说明:
          路径可以是以文件结尾,也可以是以文件夹结尾
          路径可以是相对路径,也可以是绝对路径
          路径可以是存在,也可以是不存在
          创建File对象,只是把字符串路径封装为File对象,不考虑路径的真假情况
    
  • File(String parent, String child)根据 parent 路径名字符串和 child 路径名字符串创建一个新 File 实例

      参数:
          String parent:父路径
          String child:子路径
      好处:
           父路径和子路径,可以单独书写,使用起来非常灵活
           父路径和子路径都可以变化
    
  • File(File parent, String child)根据 parent 抽象路径名和 child 路径名字符串创建一个新 File 实例

      参数:
          File parent:父路径
          String child:子路径
      好处:
           父路径和子路径,可以单独书写,使用起来非常灵活;父路径和子路径都可以变化
           父路径是File类型,可以使用File的方法对路径进行一些操作,再使用路径创建对象
    
    public class Demo02File {
        public static void main(String[] args) {
            /*
                File类的构造方法
             */
            //show02("c:\\","a.txt");//c:\a.txt
            //show02("d:\\","a.txt");//d:\a.txt
            show03();
    
            File f = new File("C:\\Users\\itcast\\IdeaProjects\\shungyuan");
            long length = f.length();
            System.out.println(length);
        }
    	//File(File parent, String child)
        private static void show03() {
            File parent = new File("c:\\");
            File file = new File(parent,"hello.java");
            System.out.println(file);//c:\hello.java
        }
    	//File(String parent, String child)
        private static void show02(String parent, String child) {
            File file = new File(parent,child);
            System.out.println(file);//c:\a.txt
        }
    	//File(String pathname)
        private static void show01() {
            File f1 = new File("C:\\Users\\itcast\\IdeaProjects\\shungyuan\\a.txt");
            System.out.println(f1);//重写了Object类的toString方法 C:\Users\itcast\IdeaProjects\shungyuan\a.txt
    
            File f2 = new File("C:\\Users\\itcast\\IdeaProjects\\shungyuan");
            System.out.println(f2);//C:\Users\itcast\IdeaProjects\shungyuan
    
            File f3 = new File("b.txt");
            System.out.println(f3);//b.txt
        }
    }
    

File类获取功能的方法

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

public class Demo03File {
    public static void main(String[] args) {
        show04();
    }

    /*
        public long length()  :返回由此File表示的文件的长度。
        获取的是构造方法指定的文件的大小,以字节为单位
        注意:
            文件夹是没有大小概念的,不能获取文件夹的大小
            如果构造方法中给出的路径不存在,那么length方法返回0
     */
    private static void show04() {
        File f1 = new File("C:\\develop\\a\\1.jpg");
        long l1 = f1.length();
        System.out.println(l1);//780831字节

        File f2 = new File("C:\\develop\\a\\2.jpg");
        System.out.println(f2.length());//0

        File f3 = new File("C:\\develop\\a");
        System.out.println(f3.length());//0 文件夹没有大小概念的
    }

    /*
        public String getName()  :返回由此File表示的文件或目录的名称。
        获取的就是构造方法传递路径的结尾部分(文件/文件夹)
     */
    private static void show03() {
        File f1 = new File("C:\\Users\\itcast\\IdeaProjects\\shungyuan\\a.txt");
        String name1 = f1.getName();
        System.out.println(name1);//a.txt

        File f2 = new File("C:\\Users\\itcast\\IdeaProjects\\shungyuan");
        String name2 = f2.getName();
        System.out.println(name2);//shungyuan
    }

    /*
        public String getPath() :将此File转换为路径名字符串。
        获取的构造方法中传递的路径

        toString方法调用的就是getPath方法
        源码:
            public String toString() {
                return getPath();
            }
     */
    private static void show02() {
        File f1 = new File("C:\\Users\\itcast\\IdeaProjects\\shungyuan\\a.txt");
        File f2 = new File("a.txt");
        String path1 = f1.getPath();
        System.out.println(path1);//C:\Users\itcast\IdeaProjects\shungyuan\a.txt
        String path2 = f2.getPath();
        System.out.println(path2);//a.txt

        System.out.println(f1);//C:\Users\itcast\IdeaProjects\shungyuan\a.txt
        System.out.println(f1.toString());//C:\Users\itcast\IdeaProjects\shungyuan\a.txt
    }

    /*
        public String getAbsolutePath() :返回此File的绝对路径名字符串。
        获取的构造方法中传递的路径
        无论路径是绝对的还是相对的,getAbsolutePath方法返回的都是绝对路径
     */
    private static void show01() {
        File f1 = new File("C:\\Users\\itcast\\IdeaProjects\\shungyuan\\a.txt");
        String absolutePath1 = f1.getAbsolutePath();
        System.out.println(absolutePath1);//C:\Users\itcast\IdeaProjects\shungyuan\a.txt

        File f2 = new File("a.txt");
        String absolutePath2 = f2.getAbsolutePath();
        System.out.println(absolutePath2);//C:\Users\itcast\IdeaProjects\shungyuan\a.txt
    }
}

File类判断功能的方法

  • public boolean exists():此File表示的文件或目录是否实际存在
    用于判断构造方法中的路径是否存在
    存在:true
    不存在:false
  • public boolean isDirectory():此File表示的是否为目录
    用于判断构造方法中给定的路径是否以文件夹结尾
    是:true
    否:false
  • public boolean isFile():此File表示的是否为文件
    用于判断构造方法中给定的路径是否以文件结尾
    是:true
    否:false

注意

  • 电脑的硬盘中只有文件/文件夹,两个方法是互斥
  • 这两个方法使用前提,路径必须是存在的,否则都返回false

代码实现

public class Demo04File {
    public static void main(String[] args) {
        show02();
    }

	//public boolean isDirectory()和public boolean isFile()
    private static void show02() {
        File f1 = new File("C:\\Users\\itcast\\IdeaProjects\\shung");

        //不存在,就没有必要获取
        if(f1.exists()){
            System.out.println(f1.isDirectory());
            System.out.println(f1.isFile());
        }

        File f2 = new File("C:\\Users\\itcast\\IdeaProjects\\shungyuan");
        if(f2.exists()){
            System.out.println(f2.isDirectory());//true
            System.out.println(f2.isFile());//false
        }

        File f3 = new File("C:\\Users\\itcast\\IdeaProjects\\shungyuan\\shungyuan.iml");
        if(f3.exists()){
            System.out.println(f3.isDirectory());//false
            System.out.println(f3.isFile());//true
        }
    }

	//public boolean exists()
    private static void show01() {
        File f1 = new File("C:\\Users\\itcast\\IdeaProjects\\shungyuan");
        System.out.println(f1.exists());//true

        File f2 = new File("C:\\Users\\itcast\\IdeaProjects\\shung");
        System.out.println(f2.exists());//false

        File f3 = new File("shungyuan.iml");//相对路径 C:\Users\itcast\IdeaProjects\shungyuan\shungyuan.iml
        System.out.println(f3.exists());//true

        File f4 = new File("a.txt");
        System.out.println(f4.exists());//false
    }
}

File类创建删除功能的方法

  • public boolean createNewFile()

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

      public boolean mkdir() :创建单级空文件夹
      public boolean mkdirs() :既可以创建单级空文件夹,也可以创建多级文件夹
      创建文件夹的路径和名称在构造方法中给出(构造方法的参数)
      返回值:
      		true:文件夹不存在,创建文件夹,返回true
      		false:文件夹存在,不会创建,返回false;构造方法中给出的路径不存在返回false
      注意:
      		此方法只能创建文件夹,不能创建文件
    
  • public boolean delete()

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

代码实现

public class Demo05File {
    public static void main(String[] args) throws IOException {
        show03();
    }

    //public boolean delete()
    private static void show03() {
        File f1 = new File("08_FileAndRecursion\\新建文件夹");
        boolean b1 = f1.delete();
        System.out.println("b1:"+b1);

        File f2 = new File("08_FileAndRecursion\\abc.txt");
        System.out.println(f2.delete());
    }
    
    //public boolean mkdir()和public boolean mkdirs()
    private static void show02() {
        File f1 = new File("08_FileAndRecursion\\aaa");
        boolean b1 = f1.mkdir();
        System.out.println("b1:"+b1);

        File f2 = new File("08_FileAndRecursion\\111\\222\\333\\444");
        boolean b2 = f2.mkdirs();
        System.out.println("b2:"+b2);

        File f3 = new File("08_FileAndRecursion\\abc.txt");
        boolean b3 = f3.mkdirs();//看类型,是一个文件
        System.out.println("b3:"+b3);

        File f4 = new File("08_F\\ccc");
        boolean b4 = f4.mkdirs();//不会抛出异常,路径不存在,不会创建
        System.out.println("b4:"+b4);
    }

    //public boolean createNewFile()
    private static void show01() throws IOException {
        File f1 = new File("C:\\Users\\itcast\\IdeaProjects\\shungyuan\\08_FileAndRecursion\\1.txt");
        boolean b1 = f1.createNewFile();
        System.out.println("b1:"+b1);

        File f2 = new File("08_FileAndRecursion\\2.txt");
        System.out.println(f2.createNewFile());

        File f3 = new File("08_FileAndRecursion\\新建文件夹");
        System.out.println(f3.createNewFile());//不要被名称迷糊,要看类型

        File f4 = new File("08_FileAndRecursi\\3.txt");
        System.out.println(f4.createNewFile());//路径不存在,抛出IOException
    }
}

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

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

注意:

  • list方法和listFiles方法遍历的是构造方法中给出的目录
  • 如果构造方法中给出的目录的路径不存在,会抛出空指针异常
  • 如果构造方法中给出的路径不是一个目录,也会抛出空指针异常
public class Demo06File {
    public static void main(String[] args) {
        show02();
    }

    /*
        public File[] listFiles() :返回一个File数组,表示该File目录中的所有的子文件或目录。
        遍历构造方法中给出的目录,会获取目录中所有的文件/文件夹,把文件/文件夹封装为File对象,多个File对象存储到File数组中
     */
    private static void show02() {
        File file = new File("C:\\Users\\itcast\\IdeaProjects\\shungyuan\\08_FileAndRecursion");
        File[] files = file.listFiles();
        for (File f : files) {
            System.out.println(f);
        }
    }

    /*
        public String[] list() :返回一个String数组,表示该File目录中的所有子文件或目录。
        遍历构造方法中给出的目录,会获取目录中所有文件/文件夹的名称,把获取到的多个名称存储到一个String类型的数组中
     */
    private static void show01() {
        //File file = new File("C:\\Users\\itcast\\IdeaProjects\\shungyuan\\08_FileAndRecursion\\1.txt");//NullPointerException
        //File file = new File("C:\\Users\\itcast\\IdeaProjects\\shungyuan\\08_Fi");//NullPointerException
        File file = new File("C:\\Users\\itcast\\IdeaProjects\\shungyuan\\08_FileAndRecursion");
        String[] arr = file.list();
        for (String fileName : arr) {
            System.out.println(fileName);
        }
    }
}

(二)递归

递归概念&分类&注意事项

  • 递归的分类

      递归分为两种,直接递归和间接递归
      直接递归称为方法自身调用自己
      间接递归可以A方法调用B方法,B方法调用C方法,C方法调用A方法
    
  • 注意事项

      递归一定要有条件限定,保证递归能够停止下来,否则会发生栈内存溢出
      在递归中虽然有限定条件,但是递归次数不能太多,否则也会发生栈内存溢出
      构造方法禁止递归
    
  • 递归的使用前提

      当调用方法的时候,方法的主体不变,每次调用方法的参数不同,可以使用递归
    


代码实现

public class Demo01Recurison {
    public static void main(String[] args) {
        //a();
        b(1);
    }

    /*
        构造方法,禁止递归
            编译报错:构造方法是创建对象使用的,一直递归会导致内存中有无数多个对象,直接编译报错
     */
    public Demo01Recurison() {
        //Demo01Recurison();
    }

    /*
            在递归中虽然有限定条件,但是递归次数不能太多。否则也会发生栈内存溢出。
            11157
                Exception in thread "main" java.lang.StackOverflowError
         */
    private static void b(int i) {
        System.out.println(i);
        if(i==20000){
            return; //结束方法
        }
        b(++i);
    }

    /*
        递归一定要有条件限定,保证递归能够停止下来,否则会发生栈内存溢出。
        Exception in thread "main" java.lang.StackOverflowError
     */
    private static void a() {
        System.out.println("a方法!");
        a();
    }
}

练习:使用递归计算1-n之间的和

public class Demo02Recurison {
    public static void main(String[] args) {
        int s = sum(3);
        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;
        }

        //获取下一个被加的数字(n-1)
        return n + sum(n-1);
    }
}

练习:使用递归计算阶乘

/*
    练习:
        使用递归计算阶乘
        n的阶乘:n! = n * (n-1) *...* 3 * 2 * 1

 */
public class Demo03Recurison {
    public static void main(String[] args) {
        int jiecheng = jc(5);
        System.out.println(jiecheng);
    }

    /*
        定义方法使用递归计算阶乘
        5的阶乘: 5! = 5*(5-1)*(5-2)*(5-3)*(5-4)=5*4*3*2*1
        递归结束的条件
            获取到1的时候结束
        递归的目的
            获取下一个被乘的数字(n-1)
        方法的参数发生变化
            5,4,3,2,1
     */
    public static int jc(int n){
        //获取到1的时候结束
        if(n==1){
            return 1;
        }
        //获取下一个被乘的数字(n-1)
        return n * jc(n-1);
    }
}

练习:递归打印多级目录

需求:遍历c:\abc文件夹,及abc文件夹的子文件夹

    c:\\abc
    c:\\abc\\abc.txt
    c:\\abc\\abc.java
    c:\\abc\\a
    c:\\abc\\a\\a.jpg
    c:\\abc\\a\\a.java
    c:\\abc\\b
    c:\\abc\\b\\b.java
    c:\\abc\\b\\b.txt

代码实现

public class Demo04Recurison {
    public static void main(String[] args) {
        File file = new File("c:\\abc");
        getAllFile(file);
    }

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

综合案例:文件搜索

需求: 遍历c:\abc文件夹,及abc文件夹的子文件夹,只要.java结尾的文件

    c:\\abc
    c:\\abc\\abc.txt
    c:\\abc\\abc.java
    c:\\abc\\a
    c:\\abc\\a\\a.jpg
    c:\\abc\\a\\a.java
    c:\\abc\\b
    c:\\abc\\b\\b.java
    c:\\abc\\b\\b.txt

代码实现

public class Demo05Recurison {
    public static void main(String[] args) {
        File file = new File("c:\\abc");
        getAllFile(file);
    }

    /*
        定义一个方法,参数传递File类型的目录
        方法中对目录进行遍历
     */
    public static void getAllFile(File dir){
        //System.out.println(dir);//打印被遍历的目录名称
        File[] files = dir.listFiles();
        for (File f : files) {
            //对遍历得到的File对象f进行判断,判断是否是文件夹
            if(f.isDirectory()){
                //f是一个文件夹,则继续遍历这个文件夹
                //我们发现getAllFile方法就是传递文件夹,遍历文件夹的方法
                //所以直接调用getAllFile方法即可:递归(自己调用自己)
                getAllFile(f);
            }else{
                //f是一个文件,直接打印即可
                /*
                    c:\\abc\\abc.java
                    只要.java结尾的文件
                    1.把File对象f,转为字符串对象
                 */
                //String name = f.getName();//abc.java
                //String path = f.getPath();//c:\\abc\\abc.java
                //String s = f.toString();//c:\\abc\\abc.java

                //把字符串,转换为小写
                //s = s.toLowerCase();

                //2.调用String类中的方法endsWith判断字符串是否是以.java结尾
                //boolean b = s.endsWith(".java");

                //3.如果是以.java结尾的文件,则输出
                /*if(b){
                    System.out.println(f);
                }*/

                if(f.getName().toLowerCase().endsWith(".java")){
                    System.out.println(f);
                }
            }
        }
    }
}

(三)过滤器

FileFilter过滤器的原理和使用

在File类中有两个跟ListFiles重载相关的方法,方法的参数传递的就是过滤器

File[] listFiles(FileFilter filter)

java.io.FileFilter接口:
		用于抽象路径名(File对象)的过滤器
作用:
		用来过滤文件(File对象)
抽象方法:
		boolean accept(File pathname):测试指定抽象路径名是否应该包含在某个路径名列表中
参数:
		File pathname:使用ListFiles方法遍历目录,得到的每一个文件对象

File[] listFiles(FilenameFilter filter)

java.io.FilenameFilter接口:
		实现此接口的类实例可用于过滤器文件名。
作用:
		用于过滤文件名称
抽象方法:
		用来过滤文件的方法
boolean accept(File dir, String name):
		测试指定文件是否应该包含在某一文件列表中。
参数:
		File dir:构造方法中传递的被遍历的目录
		String name:使用ListFiles方法遍历目录,获取的每一个文件/文件夹的名称
注意:
		两个过滤器接口是没有实现类的,需要我们自己写实现类,重写过滤的方法accept,在方法中自己定义过滤的规则


代码实现

public class Demo01Filter {
    public static void main(String[] args) {
        File file = new File("c:\\abc");
        getAllFile(file);
    }

    /*
        定义一个方法,参数传递File类型的目录
        方法中对目录进行遍历
     */
    public static void getAllFile(File dir){
        File[] files = dir.listFiles(new FileFilterImpl());//传递过滤器对象
        for (File f : files) {
            //对遍历得到的File对象f进行判断,判断是否是文件夹
            if(f.isDirectory()){
                //f是一个文件夹,则继续遍历这个文件夹
                //我们发现getAllFile方法就是传递文件夹,遍历文件夹的方法
                //所以直接调用getAllFile方法即可:递归(自己调用自己)
                getAllFile(f);
            }else{
                //f是一个文件,直接打印即可
                System.out.println(f);
            }
        }
    }
}
/*
    创建过滤器FileFilter的实现类,重写过滤方法accept,定义过滤规则
 */
public class FileFilterImpl implements FileFilter{
    @Override
    public boolean accept(File pathname) {
        /*
            过滤的规则:
            在accept方法中,判断File对象是否是以.java结尾
            是就返回true
            不是就返回false
         */
        //如果pathname是一个文件夹,返回true,继续遍历这个文件夹
        if(pathname.isDirectory()){
            return true;
        }

        return pathname.getName().toLowerCase().endsWith(".java");
    }
}

使用和Lambda表达式FileNameFilter过滤器

public class Demo02Filter {
    public static void main(String[] args) {
        File file = new File("c:\\abc");
        getAllFile(file);
    }

    /*
        定义一个方法,参数传递File类型的目录
        方法中对目录进行遍历
     */
    public static void getAllFile(File dir){
        //传递过滤器对象 使用匿名内部类
        /*File[] files = dir.listFiles(new FileFilter() {
            @Override
            public boolean accept(File pathname) {
                //过滤规则,pathname是文件夹或者是.java结尾的文件返回true
                return pathname.isDirectory() || pathname.getName().toLowerCase().endsWith(".java");
            }
        });*/

        //使用Lambda表达式优化匿名内部类(接口中只有一个抽象方法)
        /*File[] files = dir.listFiles((File pathname)->{
            return pathname.isDirectory() || pathname.getName().toLowerCase().endsWith(".java");
        });*/

        File[] files = dir.listFiles(pathname->pathname.isDirectory() || pathname.getName().toLowerCase().endsWith(".java"));

        /*File[] files = dir.listFiles(new FilenameFilter() {
            @Override
            public boolean accept(File dir, String name) {
                //过滤规则,pathname是文件夹或者是.java结尾的文件返回true
                return new File(dir,name).isDirectory() || name.toLowerCase().endsWith(".java");
            }
        });*/

        //使用Lambda表达式优化匿名内部类(接口中只有一个抽象方法)
        /*File[] files = dir.listFiles((File d, String name)->{
            //过滤规则,pathname是文件夹或者是.java结尾的文件返回true
            return new File(d,name).isDirectory() || name.toLowerCase().endsWith(".java");
        });*/

        //File[] files = dir.listFiles((d,name)->new File(d,name).isDirectory() || name.toLowerCase().endsWith(".java"));

        for (File f : files) {
            //对遍历得到的File对象f进行判断,判断是否是文件夹
            if(f.isDirectory()){
                //f是一个文件夹,则继续遍历这个文件夹
                //我们发现getAllFile方法就是传递文件夹,遍历文件夹的方法
                //所以直接调用getAllFile方法即可:递归(自己调用自己)
                getAllFile(f);
            }else{
                //f是一个文件,直接打印即可
                System.out.println(f);
            }
        }
    }
}

(四)IO字节流

IO概述(概念&分类)

一切皆为字节

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

字节输出流:OutputStream类&FileOutputStream类

java.io.OutputStream:字节输出流
此抽象类是表示输出字节流的所有类的超类
该类定义了一些子类共性的成员方法:

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

java.io.FileOutputStream extends OutputStream:文件字节输出流

作用:
		把内存中的数据写入到硬盘的文件中
构造方法:
		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 Demo01OutputStream {
    public static void main(String[] args) throws IOException {
        //1.创建一个FileOutputStream对象,构造方法中传递写入数据的目的地
        FileOutputStream fos = new FileOutputStream("09_IOAndProperties\\a.txt");
        //2.调用FileOutputStream对象中的方法write,把数据写入到文件中
        //public abstract void write(int b) :将指定的字节输出流。
        fos.write(97);
        //3.释放资源(流使用会占用一定的内存,使用完毕要把内存清空,提供程序的效率)
        //fos.close();
    }
}

JavaSE基础加强之File类与IO流(六)_第2张图片

文件存储的原理

字节输出流写多个字节的方法

  • public void write(byte[] b):将b.length字节从指定的字节数组写入此输出流
  • public void write(byte[] b, int off, int len):从指定的字节数组写入len字节,从偏移量off开始输出到此输出流

代码实现

public class Demo02OutputStream {
    public static void main(String[] args) throws IOException {
        //创建FileOutputStream对象,构造方法中绑定要写入数据的目的地
        FileOutputStream fos = new FileOutputStream(new File("09_IOAndProperties\\b.txt"));
        //调用FileOutputStream对象中的方法write,把数据写入到文件中
        //在文件中显示100,写个字节
        fos.write(49);
        fos.write(48);
        fos.write(48);

        /*
            public void write(byte[] b):将 b.length字节从指定的字节数组写入此输出流。
            一次写多个字节:
                如果写的第一个字节是正数(0-127),那么显示的时候会查询ASCII表
                如果写的第一个字节是负数,那第一个字节会和第二个字节,两个字节组成一个中文显示,查询系统默认码表(GBK)
         */
        byte[] bytes = {65,66,67,68,69};//ABCDE
        //byte[] bytes = {-65,-66,-67,68,69};//烤紻E
        fos.write(bytes);

        /*
            public void write(byte[] b, int off, int len) :把字节数组的一部分写入到文件中
                int off:数组的开始索引
                int len:写几个字节
         */
        fos.write(bytes,1,2);//BC

        /*
            写入字符的方法:可以使用String类中的方法把字符串,转换为字节数组
                byte[] getBytes()  把字符串转换为字节数组
         */
        byte[] bytes2 = "你好".getBytes();
        System.out.println(Arrays.toString(bytes2));//[-28, -67, -96, -27, -91, -67]
        fos.write(bytes2);

        //释放资源
        fos.close();
    }
}

注意:UTF-8编码是3个字节对应一个中文,而GBK编码是2个字节对应一个中文

字节输出流的续写换行

经过以上的演示,每次程序运行,创建输出流对象,都会清空目标文件中的数据
那么如何保留目标文件中数据,还能继续添加新数据呢?
追加写/续写:使用两个参数的构造方法:

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

代码实现

public class Demo03OutputStream {
    public static void main(String[] args) throws IOException {
        FileOutputStream fos = new FileOutputStream("09_IOAndProperties\\c.txt",true);
        for (int i = 1; i <=10 ; i++) {
            fos.write("你好".getBytes());
            fos.write("\r\n".getBytes());
        }

        fos.close();
    }
}

字节输入流:InputStream类&FileInputStream类

  • java.io.InputStream:字节输入流
    此抽象类是表示字节输入流的所有类的超类
    该类定义了所有子类共性的方法:

      int read()从输入流中读取数据的下一个字节
      int read(byte[] b) 从输入流中读取一定数量的字节,并将其存储在缓冲区数组 b 中
      void close() 关闭此输入流并释放与该流关联的所有系统资源
    
  • 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 Demo01InputStream {
    public static void main(String[] args) throws IOException {
        //1.创建FileInputStream对象,构造方法中绑定要读取的数据源
        FileInputStream fis = new FileInputStream("09_IOAndProperties\\c.txt");
        //2.使用FileInputStream对象中的方法read,读取文件
        //int read()读取文件中的一个字节并返回,读取到文件的末尾返回-1
        /*int len = fis.read();
        System.out.println(len);//97 a

        len = fis.read();
        System.out.println(len);// 98 b

        len = fis.read();
        System.out.println(len);//99 c

        len = fis.read();
        System.out.println(len);//-1

        len = fis.read();
        System.out.println(len);//-1*/

        /*
            发现以上读取文件是一个重复的过程,所以可以使用循环优化
            不知道文件中有多少字节,使用while循环
            while循环结束条件,读取到-1的时候结束

            布尔表达式(len = fis.read())!=-1
                1.fis.read():读取一个字节
                2.len = fis.read():把读取到的字节赋值给变量len
                3.(len = fis.read())!=-1:判断变量len是否不等于-1
         */
        int len = 0; //记录读取到的字节
        while((len = fis.read())!=-1){
            System.out.print(len);//abc
        }

        //3.释放资源
        fis.close();
    }
}

字节输入流一次读取多个字节

  • int read(byte[] b)从输入流中读取一定数量的字节,并将其存储在缓冲区数组 b 中
    明确两件事情:

      1.方法的参数byte[]的作用?
      		起到缓冲作用,存储每次读取到的多个字节
      		数组的长度一把定义为1024(1kb)或者1024的整数倍
      2.方法的返回值int是什么?
      		每次读取的有效字节个数
    
  • String类的构造方法:

      String(byte[] bytes) :把字节数组转换为字符串
      String(byte[] bytes, int offset, int length):把字节数组的一部分转换为字符串 offset:数组的开始索引 length:转换的字节个数
    

代码实现

public class Demo02InputStream {
    public static void main(String[] args) throws IOException {
        //创建FileInputStream对象,构造方法中绑定要读取的数据源
        FileInputStream fis = new FileInputStream("09_IOAndProperties\\b.txt");
        //使用FileInputStream对象中的方法read读取文件
        //int read(byte[] b) 从输入流中读取一定数量的字节,并将其存储在缓冲区数组 b 中。
        /*byte[] bytes = new byte[2];
        int len = fis.read(bytes);
        System.out.println(len);//2
        //System.out.println(Arrays.toString(bytes));//[65, 66]
        System.out.println(new String(bytes));//AB

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

        len = fis.read(bytes);
        System.out.println(len);//1
        System.out.println(new String(bytes));//ED

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

        /*
            发现以上读取时一个重复的过程,可以使用循环优化
            不知道文件中有多少字节,所以使用while循环
            while循环结束的条件,读取到-1结束
         */
        byte[] bytes = new byte[1024];//存储读取到的多个字节
        int len = 0; //记录每次读取的有效字节个数
        while((len = fis.read(bytes))!=-1){
            //String(byte[] bytes, int offset, int length) 把字节数组的一部分转换为字符串 offset:数组的开始索引 length:转换的字节个数
            System.out.println(new String(bytes,0,len));
        }

        //释放资源
        fis.close();
    }
}

练习:文件复制


文件复制练习:一读一写

明确:
	数据源:c:\\1.jpg
	数据的目的地::d:\\1.jpg

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

代码实现

public class Demo01CopyFile {
    public static void main(String[] args) throws IOException {
        long s = System.currentTimeMillis();
        //1.创建一个字节输入流对象,构造方法中绑定要读取的数据源
        FileInputStream fis = new FileInputStream("c:\\1.jpg");
        //2.创建一个字节输出流对象,构造方法中绑定要写入的目的地
        FileOutputStream fos = new FileOutputStream("d:\\1.jpg");
        //一次读取一个字节写入一个字节的方式
        //3.使用字节输入流对象中的方法read读取文件
        /*int len = 0;
        while((len = fis.read())!=-1){
            //4.使用字节输出流中的方法write,把读取到的字节写入到目的地的文件中
            fos.write(len);
        }*/

        //使用数组缓冲读取多个字节,写入多个字节
        byte[] bytes = new byte[1024];
        //3.使用字节输入流对象中的方法read读取文件
        int len = 0;//每次读取的有效字节个数
        while((len = fis.read(bytes))!=-1){
            //4.使用字节输出流中的方法write,把读取到的字节写入到目的地的文件中
            fos.write(bytes,0,len);
        }

        //5.释放资源(先关写的,后关闭读的;如果写完了,肯定读取完毕了)
        fos.close();
        fis.close();
        long e = System.currentTimeMillis();
        System.out.println("复制文件共耗时:"+(e-s)+"毫秒");
    }
}

使用字节流读取中文的问题

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

使用字节流读取中文文件
1个中文
    GBK:占用两个字节
    UTF-8:占用3个字节

所以如果是UTF-8编码,每一次只能读取三分之一个中文,自然会乱码
总结:处理音频、图片和视频可以用字节流,但是处理文本信息,还是建议用字符流

(五)IO字符流

字符输入流:Reader&FileReader

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

      int read() 读取单个字符并返回。
      int read(char[] cbuf)一次读取多个字符,将字符读入数组。
      void close() 关闭该流并释放与之关联的所有资源。
    
  • java.io.FileReader extends InputStreamReader extends Reader:文件字符输入流

      作用:
      		把硬盘文件中的数据以字符的方式读取到内存中
      构造方法:
      		FileReader(String fileName)
      		FileReader(File file)
      参数:
      		String fileName:文件的路径
      		File file:一个文件
      FileReader构造方法的作用:
      		1.创建一个FileReader对象
      		2.会把FileReader对象指向要读取的文件
    

字符输入流读取字符数据

字符输入流的使用步骤:

1.创建FileReader对象,构造方法中绑定要读取的数据源
2.使用FileReader对象中的方法read读取文件
3.释放资源

代码实现

public class Demo02Reader {
    public static void main(String[] args) throws IOException {
        //1.创建FileReader对象,构造方法中绑定要读取的数据源
        FileReader fr = new FileReader("09_IOAndProperties\\c.txt");
        //2.使用FileReader对象中的方法read读取文件
        //int read() 读取单个字符并返回。
        /*int len = 0;
        while((len = fr.read())!=-1){
            System.out.print((char)len);
        }*/

        //int read(char[] cbuf)一次读取多个字符,将字符读入数组。
        char[] cs = new char[1024];//存储读取到的多个字符
        int len = 0;//记录的是每次读取的有效字符个数
        while((len = fr.read(cs))!=-1){
            /*
                String类的构造方法
                String(char[] value) 把字符数组转换为字符串
                String(char[] value, int offset, int count) 把字符数组的一部分转换为字符串 offset数组的开始索引 count转换的个数
             */
            System.out.println(new String(cs,0,len));
        }

        //3.释放资源
        fr.close();
    }
}

字符输出流:Writer类&FileWriter类

  • 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():关闭此流,但要先刷新它
    
  • java.io.FileWriter extends OutputStreamWriter extends Writer:文件字符输出流

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

字符输出流的基本使用:一次写单个字符

字符输出流的使用步骤(重点):
		1.创建FileWriter对象,构造方法中绑定要写入数据的目的地
		2.使用FileWriter中的方法write,把数据写入到内存缓冲区中(字符转换为字节的过程)
		3.使用FileWriter中的方法flush,把内存缓冲区中的数据,刷新到文件中
		4.释放资源(会先把内存缓冲区中的数据刷新到文件中)

代码实现

public class Demo01Writer {
    public static void main(String[] args) throws IOException {
        //1.创建FileWriter对象,构造方法中绑定要写入数据的目的地
        FileWriter fw = new FileWriter("09_IOAndProperties\\d.txt");
        //2.使用FileWriter中的方法write,把数据写入到内存缓冲区中(字符转换为字节的过程)
        //void write(int c) 写入单个字符。
        fw.write(97);
        //3.使用FileWriter中的方法flush,把内存缓冲区中的数据,刷新到文件中
        //fw.flush();
        //4.释放资源(会先把内存缓冲区中的数据刷新到文件中)
        fw.close();
    }
}

flush方法close方法的区别

flush :刷新缓冲区,流对象可以继续使用
close:  先刷新缓冲区,然后通知系统释放资源。流对象不可以再被使用了
public class Demo02CloseAndFlush {
    public static void main(String[] args) throws IOException {
        //1.创建FileWriter对象,构造方法中绑定要写入数据的目的地
        FileWriter fw = new FileWriter("09_IOAndProperties\\e.txt");
        //2.使用FileWriter中的方法write,把数据写入到内存缓冲区中(字符转换为字节的过程)
        //void write(int c) 写入单个字符。
        fw.write(97);
        //3.使用FileWriter中的方法flush,把内存缓冲区中的数据,刷新到文件中
        fw.flush();
        //刷新之后流可以继续使用
        fw.write(98);

        //4.释放资源(会先把内存缓冲区中的数据刷新到文件中)
        fw.close();

        //close方法之后流已经关闭了,已经从内存中消失了,流就不能再使用了
        fw.write(99);//IOException: Stream closed
    }
}

字符输出流的基本使用:一次写多个字符

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写的字符个数

代码实现

public class Demo03Writer {
    public static void main(String[] args) throws IOException {
        FileWriter fw = new FileWriter("09_IOAndProperties\\f.txt");
        char[] cs = {'a','b','c','d','e'};
        //void write(char[] cbuf)写入字符数组。
        fw.write(cs);//abcde

        //void write(char[] cbuf, int off, int len)写入字符数组的某一部分,off数组的开始索引,len写的字符个数。
        fw.write(cs,1,3);//bcd

        //void write(String str)写入字符串。
        fw.write("传智播客");//传智播客

        //void write(String str, int off, int len) 写入字符串的某一部分,off字符串的开始索引,len写的字符个数。
        fw.write("黑马程序员",2,3);//程序员

        fw.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
    

代码实现

public class Demo04Writer {
    public static void main(String[] args) throws IOException {
        FileWriter fw = new FileWriter("09_IOAndProperties\\g.txt",true);
        for (int i = 0; i <10 ; i++) {
            fw.write("HelloWorld"+i+"\r\n");
        }

        fw.close();
    }
}

使用try_catch_finally处理流中的异常

在jdk1.7之前使用try catch finally 处理流中的异常
格式:
    try{
        可能会产出异常的代码
    }catch(异常类变量 变量名){
        异常的处理逻辑
    }finally{
        一定会执行的代码
        资源释放
    }

代码实现

public class Demo01TryCatch {
    public static void main(String[] args) {
        //提高变量fw的作用域,让finally可以使用
        //变量在定义的时候,可以没有值,但是使用的时候必须有值
        //fw = new FileWriter("09_IOAndProperties\\g.txt",true); 执行失败,fw没有值,fw.close会报错
        FileWriter fw = null;
        try{
            //可能会产出异常的代码
            fw = new FileWriter("w:\\09_IOAndProperties\\g.txt",true);
            for (int i = 0; i <10 ; i++) {
                fw.write("HelloWorld"+i+"\r\n");
            }
        }catch(IOException e){
            //异常的处理逻辑
            System.out.println(e);
        }finally {
            //一定会指定的代码
            //创建对象失败了,fw的默认值就是null,null是不能调用方法的,会抛出NullPointerException,需要增加一个判断,不是null在把资源释放
            if(fw!=null){
                try {
                    //fw.close方法声明抛出了IOException异常对象,所以我们就的处理这个异常对象,要么throws,要么try catch
                    fw.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

        }
    }
}

JDK7JDK9流中异常的处理

  • JDK7的新特性

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

代码实现

public class Demo02JDK7 {
    public static void main(String[] args) {
        try(//1.创建一个字节输入流对象,构造方法中绑定要读取的数据源
            FileInputStream fis = new FileInputStream("c:\\1.jpg");
            //2.创建一个字节输出流对象,构造方法中绑定要写入的目的地
            FileOutputStream fos = new FileOutputStream("d:\\1.jpg");){

            //可能会产出异常的代码
            //一次读取一个字节写入一个字节的方式
            //3.使用字节输入流对象中的方法read读取文件
            int len = 0;
            while((len = fis.read())!=-1){
                //4.使用字节输出流中的方法write,把读取到的字节写入到目的地的文件中
                fos.write(len);
            }

        }catch (IOException e){
            //异常的处理逻辑
            System.out.println(e);
        }
    }
}
  • JDK9的新特性

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

代码实现

public class Demo03JDK9 {
    public static void main(String[] args) throws IOException {
        //1.创建一个字节输入流对象,构造方法中绑定要读取的数据源
        FileInputStream fis = new FileInputStream("c:\\1.jpg");
        //2.创建一个字节输出流对象,构造方法中绑定要写入的目的地
        FileOutputStream fos = new FileOutputStream("d:\\1.jpg");

        try(fis;fos){
            //一次读取一个字节写入一个字节的方式
            //3.使用字节输入流对象中的方法read读取文件
            int len = 0;
            while((len = fis.read())!=-1){
                //4.使用字节输出流中的方法write,把读取到的字节写入到目的地的文件中
                fos.write(len);
            }
        }catch (IOException e){
            System.out.println(e);
        }

        //fos.write(1);//Stream Closed

    }
}

(六)Properties集合

java.util.Properties集合 extends Hashtable implements Map
Properties 类表示了一个持久的属性集,Properties 可以存在流中从流中加载

Properties集合是一个唯一IO流相结合的集合
可以使用Properties集合中的store()方法,把集合中的临时数据,持久化写入到硬盘中存储
可以使用Properties集合中的load()方法,把硬盘中保存的文件(键值对),读取到集合中使用
属性列表中每个键及其对应值都是一个字符串
Properties集合是一个双列集合,key和value默认都是字符串

代码实现

public class Demo01Properties {
    public static void main(String[] args) throws IOException {
        show03();
    }

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

    /*
        可以使用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.释放资源
     */
    private static void show02() throws IOException {
        //1.创建Properties集合对象,添加数据
        Properties prop = new Properties();
        prop.setProperty("赵丽颖","168");
        prop.setProperty("迪丽热巴","165");
        prop.setProperty("古力娜扎","160");

        //2.创建字节输出流/字符输出流对象,构造方法中绑定要输出的目的地
        //FileWriter fw = new FileWriter("09_IOAndProperties\\prop.txt");

        //3.使用Properties集合中的方法store,把集合中的临时数据,持久化写入到硬盘中存储
        //prop.store(fw,"save data");

        //4.释放资源
        //fw.close();

        prop.store(new FileOutputStream("09_IOAndProperties\\prop2.txt"),"");
    }

    /*
        使用Properties集合存储数据,遍历取出Properties集合中的数据
        Properties集合是一个双列集合,key和value默认都是字符串
        Properties集合有一些操作字符串的特有方法
            Object setProperty(String key, String value) 调用 Hashtable 的方法 put。
            String getProperty(String key) 通过key找到value值,此方法相当于Map集合中的get(key)方法
            Set stringPropertyNames() 返回此属性列表中的键集,其中该键及其对应值是字符串,此方法相当于Map集合中的keySet方法
     */
    private static void show01() {
        //创建Properties集合对象
        Properties prop = new Properties();
        //使用setProperty往集合中添加数据
        prop.setProperty("赵丽颖","168");
        prop.setProperty("迪丽热巴","165");
        prop.setProperty("古力娜扎","160");
        //prop.put(1,true);

        //使用stringPropertyNames把Properties集合中的键取出,存储到一个Set集合中
        Set<String> set = prop.stringPropertyNames();

        //遍历Set集合,取出Properties集合的每一个键
        for (String key : set) {
            //使用getProperty方法通过key获取value
            String value = prop.getProperty(key);
            System.out.println(key+"="+value);
        }
    }
}

(七)缓冲流

缓冲流的原理

注意:缓冲流搭配数组使用,效率更高

字节缓冲:BufferedOutputStream

java.io.BufferedOutputStream extends OutputStream:字节缓冲输出流

继承自父类的共性成员方法:
		public void close() :关闭此输出流并释放与此流相关联的任何系统资源
		public void flush() :刷新此输出流并强制任何缓冲的输出字节被写出
		public void write(byte[] b):将 b.length字节从指定的字节数组写入此输出流
		public void write(byte[] b, int off, int len) :从指定的字节数组写入 len字节,从偏移量 off开始输出到此输出流
		public abstract void write(int b) :将指定的字节输出流		
构造方法:
		BufferedOutputStream(OutputStream out)  创建一个新的缓冲输出流,以将数据写入指定的底层输出流
		BufferedOutputStream(OutputStream out, int size)  创建一个新的缓冲输出流,以将具有指定缓冲区大小的数据写入指定的底层输出流
参数:
		OutputStream out:字节输出流
		int size:指定缓冲流内部缓冲区的大小,不指定默认	

使用步骤(重点)

1.创建FileOutputStream对象,构造方法中绑定要输出的目的地
2.创建BufferedOutputStream对象,构造方法中传递FileOutputStream对象,提高FileOutputStream对象效率
3.使用BufferedOutputStream对象中的方法write,把数据写入到内部缓冲区中
4.使用BufferedOutputStream对象中的方法flush,把内部缓冲区中的数据,刷新到文件中
5.释放资源(会先调用flush方法刷新数据,第4部可以省略)

代码实现

public class Demo01BufferedOutputStream {
    public static void main(String[] args) throws IOException {
        //1.创建FileOutputStream对象,构造方法中绑定要输出的目的地
        FileOutputStream fos = new FileOutputStream("10_IO\\a.txt");
        //2.创建BufferedOutputStream对象,构造方法中传递FileOutputStream对象对象,提高FileOutputStream对象效率
        BufferedOutputStream bos = new BufferedOutputStream(fos);
        //3.使用BufferedOutputStream对象中的方法write,把数据写入到内部缓冲区中
        bos.write("我把数据写入到内部缓冲区中".getBytes());
        //4.使用BufferedOutputStream对象中的方法flush,把内部缓冲区中的数据,刷新到文件中
        bos.flush();
        //5.释放资源(会先调用flush方法刷新数据,第4部可以省略)
        bos.close();
    }
}

字节缓冲:BufferedInputStream

java.io.BufferedInputStream extends InputStream:字节缓冲输入流

继承自父类的成员方法:
		int read()从输入流中读取数据的下一个字节。
		int read(byte[] b) 从输入流中读取一定数量的字节,并将其存储在缓冲区数组 b 中。
		void close() 关闭此输入流并释放与该流关联的所有系统资源。
构造方法:
		BufferedInputStream(InputStream in) 创建一个 BufferedInputStream 并保存其参数,即输入流 in,以便将来使用。
		BufferedInputStream(InputStream in, int size) 创建具有指定缓冲区大小的 BufferedInputStream 并保存其参数,即输入流 in,以便将来使用。
参数:
		InputStream in:字节输入流
		int size:指定缓冲流内部缓冲区的大小,不指定默认

使用步骤(重点):

1.创建FileInputStream对象,构造方法中绑定要读取的数据源
2.创建BufferedInputStream对象,构造方法中传递FileInputStream对象,提高FileInputStream对象的读取效率
3.使用BufferedInputStream对象中的方法read,读取文件
4.释放资源

代码实现

public class Demo02BufferedInputStream {
    public static void main(String[] args) throws IOException {
        //1.创建FileInputStream对象,构造方法中绑定要读取的数据源
        FileInputStream fis = new FileInputStream("10_IO\\a.txt");
        //2.创建BufferedInputStream对象,构造方法中传递FileInputStream对象,提高FileInputStream对象的读取效率
        BufferedInputStream bis = new BufferedInputStream(fis);
        //3.使用BufferedInputStream对象中的方法read,读取文件
        //int read()从输入流中读取数据的下一个字节。
        /*int len = 0;//记录每次读取到的字节
        while((len = bis.read())!=-1){
            System.out.println(len);
        }*/

        //int read(byte[] b) 从输入流中读取一定数量的字节,并将其存储在缓冲区数组 b 中。
        byte[] bytes =new byte[1024];//存储每次读取的数据
        int len = 0; //记录每次读取的有效字节个数
        while((len = bis.read(bytes))!=-1){
            System.out.println(new String(bytes,0,len));
        }

        //4.释放资源
        bis.close();
    }
}

字符缓冲输出流:BufferedWriter

java.io.BufferedWriter extends 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():关闭此流,但要先刷新它
构造方法:
		BufferedWriter(Writer out):创建一个使用默认大小输出缓冲区的缓冲字符输出流
		BufferedWriter(Writer out, int sz):创建一个使用给定大小输出缓冲区的新缓冲字符输出流
参数:
		Writer out:字符输出流
		int sz:指定缓冲区的大小,不写默认大小
特有的成员方法:
		void newLine():写入一个行分隔符。会根据不同的操作系统,获取不同的行分隔符
换行:
		windows:\r\n
		linux:/n
		mac:/r

使用步骤

1.创建字符缓冲输出流对象,构造方法中传递字符输出流
2.调用字符缓冲输出流中的方法write,把数据写入到内存缓冲区中
3.调用字符缓冲输出流中的方法flush,把内存缓冲区中的数据,刷新到文件中
4.释放资源

代码实现

public class Demo03BufferedWriter {
    public static void main(String[] args) throws IOException {
        //System.out.println();
        //1.创建字符缓冲输出流对象,构造方法中传递字符输出流
        BufferedWriter bw = new BufferedWriter(new FileWriter("10_IO\\c.txt"));
        //2.调用字符缓冲输出流中的方法write,把数据写入到内存缓冲区中
        for (int i = 0; i <10 ; i++) {
            bw.write("传智播客");
            //bw.write("\r\n");
            bw.newLine();
        }
        //3.调用字符缓冲输出流中的方法flush,把内存缓冲区中的数据,刷新到文件中
        bw.flush();
        //4.释放资源
        bw.close();
    }
}

字符缓冲输入流:BufferedReader

java.io.BufferedReader extends Reader:字符缓冲输入流

继承自父类的共性成员方法:
		int read():读取单个字符并返回。
		int read(char[] cbuf):一次读取多个字符,将字符读入数组。
		void close():关闭该流并释放与之关联的所有资源。
构造方法:
		BufferedReader(Reader in):创建一个使用默认大小输入缓冲区的缓冲字符输入流。
		BufferedReader(Reader in, int sz):创建一个使用指定大小输入缓冲区的缓冲字符输入流。
参数:
		Reader in:字符输入流
特有的成员方法:
		String readLine():读取一个文本行。读取一行数据
行的终止符号:
		通过下列字符之一即可认为某行已终止:换行 ('\n')、回车 ('\r') 或回车后直接跟着换行(\r\n)。
返回值:
		包含该行内容的字符串,不包含任何行终止符,如果已到达流末尾,则返回 null

使用步骤

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

代码实现

public class Demo04BufferedReader {
    public static void main(String[] args) throws IOException {
        //1.创建字符缓冲输入流对象,构造方法中传递字符输入流
        BufferedReader br = new BufferedReader(new FileReader("10_IO\\c.txt"));

        //2.使用字符缓冲输入流对象中的方法read/readLine读取文本
        /*String line = br.readLine();
        System.out.println(line);

        line = br.readLine();
        System.out.println(line);

        line = br.readLine();
        System.out.println(line);

        line = br.readLine();
        System.out.println(line);*/

        /*
            发下以上读取是一个重复的过程,所以可以使用循环优化
            不知道文件中有多少行数据,所以使用while循环
            while的结束条件,读取到null结束
         */
        String line;
        while((line = br.readLine())!=null){
            System.out.println(line);
        }

        //3.释放资源
        br.close();
    }
}

练习:对文本的内容进行排序

    对文本的内容进行排序
    按照(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 Demo05Test {
    public static void main(String[] args) throws IOException {
        //1.创建一个HashMap集合对象,可以:存储每行文本的序号(1,2,3,..);value:存储每行的文本
        HashMap<String,String> map = new HashMap<>();
        //2.创建字符缓冲输入流对象,构造方法中绑定字符输入流
        BufferedReader br = new BufferedReader(new FileReader("10_IO\\in.txt"));
        //3.创建字符缓冲输出流对象,构造方法中绑定字符输出流
        BufferedWriter bw = new BufferedWriter(new FileWriter("10_IO\\out.txt"));
        //4.使用字符缓冲输入流中的方法readline,逐行读取文本
        String line;
        while((line = br.readLine())!=null){
            //5.对读取到的文本进行切割,获取行中的序号和文本内容
            String[] arr = line.split("\\.");
            //6.把切割好的序号和文本的内容存储到HashMap集合中(key序号是有序的,会自动排序1,2,3,4..)
            map.put(arr[0],arr[1]);
        }

        //7.遍历HashMap集合,获取每一个键值对
        for(String key : map.keySet()){
            String value = map.get(key);
            //8.把每一个键值对,拼接为一个文本行
            line = key + "." + value;
            //9.把拼接好的文本,使用字符缓冲输出流中的方法write,写入到文件中
            bw.write(line);
            bw.newLine();//写换行
        }
        //10.释放资源
        bw.close();
        br.close();
    }
}

(八)转换流

编码引出的问题:FileReader读取GBK编码的文件会乱码

FileReader可以读取IDE默认编码格式(UTF-8)的文件
FileReader读取系统默认编码(中文GBK)会产生乱码���

代码实现

public class Demo01FileReader {
    public static void main(String[] args) throws IOException {
        FileReader fr = new FileReader("10_IO\\我是GBK格式的文本.txt");
        int len = 0;
        while((len = fr.read())!=-1){
            System.out.print((char)len);//���
        }
        fr.close();
    }
}

其实我们在IDEA打开一个GBK编码的文件时,就会建议我们把它重载为UTF-8编码了,如下图:
JavaSE基础加强之File类与IO流(六)_第3张图片

转换流的原理

OutputStreamWriter介绍&代码实现

java.io.OutputStreamWriter extends Writer:是字符流通向字节流的桥梁
可使用指定的 charset 将要写入流中的字符编码成字节(编码:把能看懂的变成看不懂)

继续自父类的共性成员方法:
		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():关闭此流,但要先刷新它
构造方法:
		OutputStreamWriter(OutputStream out):创建使用默认字符编码的 OutputStreamWriter
		OutputStreamWriter(OutputStream out, String charsetName):创建使用指定字符集的 OutputStreamWriter
参数:
		OutputStream out:字节输出流,可以用来写转换之后的字节到文件中
		String charsetName:指定的编码表名称,不区分大小写,可以是utf-8/UTF-8,gbk/GBK,...不指定默认使用UTF-8

使用步骤

1.创建OutputStreamWriter对象,构造方法中传递字节输出流和指定的编码表名称
2.使用OutputStreamWriter对象中的方法write,把字符转换为字节存储缓冲区中(编码)
3.使用OutputStreamWriter对象中的方法flush,把内存缓冲区中的字节刷新到文件中(使用字节流写字节的过程)
4.释放资源

代码实现

public class Demo02OutputStreamWriter {
    public static void main(String[] args) throws IOException {
        //write_utf_8();
        write_gbk();
    }

    /*
       使用转换流OutputStreamWriter写GBK格式的文件
    */
    private static void write_gbk() throws IOException {
        //1.创建OutputStreamWriter对象,构造方法中传递字节输出流和指定的编码表名称
        OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("10_IO\\gbk.txt"),"GBK");
        //2.使用OutputStreamWriter对象中的方法write,把字符转换为字节存储缓冲区中(编码)
        osw.write("你好");
        //3.使用OutputStreamWriter对象中的方法flush,把内存缓冲区中的字节刷新到文件中(使用字节流写字节的过程)
        osw.flush();
        //4.释放资源
        osw.close();
    }

    /*
        使用转换流OutputStreamWriter写UTF-8格式的文件
     */
    private static void write_utf_8() throws IOException {
        //1.创建OutputStreamWriter对象,构造方法中传递字节输出流和指定的编码表名称
        //OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("10_IO\\utf_8.txt"),"utf-8");
        OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("10_IO\\utf_8.txt"));//不指定默认使用UTF-8
        //2.使用OutputStreamWriter对象中的方法write,把字符转换为字节存储缓冲区中(编码)
        osw.write("你好");
        //3.使用OutputStreamWriter对象中的方法flush,把内存缓冲区中的字节刷新到文件中(使用字节流写字节的过程)
        osw.flush();
        //4.释放资源
        osw.close();
    }
}

InputStreamReader介绍&代码实现

java.io.InputStreamReader extends Reader:是字节流通向字符流的桥梁
它使用指定的 charset 读取字节并将其解码为字符。(解码:把看不懂的变成能看懂的)

继承自父类的共性成员方法:
		int read():读取单个字符并返回
		int read(char[] cbuf):一次读取多个字符,将字符读入数组
		void close():关闭该流并释放与之关联的所有资源
构造方法:
		InputStreamReader(InputStream in):创建一个使用默认字符集的 InputStreamReader。
		InputStreamReader(InputStream in, String charsetName):创建使用指定字符集的 InputStreamReader
参数:
		InputStream in:字节输入流,用来读取文件中保存的字节
		String charsetName:指定的编码表名称,不区分大小写,可以是utf-8/UTF-8,gbk/GBK,...不指定默认使用UTF-8

使用步骤

1.创建InputStreamReader对象,构造方法中传递字节输入流和指定的编码表名称
2.使用InputStreamReader对象中的方法read读取文件
3.释放资源	

注意事项:构造方法中指定的编码表名称要和文件的编码相同,否则会发生乱码

代码实现

public class Demo03InputStreamReader {
    public static void main(String[] args) throws IOException {
        //read_utf_8();
        read_gbk();
    }


    /*
        使用InputStreamReader读取GBK格式的文件
     */
    private static void read_gbk() throws IOException {
        //1.创建InputStreamReader对象,构造方法中传递字节输入流和指定的编码表名称
        //InputStreamReader isr = new InputStreamReader(new FileInputStream("10_IO\\gbk.txt"),"UTF-8");//???
        InputStreamReader isr = new InputStreamReader(new FileInputStream("10_IO\\gbk.txt"),"GBK");//你好

        //2.使用InputStreamReader对象中的方法read读取文件
        int len = 0;
        while((len = isr.read())!=-1){
            System.out.println((char)len);
        }
        //3.释放资源
        isr.close();
    }

    /*
        使用InputStreamReader读取UTF-8格式的文件
     */
    private static void read_utf_8() throws IOException {
        //1.创建InputStreamReader对象,构造方法中传递字节输入流和指定的编码表名称
        //InputStreamReader isr = new InputStreamReader(new FileInputStream("10_IO\\utf_8.txt"),"UTF-8");
        InputStreamReader isr = new InputStreamReader(new FileInputStream("10_IO\\utf_8.txt"));//不指定默认使用UTF-8
        //2.使用InputStreamReader对象中的方法read读取文件
        int len = 0;
        while((len = isr.read())!=-1){
            System.out.println((char)len);
        }
        //3.释放资源
        isr.close();
    }
}

练习:转换文件编码

将GBK编码的文本文件,转换为UTF-8编码的文本文件。
分析:
    1.创建InputStreamReader对象,构造方法中传递字节输入流和指定的编码表名称GBK
    2.创建OutputStreamWriter对象,构造方法中传递字节输出流和指定的编码表名称UTF-8
    3.使用InputStreamReader对象中的方法read读取文件
    4.使用OutputStreamWriter对象中的方法write,把读取的数据写入到文件中
    5.释放资源

代码实现

public class Demo04Test {
    public static void main(String[] args) throws IOException {
        //1.创建InputStreamReader对象,构造方法中传递字节输入流和指定的编码表名称GBK
        InputStreamReader isr = new InputStreamReader(new FileInputStream("10_IO\\我是GBK格式的文本.txt"),"GBK");
        //2.创建OutputStreamWriter对象,构造方法中传递字节输出流和指定的编码表名称UTF-8
        OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("10_IO\\我是utf_8格式的文件.txt"),"UTF-8");
        //3.使用InputStreamReader对象中的方法read读取文件
        int len = 0;
        while((len = isr.read())!=-1){
            //4.使用OutputStreamWriter对象中的方法write,把读取的数据写入到文件中
            osw.write(len);
        }
        //5.释放资源
        osw.close();
        isr.close();
    }
}

(九)序列化流

序列化反序列化的概述

对象的序列化流:ObjectOutputStream

java.io.ObjectOutputStream extends OutputStream:对象的序列化流

作用:
		把对象以流的方式写入到文件中保存
构造方法:
		ObjectOutputStream(OutputStream out) 创建写入指定 OutputStream 的 ObjectOutputStream
参数:
		OutputStream out:字节输出流
特有的成员方法:
		void writeObject(Object obj):将指定的对象写入 ObjectOutputStream

使用步骤

1.创建ObjectOutputStream对象,构造方法中传递字节输出流
2.使用ObjectOutputStream对象中的方法writeObject,把对象写入到文件中
3.释放资源

代码实现

public class Demo01ObjectOutputStream {
    public static void main(String[] args) throws IOException {
        //1.创建ObjectOutputStream对象,构造方法中传递字节输出流
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("10_IO\\person.txt"));
        //2.使用ObjectOutputStream对象中的方法writeObject,把对象写入到文件中
        oos.writeObject(new Person("小美女",18));
        //3.释放资源
        oos.close();
    }
}
序列化和反序列化的时候,会抛出NotSerializableException没有序列化异常
类通过实现 java.io.Serializable 接口以启用其序列化功能。未实现此接口的类将无法使其任何状态序列化或反序列化。
Serializable接口也叫标记型接口
要进行序列化和反序列化的类必须实现Serializable接口,就会给类添加一个标记
当我们进行序列化和反序列化的时候,就会检测类上是否有这个标记
	有:就可以序列化和反序列化
	没有:就会抛出 NotSerializableException异常
public class Person implements Serializable{
    private String name;
    public int age;

    public Person() {
    }

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

对象的反序列化流:ObjectInputStream

java.io.ObjectInputStream extends InputStream:对象的反序列化流

作用:
		把文件中保存的对象,以流的方式读取出来使用
构造方法:
		ObjectInputStream(InputStream in):创建从指定 InputStream 读取的 ObjectInputStream
参数:
		InputStream in:字节输入流
特有的成员方法:
		Object readObject():从ObjectInputStream读取对象

使用步骤

1.创建ObjectInputStream对象,构造方法中传递字节输入流
2.使用ObjectInputStream对象中的方法readObject读取保存对象的文件
3.释放资源
4.使用读取出来的对象(打印)

readObject方法声明抛出了ClassNotFoundException(class文件找不到异常)
当不存在对象的class文件时抛出此异常

反序列化的前提

1.类必须实现Serializable
2.必须存在类对应的class文件

代码实现

public class Demo02ObjectInputStream {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        //1.创建ObjectInputStream对象,构造方法中传递字节输入流
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("10_IO\\person.txt"));
        //2.使用ObjectInputStream对象中的方法readObject读取保存对象的文件
        Object o = ois.readObject();
        //3.释放资源
        ois.close();
        //4.使用读取出来的对象(打印)
        System.out.println(o);
        Person p = (Person)o;
        System.out.println(p.getName()+p.getAge());
    }
}

瞬态关键字:transient

static关键字:静态关键字
    静态优先于非静态加载到内存中(静态优先于对象进入到内存中)
    被static修饰的成员变量不能被序列化的,序列化的都是对象
    private static int age;
    oos.writeObject(new Person("小美女",18));
    Object o = ois.readObject();
    Person{name='小美女', age=0}

transient关键字:瞬态关键字
    被transient修饰成员变量,不能被序列化
    private transient int age;
    oos.writeObject(new Person("小美女",18));
    Object o = ois.readObject();
    Person{name='小美女', age=0}

总结:transient关键字和static关键字在序列化这一点来看是一样的,但是transient并不是静态的,所以如果我们不想某个属性被序列化,可以使用transient

InvalidClassException异常&原理

另外,当JVM反序列化对象时,能找到class文件,但是class文件在序列化对象之后发生了修改,那么反序列化操作也会失败,抛出一个InvalidClassException异常

发生这个异常的原因如下:

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


练习:序列化集合

    当我们想在文件中保存多个对象的时候
    可以把多个对象存储到一个集合中
    对集合进序列化和反序列化
分析:
    1.定义一个存储Person对象的ArrayList集合
    2.往ArrayList集合中存储Person对象
    3.创建一个序列化流ObjectOutputStream对象
    4.使用ObjectOutputStream对象中的方法writeObject,对集合进行序列化
    5.创建一个反序列化ObjectInputStream对象
    6.使用ObjectInputStream对象中的方法readObject读取文件中保存的集合
    7.把Object类型的集合转换为ArrayList类型
    8.遍历ArrayList集合
    9.释放资源

代码实现

public class Demo03Test {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        //1.定义一个存储Person对象的ArrayList集合
        ArrayList<Person> list = new ArrayList<>();
        //2.往ArrayList集合中存储Person对象
        list.add(new Person("张三",18));
        list.add(new Person("李四",19));
        list.add(new Person("王五",20));
        //3.创建一个序列化流ObjectOutputStream对象
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("10_IO\\list.txt"));
        //4.使用ObjectOutputStream对象中的方法writeObject,对集合进行序列化
        oos.writeObject(list);
        //5.创建一个反序列化ObjectInputStream对象
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("10_IO\\list.txt"));
        //6.使用ObjectInputStream对象中的方法readObject读取文件中保存的集合
        Object o = ois.readObject();
        //7.把Object类型的集合转换为ArrayList类型
        ArrayList<Person> list2 = (ArrayList<Person>)o;
        //8.遍历ArrayList集合
        for (Person p : list2) {
            System.out.println(p);
        }
        //9.释放资源
        ois.close();
        oos.close();
    }
}

(十)打印流

平时我们在控制台打印输出,是调用print方法和println方法完成的,这两个方法都来自于java.io.PrintStream类,该类能够方便地打印各种数据类型的值,是一种便捷的输出方式。

PrintStream extends OutputStream:打印流

继承自父类的成员方法:
		public void close():关闭此输出流并释放与此流相关联的任何系统资源
		public void flush():刷新此输出流并强制任何缓冲的输出字节被写出
		public void write(byte[] b):将 b.length字节从指定的字节数组写入此输出流
		public void write(byte[] b, int off, int len):从指定的字节数组写入 len字节,从偏移量 off开始输出到此输出流
		public abstract void write(int b) :将指定的字节输出流
构造方法:
		PrintStream(File file):输出的目的地是一个文件
		PrintStream(OutputStream out):输出的目的地是一个字节输出流
		PrintStream(String fileName):输出的目的地是一个文件路径

PrintStream特点

PrintStream 为其他输出流添加了功能,使它们能够方便地打印各种数据值表示形式。
1.只负责数据的输出,不负责数据的读取
2.与其他输出流不同,PrintStream 永远不会抛出 IOException
3.有特有的方法,print,println
		void print(任意类型的值)
		void println(任意类型的值并换行)

注意

如果使用继承自父类的write方法写数据,那么查看数据的时候会查询编码表 97->a
如果使用自己特有的方法print/println方法写数据,写的数据原样输出 97->97
public class Demo01PrintStream {
    public static void main(String[] args) throws FileNotFoundException {
        //System.out.println("HelloWorld");

        //创建打印流PrintStream对象,构造方法中绑定要输出的目的地
        PrintStream ps = new PrintStream("10_IO\\print.txt");
        //如果使用继承自父类的write方法写数据,那么查看数据的时候会查询编码表 97->a
        ps.write(97);
        //如果使用自己特有的方法print/println方法写数据,写的数据原样输出 97->97
        ps.println(97);
        ps.println(8.8);
        ps.println('a');
        ps.println("HelloWorld");
        ps.println(true);

        //释放资源
        ps.close();
    }
}
可以改变输出语句的目的地(打印流的流向)
输出语句,默认在控制台输出
使用System.setOut方法改变输出语句的目的地改为参数中传递的打印流的目的地
static void setOut(PrintStream out)
重新分配“标准”输出流
public class Demo02PrintStream {
    public static void main(String[] args) throws FileNotFoundException {
        System.out.println("我是在控制台输出");

        PrintStream ps = new PrintStream("10_IO\\目的地是打印流.txt");
        System.setOut(ps);//把输出语句的目的地改变为打印流的目的地
        System.out.println("我在打印流的目的地中输出");

        ps.close();
    }
}

你可能感兴趣的:(JavaSE基础加强,java,字符串,javase,stream,block)