Java File与IO流学习笔记

内存中存放的都是临时数据,但是在断电或者程序终止时都会丢失

而硬盘则可以长久存储数据,即使断电,程序终止,也不会丢失

File

  • Filejava.io.包下的类,File类的对象,用于代表当前操作系统的文件(可以是文件,或文件夹)
  • 可以获取文件信息(大小,文件名,修改时间)
  • 判断文件的类型
  • 创建文件或者文件夹
  • 删除文件或者文件夹…
  • 但是File类只能对文件本身进行操作,不能读写文件里面存储的数据
创建File对象
构造器 说明
public File(String pathname) 根据文件路径创建文件对象
public File(String parent,String child) 根据父路径和子路径名字创建文件对象
public File(File parent,String child) 根据父路径对应文件对象和子路径名字创建文件对象

案例

Main.java

import java.io.File;

public class Main {
    public static void main(String[] args) {
        // 创建一个File对象,指代某个具体的文件
        File file = new File("C:\\Users\\huihui\\Desktop\\test.txt");
        System.out.println(file); // C:\Users\huihui\Desktop\test.txt
        System.out.println(file.length()); // 文件大小 0
        File file1 = new File("C:/Users/huihui/Desktop/test.txt");
        System.out.println(file1); // C:\Users\huihui\Desktop\test.txt
        File file2 = new File("C:" + File.separator +"Users"+ File.separator + "huihui"+ File.separator + "Desktop"+ File.separator + "test.txt");
        System.out.println(file2); // C:\Users\huihui\Desktop\test.txt
    }
}

注意:File对象也可以指代一个不存在的文件路径

Main.java

import java.io.File;

public class Main {
    public static void main(String[] args) {
        File file = new File("aa/bb/cc");
        System.out.println(file.length()); // 0
        // 可以通过exists()方法判断文件是否存在
        System.out.println(file.exists()); // false
    }
}
路径
  • 路径分为绝对路径(带盘符)和相对路径(不带盘符)
import java.io.File;

public class Main {
    public static void main(String[] args) {
        // 据对路径,带盘符(是种写死的路径)
        File file = new File("D:\\Idea\\Codeing\\Day01\\src\\Demo\\Student.java");
        System.out.println(file); // D:\Idea\Codeing\Day01\src\Demo\Student.java
        // 相对路径,不带盘符(是种灵活的路径),默认回去当前工程下寻找文件
        File file1 = new File("src\\Demo\\Student.java");
        System.out.println(file1); // src\Demo\Student.java
    }
}
方法
判断文件类型,获取文件信息

Java File与IO流学习笔记_第1张图片

案例

import java.io.File;
import java.text.SimpleDateFormat;
import java.util.logging.SimpleFormatter;

public class Main {
    public static void main(String[] args) {
        File file = new File("C:\\Users\\huihui\\Desktop\\test.txt");
        // 1. exists() 文件是否存在
        System.out.println(file.exists()); // true
        // 2. isFile() 判断是否是文件
        System.out.println(file.isFile()); // true
        // 3. isDirectory() 判断是否是文件夹
        System.out.println(file.isDirectory()); // false
        // 4. getName() 获取文件的名称包括后缀
        System.out.println(file.getName()); // test.txt
        // 5. length() 获取文件大小,返回字节个数
        System.out.println(file.length()); // 0
        // 6. laseModified() 获取文件的最后修改时间,默认返回的是时间戳
        // System.out.println(file.lastModified()); // 1696232257337
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        System.out.println(sdf.format(file.lastModified())); // 2023-10-02 15:37:37
        // 7. getPath() 获取创建文件对象时,使用的路径(你写的是什么路径,它就会返回什么路径)
        System.out.println(file.getPath()); // C:\Users\huihui\Desktop\test.txt
        // 8. getAbsolutePath() 获取绝对路径
        System.out.println(file.getAbsolutePath()); // C:\Users\huihui\Desktop\test.txt
    }
}
创建或者删除文件

Java File与IO流学习笔记_第2张图片

案例

import java.io.File;
import java.io.IOException;

public class Main {
    public static void main(String[] args) throws IOException {
        // 1. createNewFile() 创建一个新的文件,但是需要抛出异常。返回一个布尔值,如果存在,再创建就会返回false
        File file = new File("D:\\Idea\\Codeing\\Day01\\src\\Demo\\test.txt");
        System.out.println(file.createNewFile()); // true
        // 2. mkdir() 用于创建文件夹,但是只能创建一级文件夹。返回一个布尔值
        File f2 = new File("D:\\Idea\\Codeing\\Day01\\src\\Demo01");
        System.out.println(f2.mkdir()); // true
        // 3. mkdirs() 用于创建文件夹,可以创建多级文件夹
        File f3 = new File("D:/Idea/Codeing/Day01/src/Demo02/aa/bb");
        System.out.println(f3.mkdirs()); // true
        // 4. delete() 删除文件或者空文件,但是不能删除非空文件夹
    }
}
遍历文件夹

File类提供的遍历文件夹的功能

方法 说明
public String[] list() 获取当前目录下所有的“一级文件名称”,返回一个字符串数组
public File[] listFiles() 获取当前目录下所有的“一级文件对象”,返回一个文件对象数组

使用listFiles注意事项:

  • 当主调是文件,或者路径不存在时,返回null
  • 当主调是空文件夹时,返回一个长度为0的数组
  • 当主调是一个有内容的文件夹时,将里面所有一级文件和文件夹的路径放在File数组中返回
  • 当主调是一个文件夹,且里面有隐藏文件时,将里面所有文件和文件夹的路径放在File数组中返回,包含隐藏文件
  • 当主调是一个文件夹,但是没有权限访问改文件夹时,返回null
import java.io.File;
import java.util.Arrays;

public class Main {
    public static void main(String[] args) {
        File file = new File("D:\\Idea\\Codeing\\Day01\\src\\Demo");
        // 1. public String[] list(): 获取当前目录下所有的 "一级文件名称"
        String[] list = file.list();
        System.out.println(Arrays.toString(list)); // [Student.java, test.txt]
        // 2. public File[] listFiles(): 获取当前目录下所有的 "一级文件对象"
        File[] files = file.listFiles();
        for (File file1 : files) {
            System.out.println(file1.isFile()); // true true
            System.out.println(file1.getAbsolutePath()); // 
        }
    }
}
递归

案例1:修改文件序号

import java.io.File;

public class Main {
    public static void main(String[] args) {
        // 改变某个文件夹下的文件的序号,要求从19开始
        File f = new File("C:\\Users\\huihui\\Desktop\\test");
        // 1. 得到里面所有的文件对象
        File[] files = f.listFiles();
        for (File file : files) {
            String name = file.getName();
            String start = name.substring(0, name.indexOf("."));
            String end = name.substring(name.indexOf("."));
            String newName = (Integer.valueOf(start) + 18) + end;
            // 开始改名
            file.renameTo(new File(f, newName));
        }
    }
}

递归就是方法调用自身,递归一定要有一个结束条件,不然会无限递归,造成栈内存溢出(因为方法会进入栈内,每次调用都会进栈,但是一直没有出来的话,栈内存中的方法就会越来越多,就会溢出)

递归形式:

  1. 直接递归:方法自己调用自己
  2. 间接递归:方法调用其他方法,其他方法又回调方法自己

直接递归:

public class Main {
    public static void main(String[] args) {
        print(0);
    }

    // 直接递归:方法内部直接调用自己
    public static void print(int num) {
        if(num < 10) {
            System.out.println("打印输出");
            print(++num);
        }
    }
}

间接递归:

public class Main {
    public static void main(String[] args) {
        print(0);
    }

    public static void print(int num) {
        temp(num);
    }
    // 间接递归:别的方法调用自己
    public static void temp(int num) {
        if(num < 10) {
            System.out.println("打印输出");
            print(++num);
        }
    }
}
求阶乘
public class Main {
    public static void main(String[] args) {
        System.out.println(print(5)); // 120
    }

    // 计算结阶乘
    public static int print(int num) {
        if(num == 1) return 1;
        return print((num - 1)) * num;
    }
}

Java File与IO流学习笔记_第3张图片

求1-n的和
public class Main {
    public static void main(String[] args) {
        System.out.println(sum(5)); // 15
    }

    // 计算1-n的和
    public static int sum(int num) {
        if(num == 1) return 1;
        return sum(num - 1) + num;
    }
}
猴子吃桃

猴子第一天摘下若干桃子,当即吃了一般,觉得好不过瘾,于是又多吃了一个。第二天又吃了前天剩余桃子数量的一般,觉得好不过瘾于是又多吃了一个。以后每天都是吃昨天剩余桃子数量的一半加一个。到第十天就只剩一个桃子了。问猴子摘了多少桃子?

f(x) - f(x)/2 - 1 = f(x + 1) // 昨天吃的减去前天的一半再减1,就是今天的

public class Main {
    public static void main(String[] args) {
        System.out.println(sum(1)); // 1534
    }

    // 计算1-n的和
    public static int sum(int num) {
        if(num == 10) return 1;
        return 2 * sum(num + 1) + 2;
    }
}
搜索文件

D:盘中,搜索QQScLauncher.exe这个文件,找到后直接输出位置并打开

import java.io.File;
import java.io.IOException;

public class Main {
    public static void main(String[] args) throws IOException {
        String path = getPath(new File("D:\\QQ\\Bin"), "QQScLauncher.exe");
        System.out.println(path); // C:\Users\huihui\Desktop\Test\test.txt
    }

    // 在c盘中找到 test.txt 文件,并输出位置
    public static String getPath(File dir, String fileName) throws IOException {
        if(dir == null || !dir.exists() || dir.isFile()) return "您输入的路径有误";
        // 首先遍历一级文件
        File[] files = dir.listFiles();
        // 判断当前目录下是否存在一级文件对象,一级是否可以拿到一级文件对象
        if(files != null && files.length > 0) {
            // 找到里面的文件并对比
            for (File file : files) {
                if(file.isFile() && file.getName().contains(fileName)) {
                    // 打开文件 运行时对象
                    Runtime runtime = Runtime.getRuntime();
                    runtime.exec(file.getAbsolutePath());
                    return file.getAbsolutePath();
                } else {
                    getPath(file, fileName);
                }
            }
        }
        return "没有搜索到";
    }
}
删除非空文件夹
import java.io.File;

public class Main {
    public static void main(String[] args) {
        delFile(new File("C:/Users/huihui/Desktop/kong"));
    }

    // 删除非空文件夹
    public static void delFile(File file) {
        // 判空,不存在或者传的为空则不处理
        if(!file.exists() || file == null) return;
        // 如果是文件,则直接删除
        if(file.isFile()) {
            file.delete();
            return;
        }
        // 存在且是文件夹
        File[] files = file.listFiles(); // 拿到下面的一级文件对象
        if(files == null) return; // 如果文件夹为空(没有权限),则不处理
        // 有权限,不为空的文件夹,遍历进行删除里面的内容,并且最后要把自己空的文件夹删除
        for (File f : files) {
            if(f.isFile()) { // 是文件,直接删除
                f.delete();
            } else { // 文件夹,接着递归
                delFile(f);
            }
        }
        file.delete(); // 最后别忘了把自己空的文件夹删除
    }
}

字符集

Java File与IO流学习笔记_第4张图片

UTF-8

重点:

  • ASCII字符集:只有英文,数字,符号等。占1个字节
  • GBK字符集:汉字占2个字节,英文,数字占1个字节
  • UTF-8字符集:汉字占3个字节,英文,数字占1个字节

字符编码时使用的字符集,喝解码时使用的字符集必须一致,否则就会出现乱码。

但是数字和英文一般都不会出现乱码,因为大部分字符集都要兼容ASCII

编码与解码

编码:把字符按照指定字符集编码成字节

解码:把字节按照指定字符集解码成字符

编码
方法名 说明
byte[] getBayes() 使用平台的默认字符集将改String编码为一系列字节,将结果存储到新的字节数组中
byte[] getBytes(String charsetName) 使用指定的字符集将改String编码为一系列字节,将结果存储到新的字节数组中
import java.io.UnsupportedEncodingException;
import java.util.Arrays;

public class Main {
    public static void main(String[] args) throws UnsupportedEncodingException {
        String str = "a的1";
        // 编码
        byte[] b1 = str.getBytes(); // 按照平台默认的字符编码规则编码
        System.out.println(Arrays.toString(b1)); // [97, -25, -102, -124, 49] 97:a 49:1 中间3个表示中文
        byte[] gbks = str.getBytes("GBK");
        System.out.println(Arrays.toString(gbks)); // [97, -75, -60, 49] gbk编码规则中中文是2个字节
    }
}
解码
方法名 说明
String(byte[] bytes) 通过使用平台的默认字符集解码,指定的字节数组,来构造新的String
String(byte[] bytes, String charsetName) 通过指定的字符集解码,指定的租界数组,来构造新的String
import java.io.UnsupportedEncodingException;
import java.util.Arrays;

public class Main {
    public static void main(String[] args) throws UnsupportedEncodingException {
        String str = "a的1";
        // 编码
        byte[] b1 = str.getBytes(); // 按照平台默认的字符编码规则编码
        System.out.println(Arrays.toString(b1)); // [97, -25, -102, -124, 49] 97:a 49:1 中间3个表示中文
        byte[] gbks = str.getBytes("GBK");
        System.out.println(Arrays.toString(gbks)); // [97, -75, -60, 49] gbk编码规则中中文是2个字节
        // 解码
        String s = new String(b1);
        System.out.println(s); // a的1
        String gbk = new String(gbks, "GBK");
        System.out.println(gbk); // a的1
    }
}

IO流

Java File与IO流学习笔记_第5张图片

  • File代表文件。而IO流则可以读写数据

Java File与IO流学习笔记_第6张图片

应用:

Java File与IO流学习笔记_第7张图片

分类:
Java File与IO流学习笔记_第8张图片

具体体系

Java File与IO流学习笔记_第9张图片

输入输出都是以内存为基准的。程序在内存中运行,读写速度快,但是断电或者关闭就会清空数据。而硬盘(又叫磁盘)却可以永久存储数据,但是读写速度相对较慢。

输入:磁盘向内存中输入,也可以说内存读磁盘的数据

输出:内存向磁盘输出,也可以说内存向磁盘写入数据

内存和磁盘想要读写数据,就必须建立连接,而这个链接,就是流。类似与一个管道,只不过管道中流的是数据

数据又分为字节和字符,字节可以操作任意文件,而字符专注与操作各种文本文件(例如txt文件.java文件。。。)

输入流
  • 然后按照文件的最小单位,又可以分为 字节输入流字符输入流
字节输入流
  • InputStream抽象类表示,比较常用的实现类是FileInputStream

作用:

  • 以内存为基准,可以把磁盘文件中的数据以字节的ing是读入到内存中去(也就是读入带程序中去,因为程序放在内存中运行)

首先要创建管道,链接磁盘和内存

构造器 说明
public FileInputStream(File file) 创建字节输入流管道与源文件接通
public FileInputStream(String pathName) 创建字节输入流管道与源文件接通

然后就可以Java提供的方法进行读写了

方法名 说明
public int read() 每次读取一个字节返回,如果发现没有数据可读会返回-1
public int read(byte[] buffer) 每次用一个字节数组去读取数据,返回字节数组读取了多少个字节,如果返现没有数据可读就会返回-1

测试文件

a测试文件
读取一个字节
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;

public class Main {
    public static void main(String[] args) throws IOException {
        // 首先创建管道链接
        InputStream fis = new FileInputStream("D:\\Idea\\Codeing\\Day01\\src\\test.txt");
        // 读数据,一次只读一个字节
        int read = fis.read();
        System.out.println((char) read); // a
        // 最后别忘关闭通道(关闭流)
        fis.close();
    }
}

对上面的改造优化

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

public class Main {
    public static void main(String[] args) throws IOException {
        // 首先创建管道链接
        InputStream fis = new FileInputStream("D:\\Idea\\Codeing\\Day01\\src\\test.txt");
        // 读数据,一次只读一个字节
        int num; // 记录读取的字符
        while ((num = fis.read()) != -1) { // 不等于-1说明还有字节
            System.out.print((char) num); // a测试文件
        }
        // 最后别忘关闭通道(关闭流)
        fis.close();
    }
}

但是这种有问题:读取汉字的时候会乱码(因为utf8中,汉字是3个字节,这一次只能读一个,肯定会乱码),并且读取性能不高

读取多个字节
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;

public class Main {
    public static void main(String[] args) throws IOException {
        // 首先创建管道链接
        InputStream fis = new FileInputStream("D:\\Idea\\Codeing\\Day01\\src\\test.txt");
        // 读数据,可以自己设定一次读多少个字节
        byte[] bytes = new byte[3];

        // 第一次读取
        // read(byte[] buffer) 返回的是一次读取了多少个字节个数。如果最后一次读取,不到3个字节,那有几个字节,它就是几
        int len = fis.read(bytes);
        System.out.println(len); // 3
        // bytes 中存放的就是读取的3个字节 ['a','1','2']
        System.out.println(new String(bytes)); // a12

        // 第二次读取
        int len2 = fis.read(bytes);
        System.out.println(len2); // 2 因为最后的字节只有2个,不到3个,所以为2
        // bytes 中存放的就是读取的最新的3个字节 ['3','4','2'] 最后一个因为没有,所以还是用的原先的
        System.out.println(new String(bytes)); // 342
        // 但是显然上面的是不对的,正确的应该是 34 不应该有2
        System.out.println(new String(bytes, 0, len2)); // 34 第2个参数是开始位置,第3个参数是结束位置
    }
}

进行循环优化

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

public class Main {
    public static void main(String[] args) throws IOException {
        // 首先创建管道链接
        InputStream fis = new FileInputStream("D:\\Idea\\Codeing\\Day01\\src\\test.txt");
        // 读数据,可以自己设定一次读多少个字节
        byte[] bytes = new byte[3];

        // 当读取的长度不为我们设定的3时,就说明读取到到最后一个了
        int len; // 每次读取的字节长度
        while (((len = fis.read(bytes)) != -1)) {
            String s = new String(bytes, 0, len);
            System.out.println(s); // a12 34
        }
        // 关闭管道
        fis.close();
    }
}

但是这种一次读取多个字节,汉字依然是无法读取的。但是文件肯定是要有读取中文的现象,那我们改如何解决中文乱码的问题呢?

其实有一种办法,就是定义一个与文件一样大的字节数组,一次性读取文件的全部字节,这样不会出现中文乱码了

解决中文乱码的方法1

读取的文件:

a中文
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;

public class Main {
    public static void main(String[] args) throws IOException {
        // 首先创建管道链接
        InputStream fis = new FileInputStream("D:\\Idea\\Codeing\\Day01\\src\\test.txt");
        // 获取文件大小
        File file = new File("D:\\Idea\\Codeing\\Day01\\src\\test.txt");
        long length = file.length(); // 硬盘中的存储空间比较大,所以用的long类型
        // 读数据,可以自己设定一次读多少个字节
        byte[] bytes = new byte[(int)length]; // 内存里的存储空间比较小,所以用的int类型

        fis.read(bytes);
        System.out.println(new String(bytes)); // a中文
        fis.close();
    }
}

但是上面的方法,其实只能读取小的文件,超过int范围的文件一次性就读取不了了

解决中文乱码的方法2

Java官方为InputStrem提供了方法,可以直接把文件的全部字节读取到一个字节数组中返回

public byte[] readAllbytes() throw IOException // 直接将当前字节输入流对应的文件对象的字节数据撞到一个字节数组返回

那上面方法1就可以进行下面的简化

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

public class Main {
    public static void main(String[] args) throws IOException {
        // 首先创建管道链接
        InputStream fis = new FileInputStream("D:\\Idea\\Codeing\\Day01\\src\\test.txt");
        // 获取全部字节文件,并返回到一个新的字节数组
        byte[] bytes = fis.readAllBytes();

        String s = new String(bytes);
        System.out.println(s); // a中文
        fis.close();
    }
}
字符输入流
  • Reader抽象类表示,比较常用的实现类是FileReader

作用:

  • 以内存为基准,可以把文件中的数据以字符的形式读入到内存中去

首先要创建管道,链接磁盘和内存

构造器 说明
public FileReader(File file) 创建字符输入流管道与文件接通
public FileReader(String pathName) 创建字符输入流管道与源文件接通

然后就是方法进行写入

方法名 说明
public int read() 每次读取一个字符返回,如果发现没有数据可读会返回-1
public int read(char[] buffer) 每次用一个字符数组去读取数据,返回字符数组读取了多少个字符,如果发现没有数据可读会返回-1
读取一个字符
import java.io.FileReader;
import java.io.IOException;

public class Main {
    public static void main(String[] args) throws IOException {
        // 首先创建一个字符链接管道
        FileReader fr = new FileReader("D:\\Idea\\Codeing\\Day01\\src\\test.txt");
        // 然后开始读,一次读取一个字符
        int read = fr.read();
        System.out.println((char) fr.read());
        fr.close();
    }
}

上面的可以进行优化

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

public class Main {
    public static void main(String[] args) throws IOException {
        // 首先创建一个字符链接管道
        FileReader fr = new FileReader("D:\\Idea\\Codeing\\Day01\\src\\test.txt");
        // 然后开始读,一次读取一个字符
        int num;
        while ((num = fr.read()) != -1) {
            System.out.println((char) num);
        }
        fr.close();
    }
}
读取多个字符
import java.io.FileReader;
import java.io.IOException;

public class Main {
    public static void main(String[] args) throws IOException {
        // 首先创建一个字符链接管道
        FileReader fr = new FileReader("D:\\Idea\\Codeing\\Day01\\src\\test.txt");
        char[] chars = new char[3];
        // 读取多个字符
        int len; // 记住每次读取了多少个字符
        while ((len = fr.read(chars)) != -1) {
            System.out.print(new String(chars,0,len)); // 被复制的内容
        }
        fr.close();
    }
}
输出流
  • 然后按照文件的最小单位,又可以分为 字节输出流字符输出流
字节输出流
  • OutputStream抽象类表示,比较常用的实现类是FileOutputStream

作用:

  • 以内存为基准,把内存中的数据以字节的形式写出到文件中去

首先要创建管道,链接磁盘和内存

构造器 说明
public FileOutputStream(File file) 创建字节输出流管道与源文件接通
public FileOutputStream(String filePath) 创建字节输出流管道与源文件接通
public FileOutputStream(File file,boolean append) 创建字节输出流管道与源文件对象接通,可追加数据
public FileOutputStream(String filePath,boolean append) 创建字节输出流管道与源文件对象接通,可追加数据

然后就可以Java提供的方法进行读写了

方法名 说明
public void write(int a) 写一个字节出去
public void write(byte[] buffer) 写一个字节数组进去
public void write(byte[] buffer, int pos, int len) 写一个字节数组的一部分进去
public void close() throw IOException 关闭流

测试文件(空文件)

写入磁盘一个字节
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;

public class Main {
    public static void main(String[] args) throws IOException {
        // 首先创建一个内存到硬盘的管道
        OutputStream fos = new FileOutputStream("D:\\Idea\\Codeing\\Day01\\src\\test.txt");
        // 往磁盘中写入一个字节
        fos.write(97); // 97 ASCII 表示a
        fos.write('b'); // ''  单引号包裹的本来就是一个字节 byte类型
        fos.close();
    }
}

也是不能直接写入中文,因为在utf-8编码中,一个中文是占3个字节,这里是只写入一个字节。所以写入中文会乱码,但是我们可以通过写入一个字节数组,来进行添加中文,通过调用StringgetBytes()方法可以得到对应的字节数组

添加中文
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;

public class Main {
    public static void main(String[] args) throws IOException {
        // 首先创建一个内存到硬盘的管道
        OutputStream fos = new FileOutputStream("D:\\Idea\\Codeing\\Day01\\src\\test.txt");
        // 往磁盘中写入一个字节
        fos.write(97); // 97 ASCII 表示a
        fos.write('b'); // ''  单引号包裹的本来就是一个字节 byte类型
        // 添加中文 通过 String 的 getBytes() 方法可以直接得到一个字节数组
        byte[] bytes = "中文".getBytes();
        fos.write(bytes);
        fos.close();
    }
}
指定添加部分
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;

public class Main {
    public static void main(String[] args) throws IOException {
        // 首先创建一个内存到硬盘的管道
        OutputStream fos = new FileOutputStream("D:\\Idea\\Codeing\\Day01\\src\\test.txt");
        // 往磁盘中写入一个字节
        fos.write(97); // 97 ASCII 表示a
        fos.write('b'); // ''  单引号包裹的本来就是一个字节 byte类型
        // 添加中文 通过 String 的 getBytes() 方法可以直接得到一个字节数组
        byte[] bytes = "中文".getBytes();
        fos.write(bytes);
        // 当然,也可以指定添加的部分
        byte[] bytes2 = "添加前两个字".getBytes();
        fos.write(bytes2,0,6); // 一个中文是3个字节,所以添加前两个字,只需要添加6个字节
        fos.close();
    }
}

但是上面的添加,都会直接把原先文件里的东西全替代掉。有的时候,我们其实是不想让添加的内容把原先的内容替换掉的,所以就需要创建管道的时候指定是添加的(构造器的第二个参数为true)

后面追加内容

测试文件

文件原本的内容
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;

public class Main {
    public static void main(String[] args) throws IOException {
        // 首先创建一个内存到硬盘的管道,如果是追加内容,则需要把第二个参数设为true
        OutputStream fos = new FileOutputStream("D:\\Idea\\Codeing\\Day01\\src\\test.txt",true);
        // 往磁盘中写入一个字节
        fos.write(97); // 97 ASCII 表示a
        fos.write('b'); // ''  单引号包裹的本来就是一个字节 byte类型
        // 添加中文 通过 String 的 getBytes() 方法可以直接得到一个字节数组
        byte[] bytes = "中文".getBytes();
        fos.write(bytes);
        // 当然,也可以指定添加的部分
        byte[] bytes2 = "添加前两个字".getBytes();
        fos.write(bytes2,0,6); // 一个中文是3个字节,所以添加前两个字,只需要添加6个字节
        // 添加换行
        fos.write("\r\n".getBytes());
        fos.close();
    }
}

最后文件显示

文件原本的内容
ab中文添加
文件复制
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class Main {
    public static void main(String[] args) throws IOException {
        // 1. 首先创建一个输入流,把需要复制的内容用一个字节数组存储起来
        FileInputStream fis = new FileInputStream("D:\\Idea\\Codeing\\Day01\\src\\test.txt");
        // 1.1 创建一个存储字节的数组
        byte[] bytes = fis.readAllBytes();
        
        // 2. 把存起来的字符数组输出到指定的文件里去
        FileOutputStream fos = new FileOutputStream("D:\\Idea\\Codeing\\Day01\\src\\get.txt", true);
        fos.write(bytes);

        fis.close();
        fos.close();
        System.out.println("复制完成");
    }
}

但是上面这种,文件特别大的话,可能会出错。下面有一个更通用的方法

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

public class Main {
    public static void main(String[] args) throws IOException {
        // 1. 首先创建一个输入流,把需要复制的内容用一个字节数组存储起来
        FileInputStream fis = new FileInputStream("D:\\Idea\\Codeing\\Day01\\src\\test.txt");
        // 2. 把存起来的字符数组输出到指定的文件里去  这里后面不加true也是追加,因为同一个流不会覆盖
        FileOutputStream fos = new FileOutputStream("D:\\Idea\\Codeing\\Day01\\src\\get.txt", true);

        // 3. 创建一个字节数组,负责转移字节数据
        byte[] bytes = new byte[1024]; // 先给个1kb
        // 4. 然后开始循环写入
        int len; // 记录每次读取了多少个字节
        while ((len = fis.read(bytes)) != -1) {
            fos.write(bytes,0,len);
        }

        fis.close();
        fos.close();
        System.out.println("复制完成");
    }
}
字符输出流
  • Writer抽象类表示,比较常用的实现类是FileWriter
  • 字符输出流输出字符后,必须输出流,或者关闭流,写出去的数据才能生效

作用:

  • 以内存为基准,可以把内存中的数据以字符的形式输出到磁盘中去

首先要创建管道,链接磁盘和内存

构造器 说明
public FileWriter(File file) 创建字符输出流管道与源文件对象接通
public FileWriter(String pathName) 创建字符输出流管道与源文件路径接通
public FileWriter(File file,Boolean append) 创建字节输出流管道与源文件对象接通,可追加数据
public FileWriter(String fillpath,boolean append) 创建字节输出流管道与源文件路径接通,可追加数据

然后就是方法进行输出

方法名 说明
void write(int c) 每次写入一个字符,如果发现没有数据可写会返回-1
void write(String str) 写一个字符串
void write(String str, int off, int len) 写一个字符串的一部分
void write(char[] cbuf) 写入一个字符数组
void write(char[] cbuf, int off, int len) 写入字符数组的一部分
写入磁盘一个字符
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;

public class Main {
    public static void main(String[] args) {
        File f = new File("D:\\Idea\\Codeing\\Day01\\src\\test.txt");
        try(
                // 创建链接管道
                Writer fw = new FileWriter(f);
                ) {
            // 一次写入磁盘一个字符
            fw.write(97);
            fw.write('b');
            fw.write('中');
            // 或者写一个字符串
            fw.write("国");
            // 或者写入字符串的一部分
            fw.write("人123",0,1);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}
写入多个字符
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;

public class Main {
    public static void main(String[] args) {
        File f = new File("D:\\Idea\\Codeing\\Day01\\src\\test.txt");
        try(
                // 创建链接管道
                Writer fw = new FileWriter(f);
                ) {
            // 写入一个字符数组
            char[] chars = "1a中".toCharArray();
            fw.write(chars);
            // 直接写入一个字符串
            fw.write("几点开始");
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}
关闭流
  • 调用流的close方法即可关闭流的通道。但是有的时候,可能代码会出现一些问题,造成没法办法正常走到close方法,所以就需要错误捕获
tryCatch

try { // 可能会出现错误的代码 } catch (异常类名 变量名) { // 错误处理 } finally { // 不管成功还是失败都一定会执行的 }

  • 除非JVM终止(System.exit()),不然finally里的代码一定会执行的
  • 作用:一般用在程序执行完成后进行资源的释放操作
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Objects;

public class Main {
    public static void main(String[] args) {
        // 快捷键:选中代码 ctrl + alt + t
        FileInputStream fis = null;
        FileOutputStream fos = null;
        try {
            // 1. 首先创建一个输入流,把需要复制的内容用一个字节数组存储起来
            fis = new FileInputStream("D:\\Idea\\Codeing\\Day01\\src\\test.txt");
            // 2. 把存起来的字符数组输出到指定的文件里去  这里后面不加true也是追加,因为同一个流不会覆盖
            fos = new FileOutputStream("D:\\Idea\\Codeing\\Day01\\src\\get.txt", true);

            // 3. 创建一个字节数组,负责转移字节数据
            byte[] bytes = new byte[1024]; // 先给个1kb
            // 4. 然后开始循环写入
            int len; // 记录每次读取了多少个字节
            while ((len = fis.read(bytes)) != -1) {
                fos.write(bytes,0,len);
            }

            System.out.println("复制完成");
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            try {
                if(Objects.isNull(fis)) fis.close();
                if(Objects.isNull(fos)) fos.close();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }
}
tryWithResource
  • tryCatch更简单
  • 括号里面只能放资源对象,例如流对象,并且会自动关闭流
  • 资源都会实现一个AutoCloseable接口,并且资源都会有一个close方法,并且会自动调用

try(定义资源1; 定义资源2; …) { // 可能出现异常的代码; } carch(异常类名 变量名) { // 异常的处理代码; }

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

public class Main {
    public static void main(String[] args) {
        try(
                // 1. 首先创建一个输入流,把需要复制的内容用一个字节数组存储起来
                FileInputStream fis = new FileInputStream("D:\\Idea\\Codeing\\Day01\\src\\test.txt");
                // 2. 把存起来的字符数组输出到指定的文件里去  这里后面不加true也是追加,因为同一个流不会覆盖
                FileOutputStream fos = new FileOutputStream("D:\\Idea\\Codeing\\Day01\\src\\get.txt", true);
                ) {
            // 3. 创建一个字节数组,负责转移字节数据
            byte[] bytes = new byte[1024]; // 先给个1kb
            // 4. 然后开始循环写入
            int len; // 记录每次读取了多少个字节
            while ((len = fis.read(bytes)) != -1) {
                fos.write(bytes,0,len);
            }

            System.out.println("复制完成");
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}

缓冲流

  • 对原始流进行包装,以提高原始流读写数据的性能
字节缓冲输入流

测试数据

abcd

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;

public class Main {
    public static void main(String[] args) {
        File f = new File("D:\\IDEA\\code\\hello\\src\\test.txt");
        try(
                FileInputStream fis = new FileInputStream(f);
                InputStream bis = new BufferedInputStream(fis);
                ) {
            byte[] bytes = new byte[3];

            int len;
            while ((len = bis.read(bytes)) != -1) { // 不为-1说明就有值
                System.out.print(new String(bytes, 0, len)); // abcd
            }
        } catch (Exception e){
            e.printStackTrace(); //打印错误信息
        }
    }
}
字节缓冲输出流
import java.io.*;

public class Main {
    public static void main(String[] args) {
        File f = new File("D:\\IDEA\\code\\hello\\src\\test.txt");
        try(
                // 建立管道链接
                OutputStream os = new FileOutputStream(f,true);
                OutputStream bos = new BufferedOutputStream(os);
                ) {
            // 一次写入磁盘一个字节
            bos.write(97); // 97 ASCII中是a
            bos.write('b');
            // os.write('中'); // 添加中文是不行的,因为在utf8编码中,中文是3个字节,这里只添加一个,肯定不行
            // 一次写入磁盘多个字节
            byte[] bytes = "中国".getBytes();
            bos.write(bytes);
            // 一次写入指定个数的
            byte[] bytes1 = "abc".getBytes();
            bos.write(bytes1, 0, 2);
        } catch (Exception e){
            e.printStackTrace(); //打印错误信息
        }
    }
}
字符缓冲输入流
import java.io.*;

public class Main {
    public static void main(String[] args) {
        File f = new File("D:\\IDEA\\code\\hello\\src\\test.txt");
        try(
                // 建立管道链接
                Reader fr = new FileReader(f);
                Reader br =  new BufferedReader(fr);
                ) {
            // 1. 一次读取一个字符
            int r1 = br.read();
            System.out.println(r1); // 99 ASCII中表示c
            System.out.println((char) r1); // c
            // 2. 一次读取指定次数的字符
            char[] chars = new char[4];
            int len;
            while ((len = br.read(chars)) != -1) { // 只要不为-1,就说明还有值
                String s = new String(chars, 0, len);
                System.out.print(s); // eshishuju
            }
            // 3. 一次读取多个字符,并且指定位置和个数
            int r2 = br.read(chars, 0, 3);
            System.out.println(r2); // -1 因为上面读完了
        } catch (Exception e){
            e.printStackTrace(); // 打印错误信息
        }
    }
}
字符缓冲输出流
import java.io.*;

public class Main {
    public static void main(String[] args) {
        File f = new File("D:\\IDEA\\code\\hello\\src\\test.txt");
        try(
                // 建立管道链接
                Writer fw = new FileWriter(f);
                Writer bw =  new BufferedWriter(fw);
                ) {
            // 1. 一次写入磁盘一个字符
            bw.write(97);
            bw.write('b');
            bw.write('中');
            // 2. 一次写入一个字符数组
            char[] charArray = "你好呀".toCharArray();
            bw.write(charArray);
            // 3. 还可以直接写入字符串,这个就很方便呀
            bw.write("Java");
            // 4. 还可以指定起始位置和个数
            bw.write("Web",0,3);
        } catch (Exception e){
            e.printStackTrace(); // 打印错误信息
        }
    }
}

转换流

字符输入转换流
InputStreamReader
  • 解决不同编码时,字符读取文本内容乱码的问题
  • 解决思路:先获取文件的原始字节流,再按其真实的字符集编码转成字符输入流,这样字符输入流中的字符就不乱码了
构造器 说明
public inputStreamReader(InputStream is) 把原始的字节输入流,按照代码默认编码转成字符输入流(与直接用FileReader的效果一样)
public InputStreamReader(InputStream is,String charset) 把原始的字节输入流,按照指定字符集编码转成字符输入流

文件A GBK编码格式

中几点开始
import java.io.*;

public class Main {
    public static void main(String[] args) {
        File f = new File("D:\\Idea\\Codeing\\Day01\\src\\test.txt");
        try(
                Reader br = new BufferedReader(new FileReader(f));
                ) {
            // � 因为编译器默认编码是utf8,但是文件里面的编码是gbk 所以乱码了
            System.out.println((char) br.read());
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}

解决办法就是字符输入转换流

import java.io.*;

public class Main {
    public static void main(String[] args) {
        File f = new File("D:\\Idea\\Codeing\\Day01\\src\\test.txt");
        try(
                InputStream is = new FileInputStream(f);
                // 转成对应编码的字节输入流
                Reader isr = new InputStreamReader(is, "GBK");
                // 这样再操作就不会有乱码问题了
                Reader br = new BufferedReader(isr);
                ) {
            int c;
            while ((c = br.read()) != -1) {
                System.out.print((char) c);
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}
字符输出转换流
OutputStreamWrite
  • 可以控制写出去的字符使用什么字符集编码
  • 解决思路:获取字节输出流,再按照指定的字符集编码将其转换为字符输出流,以后写出去的字符就会用该字符集编码了
构造器 说明
public OutputStreamWriter(OutStream os) 可以把原始的字节输出流,按照代码默认编码转换成字符输出流
public OutputStreamWriter(OutputStream os,String charset) 可以把原始的字节输出流,按照指定字符集编码转成字符输出流

文件A GBK编码格式

import java.io.*;

public class Main {
    public static void main(String[] args) {
        File f = new File("D:\\Idea\\Codeing\\Day01\\src\\test.txt");
        try(
                Writer bw = new BufferedWriter(new FileWriter(f));
                ) {
            bw.write("你好呀"); // 啊浣犲ソ鍛�     乱码了,因为写入的是utf8编码的字符
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}

解决办法就是字符输出转换流

import java.io.*;

public class Main {
    public static void main(String[] args) {
        File f = new File("D:\\Idea\\Codeing\\Day01\\src\\test.txt");
        try(
                OutputStream fos = new FileOutputStream(f, true);
                // 把原始的字节输入流转成固定格式的输出流
                Writer psw = new OutputStreamWriter(fos, "GBK");
                Writer bw = new BufferedWriter(psw);
                ) {
            bw.write("你好呀"); // 啊你好呀
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}
控制写的字符使用的字符集

有两种实现方法:

  1. 调用String提供的getBytes方法

String d = “你好呀”; byte[] bytes = d.getBytes(“GBK”);

  1. 使用字符输出转换流实现

打印流

  • 打印流可以实现更方便,更高效的打印数据出去,能实现打印啥出去就是啥
printStream
  • 属于字节输出流
构造器 说明
public PrintStream(OutputStream/File/String) 打印流直接通向字节输出流/文件/文件路径
public PrintStream(String fileName,Charset charset) 可以指定写出去的字符编码
public PrintStream(OutputStream out, boolean autoFlush) 可以指定实现自动刷新
public PrintStream(OutputStream out, bollean autoFlush, String encoding) 可以指定实现自动刷新,并可指定字符的编码

写数据的方法

方法名 说明
public void println(Xxx xx) 打印任意类型的数据出去,自带换行
public void write(int/byte[]/byte[]的一部分) 可以支持写字节数据进去

基本使用

import java.io.*;

public class Main {
    public static void main(String[] args) {
        File f = new File("D:\\Idea\\Codeing\\Day01\\src\\test.txt");
        try(
                PrintStream ps = new PrintStream(f);
                ) {
            ps.println(97);
            ps.println('b');
            ps.println("你好呀"); // 97 \n b \n 你好呀
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}

指定字符集

import java.io.*;
import java.nio.charset.Charset;

public class Main {
    public static void main(String[] args) {
        File f = new File("D:\\Idea\\Codeing\\Day01\\src\\test.txt");
        try(
                // 指定写入的编码格式
                PrintStream ps = new PrintStream(f, Charset.forName("GBK"));
                ) {
            ps.println(97);
            ps.println('b');
            ps.println("你好呀"); // 97 \n b \n 你好呀
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}
PrintWriter
  • 属于字符输出流
构造器 说明
public PrintWriter(OutputStream/Writer/File/String) 打印流直接通向字节输出流/字符输出流/文件/文件路径
public PrintWriter(String fileName,Charset charset) 可以指定写出去的字符编码
public PrintWriter(OutputStream out/Writer, boolean autoFlush) 可以指定实现自动刷新
public PrintWriter(OutputStream out, bollean autoFlush, String encoding) 可以指定实现自动刷新,并可指定字符的编码

写数据的方法

方法名 说明
public void println(Xxx xx) 打印任意类型的数据出去,自带换行
public void write(int/String/char[]) 可以支持写字符数据出去

基本使用

import java.io.*;

public class Main {
    public static void main(String[] args) {
        File f = new File("D:\\Idea\\Codeing\\Day01\\src\\test.txt");
        try(
                PrintWriter pw = new PrintWriter(f);
        ) {
            pw.println(97);
            pw.println('a');
            pw.println("你好呀"); // 97 \n b \n 你好呀
        } catch (IOException e) {
            throw new RuntimeException(e); 
        }
    }
}

指定字符集

import java.io.*;

public class Main {
    public static void main(String[] args) {
        File f = new File("D:\\Idea\\Codeing\\Day01\\src\\test.txt");
        try(
                PrintWriter pw = new PrintWriter(f,"GBK");
        ) {
            pw.println(97);
            pw.println('a');
            pw.println("你好呀");
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}
追加数据
  • 因为PrintStreamPrintWriter是高级流,所以不能直接追加数据,但是我们在调用构造器的时候,可以传入一个低级输出流,在低级输出流中可以指定追加
import java.io.*;

public class Main {
    public static void main(String[] args) {
        File f = new File("D:\\Idea\\Codeing\\Day01\\src\\test.txt");
        try(
                PrintWriter pw = new PrintWriter(new FileOutputStream(f, true));
        ) {
            pw.println(97);
            pw.println('a');
            pw.println("你好呀");
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}
区别
  • 打印数据的功能上一摸一样:都是使用方便,性能高效
  • PrintStream继承字节输出流OutputStream,因此支持字节数据的方法
  • PrintWriter继承字符输出流Writer,因此支持字符数据的方法
应用

输出语句的重定向,可以把输出语句的打印位置改到某个文件中去

import java.io.*;

public class Main {
    public static void main(String[] args) {
        System.out.println("会打印文件外");
        File f = new File("D:\\Idea\\Codeing\\Day01\\src\\test.txt");
        try(
                // PrintWriter pw = new PrintWriter(new FileOutputStream(f));
                PrintStream ps = new PrintStream(f);
        ) {
            // 把系统默认的打印流对象改成自己设置的打印流
            System.setOut(ps);

            System.out.println("会打印在文件里");
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}

数据流

  • 操作的都是字节的
DataOutputStream
  • 属于字节输出流,允许把数据和其类型一并写出去
构造器 说明
public DataOutputStream(OutputStream out) 创建新数据输出流包装基本的字节输出流

方法

方法 说明
public final void writeByte(int v) throws IOException 将byte类型的数据写入基本的字节输出流
public final void writeInt(int v) throws IOException 将Int类型的数据写入基本的字节输出流
public final void writeDouble(Double v) throws IOException 将Double类型的数据写入基本的字节输出流
public final void writeUtf(String str) throws IOException 将字符串数据以UTF-8编码字节写入基本的字节输出流
public void write(int/byte[]/byte[]的一部分) 支持写字节数据出去

案例

import java.io.*;

public class Main {
    public static void main(String[] args) {
        File f = new File("D:\\Idea\\Codeing\\Day01\\src\\test.txt");
        try(
                OutputStream fps = new FileOutputStream(f);
                // 写入数据和其类型,就需要用到数据流
                DataOutputStream dos = new DataOutputStream(fps);
        ) {
            // 写入一个字节
            dos.writeByte(97); // a
            // 写入一个数字
            dos.writeInt(97); // 97
            // 写入一个布尔
            dos.writeBoolean(true);
            // 写入一个小数
            dos.writeDouble(99.99);
            // 写入一个字符串
            dos.writeUTF("你好呀"); // a   a@X�\(� 	你好呀    这不是乱码,是有类型就这样
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}
DataInputStream
  • 属于字节输入流,用于读取数据输出流写出去的数据
构造器 说明
public DataInputStream(InputStream is) 创建新数据输入流包装基本的字节输出流

方法

方法 说明
public final void readByte() throws IOException 读取字节数据返回
public final int readInt() throws IOException 读取int类型的数据返回
public final double readDouble() throws IOException 读取double类型的数据返回
public final String readUtf() throws IOException 读取字符串数据返回(UTF-8)
public int readInt() / public int read(byte[]) 支持读字节数据进来

案例:读的顺序要跟写的顺序一样

import java.io.*;

public class Main {
    public static void main(String[] args) {
        File f = new File("D:\\Idea\\Codeing\\Day01\\src\\test.txt");
        try(
             InputStream fis = new FileInputStream(f);
             // 我们需要读取带有值和类型的,所以就需要用到数据输入流
             DataInputStream dis = new DataInputStream(fis);
        ) {
            // System.out.println(dis.read()); // 97
            System.out.println(dis.readByte()); // 97
            System.out.println(dis.readInt()); // 97
            System.out.println(dis.readBoolean()); // true
            System.out.println(dis.readDouble()); // 99.99
            System.out.println(dis.readUTF()); // 你好呀
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}

序列化流

  • 我们想把Java对象存进文件去,就需要用到对象序列化流
  • 则反序列化就是把存进去的对象序列化读取出来
  • 属于字节流
ObjectOutputStream
  • 可以把Java对象进行序列化:把Java对象存入到文件中去
  • transient修饰的成员可以不参与对象序列化
构造器 说明
public ObjectOutputStream(OutputStream out) 创建对象字节输出流,包装基础的字节输出流

方法

方法名 说明
public final void writeObject(Object o) throws IOException 把对象写出去

案例

Student.java

package Demo;
import java.io.Serializable;

// 注意:对象如果需要序列化,必须实现序列化接口
public class Student implements Serializable {
    private String name;
    private int age;
    // private double score;
    // transient 修饰,则这个成员变量将不参与序列化(因为有的时候,其实不想要某个属性展示的,例如密码)
    private transient double score;

    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 double getScore() {
        return score;
    }

    public void setScore(double score) {
        this.score = score;
    }

    public Student() {
    }

    public Student(String name, int age, double score) {
        this.name = name;
        this.age = age;
        this.score = score;
    }

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

Main.java

import Demo.Student;
import java.io.*;

public class Main {
    public static void main(String[] args) {
        Student zs = new Student("张三", 25, 100.00);

        File f = new File("D:\\Idea\\Codeing\\Day01\\src\\test.txt");
        try(
             OutputStream fos = new FileOutputStream(f);
             // 我们要写入对象,就需要用到对象序列化流
             ObjectOutputStream ois = new ObjectOutputStream(fos);
        ) {
            ois.writeObject(zs);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}
ObjectInputStream
  • 可以把Java对象进行反序列化:把存储在文件中的Java对象读入到内存中来
构造器 说明
public ObjectInputStream(InputStream is) 创建对象字节输入流,包装基础的字节输入流

方法

方法名 说明
public final Object readObject() 把存储在文件中的Java对象读出来

案例

import Demo.Student;
import java.io.*;

public class Main {
    public static void main(String[] args) {
        File f = new File("D:\\Idea\\Codeing\\Day01\\src\\test.txt");
        try(
                InputStream fis = new FileInputStream(f);
                // 我们想要读取存储在文件中的对象,就需要对象反序列化
                ObjectInputStream ois = new ObjectInputStream(fis);
        ) {
            Student s1 = (Student) ois.readObject();
            System.out.println(s1);//Student{name='张三', age=25, score=0.0} 这里score并不参与序列化,所以为初始值
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
实现一次多个对象序列化
  • 用一个ArrayList集合存储多个对象,然后直接对集合进行序列化即可
  • ArrayList集合已经实现了序列化接口

Student.java

package Demo;
import java.io.Serializable;

// 注意:对象如果需要序列化,必须实现序列化接口
public class Student implements Serializable {
    private String name;
    private int age;
    // private double score;
    // transient 修饰,则这个成员变量将不参与序列化(因为有的时候,其实不想要某个属性展示的,例如密码)
    private transient double score;

    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 double getScore() {
        return score;
    }

    public void setScore(double score) {
        this.score = score;
    }

    public Student() {
    }

    public Student(String name, int age, double score) {
        this.name = name;
        this.age = age;
        this.score = score;
    }

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

写入到磁盘

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

public class Main {
    public static void main(String[] args) {
        Student zs = new Student("张三", 25, 100.00);
        Student ls = new Student("李四", 26, 100.00);
        ArrayList<Student> students = new ArrayList<>();
        students.add(zs);
        students.add(ls);

        File f = new File("D:\\Idea\\Codeing\\Day01\\src\\test.txt");
        try(
                OutputStream fos = new FileOutputStream(f);
                // 我们要写入对象,就需要用到对象序列化流
                ObjectOutputStream ois = new ObjectOutputStream(fos);
        ) {
            // 把整个ArrayList集合对象写进去,方便我们遍历集合去读
            ois.writeObject(students);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}

读取磁盘

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

public class Main {
    public static void main(String[] args) {
        File f = new File("D:\\Idea\\Codeing\\Day01\\src\\test.txt");
        try(
                InputStream fis = new FileInputStream(f);
                // 我们想要读取存储在文件中的对象,就需要对象反序列化
                ObjectInputStream ois = new ObjectInputStream(fis);
        ) {
            ArrayList<Student> students = (ArrayList<Student>) ois.readObject();
            System.out.println(students); // [Student{name='张三', age=25, score=0.0}, Student{name='李四', age=26, score=0.0}]
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

IO框架

  • 框架就是解决某类问题,编写的一套类,接口等,可以理解成一个半成品,大多数框架都是第三方研发的

形式:

一般都是把类,接口等编译成class形式,再压缩成一个.jar结尾的文件发行出去

IO框架封装了Java提供的对文件,数据进行操作的代码,对外提供了更简单的方式来对文件进行操作,对数据进行读写等

Commons-io
  • apache开源基金组织提供的一组有关IO操作的小框架,目的是提高IO流的开发效率

FileUtils类

FileUtils类提供的部分方法展示 说明
public static void copyFile(File srcFile,File destFile) 复制文件
public static void copyDirectory(File srcDir,File destDir) 复制文件夹
public static void deleteDirectory(File directory) 删除文件夹
public static String readFileToString(File file,String encoding) 读数据
public static void writeStringToFile(File file,String data,String charname,boolean append) 写数据
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.nio.charset.Charset;

public class Main {
    public static void main(String[] args) {
        // FileUtils
        try {
            // 拷贝文件
            FileUtils.copyFile(new File("D:\\Idea\\Codeing\\Day01\\src\\in.txt"),new File("D:\\Idea\\Codeing\\Day01\\src\\out.txt"));
            // 拷贝文件夹
            FileUtils.copyDirectory(new File("C:\\Users\\huihui\\Desktop\\in"),new File("C:\\Users\\huihui\\Desktop\\out"));
            // 删除文件夹(非空的也可以)
            FileUtils.deleteDirectory(new File("C:\\Users\\huihui\\Desktop\\out"));
            // 读数据
            String s = FileUtils.readFileToString(new File("D:\\Idea\\Codeing\\Day01\\src\\in.txt"));
            System.out.println(s); // 111 不推荐使用这个了
            // 写数据 不推荐使用这个了
            FileUtils.writeStringToFile(new File("D:\\Idea\\Codeing\\Day01\\src\\in.txt"), "添加进去");
            // 追加数据
            FileUtils.writeStringToFile(new File("D:\\Idea\\Codeing\\Day01\\src\\in.txt"),"追加的数据", Charset.forName("UTF-8"),true);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

IOUtils类

IOUtils类提供的部分方法展示 说明
public static int copy(InputStream inputStream,outputStream outputStream) 复制文件
public static int copy(Reader reader,Writer writer) 复制文件
public static void write(String data,OutputStream output,String charsetName) 写数据

其实Java原生也提供了一些一行代码操作文件的处理(但是都没有成功,不知道为什么)

import java.nio.file.Files;
import java.nio.file.Path;

public class Main {
    public static void main(String[] args) {
        try {
            // 拷贝文件
            // Files.copy(Path.of("Day01\\src\\in.txt"),Path.of("Day01\\src\\out.txt"));
            // 读文件
            // System.out.println(Files.readString(Path.of("Day01\\\\src\\\\in.txt"))); //
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

你可能感兴趣的:(java,学习,笔记)