Io基础

1.文件

1.1基础知识

区分输入输出流 针对内存

  • 流:数据在数据源(文件)和程序(内存)之间经历的路径
  • 输入流:数据从数据源(文件)到程序(内存)的路径
  • 输出流:数据从程序(内存)到数据源(文件)的路径

img

1.2创建文件

  • new File(String pathname)//根据路径构建一个File对象
  • new File(File parent,String child)//根据父目录文件+子路径构建
  • new File(String parent,String child)//根据父目录+子路径构建
  • createNewFile() 创建新文件

注意:new File只是在java内存中产生文件,真正在磁盘中产生还得使用file.createNewFile()
代码案例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
package com.zjh;

import org.junit.jupiter.api.Test;

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

public class FileCreate {
    public static void main(String[] args) {

    }
    //方式1 new File(String pathname)
    @Test
    public void create01() {
        String filePath = "d:\\news1.txt";
        File file = new File(filePath);

        try {
            file.createNewFile();
            System.out.println("文件创建成功");
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
    //方式2 new File(File parent,String child) //根据父目录文件+子路径构建
    //e:\\news2.txt
    @Test
    public  void create02() {
        File parentFile = new File("d:\\");
        String fileName = "news2.txt";
        //这里的file对象,在java程序中,只是一个对象
        //只有执行了createNewFile 方法,才会真正的,在磁盘创建该文件
        File file = new File(parentFile, fileName);

        try {
            file.createNewFile();
            System.out.println("创建成功~");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    //方式3 new File(String parent,String child) //根据父目录+子路径构建
    @Test
    public void create03() {
        //String parentPath = "e:\\";
        String parentPath = "d:\\";
        String fileName = "news4.txt";
        File file = new File(parentPath, fileName);

        try {
            file.createNewFile();
            System.out.println("创建成功~");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

1.3获取文件信息

基本api

  • getName 文件名
  • getAbsolutePath 绝对路径
  • getParent 父目录
  • length 长度
    • utf-8下 一个英文字符1字节,1个汉字3字节
  • exists 是否存在
  • isFile 是否文件
  • isDirectory 是否目录
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package com.zjh;

import org.junit.jupiter.api.Test;

import java.io.File;

public class FileInformation {
    public static void main(String[] args) {

    }
    //获取文件的信息
    @Test
    public void info() {
        //先创建文件对象
        File file = new File("d:\\news1.txt");

        //调用相应的方法,得到对应信息
        System.out.println("文件名字=" + file.getName());
        //getName、getAbsolutePath、getParent、length、exists、isFile、isDirectory
        System.out.println("文件绝对路径=" + file.getAbsolutePath());
        System.out.println("文件父级目录=" + file.getParent());
        System.out.println("文件大小(字节)=" + file.length());
        System.out.println("文件是否存在=" + file.exists());//T
        System.out.println("是不是一个文件=" + file.isFile());//T
        System.out.println("是不是一个目录=" + file.isDirectory());//F


    }
}

1.4目录操作

  • mkdir创建一级目录
  • mkdirs创建多级目录
  • delete删除空目录或文件(delete删除一个目录时,需要确保该目录下面没有文件或者子目录,否则需要先删除文件和字目录)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
package com.zjh;

import org.junit.jupiter.api.Test;

import java.io.File;

public class Directory_ {
    //判断 d:\\news1.txt 是否存在,如果存在就删除
    @Test
    public void m1() {

        String filePath = "d:\\news1.txt";
        File file = new File(filePath);
        if (file.exists()) {
            if (file.delete()) {
                System.out.println(filePath + "删除成功");
            } else {
                System.out.println(filePath + "删除失败");
            }
        } else {
            System.out.println("该文件不存在...");
        }

    }

    //判断 D:\\demo02 是否存在,存在就删除,否则提示不存在
    //这里我们需要体会到,在java编程中,目录也被当做文件
    @Test
    public void m2() {

        String filePath = "D:\\demo02";
        File file = new File(filePath);
        if (file.exists()) {
            if (file.delete()) {
                System.out.println(filePath + "删除成功");
            } else {
                System.out.println(filePath + "删除失败");
            }
        } else {
            System.out.println("该目录不存在...");
        }

    }

    //判断 D:\\demo\\a\\b\\c 目录是否存在,如果存在就提示已经存在,否则就创建
    @Test
    public void m3() {
        String directoryPath = "D:\\demo\\a\\b\\c";
        File file = new File(directoryPath);
        if (file.exists()) {
            System.out.println(directoryPath + "存在..");
        } else {
            if (file.mkdirs()) { //创建一级目录使用mkdir() ,创建多级目录使用mkdirs()
                System.out.println(directoryPath + "创建成功..");
            } else {
                System.out.println(directoryPath + "创建失败...");
            }
        }
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
    
public static void main(String[] args) {
        String filePath = "D:\\demo02";
        File file = new File(filePath);
        deleteAll(file);
    }
//递归删除非空目录
    public static void deleteAll(File file){
        if (file.exists()) {
            File files[] = file.listFiles();
            int len = files.length;
            for (int i = 0; i < len; i++) {
                if (files[i].isDirectory()) {
                    deleteAll(files[i]);
                } else {
                    files[i].delete();
                }
            }
            file.delete();
        }
    }

2.IO流原理及分类

2.1IO流原理

img

  • input:读取外部数据到内存(程序)
  • output:将内存(程序)数据输出到外部储存设备中

作用:读写文件、网络通讯

2.2流的分类

  1. 按操作数据单位不同分为:字节流(8 bit)【二进制文件】,字符流(按字符)【文本文件】

注意:处理文本用字符流,而处理图片、视频等二进制文件最好使用字节流,否则可能会造成损失

  1. 按数据流的流向不同分为:输入流,输出流
  2. 按流的角色的不同分为:节点流,处理流(包装/装饰流)

四个基本抽象基类

字节流 字符流
输入流 Inputstream Reader
输出流 Onputstream Writer

派生子类一般都以父类名作为子类后缀
总览:

img

3.文件操作流

3.1FileInputStream和FileOutputStream

  • FileInputStream:文件输入流
  • BufferedInputStream:缓存字节输入流
  • ObjectedInputStream:对象字节输入流

img

3.1.1FileInputStream

主要方法:
创建:

  • FileInputStream(String FilePath)
  • FileInputStream(File file)

读取:

  • public int read()
    • 返回读取的字节
    • 一次读取一个字节,效率低
    • 可以转化为char显示字符
    • 读取中文会乱码(3字节)
  • public int read(byte[] b)
    • 一次读取一个字节数组长度的数据
    • 返回读取的字节数量
    • 读取中文如果是截断读取会乱码,如果刚好都在一个字节数组中读取出来,才不会乱码
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
package com.zjh;

import org.junit.jupiter.api.Test;

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

public class FileInputStream_ {
    public static void main(String[] args) {
    }

    /**
     * 演示读取文件...
     * 单个字节的读取,效率比较低
     * -> 使用 read(byte[] b)
     */
    @Test
    public void readFile01() {
        String filePath = "C:\\Users\\Mono\\Desktop\\a.txt";
        int readData = 0;
        FileInputStream fileInputStream = null;
        try {
            //创建 FileInputStream 对象,用于读取 文件
            fileInputStream = new FileInputStream(filePath);
            //从该输入流读取一个字节的数据。 如果没有输入可用,此方法将阻止。
            //如果返回-1 , 表示读取完毕
            while ((readData = fileInputStream.read()) != -1) {
                System.out.print((char)readData);//转成char显示
            }

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //关闭文件流,释放资源.
            try {
                fileInputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }

    /**
     * 使用 read(byte[] b) 读取文件,提高效率
     */
    @Test
    public void readFile02() {
        String filePath = "C:\\Users\\Mono\\Desktop\\a.txt";
        //字节数组
        byte[] buf = new byte[8]; //一次读取8个字节.中文截断 byte[30]则完整输出
        int readLen = 0;
        FileInputStream fileInputStream = null;
        try {
            //创建 FileInputStream 对象,用于读取 文件
            fileInputStream = new FileInputStream(filePath);
            //从该输入流读取最多b.length字节的数据到字节数组。 此方法将阻塞,直到某些输入可用。
            //如果返回-1 , 表示读取完毕
            //如果读取正常, 返回实际读取的字节数
            while ((readLen = fileInputStream.read(buf)) != -1) {
                System.out.print(new String(buf, 0, readLen));//显示
            }

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //关闭文件流,释放资源.
            try {
                fileInputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }
}

3.1.2FileOutputStream

主要方法:
创建:

  • FileOutputStream(String FilePath , boolean append)
  • FileOutputStream(File file , boolean append)

append为true表示追加模式,为false为覆盖,默认false
写入:当文件不存在,会自动创建(前提是目录存在)

  • write(int b) 写入单字节
  • write(byte b) 写入字符数组 可以用String.getbytes()将字符串转为字符数组放入
  • write(byte b,int off ,int len) off起始位置 len长度
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
package com.zjh;

import org.junit.jupiter.api.Test;

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

public class FileOutputStream_ {
    public static void main(String[] args) {

    }
    @Test
    public void writeFile() {
        //创建 FileOutputStream对象
        String filePath = "d:\\a.txt";
        FileOutputStream fileOutputStream = null;
        try {
            //得到 FileOutputStream对象 对象
            //老师说明
            //1. new FileOutputStream(filePath) 创建方式,当写入内容是,会覆盖原来的内容
            //2. new FileOutputStream(filePath, true) 创建方式,当写入内容是,是追加到文件后面
            fileOutputStream = new FileOutputStream(filePath, true);
            //写入一个字节
//            fileOutputStream.write('H');//
            //写入字符串
            String str = "hsp,world!";
            //str.getBytes() 可以把 字符串-> 字节数组
            fileOutputStream.write(str.getBytes());
            /*
            write(byte[] b, int off, int len) 将 len字节从位于偏移量 off的指定字节数组写入此文件输出流
             */
//            fileOutputStream.write(str.getBytes(), 0, 3);

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                fileOutputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

3.1.3文件复制

注意:要使用fileOutputStream.write(buf,0,readLen);,否则对于图片等文件可能会导致写入多余文件而失效

  • 边读取边写入
  • 先关闭输入流再关闭输出流,流判空
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
package com.zjh;

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

public class FileCopy {
    public static void main(String[] args) {
        FileInputStream fileInputStream = null;
        FileOutputStream fileOutputStream = null;
        String filePath = "d:\\pic.png";
        String descPath = "d:\\pic2.png";
        try {
            fileInputStream = new FileInputStream(filePath);
            fileOutputStream = new FileOutputStream(descPath);
            byte[] buf = new byte[1024];
            int readLen  = 0;
            while((readLen = fileInputStream.read(buf)) != -1){
                fileOutputStream.write(buf,0,readLen);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                if(fileInputStream != null){
                    fileInputStream.close();
                }
                if(fileOutputStream != null){
                    fileOutputStream.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }

        }
}
}

3.2FileReader&FileWriter

字符流可以方便处理字符文件

3.2.1FileReader

创建
FileReader(String/File)
读取

  • public int read()
    • 返回读取的字符
    • 一次读取一个字符,效率低
    • 可以转化为char显示字符
  • public int read(char[] b,int off ,int len)
    • 一次读取一个字符数组长度的数据
    • 返回读取的字符数量
    • 起始off下标 ,len开始
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
package com.zjh;

import org.junit.jupiter.api.Test;

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

public class FileReader_ {
    public static void main(String[] args) {

    }
    /**
     * 单个字符读取文件
     */
    @Test
    public void readFile01() {
        String filePath = "d:\\a.txt";
        FileReader fileReader = null;
        int data = 0;
        //1. 创建FileReader对象
        try {
            fileReader = new FileReader(filePath);
            //循环读取 使用read, 单个字符读取
            while ((data = fileReader.read()) != -1) {
                System.out.print((char) data);
            }

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (fileReader != null) {
                    fileReader.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 字符数组读取文件
     */
    @Test
    public void readFile02() {
        System.out.println("~~~readFile02 ~~~");
        String filePath = "d:\\a.txt";
        FileReader fileReader = null;

        int readLen = 0;
        char[] buf = new char[8];
        //1. 创建FileReader对象
        try {
            fileReader = new FileReader(filePath);
            //循环读取 使用read(buf), 返回的是实际读取到的字符数
            //如果返回-1, 说明到文件结束
            while ((readLen = fileReader.read(buf)) != -1) {
                System.out.print(new String(buf, 0, readLen));
            }

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (fileReader != null) {
                    fileReader.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

3.2.2FileWriter

注意一定要close( ),或者flush( )进行,否则无法写入文件

  • close( )、flush( )的底层都使用了OutPutStream的write( )方法写入
  • close = flush + 关闭
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
package com.zjh;

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

public class FileWriter_ {
    public static void main(String[] args) {
        String filePath = "d:\\note.txt";
        //创建FileWriter对象
        FileWriter fileWriter = null;
        char[] chars = {'a', 'b', 'c'};
        try {
            fileWriter = new FileWriter(filePath);//默认是覆盖写入
//            3) write(int):写入单个字符
            fileWriter.write('H');
////            4) write(char[]):写入指定数组
            fileWriter.write(chars);
////            5) write(char[],off,len):写入指定数组的指定部分
            fileWriter.write("韩顺平教育".toCharArray(), 0, 3);
//            6) write(string):写入整个字符串
            fileWriter.write(" 你好北京~");
            fileWriter.write("风雨之后,定见彩虹");
////            7) write(string,off,len):写入字符串的指定部分
            fileWriter.write("上海天津", 0, 2);
            //在数据量大的情况下,可以使用循环操作.


        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                //fileWriter.flush();
                //关闭文件流,等价 flush() + 关闭
                fileWriter.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        System.out.println("程序结束...");
    }
}

文件不存在会创建文件(前提:目录存在)
String.toCharArray()

4.节点流与处理流

总览

分类 字节输入 字节输出 字符输入 字符输出
抽象基类 InputStream OutputStream Reader Writer
访问文件 FileInputStream FileOutputStream FileReader FileWriter
访问数组 ByteArrayInputStream ByteArrayOutputStream CharArrayReader CharArrayWriter
访问管道 PipedInputStream PipedOutputStream PipedReader PipedWriter
访问字符串 StringReader StringWriter
以下是处理流
缓存流 BufferedInputStream BufferedOutputStream BufferedReader BufferedWriter
转换流
InputStreamReader OutputStreamReader
对象流 ObjectInputStream ObjectOutputStream
过滤流 FilterInputStream FilterOutputStream
打印流 PrintStream PrintWriter
推回输入流 PushbackInputStream PushbackInputReader
特殊流 DataInputStream DataOutputStream

4.1节点流

节点流:直接与数据源相连,读入或读出。
直接使用节点流,读写不方便,为了更快的读写文件,才有了处理流。

4.1.1常用节点流

  • 父 类 :InputStream 、OutputStream、 Reader、 Writer
  • 文 件 :FileInputStream 、 FileOutputStream 、FileReader 、FileWriter 文件进行处理的节点流
  • 数 组 :ByteArrayInputStream、 ByteArrayOutputStream、 CharArrayReader 、CharArrayWriter 对数组进行处理的节点流(对应的不再是文件,而是内存中的一个数组)
  • 字符串 :StringReader、 StringWriter 对字符串进行处理的节点流
  • 管 道 :PipedInputStream 、PipedOutputStream 、PipedReader 、PipedWriter 对管道进行处理的节点流

4.2处理流

处理流和节点流一块使用,在节点流的基础上,再套接一层,套接在节点流上的就是处理流。如BufferedReader处理流的构造方法总是要带一个其他的流对象做参数。一个流对象经过其他流的多次包装,称为流的链接。装饰者模式
注意:使用处理流时只需要关闭处理流,不需要手动关闭节点流,处理流close底层默认会关闭节点流

4.2.1常用处理流

  • 缓冲流:BufferedInputStream 、BufferedOutputStream、 BufferedReader、 BufferedWriter 增加缓冲功能,避免频繁读写硬盘。
  • 转换流:InputStreamReader 、OutputStreamReader实现字节流和字符流之间的转换。
  • 数据流: DataInputStream 、DataOutputStream 等-提供将基础数据类型写入到文件中,或者读取出来。

5.缓冲流

5.1BufferedReader&BufferedWriter

5.1.1BufferedReader

创建:
BufferedReader (Reader in)
创建使用默认人小的输入缓冲区的缓冲字符输入流。
读取:
public String readLine()
读取到末尾则返回null

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
package com.zjh;

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

public class BufferReader_ {
    public static void main(String[] args) throws IOException {
        String filePath = "d:\\a.txt";
        //创建bufferedReader
        BufferedReader bufferedReader = new BufferedReader(new FileReader(filePath));
        //读取
        String line; //按行读取, 效率高
        //说明
        //1. bufferedReader.readLine() 是按行读取文件
        //2. 当返回null 时,表示文件读取完毕
        while ((line = bufferedReader.readLine()) != null) {
            System.out.println(line);
        }
        bufferedReader.close();
    }
}

5.1.2BufferedWriter

创建:
BufferedWriter (Writer out)
写入:

  • write(int b)
  • write(char b,int off ,int len)
  • write(string b,int off ,int len)
  • newLine();
    • 使用平台自己的系统属性line.separator定义的行分隔符概念。并非所有平台都使用换行符('\ n')来终止行。因此,调用此方法来终止每个输出行,因此优选直接写入换行符
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package com.zjh;

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

public class BufferedWriter_ {
    public static void main(String[] args) throws IOException {
        String filePath = "d:\\ok.txt";
        //创建BufferedWriter
        //说明:
        //1. new FileWriter(filePath, true) 表示以追加的方式写入
        //2. new FileWriter(filePath) , 表示以覆盖的方式写入
        BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(filePath));
        bufferedWriter.write("hello, 韩顺平教育!");
        bufferedWriter.newLine();//插入一个和系统相关的换行
        bufferedWriter.write("hello2, 韩顺平教育!");
        bufferedWriter.newLine();
        bufferedWriter.write("hello3, 韩顺平教育!");
        bufferedWriter.newLine();

        //说明:关闭外层流即可 , 传入的 new FileWriter(filePath) ,会在底层关闭
        bufferedWriter.close();
    }
}

5.1.3文本文件拷贝

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package com.zjh.Buffer;

import java.io.*;

public class BufferCopy {
    public static void main(String[] args) {
        String fromPath = "d:\\a.txt";
        String toPath = "d:\\a2.txt";
        BufferedReader br = null;
        BufferedWriter bw = null;
        try {
            br = new BufferedReader(new FileReader(fromPath));
            bw = new BufferedWriter(new FileWriter(toPath));
            String line;
            while ((line = br.readLine()) != null){
                bw.write(line);
                bw.newLine();
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if(br != null){
                    br.close();
                }
                if(bw != null){
                    bw.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }
}

5.2BufferedInputStream & BufferedOutputStream

API基本与FileInputStream&FileOutputStream没啥区别,这里省略

5.2.1二进制文件拷贝

可以看到写法基本与上文3.1.3一致,但是效率会更高

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package com.zjh.Buffer;

import java.io.*;

public class BufferStreamCopy {
    public static void main(String[] args) {
        String fromPath = "d:\\pic.png";
        String toPath = "d:\\pic2.png";
        BufferedInputStream bis = null;
        BufferedOutputStream bos = null;
        try {
            bis = new BufferedInputStream(new FileInputStream(fromPath));
            bos = new BufferedOutputStream(new FileOutputStream(toPath));
            byte[] buf = new byte[1024];
            int readLen  = 0;
            while ((readLen = bis.read(buf)) != -1){
                bos.write(buf,0,readLen);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if(bis != null){
                    bis.close();
                }
                if(bos != null){
                    bos.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

6.对象流

需求:将int num=100数据存放到文件中,读出时可以知道数据内容以及数据类型

  • 序列化就是在保存数据时,保存数据的值和数据类型
  • 反序列化就是在恢复数据时,恢复数据的直和数据类型

需要让某个对象支持序列化机制,则必须让其类是可序列化的,为了让某个类是可序列化的,该类必须实现如下两个接口之一:

  • Serializable 这是一个标记接口 【推荐】 空接口,无方法
  • Externalizable 用于自定义哪些属性的序列化

6.1ObjectOutStream

使用ObjectOutputStream序列化基本数据类型和一个Dog对象(name, age),并保存到data.dat 文件中
注意:

  1. 建议在序列化的类中添加serialVersionUID,表示序列化的版本号,可以提高兼容性,即在版本升级时反序列化仍保持对象的唯一性。
    1. 在Dog改变时,系统会认为是修改而不是认为是一个全新的类
  2. 使用Serializable默认序列化所有属性,但是除了static以及transient修饰的属性
    1. 从结果可以看到是null
  3. 序列化对象时,要求里面属性的类型也需要实现序列化接口
    1. 如下面Dog类中的master类对象如果没有实现序列化接口,编译会出错
  4. 序列化具有可继承性,父类序列化,子类默认也序列化
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
package com.zjh;

import java.io.Serializable;

public class Dog implements Serializable {
    private String name;
    private int age;
    //序列化对象时,默认将里面所有属性都进行序列化,但除了static或transient修饰的成员
    private static String nation;
    private transient String color;
    //序列化对象时,要求里面属性的类型也需要实现序列化接口
    //serialVersionUID 序列化的版本号,可以提高兼容性
    private static final long serialVersionUID = 1L;
    //序列化对象时,要求里面属性的类型也需要实现序列化接口
    private Master master = new Master();

    public Dog(String name, int age, String nation, String color) {
        this.name = name;
        this.age = age;
        this.color = color;
        this.nation = nation;
    }

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

    public String getName() {
        return name;
    }

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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package com.zjh.Object;

import com.zjh.Dog;

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

public class ObjectOutStream_ {
    public static void main(String[] args) throws IOException {
        //序列化后,保存的文件格式,不是存文本,而是按照他的格式来保存
        String filePath = "d:\\data.dat";

        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(filePath));

        //序列化数据到 d:\data.dat
        oos.writeInt(100);// int -> Integer (实现了 Serializable)
        oos.writeBoolean(true);// boolean -> Boolean (实现了 Serializable)
        oos.writeChar('a');// char -> Character (实现了 Serializable)
        oos.writeDouble(9.5);// double -> Double (实现了 Serializable)
        oos.writeUTF("韩顺平教育");//String
        //保存一个dog对象
        oos.writeObject(new Dog("旺财", 10, "日本", "白色"));
        oos.close();
        System.out.println("数据保存完毕(序列化形式)");
    }
}

6.2ObjectInStream

注意

  1. 读取的顺序要和写入的顺序一致,否则会乱码
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
package com.zjh.Object;

import com.zjh.Dog;

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

public class ObjectInputStream_ {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        //指定反序列化的文件
        String filePath = "d:\\data.dat";
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filePath));
        //读取
        //老师解读
        //1. 读取(反序列化)的顺序需要和你保存数据(序列化)的顺序一致
        //2. 否则会出现异常
        System.out.println(ois.readInt());
        System.out.println(ois.readBoolean());
        System.out.println(ois.readChar());
        System.out.println(ois.readDouble());
        System.out.println(ois.readUTF());

        //dog 的编译类型是 Object , dog 的运行类型是 Dog
        Object dog = ois.readObject();
        System.out.println("运行类型=" + dog.getClass());
        System.out.println("dog信息=" + dog);//底层 Object -> Dog

        //这里是特别重要的细节:

        //1. 如果我们希望调用Dog的方法, 需要向下转型
        //2. 需要我们将Dog类的定义,放在到可以引用的位置
        Dog dog2 = (Dog)dog;
        System.out.println(dog2.getName()); //旺财..

        //关闭流, 关闭外层流即可,底层会关闭 FileInputStream 流
        ois.close();
    }
}

7.标准流

System.in 编译InputStream 运行BufferInputStream 键盘输入
System.Out 编译运行PrintStream 显示器
Scanner里面放的就是输入流

1
2
3
4
Scanner scanner = new Scanner(System.in);
System.out.println("输入内容");
String next = scanner.next();
System.out.println("next=" + next);

8.转换流

  • InputStreamReader:Reader的子类,可以将InputStream字节流(包装)转换为Reader字符流
  • OutputStreamWriter:Writer的子类,可以将OutputStream字节流(包装)转换为Writer字符流

可以指定读取的编码格式和输出的编码格式

需求分析:读取一个编码格式为gbk的文本文件,使用FileInputStream只能按照默认的utf-8格式读取,会导致乱码,这时候就需要指定编码

8.1InputStreamReader

InputStreamReader(Inputstream in,Charset cs) //创建使用给定字符集的
public String getEncoding() 返回此流使用的字符编码的名称。

8.1.1指定读取文件编码

gbk编码文件,使用utf-8读取

img

再使用InputStreamReader(Inputstream in,Charset cs)转换
使用使用BufferedReader提高读取效率

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
package com.zjh.transformation;

import java.io.*;

public class InputStreamReader_ {
    public static void main(String[] args) throws IOException {
        String filePath = "d:\\a.txt";
        String charset = "gbk";
        //使用FileInputStream读取文件
        //使用InputStreamReader将字节流转为指定编码的字符流
        //使用BufferedReader提高读取效率
        BufferedReader br = new BufferedReader(
                new InputStreamReader(
                new FileInputStream(filePath)charset));
        String s = br.readLine();
        System.out.println(s);
        //关闭最外层即可
        br.close();
    }
}

img

成功

OutputStreamWriter

将字节流FileOutputStream包装成(转换成)字符流OutputStreamWriter,对文件进行写入(按照gbk格式,可以指定其他,比如utf-8)
同样使用BufferedWriter提高效率

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
package com.zjh.transformation;

import java.io.*;

public class OutputStreamWriter_ {
    public static void main(String[] args) throws IOException {
        String filePath = "d:\\hsp.txt";
        String charSet = "gbk";
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(filePath), charSet));
        bw.write("hi, 韩顺平教育");
        bw.close();
        System.out.println("按照 " + charSet + " 保存文件成功~");
    }
}

9.打印流

打印流只有输出流没有输入流

9.1PrintStream

img

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
package com.zjh.print;

import java.io.IOException;
import java.io.PrintStream;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CharsetEncoder;

public class PrintStream_ {
    public static void main(String[] args) throws IOException {
        PrintStream out =  System.out;
        out.print("sda手打");
        out.write("张俊鸿".getBytes());
        out.close();
        
        System.setOut(new PrintStream("d:\\hh.txt"));
        PrintStream out2 =  System.out;
        out2.println("的阖家安康和接口大");
        out2.close();
    }
}

System.out是标准输出流 可以用PrintStream out接收
out.print(); 和out.write();的区别:

  • 本质都是字节流的write()方法,print多了一层判空

默认输出位置是控制台
可以使用System.setOut改变输出位置,传入PrintStream("Path"/File)打印到文本文件中

1
2
3
4
5
6
public void print(String s) {
                if (s == null) {
                    s = "null";
                }
                write(s);
            } 

9.2PrintWriter

img

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
package com.zjh.print;

import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;

public class PrintWriter_ {
    public static void main(String[] args) throws IOException {
        PrintWriter printWriter = new PrintWriter(new FileWriter("d:\\f2.txt"));
        printWriter.print("hi, 北京你好~~~~");
        printWriter.close();//flush + 关闭流, 才会将数据写入到文件..

    }
}

注意:一定要printWriter.close()或者flush(),否则不会写入,底层close()才真正调用write

10.Properties类

读取配置文件
传统方式

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
package com.zjh.Properties;

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

public class Properties01 {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new FileReader("src\\mysql.properties"));
        String line = "";
        while ((line = br.readLine())!=null){
            String[] split = line.split("=");
            //如果我们要求指定的ip值
            if("ip".equals(split[0])) {
                System.out.println(split[0] + "值是: " + split[1]);
            }
        }
        br.close();
    }
}

读取遍历,修改也麻烦
使用Properties类方便

10.2Properties

读取key=value格式,不能有空格,值也不需要引号,默认类型是String
方法

  • load:加载配置文件的键值对到Properties对象
  • list:将数据显示到指定设备
  • getProperty(key):根据键获取值
  • setProperty(key,value):设置键值对到Properties对象
  • store:将Properties中的键值对存储到配置文件,在idea中,保存信息到配置文件,如果含有中文,会存储为unicode码

10.2读取配置文件

  • getProperty(key):根据键获取值

注意如果是想将读取的String类型转为int或者其他,要用包装类
int age = Integer.parseInt(pro.getProperty("age"));

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.zjh.Properties;

import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.Properties;

public class Properties02 {
    public static void main(String[] args) throws IOException {
        //使用Properties 类来读取mysql.properties 文件
        //1. 创建Properties 对象
        Properties properties = new Properties();
        //2. 加载指定配置文件
        properties.load(new FileReader("src\\mysql.properties"));
        //3. 把k-v显示控制台
        properties.list(System.out);
        //4. 根据key 获取对应的值
        String user = properties.getProperty("user");
        String pwd = properties.getProperty("pwd");
        System.out.println("用户名=" + user);
        System.out.println("密码是=" + pwd);
    }
}

10.3写入修改

  • properties.setProperty(key,value);
    • 存在就修改value,否则就增加
  • properties.store(OutputStream/Writer,Comment);
    • 使用OutputStream,如果有中文则在配置文件中以unicode编码写出,使用Writer就直接显示中文
    • Comment在配置文件顶部写注释用
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package com.zjh.Properties;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Properties;

public class Properties03 {
    public static void main(String[] args) throws IOException {
        //使用Properties 类来创建 配置文件, 修改配置文件内容
        Properties properties = new Properties();

        properties.setProperty("charset", "utf8");
        properties.setProperty("user", "汤姆");//注意保存时,是中文的 unicode码值
        properties.setProperty("pwd", "888888");

        //将k-v 存储文件中即可
//        properties.store(new FileOutputStream("src\\mysql2.properties"), "注释");
        properties.store(new FileWriter("src\\mysql2.properties"), "注释");

        System.out.println("保存配置文件成功~");
    }
}

11.Commons IO

开发中使用封装好的Commons IO类可以简化代码

工具包

网址跳转:链接

updatedupdated2022-05-032022-05-03