第二篇:IO,对象传输的基石(二)

文章目录

一、前言

二、顺序流(合并流)

2.1 顺序流定义 / 合并流定义

JavaIO流——合并流/顺序流(SequenceInputStream):,就是把多个输入流,合并成一个流对象,且见代码1。

2.2 代码:顺序流/合并流

代码1——合并流:

package mypackage;

import java.io.FileInputStream;
import java.io.SequenceInputStream;

//合并流/顺序流(SequenceInputStream):
//	 就是把多个输入流,合并成一个流对象.

public class Test {

	public static void main(String[] args) throws Exception {
		// 不同输入流有不同对应txt文件,将输入流合并后,合并后的输入流可以操作多个文件
		SequenceInputStream sequenceInputStream = new SequenceInputStream(
		new FileInputStream("stream1.txt"),
				new FileInputStream("stream2.txt"));
		byte[] buffer = new byte[1024];
		int len = -1;

		while ((len = sequenceInputStream.read(buffer)) != -1) {
			System.out.println(new String(buffer, 0, len));
		}
		sequenceInputStream.close();
	}

}

在这里插入图片描述
在这里插入图片描述

输出1:

Java程序员就业前景好,薪资高!
一起来学习Java吧!

小结1:

1、合并流对应的对象是输入流,是对输入流合并,不是对输出流合并;

2、合并的是两个输入流而不是两个源文件:因为不同输入流有不同对应txt文件,用合并流将两个输入流合并后,可以输入两个文件,所以说,合并的是两个输入流而不是两个源文件。

3、合并流可以接收合并流作为实际参数,形成多个流的合并,也称合并嵌套。

2.3 面试金手指:合并流/顺序流

合并流定义:JavaIO流——合并流,接收两个输入流作为参数,合并两个输入流或合并流,对外形成一个更大的流,方便客户端操作。
合并流需要注意的点:
1、合并流对应的对象是输入流,是对输入流合并,不是对输出流合并;
2、因为不同输入流有不同对应txt文件,用合并流将两个输入流合并后,可以输入两个文件,注意合并的是两个输入流而不是两个源文件。
3、合并流可以接收合并流作为实际参数,形成多个流的合并,也称合并嵌套。

三、对象流(序列化和反序列化)

3.1 对象流 提供一种 序列化/反序列化 的实现方式

1.1 序列化和反序列化定义:
序列化: 指把堆内存中的Java对象数据,通过某种方式把对象存储到磁盘文件中或者传递给其他网络的节点(在网络上传输). 我们把这个过程称之为序列化.
反序列化:把磁盘文件中的对象数据或者把网络节点上的对象数据,恢复成Java对象的过程.

1.2 为什么要做序列化:
1):在分布式系统中,需要共享的数据的JavaBean对象,都得做序列化,此时需要把对象再网络上传输,此时就得把对象数据转换为二进制形式.以后存储在HttpSession中的对象,都应该实现序列化接口(只有实现序列化接口的类,才能做序列化操作).
2):服务钝化:如果服务发现某些对象好久都没有活动了,此时服务器就会把这些内存中的对象,持久化在本地磁盘文件中(Java对象–>二进制文件). 如果某些对象需要活动的时候,现在内存中去寻找,找到就使用,找不到再去磁盘文件中,反序列化我们得对象数据,恢复成Java对象.

1.3 Java对象流——JavaIO流中实现序列化的方式
使用对象流来完成序列化和反序列化操作:
ObjectOutputStream: 通过writeObject方法做序列化操作的.
ObjectInputStream: 通过readObject方法做反序列化操作的.
ObjectOutputStream ObjectInputStream,提供一种序列化方式,序列化方式有很多,现在互联网大厂常用的是protobuf,最高效的系列化方式

3.2 代码:对象流

3.2.1 实体类 没有实现序列化接口

代码1:

package mypackage;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

//对象流 ObjectInput
public class Test {

	public static void main(String[] args) throws Exception {
		File file = new File("object.txt");
		writeObject(file);
		readObject(file);
	}

	private static void writeObject(File file) throws Exception {
		ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream(file));
		objectOutputStream.writeObject(new Person("小明", 12));
		objectOutputStream.close();
	}

	private static void readObject(File file) throws Exception {
		ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(file));
		Person person = (Person) objectInputStream.readObject();
		System.out.println(person);
		objectInputStream.close();
	}
}

class Person {     // 实体类没有实现序列化接口Serializable
	private String name;
	private int age;

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

输出1:

Exception in thread "main" java.io.NotSerializableException: mypackage.Person
	at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1184)
	at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:348)
	at mypackage.Test.writeObject(Test.java:20)
	at mypackage.Test.main(Test.java:14)

小结1:因为实体类Person没有实现序列化接口Serializable,所以这里程序抛出异常java.io.NotSerializableException

3.2.2 实体类 序列化 + 不指定序列化版本号

代码2:

package mypackage1;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

//对象流 ObjectInput
public class Test {

	public static void main(String[] args) throws Exception {
		File file = new File("object.txt");
		writeObject(file);
		readObject(file);
	}

	private static void writeObject(File file) throws Exception {
		ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream(file));
		objectOutputStream.writeObject(new Person("小明", 12));
		objectOutputStream.close();
	}

	private static void readObject(File file) throws Exception {
		ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(file));
		Person person = (Person) objectInputStream.readObject();
		System.out.println(person);
		objectInputStream.close();
	}
}

class Person implements Serializable {    // 和代码1的唯一不同,实体类实现Serializable接口

	private String name;
	private int age;

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

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

}

输出2:

Person [name=小明, age=12]

小结2:实体类Person实现序列化接口后,成功解决了程序异常,一般来说,随着项目的升级,系统的class文件也会升级(增加一个字段/删除一个字段), Java通过serialVersionUID(序列化版本号)来判断字节码是否发生改变.所以我们为实体类Person添加一个序列化版本号。

3.2.3 实体类 序列化 + 指定序列化版本号

代码3:

package mypackage2;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

//对象流 ObjectInput
public class Test {

	public static void main(String[] args) throws Exception {
		File file = new File("object.txt");
		writeObject(file);
		readObject(file);
	}

	private static void writeObject(File file) throws Exception {
		ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream(file));
		objectOutputStream.writeObject(new Person("小明", 12));
		objectOutputStream.close();
	}

	private static void readObject(File file) throws Exception {
		ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(file));
		Person person = (Person) objectInputStream.readObject();
		System.out.println(person);
		objectInputStream.close();
	}
}

class Person implements Serializable {
	private static final long serialVersionUID = 1L;
	private String name;
	private int age;

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

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

}

输出3:

Person [name=小明, age=12]

小结3:给实体类Person加上序列化版本号之后,整个问题正是解决。

3.3 面试金手指:对象流 提供一种 序列化/反序列化 的实现方式

1.1 序列化和反序列化定义:
序列化: 指把堆内存中的Java对象数据,通过某种方式把对象存储到磁盘文件中或者传递给其他网络的节点(在网络上传输). 我们把这个过程称之为序列化.
反序列化:把磁盘文件中的对象数据或者把网络节点上的对象数据,恢复成Java对象的过程.

1.2 为什么要做序列化:
1):在分布式系统中,需要共享的数据的JavaBean对象,都得做序列化,此时需要把对象再网络上传输,此时就得把对象数据转换为二进制形式.以后存储在HttpSession中的对象,都应该实现序列化接口(只有实现序列化接口的类,才能做序列化操作).
2):服务钝化:如果服务发现某些对象好久都没有活动了,此时服务器就会把这些内存中的对象,持久化在本地磁盘文件中(Java对象–>二进制文件). 如果某些对象需要活动的时候,现在内存中去寻找,找到就使用,找不到再去磁盘文件中,反序列化我们得对象数据,恢复成Java对象.

1.3 Java对象流——JavaIO流中实现序列化的方式
使用对象流来完成序列化和反序列化操作:
ObjectOutputStream: 通过writeObject方法做序列化操作的.
ObjectInputStream: 通过readObject方法做反序列化操作的.
ObjectOutputStream ObjectInputStream,提供一种序列化方式,序列化方式有很多,现在互联网大厂常用的是protobuf,最高效的系列化方式

代码1:因为实体类Person没有实现序列化接口Serializable,所以这里程序抛出异常java.io.NotSerializableException。

代码2:实体类Person实现序列化接口后,成功解决了程序异常,一般来说,随着项目的升级,系统的class文件也会升级(增加一个字段/删除一个字段), Java通过serialVersionUID(序列化版本号)来判断字节码是否发生改变.所以我们为实体类Person添加一个序列化版本号。

代码3:给实体类Person加上序列化版本号之后,整个问题正是解决。

JavaIO流——对象流通过writeObject()或readObject()实现序列化和反序列化,在磁盘读写和网络请求中保证数据的正确性。

四、与控制台相关:打印流 + 扫描流

4.1 打印流定义 + 扫描流定义

打印流定义:
打印到控制台;解释了System.out.println()底层原理,System.out.println();其实等价于 PrintStream ps = System.out; ps.println(),且见代码1;

扫描流定义:
与打印流对应,解释了控制到输入底层原理,本质上是java.util.Scanner类:扫描器类,表示输入操作,且见代码2。

4.2 代码:打印流 + 扫描流

4.2.1 打印流的使用

代码1——打印流:

package mypackage_打印流;

//System.out.println();其实等价于   PrintStream ps = System.out;       ps.println()
//Java 
public class Test {

	public static void main(String[] args) {
		String name = "小明";
		int age = 12;
		// 一般输出
		System.out.println("name: " + name + " ,age: " + age);
		// 打印流输出
		String format = "name: %s ,age: %d";
		Object[] data = { "小明", 12 };
		System.out.printf(format, data);
		System.out.println();
		// 打印流简化输出
		System.out.printf("name: %s ,age: %d", data);
	}

}

输出1:

name: 小明 ,age: 12
name: 小明 ,age: 12
name: 小明 ,age: 12

小结1:打印流为Java提供了一种类似于C/C++的格式化打印,其实只是一种打印的补充格式。因为在Java语言中,System.out.println()通过+号重载可以实现字符串拼接,可以拼接出任何打印格式,所以说System.out.printf()只是提供了另一种打印格式的输出方式。

4.2.2 扫描流的使用

代码2——扫描流:

package mypackage_扫描类;

import java.util.Scanner;
//java.util.Scanner类:扫描器类,表示输入操作.
//存在的方法:  xxx表示数据类型,如byte,int ,boolean等.
//          boolean  hasNextXxx():判断是否有下一种类型的数据
//          Xxx          nextXxx():获取下一个该类型的数据.

public class Test {

	public static void main(String[] args) {
		Scanner scanner = new Scanner("Java程序员就业前景好,薪资高!一起来学习Java吧!");
		while (scanner.hasNextLine()) {
			String line = scanner.nextLine();
			System.out.println(line);

		}
		scanner.close();
	}

}

输出2:

Java程序员就业前景好,薪资高!一起来学习Java吧!

小结2:扫描类中,hasNextXxx()和nextXxx()两个方法,将字符串内容(也可以是其他内容txt、数组、boolean等)打印出来。

4.3 面试金手指:打印流 + 扫描流

打印流定义:
(1)打印到控制台;
(2)解释了System.out.println()底层原理,System.out.println();其实等价于 PrintStream ps =
System.out; ps.println(),且见代码1;

扫描流定义:
与打印流对应,解释了控制到输入底层原理,本质上是java.util.Scanner类:扫描器类,表示输入操作,且见代码2。

代码1:打印流为Java提供了一种类似于C/C++的格式化打印,其实只是一种打印的补充格式。因为在Java语言中,System.out.println()通过+号重载可以实现字符串拼接,可以拼接出任何打印格式,所以说System.out.printf()只是提供了另一种打印格式的输出方式。

代码2:扫描类中,hasNextXxx()和nextXxx()两个方法,将字符串内容(也可以是其他内容txt、数组、boolean等)打印出来。

注意:Java BIO流中,打印流用于打印到控制台,扫描流用于读入,甚至可将两个流放在一起写一个demo,可以尝试一下。

五、数据流:提供了可以读/写任意数据类型的方法

5.1 数据流定义:提供了可以读/写任意数据类型的方法,两个实现类,每个实现类都有writeXxx()和readXxx()方法

数据流定义:提供了可以读/写任意数据类型的方法,分别是:
DataOutputStream: 提供了 writeXxx(xxx value)方法.
DataInputStream: 提供了 readXxx()方法.
注意: writeXxx和readXxx必须要对应起来, writeByte写出的数据,此时只能使用readByte读取回来.且见代码1.

5.2 代码:数据流

代码1——数据流读写任意类型数据:

package mypackage;
//数据流,提供了可以读/写任意数据类型的方法:
//DataOutputStream:   提供了 writeXxx(xxx value)方法.
//DataInputStream:      提供了 readXxx()方法.
//  注意: writeXxx和readXxx必须要对应起来,  writeByte写出的数据,此时只能使用readByte读取回来.

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;

public class Test {
	// 先写出到data.txt文件中去,然后再读入程序,再打印出来
	public static void main(String[] args) throws Exception {
		File file = new File("data.txt");
		write(file);
		read(file);
	}

	private static void read(File file) throws Exception {
		DataInputStream dataInputStream = new DataInputStream(new FileInputStream(file));

		System.out.println(dataInputStream.readByte());
		System.out.println(dataInputStream.readChar());
		System.out.println(dataInputStream.readUTF());
		dataInputStream.close();
	}

	private static void write(File file) throws Exception {
		DataOutputStream dataOutputStream = new DataOutputStream(new FileOutputStream(file));
		dataOutputStream.writeByte(12);
		dataOutputStream.writeChar('男');
		dataOutputStream.writeUTF("小明");
	
		dataOutputStream.close();

	}
}

输出1:

12
男
小明

小结1:理论上,数据流可以读写任意类型的数据,8种基本类型(boolean char float double byte short int long),字符串(readChars、readUTF),数组(int数组、byte数组),程序(代码1)为缩减程序,只写了三种(byte char utf字符串)。

5.3 面试金手指:数据流

数据流定义:提供了可以读/写任意数据类型的方法,两个实现类,每个实现类都有writeXxx()和readXxx()方法。
分别是:
DataOutputStream: 提供了 writeXxx(xxx value)方法.
DataInputStream: 提供了 readXxx()方法.
注意: writeXxx和readXxx必须要对应起来, writeByte写出的数据,此时只能使用readByte读取回来.且见代码1.

数据流的使用:提供了可以读/写任意数据类型的方法,任意数据类型是指8种基本类型(boolean char float double byte short int long),字符串(readChars、readUTF),数组(int数组、byte数组),均有相应的readXxx()和WriteXxx()方法。

六、管道流:实现两个线程之间的数据交互

6.1 管道流定义:实现两个线程之间的数据交互,四个实现类

Java管道流定义:实现两个线程之间的数据交互。
包括四种:管道字节输入流PipedInputStream、管道字节输出流PipedOutputStream、
管道字符输入流PipedReder、管道字符输出流PipedWriter。

6.2 代码:管道流

管道字节输入流PipedInputStream、管道字节输出流PipedOutputStream:

package mypackage;

import java.io.PipedInputStream;
import java.io.PipedOutputStream;

//管道流:实现两个线程之间的数据交互.
//PipedInputStream
//PipedOutputStream
//PipedReder
//PipedWriter
//ThreadA使用写出流不断写出,ThreadB中注入ThreadA引用,读取ThreadA写出流中的内容,不断打印到控制台
public class Test {

	public static void main(String[] args) throws Exception {
		ThreadA threadA = new ThreadA();   
		ThreadB threadB = new ThreadB(threadA);   
		threadA.start();
		threadB.start();
	}

}

class ThreadA extends Thread {
	private PipedOutputStream pipedOutputStream = new PipedOutputStream();

	public PipedOutputStream getOut() {
		return pipedOutputStream;
	}

	@Override
	public void run() {   // run()方法中定义这个线程启动后具体干什么事情
		try {
			for (int i = 65; i < 65 + 26; i++) {
				pipedOutputStream.write(i);
			}
			pipedOutputStream.close();
		} catch (Exception e) {
			e.printStackTrace();
		}

	}
}

class ThreadB extends Thread {
	PipedInputStream pipedInputStream = null;

	public ThreadB(ThreadA threadA) throws Exception {
		pipedInputStream = new PipedInputStream(threadA.getOut());
	}

	@Override
	public void run() {
		try {
			int len = -1;
			while ((len = pipedInputStream.read()) != -1) {
				System.out.print((char) len);
			}
			pipedInputStream.close();
		} catch (Exception e) {
			e.printStackTrace();
		}

	}
}

输出1:

ABCDEFGHIJKLMNOPQRSTUVWXYZ

1、新建一个ThreadA线程,ThreadA使用管道写出流不断写出,ThreadB中注入ThreadA引用,读取ThreadA写出流中的内容,不断打印到控制台。
2、不管哪个线程,run()方法中定义这个线程启动后具体干什么事情,ThreadA的run()方法就是不断写出,ThreadB的run()就是读取ThreadA写出流中的内容,不断打印到控制台。

6.3 面试金手指:管道流

Java管道流定义:实现两个线程之间的数据交互。
1、新建一个ThreadA线程,ThreadA使用管道写出流不断写出,ThreadB中注入ThreadA引用,读取ThreadA写出流中的内容,不断打印到控制台。
2、不管哪个线程,run()方法中定义这个线程启动后具体干什么事情,ThreadA的run()方法就是不断写出,ThreadB的run()就是读取ThreadA写出流中的内容,不断打印到控制台。

七、面试金手指

7.1 顺序流 / 合并流 (定义 + 作用)

合并流定义:JavaIO流——合并流,接收两个输入流作为参数,合并两个输入流或合并流,对外形成一个更大的流,方便客户端操作。

合并流需要注意的点:
1、合并流对应的对象是输入流,是对输入流合并,不是对输出流合并;
2、因为不同输入流有不同对应txt文件,用合并流将两个输入流合并后,可以输入两个文件,注意合并的是两个输入流而不是两个源文件。
3、合并流可以接收合并流作为实际参数,形成多个流的合并,也称合并嵌套。

7.2 对象流:提供一种序列化/反序列化的实现方式

1.1 序列化和反序列化定义:
序列化: 指把堆内存中的Java对象数据,通过某种方式把对象存储到磁盘文件中或者传递给其他网络的节点(在网络上传输). 我们把这个过程称之为序列化.
反序列化:把磁盘文件中的对象数据或者把网络节点上的对象数据,恢复成Java对象的过程.

1.2 为什么要做序列化:
1):在分布式系统中,需要共享的数据的JavaBean对象,都得做序列化,此时需要把对象再网络上传输,此时就得把对象数据转换为二进制形式.以后存储在HttpSession中的对象,都应该实现序列化接口(只有实现序列化接口的类,才能做序列化操作).
2):服务钝化:如果服务发现某些对象好久都没有活动了,此时服务器就会把这些内存中的对象,持久化在本地磁盘文件中(Java对象–>二进制文件). 如果某些对象需要活动的时候,现在内存中去寻找,找到就使用,找不到再去磁盘文件中,反序列化我们得对象数据,恢复成Java对象.

1.3 Java对象流——JavaIO流中实现序列化的方式
使用对象流来完成序列化和反序列化操作:
ObjectOutputStream: 通过writeObject方法做序列化操作的.
ObjectInputStream: 通过readObject方法做反序列化操作的.
ObjectOutputStream ObjectInputStream,提供一种序列化方式,序列化方式有很多,现在互联网大厂常用的是protobuf,最高效的系列化方式

代码1:因为实体类Person没有实现序列化接口Serializable,所以这里程序抛出异常java.io.NotSerializableException。

代码2:实体类Person实现序列化接口后,成功解决了程序异常,一般来说,随着项目的升级,系统的class文件也会升级(增加一个字段/删除一个字段), Java通过serialVersionUID(序列化版本号)来判断字节码是否发生改变.所以我们为实体类Person添加一个序列化版本号。

代码3:给实体类Person加上序列化版本号之后,整个问题正是解决。

JavaIO流——对象流通过writeObject()或readObject()实现序列化和反序列化,在磁盘读写和网络请求中保证数据的正确性。

7.3 与控制台相关:打印流 + 扫描流

打印流定义:
(1)打印到控制台;
(2)解释了System.out.println()底层原理,System.out.println();其实等价于 PrintStream ps = System.out; ps.println(),且见代码1;

扫描流定义:
与打印流对应,解释了控制到输入底层原理,本质上是java.util.Scanner类:扫描器类,表示输入操作,且见代码2。

代码1:打印流为Java提供了一种类似于C/C++的格式化打印,其实只是一种打印的补充格式。因为在Java语言中,System.out.println()通过+号重载可以实现字符串拼接,可以拼接出任何打印格式,所以说System.out.printf()只是提供了另一种打印格式的输出方式。

代码2:扫描类中,hasNextXxx()和nextXxx()两个方法,将字符串内容(也可以是其他内容txt、数组、boolean等)打印出来。

注意:Java BIO流中,打印流用于打印到控制台,扫描流用于读入,甚至可将两个流放在一起写一个demo,可以尝试一下。

7.4 数据流:提供了可以读/写任意数据类型的方法,两个实现类,每个实现类都有writeXxx()和readXxx()方法

数据流定义:提供了可以读/写任意数据类型的方法,两个实现类,每个实现类都有writeXxx()和readXxx()方法。
分别是:
DataOutputStream: 提供了 writeXxx(xxx value)方法.
DataInputStream: 提供了 readXxx()方法.
注意: writeXxx和readXxx必须要对应起来, writeByte写出的数据,此时只能使用readByte读取回来.且见代码1.

数据流的使用:提供了可以读/写任意数据类型的方法,任意数据类型是指8种基本类型(boolean char float double byte short int long),字符串(readChars、readUTF),数组(int数组、byte数组),均有相应的readXxx()和WriteXxx()方法。

7.5 管道流:实现两个线程之间的数据交互

Java管道流定义:实现两个线程之间的数据交互。
1、新建一个ThreadA线程,ThreadA使用管道写出流不断写出,ThreadB中注入ThreadA引用,读取ThreadA写出流中的内容,不断打印到控制台。
2、不管哪个线程,run()方法中定义这个线程启动后具体干什么事情,ThreadA的run()方法就是不断写出,ThreadB的run()就是读取ThreadA写出流中的内容,不断打印到控制台。

八、尾声

IO,对象传输的基石(二),完成了。

天天打码,天天进步!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

祖母绿宝石

打赏一下

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值