JAVA-File类与IO流

第一节 File类

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

我们可以使用File类的方法
创建/删除/获取一个文件/文件夹,判断文件/文件夹是否存在,对文件夹进行遍历,获取文件的大小
File类是一个与系统无关的类,任何的操作系统都可以使用这个类中的方法

重点:记住三个单词
file:文件
directory:文件夹/目录
path:路径

1.1 静态成员变量

static String pathSeparator 与系统有关的路径分隔符,为了方便,它被表示为一个字符串
static char pathSeparatorChar 与系统有关的路径分隔符
static String pathSeparator 与系统有关的默认名称分隔符,为了方便,它被表示为一个字符串
static char pathSeparatorChar 与系统有关的默认名称分隔符
操作路径:路径不能写死了
C:\develop\a\a.txt Windows
C:/develop/a/a.txt Linux
应该写为:“C:“+File.separator+”develop“+File.separator+”a“+File.separator+”a.txt”

public class DemoFile {
    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:\a.txt
d:\b.txt
相对路径:是一个简化的路径,相对指的是相对于当前项目的根目录
D:\Test\src\Day17\Demo01
如果使用当前项目的根目录,路径可以简化书写为Demo01
注意:
1、路径不区分大小写
2、路径中的文件名称分隔符Windows使用反斜杠,反斜杠是转义字符,两个反斜杠代表一个普通的反斜杠

1.2 构造方法

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 Demo01File {
    public static void main(String[] args) {
        demo01();
        demo02("c:\\","demo01");
        demo03();
    }

    private static void demo03() {
        File file = new File("c:\\");
        File file1 = new File(file,"hello.java");
        System.out.println(file1);
    }

    private static void demo02(String parent, String child) {
        File file = new File(parent, child);
        System.out.println(file);
    }

    private static void demo01() {
        File file = new File("D:\\Test\\src\\Day17\\Demo01");
        System.out.println(file);
        File file1 = new File("Demo01");
        System.out.println(file1);
    }
}

1.3 File类获取功能的方法

File类获取功能的方法
public String getAbsolutePath():返回此File的绝对路径名字符串
无论路径是绝对的还是相对的,getAbsolutePath方法返回的都是绝对路径
public String getPath():将此File转换为路径名字符串
public String getName():返回由此File表示的文件或目录的名称
获取的就是构造方法传递路径的结尾部分(文件/文件夹)
public long length():返回由此File表示的文件的长度
获取的是构造方法指向的文件的大小,以字节为单位
注意:文件夹是没有大小概念的,不能获取文件夹的大小
如果构造方法中给出的路径不存在,那么length方法返回0

public class Demo02File {
    public static void main(String[] args) {
        demo01();
        demo02();
        demo03();
        demo04();
    }

    private static void demo04() {
        File file = new File("D:\\PSR\\参数计算\\1.txt");
        System.out.println(file.length());//5893
        File file1 = new File("D:\\PSR\\matlab");
        System.out.println(file1.length());//0,文件夹没有大小概念
    }

    private static void demo03() {
        File file = new File("D:\\PSR\\matlab\\1.txt");
        System.out.println(file.getName());//1.txt
        File file1 = new File("D:\\PSR\\matlab");
        System.out.println(file1.getName());//matlab
    }

    private static void demo02() {
        File file = new File("D:\\PSR\\matlab\\1.txt");
        System.out.println(file.getPath());//D:\PSR\matlab\1.txt
        File file1 = new File("1.txt");
        System.out.println(file1.getPath());//1.txt
    }

    private static void demo01() {
        File file = new File("D:\\PSR\\matlab\\1.txt");
        System.out.println(file.getAbsolutePath());//D:\PSR\matlab\1.txt
        File file1 = new File("1.txt");
        System.out.println(file1.getAbsolutePath());//D:\Test\1.txt
    }
}

1.4 File类判断功能的方法

File判断功能的方法
public boolean exists():此File表示的文件或目录是否实际存在
public boolean isDirectory():此File表示的是否为目录
public boolean isFile():此File表示的是否为文件
注意:
电脑的硬盘中只有文件/文件夹,两个方法是互斥
后两个方法使用前提,路径必须是存在的,否则都返回false

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

    private static void demo02() {
        File file = new File("D:\\PSR\\参数计算\\1.txt");
        System.out.println(file.isDirectory());//fasle
        File file1 = new File("D:\\PSR\\参数计算\\1.txt");
        System.out.println(file1.isFile());//true
        File file2 = new File("D:\\PSR\\参数计算\\2.txt");
        System.out.println(file2.isDirectory());//false
        File file3 = new File("D:\\PSR\\参数计算\\2.txt");
        System.out.println(file3.isFile());//false
    }

    private static void demo01() {
        File file = new File("D:\\PSR\\参数计算\\1.txt");
        System.out.println(file.exists());//true
        File file1 = new File("D:\\PSR\\参数计算\\2.txt");
        System.out.println(file1.exists());//false
    }
}

1.5 File类创建删除功能的方法

File类创建删除功能的方法

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

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

public boolean mkdir():创建由此File表示的目录
只能创建单级空文件夹
public boolean mkdirs():创建由此File表示的目录,包括任何必需但不存在的父目录
既可以创建单级空文件夹,也可以创建多级空文件夹
创建文件夹的路径和名称在构造方法中给出(构造方法的参数)
返回值:布尔值
true:文件夹不存在,创建文件夹,返回true
false:文件夹存在,不会创建,返回false,构造方法中给出的路径不存在返回flase
注意:此方法只能创建文件夹,不能创建文件

public class Demo04File {
    public static void main(String[] args) throws IOException {
        demo01();
        demo02();
        demo03();
    }

    private static void demo03() {
        File file = new File("D:\\PSR\\matlab\\111");
        System.out.println(file.delete());
        File file1 = new File("D:\\PSR\\matlab\\222");
        System.out.println(file1.delete());
        File file2 = new File("D:\\PSR\\matlab\\222.txt");
        System.out.println(file2.delete());
        File file3 = new File("D:\\PSR\\matlab\\1.txt");
        System.out.println(file3.delete());
    }

    private static void demo02() {
        File file = new File("D:\\PSR\\matlab\\111");
        System.out.println(file.mkdir());
        File file1 = new File("D:\\PSR\\matlab\\222\\333");
        System.out.println(file1.mkdirs());
        File file2 = new File("D:\\PSR\\matlab\\222.txt");
        System.out.println(file2.mkdirs());//看类型,也是一个文件夹,不是文件
        File file3 = new File("D:\\PSR\\matl\\222");
        System.out.println(file3.mkdirs());//true
    }

    private static void demo01() throws IOException {
        File file = new File("D:\\PSR\\matlab\\1.txt");
        System.out.println(file.createNewFile());
        File file1 = new File("2.txt");
        System.out.println(file1.createNewFile());
        //File file2 = new File("D:\\PSR\\matl\\1.txt");//错误!
        //System.out.println(file2.createNewFile());//路径不对,抛出IOException
    }
}

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

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

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

public class Demo05File {
    public static void main(String[] args) {
        demo01();
        demo02();
    }
/*
    public File[] listFiles()
    遍历构造方法中给出的目录,会获取目录中所有的文件/文件夹,把文件/文件夹封装为File对象,多个File对象存储到File数组中
 */
    private static void demo02() {
        File file = new File("D:\\PSR\\matlab");
        File[] files = file.listFiles();
        //遍历文件夹
        for (File name : files) {
            System.out.println(name);
        }
    }

    /*
        public String[] list()
        遍历构造方法中给出的目录,会获取目录中所有文件/文件夹的名称,把获取到的多个名称存储到一个String类型的数组中
     */
    private static void demo01() {
        File file = new File("D:\\PSR\\matlab");
        //File file1 = new File("D:\\PSR\\matla");//路径不存在,抛出NullPointerException
        //File file1 = new File("D:\\PSR\\matlab\\333");//路径不是目录,抛出NullPointerException
        String[] list = file.list();
        //遍历文件夹
        for (String name : list) {
            System.out.println(name);
        }
    }
}

第二节 递归

2.1 递归的概念

递归:方法自己调用自己
递归的分类:直接递归和间接递归
直接递归称为方法自身调用自己
间接递归可以A方法调用B方法,B方法调用C方法,C方法调用A方法

注意事项:
1、递归一定要有条件限定,保证递归能够停止下来,否则会发生栈内存溢出
2、在递归中虽然有限定条件,但是递归次数不能太多,否则也会发生栈内存溢出
3、构造方法,禁止递归 ,构造方法是创建对象使用的,一直递归会导致内存中有无数多个对象,直接编译报错
递归的使用前提:
当调用方法的时候,方法的主体不变,每次调用方法的参数不同,可以使用递归

public class Demo01Recurison {
    public static void main(String[] args) {
        demo01();
        demo02(1);
    }

    private static void demo02(int i) {
        System.out.println(i);
        if (i == 10000){//递归次数不能太多
            return;
        }
        demo02(++i);
    }

    private static void demo01() {
        System.out.println("demo01方法");
        //demo01();//抛出StackOverflowError
    }
    public Demo01Recurison(){
        //Demo01Recurison(); 编译报错
    }
}

2.2 练习1

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

/*
分析:
定义一个方法,使用递归计算1-n之间的和
1+2+3+...+n
n+(n-1)+(n-2)+...+1
已知:最大值n。最小值1
使用递归必须明确:
1、递归的结束条件:获取到1的时候结束
2、递归的目的:获取下一个被加的的数字(n-1)
*/
public class Demo02Recurison {
    public static void main(String[] args) {
        int s = sum(3);
        System.out.println(s);
    }
    public static int sum(int n){
        if (n == 1){
            return 1;
        }
        return n + sum(n - 1);
     }
}

使用递归求和,main方法调用sum方法,sum方法会一直调用sum方法
导致在内存中有多个sum方法(频繁的创建方法,调用方法,销毁方法)效率低下
所有如果仅仅是计算1-n之间的和,不推荐使用递归,使用for循环即可

2.3 练习2

练习:使用递归计算阶乘

/*
定义方法使用递归计算阶乘
递归结束的条件:获取到1的时候结束
递归的目的:获取下一个被乘的数字(n-1)
方法的参数发生变化
 */
public class Demo03Recurison {
    public static void main(String[] args) {
        int jiecheng = jc(5);
        System.out.println(jiecheng);
    }
    public static int jc(int n){
        if (n == 1){
            return 1;
        }
        return n * jc(n - 1);
    }
}

2.4 练习3

练习:递归打印多级目录

/*
定义一个方法,参数传递File类型的目录
方法中对目录进行遍历
 */
public class Demo04Recurison {
    public static void main(String[] args) {
        File file = new File("D:\\exp");
        getAllFile(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(f);
            }else {
                System.out.println(f);
            }
        }
    }
}

2.4 文件搜索案例

搜索目录中.docx结尾的文件

public class Demo05Recurison {
    public static void main(String[] args) {
        File file = new File("D:\\exp");
        getAllFile(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(f);
            }else {
               /* String name = f.getName();//获取目录名称
                String s = name.toLowerCase();//目录转换为小写字母
                boolean b = s.endsWith(".docx");//后缀是否以.docx结尾
                if (b){
                    System.out.println(f);
                }*/
                //链式编程
                if (f.toString().toLowerCase().endsWith(".docx")){
                    System.out.println(f);
                }
            }
        }
    }
}

第三节 过滤器

过滤器的原理和使用
在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,在方法中自己定义过滤的规则
accept方法返回值是一个布尔值
true:就会把传递过去的File对象保存到File数组中
false:就不会把传递过去的File对象保存到File数组中

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

public class FileFilterImpl implements FileFilter {
    @Override
    public boolean accept(File pathname) {
        //如果pathname是一个文件夹,返回true,继续遍历这个文件夹
        if (pathname.isDirectory()){
            return true;
        }
        //过滤的规则:
        //在accept方法中,判断File对象是否是以.java结尾
        //是就返回true
        //不是就返回false
        return pathname.toString().toLowerCase().endsWith(".docx");
    }
}
public class Demo01Filter {
    public static void main(String[] args) {
        File file = new File("D:\\exp");
        getAllFile(file);
    }
    public static void getAllFile(File dir){
        //listFiles方法一共做了3件事情:
        //1、会对构造方法中传递的目录进行遍历,获取目录中的每一个文件/文件夹-->封装为File对象
        //2、会调用参数传递的过滤器中的方法accept
        //3、会把遍历得到的每一个File对象,传递给accept方法的参数pathname
        File[] files = dir.listFiles(new FileFilterImpl());//传递过滤器对象
        for (File f : files) {
            if (f.isDirectory()){
                getAllFile(f);
            }else {
                System.out.println(f);
            }
        }
    }
}

public class Demo02Filter {
    public static void main(String[] args) {
        File file = new File("D:\\exp");
        getAllFile(file);
    }
    public static void getAllFile(File dir){
        //传递过滤器对象,使用匿名内部类
/*        File[] files = dir.listFiles(new FileFilter() {
            @Override
            public boolean accept(File pathname) {
                return pathname.isDirectory() || pathname.toString().toLowerCase().endsWith(".docx");
            }
        });*/
        /*File[] files = dir.listFiles(new FilenameFilter() {
            @Override
            public boolean accept(File d, String name) {
                return new File(d, name).isDirectory() || name.toString().toLowerCase().endsWith(".docx");
            }
        });*/
        //使用Lambda表达式
        //File[] files = dir.listFiles((d, name) -> new File(d, name).isDirectory() || name.toString().toLowerCase().endsWith(".docx"));
        //使用Lambda表达式
        File[] files = dir.listFiles(pathname -> pathname.isDirectory() || pathname.toString().toLowerCase().endsWith(".docx"));
        for (File f : files) {
            if (f.isDirectory()){
                getAllFile(f);
            }else {
                System.out.println(f);
            }
        }
    }
}

第四节 IO字节流

4.1 IO概述

i:input 输入(读取),把硬盘中的数据读取到内存中使用
o:output 输出(写入),把内存中的数据写入到硬盘中保存
流:数据(字符、字节)

4.2 字节输出流

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):将指定的字节写入此输出流

4.3 FileOutputStream类

java.io.FileOutputStream extends 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 Demo01OutputStream {
    public static void main(String[] args) throws IOException {
        FileOutputStream fos = new FileOutputStream("D:\\Test\\src\\Day17\\Demo04\\a.txt");
        fos.write(97);
        fos.close();
    }
}

一次写多个字节的方法:
public void write(byte[] b):将b.length字节从指定的字节数组写入此输出流
public void write(byte[] b, int off, int len):从指定的字节数组写入len字节,从偏移量off开始输出到此输出流
如果写的第一个字节是正数(0-127),那么显示的时候会查询ASCII表
如果写的第一个字节是负数,那第一个字节会和第二个字节,两个字节组成一个中文显示,查询系统默认码表(GBK)
写入字符串的方法:可以使用String类中的方法把字符串转换为字节数组
byte[] getBytes()

public class Demo02OutputStream {
    public static void main(String[] args) throws IOException {
        FileOutputStream fos = new FileOutputStream("D:\\Test\\src\\Day17\\Demo04\\b.txt");
        fos.write(65);
        fos.write(67);
        fos.write(89);
        //一次写多个字节
        byte[] bytes = {65, 67, 68};
        fos.write(bytes);
        byte[] bytesA = {-65, -67, -68};
        fos.write(bytesA);
        byte[] bytesB = {-65, -67, -68};
        fos.write(bytesB,1,2);
        byte[] byteC = "小松狮".getBytes();
        System.out.println(Arrays.toString(byteC));//[-27, -80, -113, -26, -99, -66, -25, -117, -82]
        fos.write(byteC);
        fos.close();
    }
}

追加写/续写:使用两个参数的构造方法
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
max:/r

public class Demo03OutputStream {
    public static void main(String[] args) throws IOException {
        FileOutputStream fos = new FileOutputStream("D:\\Test\\src\\Day17\\Demo04\\c.txt",true);
        for (int i = 0; i < 10; i++) {
            fos.write("你好小松狮".getBytes(StandardCharsets.UTF_8));
            fos.write("\r\n".getBytes(StandardCharsets.UTF_8));
        }
        fos.close();
    }
}

4.4 字节输入流

java.io.InputStream:字节输入流
此抽象类是表示字节输入流的所有类的超类
定义了所有子类共性的方法:
int read()从输入流中读取数据的下一个字节
int read(byte[] b)从输入流中读取一定数量的字节,并将其存储在缓冲区数组b中
void close()关闭此输入流并释放与该流关联的所有系统资源

4.5 FileInputStream类

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 Demo04InputStream {
    public static void main(String[] args) throws IOException {
        FileInputStream fis = new FileInputStream("D:\\Test\\src\\Day17\\Demo04\\b.txt");
        //int read()读取文件中的一个字节并返回,读取到文件的末尾返回-1
        /*int read = fis.read();
        System.out.println(read);
        int read1 = fis.read();
        System.out.println(read1);*/
        /*
        发现以上读取文件是一个重复的过程,所以可以使用循环优化
        不知道文件中有多少字节,使用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.println((char)len);
        }
        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):把字节数组的一部分转换为字符串

public class Demo05InputStream {
    public static void main(String[] args) throws IOException {
        FileInputStream fis = new FileInputStream("D:\\Test\\src\\Day17\\Demo04\\c.txt");
        /*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
        int len1 = fis.read(bytes);
        System.out.println(len1);//2
        System.out.println(new String(bytes));//CD
        int len2 = fis.read(bytes);
        System.out.println(len2);//2
        System.out.println(new String(bytes));//E
        int len3 = fis.read(bytes);
        System.out.println(len3);//1
        System.out.println(new String(bytes));
        int len4 = fis.read(bytes);
        System.out.println(len4);//-1
        System.out.println(new String(bytes));*/
        /*
        使用循环优化,不知道文件中有多少字节,使用while循环
        while循环结束的条件,读取到-1结束
         */
        byte[] bytes = new byte[1024];//存储读取到的多个字节
        int len = 0;//记录每次读取的有效字节个数
        while ((len = fis.read(bytes)) != -1 ){
            System.out.println(new String(bytes,0, len));//ABCDE
        }
        fis.close();
    }
}

4.6 练习

文件复制练习:一读一写
文件复制步骤:
1、创建一个字节输入流对象,构造方法中绑定要读取的数据源
2、创建一个字节输出流对象,构造方法中绑定要写入的目的地
3、使用字节输入流对象中的方法read读取文件
4、使用字节输出流中的方法write,把读取到的字节写入到目的地的文件中
5、释放资源(先关写的,后关读的;如果写完了,肯定读取完毕了)

public class CopyFile {
    public static void main(String[] args) throws IOException {
        long start = System.currentTimeMillis();
        FileInputStream fis = new FileInputStream("D:\\Test\\src\\Day17\\Demo04\\IL.jpg");
        FileOutputStream fos = new FileOutputStream("D:\\Test\\src\\Day17\\Demo03\\IL.jpg");
        //一次读取一个字节写入一个字节的方法
        /*int len = 0;
        while ((len = fis.read()) != -1){
            fos.write(len);
        }*/
        //使用数组缓冲读取多个字节,写入多个字节
        byte[] bytes = new byte[10240];
        int len = 0;//每次读取的有效字节个数
        while ((len = fis.read(bytes)) != -1){
            fos.write(bytes,0,len);
        }
        fos.close();
        fis.close();
        long end = System.currentTimeMillis();
        System.out.println("复制粘贴共耗时:" + (end - start) + "毫秒");
    }
}

第五节 IO字符流

5.1 字符输入流(Reader)

java.io.Reader:字符输入流,是字符输入流的最顶层父类,定义了一些共性的成员方法,是一个抽象类
共性的成员方法:
int read():读取单个字符并返回
int read(char[] cbuf):一次读取多个字符,将字符读入数组
void close():关闭该流并释放与之关联的所有资源

5.2 FileReader类

java.io.FileReader extends InputStreamReader extends 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 Demo01Reader {
    public static void main(String[] args) throws IOException {
        FileReader fr = new FileReader("D:\\Test\\src\\Day17\\Demo04\\b.txt");
        //int read():读取单个字符并返回
        /*int len = 0;
        while ((len = fr.read()) != -1) {
            System.out.print((char)len);
        }*/
        //int read(char[] cbuf):一次读取多个字符,将字符读入数组
        char[] chars = new char[1024];
        int len = 0;
        while ((len = fr.read(chars)) != -1){
            System.out.print(new String(chars,0,len));
        }
        fr.close();
    }
}

5.3 字符输出流(Writer)

java.io.Writer:字符输出流,是所有字符输出流的最顶层父类,是一个抽象类
共性的成员方法:
void write(int c)
void write(char[] cbuf)
abstract void write(char[] cbuf, int off, int len)
void write(String str)
void write(String str, int off, int len)
void flush()
void close()

5.4 FileWriter类

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

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

public class Demo02Writer {
    public static void main(String[] args) throws IOException {
        FileWriter fw = new FileWriter("D:\\Test\\src\\Day17\\Demo05\\a.txt");
        fw.write("小松狮");
        //fw.flush();
        fw.close();
    }
}

flush方法和close方法的区别:
flush:刷新缓冲区,流对象可以继续使用
close:先刷新缓冲区,然后通知系统释放资源,流对象不可以再使用了

public class Demo03FlushAndClose {
    public static void main(String[] args) throws IOException {
        FileWriter fw = new FileWriter("D:\\Test\\src\\Day17\\Demo05\\b.txt");
        fw.write("小松狮");
        fw.flush();
        fw.write("小锦鲤");
        fw.close();
        //fw.write("123");//IOException错误!
    }
}
//字符数据流写数据的其他方法
public class Demo04Writer {
    public static void main(String[] args) throws IOException {
        FileWriter fw = new FileWriter("D:\\Test\\src\\Day17\\Demo05\\c.txt");
        char[] chars = {'A','b','e'};
        fw.write(chars);
        fw.write(chars,1,2);
        fw.write("小松狮");
        fw.write("小松狮小锦鲤",3,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
max:/r

public class Demo05Writer {
    public static void main(String[] args) throws IOException {
        FileWriter fw = new FileWriter("D:\\Test\\src\\Day17\\Demo05\\d.txt", true);
        for (int i = 0; i < 5; i++) {
            fw.write("小松狮" + i + "\r\n");
        }
        fw.close();
    }
}

5.5 IO异常的处理

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

public class Demo05TryCatch {
    public static void main(String[] args) {
        //提高变量fw的作用域,让Finally可以使用
        //变量在定义的时候,可以没有值,但是使用的时候必须有值
        //fw = new FileWriter("D:\\\\Test\\\\src\\\\Day17\\\\Demo05\\\\e.txt", true);执行失败,fw没有值,fw.close会报错
        FileWriter fw = null;
        try{
            //可能会产生异常的代码
            fw = new FileWriter("G:\\\\Test\\\\src\\\\Day17\\\\Demo05\\\\e.txt", true);
            for (int i = 0; i < 5; i++) {
                fw.write("小松狮" + i + "\r\n");
            }
        }catch (IOException e){
            //异常的处理逻辑
            System.out.println(e);
        }finally {
            //一定会执行的代码
            //创建对象失败了,fw的默认值就是null,null是不能调用方法的
            //会抛出NullPointException,需要增加一个判断,不是null再释放资源
            if (fw != null){
                try {
                    fw.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

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

public class Demo06JDK7 {
    public static void main(String[] args) {
        try (FileInputStream fis = new FileInputStream("D:\\Test\\src\\Day17\\Demo04\\IL.jpg");
             FileOutputStream fos = new FileOutputStream("D:\\Test\\src\\Day17\\Demo05\\IL.jpg");) {
            byte[] bytes = new byte[10240];
            int len = 0;//每次读取的有效字节个数
            while ((len = fis.read(bytes)) != -1){
                fos.write(bytes,0,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 Demo07JDK9 {
    public static void main(String[] args) throws FileNotFoundException {
        FileInputStream fis = new FileInputStream("D:\\Test\\src\\Day17\\Demo04\\IL.jpg");
        FileOutputStream fos = new FileOutputStream("D:\\Test\\src\\Day17\\Demo05\\IL1.jpg");
        try (fis;fos){
            byte[] bytes = new byte[10240];
            int len = 0;//每次读取的有效字节个数
            while ((len = fis.read(bytes)) != -1){
                fos.write(bytes,0,len);
            }
        }catch (IOException e){
            System.out.println(e);
        }
                //fos.write(1);//Stream Closed,IOException
    }
}

第六节 Properties集合

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

使用Properties集合存储数据,遍历取出Properties集合中的数据
Properties集合有一些操作字符串的特有方法
Object setProperty(String key, String value)调用Hashtable的方法put
String getProperty(String key)通过key找到value值,此方法相当于Map集合中的get(key)方法
Set stringPropertyNames()返回此属性列表中的键集,其中该键及其对应值是字符串,此方法相当于Map集合中的keySet方法

public class Demo01Properties {
    public static void main(String[] args) {
        demo01();
    }
    private static void demo01() {
        Properties prop = new Properties();
        prop.setProperty("小松狮","16");
        prop.setProperty("小松狮1","17");
        prop.setProperty("小松狮2","18");
        Set<String> set = prop.stringPropertyNames();
        for (String key : set) {
            String value = prop.getProperty(key);
            System.out.println(key + value);
        }
    }
}

6.1 store方法

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、释放资源

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

    private static void demo01() throws IOException {
        Properties prop = new Properties();
        prop.setProperty("小松狮","16");
        prop.setProperty("小松狮1","17");
        prop.setProperty("小松狮2","18");
        FileWriter fw = new FileWriter("D:\\Test\\src\\Day17\\Demo06\\a.txt");
        prop.store(fw,"");
        fw.close();
    }
}

6.2 load方法

load,把硬盘中保存的文件(键值对),读取到集合中使用
void load(InputStream inStream)
void load(Reader reader)

参数:
InputStream inStream:字节输入流,不能读取含有中文的键值对
Reader reader:字符输入流,能读取含有中文的键值对
使用步骤:
1、创建Properties集合对象
2、使用Properties集合对象中的方法load读取保存键值对的文件
3、遍历Properties集合
注意:
1、存储键值对的文件中,键与值默认的连接符号可以使用=,空格(其他符号)
2、存储键值对的文件中,可以使用#进行注释,被注释的键值对不会再被读取
3、存储键值对的文件中,键与值默认都是字符串,不用再加引号

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

    private static void demo01() throws IOException {
        Properties prop = new Properties();
        prop.load(new FileReader("D:\\Test\\src\\Day17\\Demo06\\a.txt"));
        Set<String> set = prop.stringPropertyNames();
        for (String key : set) {
            String value = prop.getProperty(key);
            System.out.println(key + "=" + value);
        }
    }
}

第七节 缓冲流

7.1 概念

缓冲流,也叫高效流,是对四个基本的FileXxx流的增强,根据数据类型分为:
字节缓冲流:BufferedInputStream,BufferedOutputStream
字符缓冲流:BufferedReader,BufferedWriter

7.2 BufferedOutputStream

java.io.BufferedOutputStream extends OutputStream
BufferedOutputStream:字节缓冲输出流
继承自父类的共性成员方法

构造方法:
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方法刷新数据,第四步可以省略)

public class Demo01BufferedInput {
    public static void main(String[] args) throws IOException {
        FileOutputStream fos = new FileOutputStream("D:\\Test\\src\\Day17\\Demo07\\a.txt");
        BufferedOutputStream bos = new BufferedOutputStream(fos);
        bos.write("小松狮小锦鲤".getBytes(StandardCharsets.UTF_8));
        bos.flush();
        bos.close();
    }
}

7.3 BufferedInputStream

java.io.BufferedInputStream extends InputStream
BufferedInputStream:字节缓冲输入流
继承自父类的成员方法

构造方法:
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、释放资源

public class Demo02BufferedInputStream {
    public static void main(String[] args) throws IOException {
        FileInputStream fis = new FileInputStream("D:\\Test\\src\\Day17\\Demo07\\a.txt");
        BufferedInputStream bis = new BufferedInputStream(fis);
       /* int len = 0;//记录每次读取到的字节
        while ((len = bis.read()) != -1){
            System.out.println(len);
        }*/
        int len = 0;
        byte[] bytes = new byte[1024];
        while ((len = bis.read(bytes)) != -1){
            System.out.println(new String(bytes, 0, len));
        }
        bis.close();
    }
}

7.4 缓冲流测试复制文件效率

import java.io.*;

/*
文件复制练习:一读一写
文件复制的步骤:
1、创建字节缓冲输入流对象,构造方法中传递字节输入流
2、创建字节缓冲输出流对象,构造方法中传递字节输出流
3、使用字节缓冲输入流对象中的方法read,读取文件
4、使用字节缓冲输出流中的方法write,把读取的数据写入到内部缓冲区中
4、释放资源(会先把缓冲区中的数据,刷新到文件中)
 */
public class Copy {
    public static void main(String[] args) throws IOException {
        long start = System.currentTimeMillis();
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream("D:\\Test\\src\\Day17\\Demo04\\IL.jpg"));
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("D:\\Test\\src\\Day17\\Demo07\\IL.jpg"));
        /*int len = 0;
        while ((len = bis.read()) != -1){
            bos.write(len);
        }//80毫秒*/
        int len = 0;
        byte[] bytes = new byte[1024];
        while ((len = bis.read(bytes)) != -1){
            bos.write(bytes, 0, len);
        }//10毫秒
        bos.close();
        bis.close();
        long end = System.currentTimeMillis();
        System.out.println("复制粘贴文件共用时:" + (end - start) + "毫秒");
    }
}

7.5 BufferedWriter

java.io.BufferedWriter extends Writer
BufferedWriter:字符缓冲输出流
继承自父类的共性成员方法

构造方法:
BufferedWriter(Writer out):创建一个使用默认大小输出缓冲区的缓冲字符输出流
BufferedWriter(Writer out, int sz):创建一个使用给定大小输出缓冲区的新缓冲字符输出流
参数:
Writer out:字符输出流
我们可以传递FileWriter,缓冲流会给FileWriter增加一个缓冲区,提高FileWriter的写入效率
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 {
        BufferedWriter bw = new BufferedWriter(new FileWriter("D:\\Test\\src\\Day17\\Demo07\\b.txt"));
        for (int i = 0; i < 5; i++) {
            bw.write("小松狮");
            bw.newLine();
        }
        bw.close();
    }
}

7.6 BufferedReader

java.io.BufferedReader extends Reader
继承自父类的共性成员方法

构造方法:
BufferedReader(Reader in):创建一个使用默认大小输入缓冲区的缓冲字符输入流
BufferedReader(Reader in, int sz):创建一个使用指定大小输入缓冲区的缓冲字符输入流
参数:
Reader in:字符输入流
我们可以传递FileReader,缓冲流会给FileReader增加一个缓冲区,提高FileReader的读取效率
int sz:指定缓冲区的大小,不写默认大小
特有方法:
String readLine()读取一个文本行,读取一行数据
行的终止符号:通过下列字符之一即可认为某行已终止:换行(‘\n’)、回车(‘\r’)、或(\r\n)
返回值:包含该行内容的字符串,不包含任何行终止符,如果已到达流末尾,则返回null
使用步骤:
1、创建字符缓冲输入流对象,构造方法中传递字符输入流
2、使用字符缓冲输入流对象中的方法read/readLine读取文本
3、释放资源

public class Demo04BufferedReader {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new FileReader("D:\\Test\\src\\Day17\\Demo07\\b.txt"));
        String line;
        while ((line = br.readLine()) != null){
            System.out.println(line);
        }
        br.close();
    }
}

7.7 练习

练习:对文本的内容进行排序
按照(1,2,3…)顺序排序

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

第八节 转换流

8.1 编码引出的问题

/*
FileReader可以读取默认编码格式(UTF-8)的文件
FileReader读取系统默认编码(中文GBK)会产生乱码:С��ʨ
 */
public class Demo01Reader {
    public static void main(String[] args) throws IOException {
        FileReader fr = new FileReader("D:\\Test\\src\\Day17\\Demo08\\1.txt");
        int len = 0;
        while ((len = fr.read()) != -1){
            System.out.print((char) len);
        }
        fr.close();
    }
}

8.2 OutputStreamWriter

java.io.OutputStreamWriter extends Writer
OutputStreamWriter是字符流通向字节流的桥梁:可使用指定的charset将要写入流中的字符编码成字节
继承自父类的共性成员方法

构造方法:
OutputStreamWriter(OutputStream out)创建使用默认字符编码的OutputStreamWriter
OutputStreamWriter(OutputStream out, String charsetName)创建使用指定字符集的OutputStreamWriter
参数:
OutputStream out:字节输出流,可以用来写转换之后的字节到文件中
String charsetName:指定的编码表名称,不区分大小写,可以是utf-8,gbk…不指定默认使用UTF-8
使用步骤:
1、创建OutputStreamWriter对象,构造方法中传递字节输出流和指定的编码表名称
2、使用OutputStreamWriter对象中的方法write,把字符转换为字节存储到缓冲区中(编码)
3、使用OutputStreamWriter对象中的方法flush,把内存缓冲区中的字节刷新到文件中(使用字节流写字节的过程)
4、释放资源

public class Demo02OutputStream {
    public static void main(String[] args) throws IOException {
        OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("D:\\Test\\src\\Day17\\Demo08\\gbk.txt"),"gbk");
        osw.write("小松狮");
        osw.flush();
        osw.close();
    }
}

8.3 InputStreamReader

java.io.InputStreamReader extends Reader
InputStreamReader:是字节流通向字符流的桥梁:它使用指定的charset读取字节并将其解码为字符
继承自父类的共性成员方法

构造方法:
InputStreamReader(InputStream in):创建一个使用默认字符集的InputStreamReader
InputStreamReader(InputStream in, String charsetName):创建使用指定字符集的InputStreamReader
参数:
InputStream in:字节输入流,用来读取文件中保存的字节
String charsetName:指定的编码表名称,不区分大小写,可以是utf-8,gbk…不指定默认使用UTF-8
使用步骤:
1、创建InputStreamReader对象,构造方法中传递字节输入流和指定的编码表名称
2、使用InputStreamReader对象中的方法read读取文件
3、释放资源
注意事项:
构造方法中指定的编码表名称要和文件的编码相同,否则会发生乱码

public class Demo02InputStream {
    public static void main(String[] args) throws IOException {
        InputStreamReader isr = new InputStreamReader(new FileInputStream("D:\\Test\\src\\Day17\\Demo08\\gbk.txt"),"gbk");
        int len = 0;
        char[] chars = new char[1024];
        while ((len = isr.read(chars)) != -1){
            System.out.println(new String(chars, 0, len));
        }
        isr.close();
    }
}

8.4 练习

练习:将GBK编码的文本文件,转换为UTF-8编码的文本文件

/*
练习:将GBK编码的文本文件,转换为UTF-8编码的文本文件
分析:
1、创建InputStreamReader对象,构造方法中传递字节输入流和指定的编码表名称gbk
2、创建OutputStreamWriter对象,构造方法中传递字节输出流和指定的编码表名称UTF-8
3、使用InputStreamReader对象中的方法read读取文件
4、使用OutputStreamWriter对象中的方法write,把读取的数据写入到文件中
5、释放资源
 */
public class Test {
    public static void main(String[] args) throws IOException {
        InputStreamReader isr = new InputStreamReader(new FileInputStream("D:\\Test\\src\\Day17\\Demo08\\gbk.txt"),"GBK");
        OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("D:\\Test\\src\\Day17\\Demo08\\utf_8.txt"),"utf-8");
        int len = 0;
        while ((len = isr.read()) != -1){
            osw.write(len);
        }
        osw.close();
        isr.close();
    }
}

第九节 序列化流

把对象以流的方式,写入到文件中保存,叫写对象,也叫对象的序列化
对象中包含的不仅仅是字符,使用字节流
ObjectOutputStream:对象的序列化流
把文件中保存的对象,以流的方式读取出来,叫做读对象,也叫对象的反序列化
读取的文件保存的都是字节,使用字节流
ObjectInputStream:对象的反序列化流

9.1 ObjectOutputStream

java.io.ObjectOutputStream extends OutputStream
ObjectOutputStream:对象的序列化流
作用:把对象以流的方式写入到文件中保存

构造方法
ObjectOutputStream(OutputStream out)创建写入指定OutputStream的ObjectOutputStream
参数:
OutputStream out:字节输出流
特有的成员方法:
void writeObject(Object obj)将指定的对象写入ObjectOutputStream
使用步骤:
1、创建ObjectOutputStream对象,构造方法中传递字节输出流
2、使用ObjectOutputStream对象中的writeObject,把对象写入到文件中
3、释放资源

import java.io.Serializable;

/*
序列化和反序列化的时候,会抛出NotSerializableException没有序列化异常
类通过实现java.io.Serializable接口以启用其序列化功能
未实现此接口的类将无法使其任何状态序列化或反序列化
Serializable接口也叫标记型接口
要进行序列化和反序列化的类必须实现Serializable接口,就会给类添加一个标记
当我们进行序列化和反序列化的时候,就会检测类上是否有这个标记
有:就可以序列化和反序列化
没有:就会抛出NotSerializableException异常
 */
public class Person implements Serializable {
    private String name;
    private 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;
    }
}
public class Demo01ObjectOutputStream {
    public static void main(String[] args) throws IOException {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("D:\\Test\\src\\Day17\\Demo09\\person.txt"));
        oos.writeObject(new Person("小松狮",18));
        oos.close();
    }
}

9.2 ObjectInputStream

java.io.ObjectInputStream extends InputStream
ObjectInputStream:对象的反序列化流
作用:把文件中保存的对象,以流的方式读取出来使用

构造方法:
ObjectInputStream(InputStream in)创建从指定InputStream读取的ObjectInputStream
参数:
InputStream:字节输入流
特有的成员方法:
Object readObject()从ObjectInputStream读取对象
使用步骤:
1、创建ObjectInputStream对象,构造方法中传递字节输入流
2、使用ObjectInputStream对象中的方法readObject读取保存对象的文件
3、释放资源
4、使用读取出来的对象(打印)
readObject方法声明抛出了ClassNotFoundException
当不存在对象的class文件时抛出此异常
反序列化的前提:
1、类必须实现Serializable
2、必须存在类对应的class文件

public class Demo01ObjectInputStream {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("D:\\Test\\src\\Day17\\Demo09\\person.txt"));
        Object obj = ois.readObject();
        ois.close();
        System.out.println(obj);
    }
}

static关键字:静态关键字
静态优先于非静态加载到内存中(静态优先于对象进入到内存中)
被static修饰的成员变量不能被序列化的,序列化的都是对象
transient关键字:瞬态关键字
被transient修饰的成员变量,不能被序列化

9.3 练习

import java.io.*;
import java.util.ArrayList;

/*
练习:序列化集合
当我们想在文件中保存多个对象的时候,可以把对个对象存储到一个集合中
对集合进行序列化和反序列化
分析:
1、定义一个存储Person对象的ArrayList集合
2、往ArrayList集合中存储Person对象
3、创建一个序列化流对象
4、使用序列化流对象中的方法writeObject对集合进行序列化
5、创建一个反序列化流对象
6、使用反序列化流对象中的方法readObject读取文件中保存的集合
7、把Object类型的集合转换为ArrayList类型
8、遍历ArrayList集合
9、释放资源
 */
public class Test {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        ArrayList<Person> list = new ArrayList<>();
        list.add(new Person("小松狮",18));
        list.add(new Person("小锦鲤",19));
        list.add(new Person("小松狮",18));
        list.add(new Person("小锦鲤",19));
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("D:\\Test\\src\\Day17\\Demo09\\person.txt"));
        oos.writeObject(list);
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("D:\\Test\\src\\Day17\\Demo09\\person.txt"));
        Object o = ois.readObject();
        ArrayList<Object> array = (ArrayList<Object>)o;
        for (Object list1 : array) {
            System.out.println(list1);
        }
        ois.close();
        oos.close();
    }
}

第十节 打印流

import java.io.FileNotFoundException;
import java.io.PrintStream;

/*
java.io.PrintStream:打印流
PrintStream为其他输出流添加了功能,使他们能够方便地打印各种数据值表示形式
PrintStream特点:
1、只负责数据的输出,不负责数据的读取
2、与其他输出流不同,PrintStream永远不会抛出IOException
3、有特有方法,print,println
void print(任意类型的值)
void println(任意类型的值并换行)
构造方法:
PrintStream(File file):输出的目的地是一个文件
PrintStream(OutputStream out):输出的目的地是一个字节输出流
PrintStream(String fileName):输出的目的地是一个文件路径
PrintStream extends OutputStream
继承自父类的成员方法
注意:
如果使用继承自父类的write方法写数据,那么查看数据的时候会查询编码表 97->a
如果使用自己特有的方法print/println方法写数据,写的数据原样输出 97->97
 */
public class Demo01PrintStream {
    public static void main(String[] args) throws FileNotFoundException {
        PrintStream ps = new PrintStream("D:\\Test\\src\\Day17\\Demo10\\print.txt");
        //ps.write(1);
        ps.print(1);
        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(new FileOutputStream("D:\\Test\\src\\Day17\\Demo10\\打印流目的地.txt"));
        System.setOut(ps);
        System.out.println("打印流的目的地中输出");
        ps.close();
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值