一、字节流与字符流

  • 输入流:用于读取数据 – 将数据写入内存进行展示,即将数据从其他设备读取到内存中的流。
  • 输出流:用于数据保存 – 将数据写入磁盘,可持久化存储,即将数据从内存中写出到其他设备上的流。

在字节流(以字节为单位)中,输出数据使用OutStream类完成,输入使用的是InputStream类完成。(所有字节流的父类)

在字符流(以字符为单位)中,输出数据使用Writer类完成,输入使用Reader完成。(所有字符流的父类)

其中,字节流主要操作byte类型数据,以byte数组为准。

如果想对文件进行读写,首先需要创建一个文件对象,如下:

1
2
3
4
5
6
7
public class FileDemo01 {
public static void main(String[] args) {
String pathname = "a.txt";
File file = new File(pathname);
System.out.println(file); // a.txt
}
}

从上面代码段可以看出,File接收的参数是文件路径,返回的是File对象。但是,直接打印File时,返回的是pathname,即为传入的参数。所以,在File类中,重写了toString方法。后面当我们拿到File对象后,就可以进行后续对当前文件的一系列操作了。

File对象常见的方法:

1
2
3
4
5
6
7
8
9
10
11
12
public class FileDemo01 {
public static void main(String[] args) {
String pathname = "in.txt";
File file = new File(pathname);
System.out.println(file);
String absolutePath = file.getAbsolutePath(); // 获取in.txt文件的绝对路径 -->String
File absoluteFile = file.getAbsoluteFile(); // 获取的是文件对象
String name = absoluteFile.getName(); // 文件名 --> in.txt
long length = absoluteFile.length(); // 文件长度
long length1 = file.length(); // 同上
}
}

二、字节流的读写操作

1.字节流的输出
1
2
3
4
<details>
<summary>
字节流的写入
</summary>
1
2
3
4
5
6
7
8
9
10
11
public class FileDemo05 {
public static void main(String[] args) throws IOException {
File file = new File("test1.txt")
FileOutputStream fos = new FileOutputStream(file, true);// 在原有的内容进行追加
byte[] bytes1 = "问:请再添加一条会怎么样".getBytes(); // 获取输入的字节数组
byte[] b = "\n".getBytes(); // 换行
fos.write(b);
fos.write(bytes1);
fos.close();
}
}

文件存储原理

2.字节流的读取
字节流的读取
1
2
3
4
5
6
7
8
9
10
11
12
public class FileDemo05 {
public static void main(String[] args) throws IOException {
File file = new File("test1.txt")
FileInputStream fis = new FileInputStream(file);
byte[] bytes = new byte[1024]; // 设置一次读取的字节数组长度
int length = -1; //
while ((length = fis.read(bytes)) != -1) { // 循环
System.out.println(new String(bytes, 0, length));
}
fis.close();
}
}

当一次读取一个字节时,length中存放的是对应字节的ASCII码数值;当一次读取多个字符时,length中记录的是数组的有效长度。

字节流读取文件

对上图中一次读取多个字节的思考:

上图右边读取多个字符中,在读取时,若没有指定数组的有效长度,会出现重复的情况。例如:这里我们期望读取到是ABCDE,但最终的情况会是ABCDEDED。这里设定的缓冲区读取的数组长度为2。

第一次读取时:缓冲区数组对应的内容是{A,B}(这里对应的其实是ASCII码)。len = 2.

第二次读取时:读取到缓冲区对应的内容是{C,D},len = 2.

第三次读取时:读取到缓冲区对应内容,只有E,但是原来的数组中存储的是{C,D},故将E覆盖C。所以此时缓冲区对应的内容是{E,D},len = 1.故此轮输出是ED

注意:又因为没有返回-1,所以继续读取。

第四次读取时:文件中没有有效内容,返回-1。但数组中是{E,D},所以再次打印时,还是ED

因此在读取操作时,需要注意两点:

  1. 未读取到有效内容,返回-1时,则停止读取。

    while(len != -1){}

  2. 读取时,指定数组的有效长度,而数组的有效长度,又可以通过len进行指定。

    new String(bytes,0,len)

3.案例 — 统计并打印指定文件夹.java文件

思路:

  • 一个文件夹下可能既包含文件,又包含文件夹。所以采取的方式是:遇到文件夹就继续进入,遇到文件则判断文件是否是.java文件,故采用递归的方式。
  • 由于我们需要过滤出.java文件,所以我们有两种方法:
    1. 对文件名进行判断.endWith(".java")
    2. 实现FileFilter接口,重写其中的accept方法。
accept接口实现的方式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class FileFilterImpl implements FileFilter {
@Override
public boolean accept(File pathname) {
/*
判断:
1.是否是文件
2.文件名后缀是否为.java
不满足返回false,满足则返回true
*/
if (pathname.isFile() && !pathname.getName().endsWith(".java")) {
return false;
}
return true;
}
}
主方法的实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class FindJavaDemo05 {

private static int count = 0; // 用于记录.java文件的个数
public static void main(String[] args) {
File file = new File("../"); // 选取的文件夹
getAllFile(file);
System.out.println(count);
}

private static void getAllFile(File dir) {
File[] files = dir.listFiles(new FileFilterImpl()); // 获取子文件及子文件夹 -- 并采用文件过滤器
for (File f : files) {
if (f.isDirectory()) { // 当前对象是文件夹,则递归调用
getAllFile(f);
} else { // 否则打印出.java并记录
count++;
System.out.println(f);
}
}
}
}

File[] files = dir.listFiles(new FileFilterImpl())的思考:

其中接口实现accept方法,用于过滤满足方法体条件的文件对象。

首先dir.listFiles遍历出每一个File对象,每一个都作为FileFilterImplaccpet方法的参数进行传入。

然后,调用accpet函数,对当前传入的参数(File对象)进行判断操作。如果满足方法体条件,返回true,否则返回false

最后,当.listFiles接收到true时,就将当前的File对象添加入File[]数组中,否则就不加入。

三、字符流的读写操作

字符流以字符为单位,专门用于处理文本文件。若用字节流读取中文字符时,可能不会显示完整的字符,因为一个中文字符可能占用多个字节存储。

同样在字符输出流中,同样有ReaderWriter两种读取和写入的抽象类。

  • Reader

    用于读取字符流的所有类的超类,可以读取字符信息到内存中。其中,字符输入流的基本共性功能方法有:

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

    用于写出字符流的所有类的超类,将指定的字符信息写出到目的地。以下为字节输出流的基本共性功能方法。

    • void write(int c):写入单个字符。
    • void write(char[] cbuf):写入字符数组。
    • abstract void write(char[] cbuf,int off,int len):写入字符数组的某一部分,off数组的开始索引,len写的字符个数。
    • void write(String str):写入字符串。
    • void write(String str,int off,int len):写入字符串的某一部分,off数组的开始索引,len写的字符个数。
    • void flush():刷新该流的缓冲。
    • void close():关闭此流,再次此前先进行刷新。

以上可对OutStreamInStream进行类比。

1.FileReader类

注意:读取文件时,构造时使用系统默认的字符编码和默认字节缓冲区。

  • 字符编码:字节与字符的对应规则。idea中默认的是UTF-8
  • 字节缓冲区:一个字节数组,用来临时存储字节数据。

构造方法

  • FileWriter(File file)
  • FileWriter(String name)

具体使用方法等同FileInputStream,字节输入流。

2.FileWriter

其构造时使用系统默认的字符编码和默认字节缓冲区。

构造方法

  • FileWriter(File file):创建一个新的FileWriter,给定要读取的File对象。
  • FileWriter(String fileName):同上,传入的参数是文件的名称(路径)。

其使用方法同字节流的使用,需要注意的一点是:在写出数据以后,如果未调用close方法,数据只是保存到了缓冲区,并未写出到文件中。因此,字节流在操作时本身不会用到缓冲区(内存),是文件本身操作的。

字符流在操作时使用了缓冲区,通过缓冲区再操作文件。

因为内置缓冲区的原因,如果不关闭输出流,无法写出字符到文件中。但是关闭的流对象,是无法继续写出数据的。当既想写出数据,又想继续使用流,就需要flush方法。

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

输出同字节输出流。

1.写出字符数组

string.toCharArray()

2.写出字符串

writer(String str),writer(String str,int off,int len)

四、属性集

java.util.Properties 继承于Hashtable ,来表示一个持久的属性集。它使用键值结构存储数据,每个键及其对应值都是一个字符串。该类也被许多Java类使用,比如获取系统属性时,System.getProperties 方法就是返回一个Properties对象。

4.1 Properties类
  1. 构造方法

    • public Properties() :创建一个空的属性列表。
  2. 基本的存储方法

    • public Object setProperty(String key, String value) : 保存一对属性。

    • public String getProperty(String key) :使用此属性列表中指定的键搜索属性值。

    • public Set<String> stringPropertyNames() :所有键的名称的集合。

    通常在不知道键值的情况下,获取其键值的方法。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    public static void main(String[] args) throws FileNotFoundException {
    // 创建属性集对象
    Properties properties = new Properties();
    // 添加键值对元素
    properties.setProperty("filename", "a.txt");
    properties.setProperty("length", "209385038");
    properties.setProperty("location", "D:\\a.txt");
    // 遍历属性集,获取所有键的集合
    Set<String> strings = properties.stringPropertyNames();
    // 打印键值对
    for (String key : strings ) {
    System.out.println(key+" -- "+properties.getProperty(key));
    }
    }
4.2 与流相关的方法
  • public void load(InputStream inStream): 从字节输入流中读取键值对。

参数中使用了字节输入流,通过流对象,可以关联到某文件上,这样就能够加载文本中的数据了。文本数据格式:

1
2
3
filename=a.txt
length=209385038
location=D:\a.txt

文本中的数据必须是键值对格式,可以冒号,逗号,等号等符号分隔。

加载代码示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class ProDemo2 {
public static void main(String[] args) throws FileNotFoundException {
// 创建属性集对象
Properties pro = new Properties();
// 加载文本中信息到属性集
pro.load(new FileInputStream("read.txt"));
// 遍历集合并打印
Set<String> strings = pro.stringPropertyNames();
for (String key : strings ) {
System.out.println(key+" -- "+pro.getProperty(key));
}
}
}
输出结果:
filename -- a.txt
length -- 209385038
location -- D:\a.txt

五、缓冲流

缓冲流,也是高效流,是对4个基本的FileXxx流的增强,所以对应的也是4个流。同理按照数据类型分类:

  • 字节缓冲流:BufferedInputStreamBufferedOutputStream
  • 字符缓冲流:BufferedReaderBufferedWriter

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

5.1 字节缓冲流

1. 构造方法

  • public BufferedInputStream(InputStream in) :创建一个新的缓冲输入流,传递字节输入流。
  • public BufferedOutputStream(OutputStream out): 创建一个新的缓冲输出流,传递字节输出流。

2.用例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class BufferedDemo {
public static void main(String[] args) throws IOException {
long start = System.currentTimeMillis();
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("/navicat120_premium_en.dmg")); // 创建字节输入缓冲区
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("copy.dmg")); // 字节输出缓冲区
int len;
byte[] bytes = new byte[1024]; // 缓冲数组 -- 1024
while ((len = bis.read(bytes)) != -1) {

bos.write(bytes, 0, len); // 输出 -- 写入数据
}

long end = System.currentTimeMillis();
System.out.println(end - start);
}
}
5.2 字符缓冲流

1.构造方法

  • public BufferedReader(Reader in) :创建一个新的缓冲输入流。
  • public BufferedWriter(Writer out): 创建一个新的缓冲输出流。
1
2
3
4
// 创建字符缓冲输入流
BufferedReader br = new BufferedReader(new FileReader("br.txt"));
// 创建字符缓冲输出流
BufferedWriter bw = new BufferedWriter(new FileWriter("bw.txt"));

2.特有方法

字符缓冲流的基本方法与普通字符流调用方式一致,不再阐述,我们来看它们具备的特有方法。

  • BufferedReaderpublic String readLine(): 读一行文字。
  • BufferedWriterpublic void newLine(): 写一行分隔符,由系统属性定义符号。

readLine方法演示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class BufferedReaderDemo {
public static void main(String[] args) throws IOException {
// 创建流对象
BufferedReader br = new BufferedReader(new FileReader("in.txt"));
// 定义字符串,保存读取的一行文字
String line = null;
// 循环读取,读取到最后返回null,可以把readLine看做是一个指针
while ((line = br.readLine())!=null) {
System.out.print(line);
System.out.println("------");
}
// 释放资源
br.close();
}
}

newLine方法演示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class BufferedWriterDemo throws IOException {
public static void main(String[] args) throws IOException {
// 创建流对象
BufferedWriter bw = new BufferedWriter(new FileWriter("out.txt"));
// 写出数据
bw.write("Hello");
// 写出换行
bw.newLine();
bw.write("World");
bw.newLine();
bw.write("!");
bw.newLine(); // 输出换行
// 释放资源
bw.close(); //
}
}

输出效果:
Hello
World
!

3.原理

缓冲区原理

由上图可以看到,当我们使用普通的字节输出流时,由OS进行内存到硬盘的读写时,字符是一个一个读取,这样增加了文件从内存(其中经过缓存)读取到磁盘次数,从而增加了读取的时间;当我们使用缓冲流时,每次操作系统从内存读取到缓冲区的数据就是多个字符,然后再读取到硬盘,由于每次读取的数量多了,那么总的读取次数就肯定减少了,所以就能够减少我们读写的时间。

六、转换流

6.1 字符编码和字符集
  1. 字符编码

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

编码:字符->字节

解码:字节->字符

  • 字符编码Character Encoding : 就是一套自然语言的字符与二进制数之间的对应规则。
    • 编码表:生活中文字和计算机中二进制的对应规则
图片转换流图解-桥梁
  1. 字符集
  • 字符集 Charset:也叫编码表。是一个系统支持的所有字符的集合,包括各国家文字、标点符号、图形符号、数字等。

计算机要准确的存储和识别各种字符集符号,需要进行字符编码,一套字符集必然至少有一套字符编码。常见字符集有ASCII字符集、GBK字符集、Unicode字符集等。

当指定了编码,它所对应的字符集自然就指定了,所以编码才是我们最终要关心的。

6.2 InputStreamReader类

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

1.构造方法

  • InputStreamReader(InputStream in): 创建一个使用默认字符集的字符流。
  • InputStreamReader(InputStream in, String charsetName): 创建一个指定字符集的字符流。

构造举例,代码如下:

1
2
InputStreamReader isr = new InputStreamReader(new FileInputStream("in.txt"));
InputStreamReader isr2 = new InputStreamReader(new FileInputStream("in.txt") , "GBK");

2.指定编码读取

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class ReaderDemo2 {
public static void main(String[] args) throws IOException {
// 定义文件路径,文件为gbk编码
String FileName = "E:\\file_gbk.txt";
// 创建流对象,默认UTF8编码
InputStreamReader isr = new InputStreamReader(new FileInputStream(FileName));
// 创建流对象,指定GBK编码
InputStreamReader isr2 = new InputStreamReader(new FileInputStream(FileName) , "GBK");
// 定义变量,保存字符
int read;
// 使用默认编码字符流读取,乱码
while ((read = isr.read()) != -1) {
System.out.print((char)read); // ��Һ�
}
isr.close();

// 使用指定编码字符流读取,正常解析
while ((read = isr2.read()) != -1) {
System.out.print((char)read);// 大家好
}
isr2.close();
}
}
6.3 OutputStreamWriter类

转换流java.io.OutputStreamWriter ,是Writer的子类,是从字符流到字节流的桥梁。使用指定的字符集将字符编码为字节。它的字符集可以由名称指定,也可以接受平台的默认字符集。

1.构造方法

  • OutputStreamWriter(OutputStream in): 创建一个使用默认字符集的字符流。
  • OutputStreamWriter(OutputStream in, String charsetName): 创建一个指定字符集的字符流。

构造举例,代码如下:

1
2
OutputStreamWriter isr = new OutputStreamWriter(new FileOutputStream("out.txt"));
OutputStreamWriter isr2 = new OutputStreamWriter(new FileOutputStream("out.txt") , "GBK");

2.指定编码输出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class OutputDemo {
public static void main(String[] args) throws IOException {
// 定义文件路径
String FileName = "E:\\out.txt";
// 创建流对象,默认UTF8编码
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(FileName));
// 写出数据
osw.write("你好"); // 保存为6个字节
osw.close();

// 定义文件路径
String FileName2 = "E:\\out2.txt";
// 创建流对象,指定GBK编码
OutputStreamWriter osw2 = new OutputStreamWriter(new FileOutputStream(FileName2),"GBK");
// 写出数据
osw2.write("你好");// 保存为4个字节
osw2.close();
}
}
6.4 字符流转换实例

案例:

输入条件:给定文件中,输入文件指定为gbk格式。

输出条件:将文件以utf-8格式进行输出。

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
public class TransDemo{
public static void main(String[] args){
// 1.定义文件路径
String srcFile = "file_gbk.txt";
String destFile = "file_utf8.txt";
// 2.创建流对象
// 2.1 转换输入流,指定GBK编码
InputStreamReader isr = new InputStreamReader(new FileInputStream(srcFile) , "GBK");
// 2.2 转换输出流,默认utf8编码
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(destFile));
// 3.读写数据
// 3.1 定义数组
char[] cbuf = new char[1024];
// 3.2 定义长度
int len;
// 3.3 循环读取
while ((len = isr.read(cbuf))!=‐1) {
// 循环写出
osw.write(cbuf,0,len);
}
// 4.释放资源
osw.close();
isr.close();
}
}

七.序列化

Java 提供了一种对象序列化的机制。用一个字节序列可以表示一个对象,该字节序列包含该对象的数据对象的类型对象中存储的属性等信息。字节序列写出到文件之后,相当于文件中持久保存了一个对象的信息。

同样,该字节序列还可以从文件中读取回来,重构对象,对其进行反序列化操作。对象的数据对象的类型对象中存储的数据信息,都可以用来在内存中创建对象。

序列化:将对象转换为字节。

反序列化:字节重构为对象。

7.1 ObjectOutputStream类

java.io.ObjectOutputStream类,将Java对象的原始数据类型写出到文件,实现对象的持久存储。

构造方法:

  • public ObjectOutputStream(OutputStream out) : 创建一个指定OutputStreamObjectOutputStream

举例:

1
2
FileOutputStream fileOut = new FileOutputStream("employee.txt");
ObjectOutputStream out = new ObjectOutputStream(fileOut);
7.2 序列化操作
  1. 一个对象要想序列化,必须满足两个条件:
    • 该类必须实现java.io.Serializable接口,Serializable是一个标记接口,不实现此接口的类将不会使任何状态序列化或反序列化,会抛出NotSerializableException
    • 该类的所有属性必须是可序列化的。如果有一个属性不需要可序列化的,则该属性必须注明是瞬态的,使用transient关键字修饰。
1
2
3
4
5
6
7
8
9
10
public class Employee implements Serializable {

private String name;
private String address;
public transient int age; // 通过transient瞬态修饰成员,不会被序列化

public void addressCheck() {
System.out.println("Address check:" + name + "--" + address);
}
}
  1. 写出对象方法

    • public final void writeObject (Object obj):将指定的对象写出。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    public class SerializeDemo {
    public static void main(String[] args) {
    Employee employee = new Employee();
    employee.setAge(21);
    employee.setAddress("江苏");
    employee.setName("Tom");

    ObjectOutputStream out = null;
    try {
    // 创建序列化对象
    out = new ObjectOutputStream(new FileOutputStream("employee.txt"));
    // 写出对象
    out.writeObject(employee);
    // 释放资源
    out.close();
    System.out.println("Serialized data is saved"); // 姓名,地址被序列化,年龄没有被序列化
    } catch (IOException e) {
    e.printStackTrace();
    }

    }
    }
7.3 反序列化操作

ObjectInputStream反序列化流,将之前使用ObjectOutputStream序列化的原始数据恢复为对象。

构造方法:

  • public ObjectInputStream(InputStream in) : 创建一个指定InputStreamObjectInputStream
7.3.1 反序列化操作一

通过查找一个对象的class文件,即可将其进行反序列化操作,调用ObjectInputStream读取对象的方法:

  • public final Object readObject () : 读取一个对象。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class SerializeDemo01 {
public static void main(String[] args) {
Employee e = null;
try {
ObjectInputStream in = new ObjectInputStream(new FileInputStream("employee.txt"));
e = (Employee) in.readObject();
in.close();
} catch (IOException ex) {
ex.printStackTrace();
} catch (ClassNotFoundException ex) {
System.out.println("Employee class not found");
ex.printStackTrace();
return;
}
System.out.println("name:" + e.getName());
System.out.println("address:" + e.getAddress());
System.out.println("age:"+e.getAge());
}
}

输出结果:
name:Tom
address:江苏
age:0 // age没有被初始化,所以输出为0(Integer类型的初始化值)

由于JVM可以反序列化对象,它必须是能够找到class文件的类。如果找不到该类的class文件,则抛出一个ClassNotFoundException异常。

7.3.2 反序列化操作二

JVM反序列化对象时,能够找到class文件,但是class文件在序列化对象之后发生了修改,那么反序列化操作也会失败,抛出一个InvalidClassException异常。发生这个异常的原因如下:

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

Serializable接口给需要序列化的类,提供了一个序列号版本号。serialVersionUID该版本号的目的在于验证序列化的对象和对应类是否版本匹配。

1
2
// 加入序列版本号
private static final long serialVersionUID = 1L;
7.4 序列化案例
  1. 将存有多个自定义对象的集合序列化操作,保存到自定义文件中。
  2. 反序列化此文件,并遍历集合,打印对象信息。

分析:

  1. 将若干学生对象,保存到集合中。
  2. 把集合进行序列化。
  3. 反序列化读取时,只需要读取一次,转换为集合类型。
  4. 遍历集合,打印所有的学生信息。

首先创建一个student类,再创建集合类的对象,序列化和反序列化的对象都是这个集合对象。

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
public class DemoSerialize {

public static void main(String[] args) {
// serialize();// 对象序列化
rSerialize(); // 对象反序列化
}

/**
* 对象序列化操作
*/
public static void serialize() {
Student stu1 = new Student("张三", 12);
Student stu2 = new Student("李四", 10);
Student stu3 = new Student("王五", 11);

StudentList list = new StudentList();
List<Student> students = new ArrayList<>();

students.add(stu1);
students.add(stu2);
students.add(stu3);

list.setStudents(students);

ObjectOutputStream os = null;
try {
os = new ObjectOutputStream(new FileOutputStream("students.txt"));
os.writeObject(list);
os.close();
System.out.println("完成序列化");
} catch (IOException e) {
e.printStackTrace();
}
}

/**
* 对象反序列化操作
*/
public static void rSerialize() {

StudentList students = null;
try {
ObjectInputStream in = new ObjectInputStream(new FileInputStream("students.txt"));
students = (StudentList) in.readObject();
in.close(); // 资源释放
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
System.out.println("Students class not found");
e.printStackTrace();
return;
}

List<Student> studentList = students.getStudents();
System.out.println(studentList);
System.out.println("-----");
for (Student student : studentList) {
System.out.println(student);
}
}
}

八、打印流

printprintln方法来自于java.io.PrintStream类,该类能够方便地打印各种数据类型的值,是一种便捷的输出方法。

8.1 PrintStream类

构造方法:

  • public PrintStream(String fileName) : 使用指定的文件名创建一个新的打印流。
1
PrintStream ps = new PrintStream("ps.txt");

改变打印流的流向:

System.out就是PrintStream类型的,只不过它的流向是系统规定的,打印在控制台上。通过PrintStream类改变打印流流向。

1
2
3
4
5
6
7
8
9
10
11
public class PrintStreamDemo {
public static void main(String[] args) throws FileNotFoundException {
System.out.println(97);

// 创建打印流,指定文件的名称
PrintStream ps = new PrintStream("ps.txt");
// 设置系统的打印流流向,输出到ps.txt文件上
System.setOut(ps);
System.out.println(97);
}
}

Comment