Java 21-5 工具流
用于解决问题的特有流
文本输入输出流
PrintStream(字节流) PrintWriter(字符流)
特点:打印 不抛出异常
解决问题:
方便的打印各种数据值形式
保证数值的表现形式不变
(写的是什么样子 目的地就是什么样子)
PrintWriter 一样具备打印功能
目的:File对象 字符串路径 字节输出流 字符输出流
System.out.println 利用的就是PrintStream方法
System.in&out
示例1 演示PrintStream方法
public class PrintStream_Test1 {
public static void main(String[] args) throws FileNotFoundException {
// 1.创建PrintStream对象 目的定位文件
PrintStream out=new PrintStream("D:\\IO\\Test1.txt");
// 将数据打印到文件中
out.write(97);
// 对于字节流的write方法 一次只写出一个字节 也就是将一个整数的最低8位写出
out.println(97);
// 原理 将数值转换成字符串
out.close();
}
}
示例2 从键盘输入字母 并转成大写返回到显示器上
public class PrintWriter_Test1 {
public static void main(String[] args) throws IOException {
BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
PrintWriter pw=new PrintWriter(System.out,true);
// 有缓冲区 做编码解码转换 true 为自动刷新功能
String line=null;
while ((line=br.readLine())!=null){
if (line.equals("over")) {
break;
}
pw.println(line.toUpperCase());}
pw.close();
}
}
序列流:流对象有序的排列 合并
SequenceInputStream
内部封装一个有序集合 将多个源封装成一个源 对应一个目的
将多个输入流 封装成一个输入流
特殊在构造器上一初始化就可以传入多个流
使用场景:对多个文件进行数据合并
用于操作对象的流对象。对象的序列化
ObjectInputStream ObjectOutputStream
构造方法
方法
常用到方法Collections.enumeration();
创建指定集合的枚举
示例1 将指定几个文件 合并成一个文件
public class SequenceInputStream_Test1 {
public static void main(String[] args) throws IOException {
// 如何获取一个Enumeration呢?vector
ArrayList<FileInputStream> a1=new ArrayList<>();
// 添加三个输出流对象,和指定的具体文件关联
for (int i = 1; i <4; i++) {
a1.add(new FileInputStream("D:\\IO\\"+i+".txt"));
}
// 创建指定集合枚举
// 没有的功能去Collection中找
Enumeration<FileInputStream> en= Collections.enumeration(a1);
// 创建序列流对象并添加枚举
SequenceInputStream sis=new SequenceInputStream(en);
FileOutputStream fos=new FileOutputStream("D:\\IO\\4.txt");
byte[] bytes=new byte[1024];
int len=0;
while ((len=sis.read(bytes))!=-1)
fos.write(bytes,0,len);
sis.close();
fos.close();
}
}
随机读取存储流
RandomAccessFile
只能操作文件
即能读又能写 维护了一个byte数组 内部定义了字节流
通过指针的操作可以实现对文件任意位置的读取和写入
特点: 随机写入 需要指针 指针位置与字节有关
功能:getFilePointer seek用于操作文件指针的方法
可以多线程写入
多个线程每个写一部分
该类的实例支持读取和写入随机访问文件。 随机访问文件的行为类似于存储在文件系统中的大量字节。 有一种游标,或索引到隐含的数组,称为文件指针 ; 输入操作读取从文件指针开始的字节,并使文件指针超过读取的字节。 如果在读/写模式下创建随机访问文件,则输出操作也可用; 输出操作从文件指针开始写入字节,并将文件指针提前到写入的字节。 写入隐式数组的当前端的输出操作会导致扩展数组。 文件指针可以通过读取getFilePointer方法和由设置seek方法。
String mode为对流的权限操作
mode值的意思是“R”打开仅供阅读。调用结果对象的任何写入方法都将导致引发IOException。”rw“开放阅读和写作。如果该文件不存在,则将尝试创建它。“RWS“与“rw”一样,开放读写,还要求对文件内容或元数据的每次更新都同步写入底层存储设备。”与“rw”一样,rwd“开放读写”,还要求对文件内容的每次更新都同步写入底层存储设备。
示例1 向文件中写入信息
public class RandomAccessFile_Test1 {
public static void main(String[] args) throws IOException {
writeFile();
}
private static void writeFile() throws IOException {
// 创建一个随机访问文件的对象
// 不存在创建 存在不创建 也不覆盖
RandomAccessFile raf=new RandomAccessFile("D:\\IO\\random.txt","rw");
// 写入姓名和年龄
raf.write("张三".getBytes());
raf.writeInt(97);//保证整数字节的原样性
raf.write("李四".getBytes());
raf.writeInt(98);//保证整数字节的原样性
raf.close();
}
}
示例2 指针操作 覆盖年龄 及打印
public class RandomAccessFile_Test2 {
public static void main(String[] args) throws IOException {
RandomAccessFile raf=new RandomAccessFile("D:\\IO\\random.txt","rw");
//设置指针位置
raf.seek(10);
raf.write("于松江".getBytes());
raf.writeInt(99);
// 打印指针所在位置
System.out.println(raf.getFilePointer());
raf.seek(0);
// 创建缓冲区数组将名字放入字节数组中
byte[] buf=new byte[6];
// 通过read自动解码
raf.read(buf);
String name=new String(buf);
int age=raf.readInt();
System.out.println(name+":"+age);
raf.close();
}
}
管道流
PipedInputStream PipedOutputStream
PipedReader PipedWriter
读取管道和写入管道可以连接
需要使用多线程技术 单线程死锁
管道输入流应连接到管道输出流; 管道输入流然后提供写入管道输出流的任何数据字节。 典型地,数据被从一个读PipedInputStream
对象由一个线程并且数据被写入到对应的PipedOutputStream
通过一些其它线程。 不建议尝试从单个线程使用这两个对象,因为它可能会使线程死锁。 管道输入流包含一个缓冲区,在读取操作中将读取操作与限制内的操作相分离。 的管道被认为是broken如果正在提供的数据字节到连接的管道输出流中的线程不再存活。
方法
示例1 连同管道 并输出
public class PipedStream_Test {
public static void main(String[] args) throws IOException {
// 创建管道对象
PipedInputStream pis=new PipedInputStream();
PipedOutputStream pos=new PipedOutputStream();
// 连接方法
pos.connect(pis);
// 启动多线程
new Thread(new Input(pis)).start();
new Thread(new OutPut(pos)).start();
}
}
//定义输入任务
class Input implements Runnable{
private PipedInputStream pis;
//从外界接受一个管道输入流进来
public Input(PipedInputStream pis) {
this.pis = pis;
}
@Override
public void run() {
byte[] bytes=new byte[2014];
int len= 0;
try {
// 读入到数组中
len = pis.read(bytes);
// 将字节数组整合成字符串
String s=new String(bytes,0,len);
System.out.println(s);
pis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
//定义输出任务
class OutPut implements Runnable{
private PipedOutputStream pos;
public OutPut(PipedOutputStream pos) {
this.pos = pos;
}
@Override
public void run() {
try {
// 写入到缓冲区中
pos.write("hi,管道来了".getBytes());
pos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
对象输入输出流 序列化和反序列化
ObjectInputStream
只有支持java.io.Serializable接口的对象才能写入流中。 每个可序列化对象的类被编码,包括类的类名和签名,对象的字段和数组的值以及从初始对象引用的任何其他对象的关闭。
方法writeObject用于将一个对象写入流中。 任何对象,包括字符串和数组,都是用writeObject编写的。 多个对象或原语可以写入流。 必须从对应的ObjectInputstream读取对象,其类型和写入次序相同。
原始数据类型也可以使用DataOutput中的适当方法写入流中。 字符串也可以使用writeUTF方法写入。
类的对象如果需要序列化 就需要实现一个Serializable标记接口
该接口给需要序列化的类提供了一个序列版本号 serialVersionUID
给被序列化的类添加的标识
序列号的目的在于验证序列化的对象和对应的类是否版本匹配
建立自己显示声明
private static final long serialVersionUID = 291613027204248679L;
private transient int age;
瞬态关键词 不需要持久化
serialVersionUID:
如果可序列化类没有显式声明serialVersionUID,则序列化运行时将根据Java(TM)对象序列化规范中所述的类的各个方面计算该类的默认serialVersionUID值。 但是, 强烈建议所有可序列化的类都明确声明serialVersionUID值,因为默认的serialVersionUID计算对类详细信息非常敏感,这可能会因编译器实现而异,因此可能会在反InvalidClassException化期间导致InvalidClassException的InvalidClassException。
因此,为了保证不同Java编译器实现之间的一致的serialVersionUID值,一个可序列化的类必须声明一个显式的serialVersionUID值。 还强烈建议,显式的serialVersionUID声明在可能的情况下使用private修饰符,因为这种声明仅适用于立即声明的类 - serialVersionUID字段作为继承成员无效。 数组类不能声明一个显式的serialVersionUID,所以它们总是具有默认的计算值,但是对于数组类,放弃了匹配serialVersionUID值的要求。
示例 将对象存储到文件中
public class ObjectOutputStream_Test1 {
public static void main(String[] args) throws IOException, ClassNotFoundException {
// ObjectWriter();//对象序列化
ObjectReader();//对象反序列化
}
private static void ObjectWriter() throws IOException {
FileOutputStream fos=new FileOutputStream("D:\\IO\\Person.object");
// 对象储存文件的规范
ObjectOutputStream oos=new ObjectOutputStream(fos);
oos.writeObject(new Person("asda",21));
oos.close();
// NotSerializableException - 要序列化的某些对象不实现java.io.Serializable接口。
// 实现Serializable实现序列化
}
//类没有找到异常
private static void ObjectReader() throws IOException, ClassNotFoundException {
FileInputStream fos=new FileInputStream("D:\\IO\\Person.object");
ObjectInputStream ois=new ObjectInputStream(fos);
Person obj=(Person) ois.readObject();
System.out.println(obj.toString());
}
}
public class Person implements Serializable{
private static final long serialVersionUID = 291613027204248679L;
// 给类显示名声一个序列版本号
private String name;
private int age;
// private transient int age;
//瞬态关键词 不需要持久化
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}