第8次文章:其他流
- 2019 年 10 月 8 日
- 笔记
各位小伙伴儿,大家周末愉快哈
1、字节数组 字节 节点流
在两台计算机上进行数据的相互的传输
输入流 :ByteArrayInputStream
使用方法:read(byte[] b,int off,int len) +close()
输出流 :ByteArrayOutputStream
使用方法:write(byte[] b,int off,int len) +toByteArray() + close() 不要使用多态
例如:
package com.peng.IO.others; import java.io.BufferedInputStream; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; public class ByteArrayDemo01 { public static void main(String[] args) throws IOException { read(write()); } /** * 写出操作 * 字节输出流与文件输出有些不同,因为字节输出流有新增方法,所以不可以使用多态 * @throws IOException */ public static byte[] write() throws IOException { //目标位置 byte[] dest ; //选择流 这里有不同点:没有将目标位置和输出流进行关联 ByteArrayOutputStream bos = new ByteArrayOutputStream(); //操作 写出 String msg = "字节输入流与文件输入流一致"; byte[] info = msg.getBytes(); bos.write(info, 0, info.length); //获取数据 dest = bos.toByteArray(); //释放资源 bos.close(); return dest; } /** * 字节输入流(ByteArrayInputStream)与文件输入流一致(FileInputStream) * @throws IOException */ public static void read(byte[] src) throws IOException { //数据源 使用外部传入的方式来获取 //选择流。通过流,打开另一端计算机的数据,所以不能在数据源的地方进行打印输出 InputStream is = new BufferedInputStream(//缓冲流 new ByteArrayInputStream(src)//节点流 ); //操作 byte[] info =new byte[1024]; int len = 0; while(-1 != (len=is.read(info))) { System.out.println(new String(info,0,len)); } //释放资源 is.close();//此处的close是一个空命令,两台计算机之间不需要释放资源 } }
在写出操作的时候,与文件输出流不同的是:字节输出流没有和写出的目标位置相关联,而是直接将内容写进字节数组输出流中,然后使用字节数组输出流将内容转化为字节数组,并返回给目标位置。
二、处理流
1、基本类型 + String 保留数据+类型
输入流:DateInputStream readXxx
输出流:DateOutputStream writeXxx
在之前的IO流中,我们仅仅是将字符相关的信息保存在了相应的文件中,却没有注意各个保存的内容参数的类型,比如int,double,float,long,String等等,那么这样的保存,在我们后期的调用相关数据时,难以快速确定各个参数的类型,所以会降低效率。现在使用DateInputStream和DateOutputStream时,就可以有效的避免这样的问题。保存数据的时候,同时保存数据相对应的类型。
例如下面的实例:
package com.peng.IO.others; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.io.InputStream; /** * 数据类型(基本类型+String)处理流 * 在保存时,同时保存数据以及其对应的数据类型 */ public class DataDemo2 { public static void main(String[] args) throws IOException { byte[] data = write(); read(data); System.out.println(data.length); } /** * 数据+类型 从字节数组中读取 * @throws IOException */ public static void read(byte[] src) throws IOException { //选择流 //选择字节输入流 InputStream is = new ByteArrayInputStream(src); DataInputStream dis = new DataInputStream( new BufferedInputStream( is ) ); //操作 读取的顺序必须与写出的顺序一致 必须存在才能读取 double point = dis.readDouble(); long num = dis.readLong(); String str = dis.readUTF(); System.out.println(point+"--->"+num+"--->"+str); //释放资源 dis.close(); } /** * 数据+类型 输出到字节数组 * @param destPath * @throws IOException */ public static byte[] write() throws IOException { double point = 2.5; long num = 100L; String str = "数据类型"; //创建目的地 byte[] dest = null; //选择流 //选择字节数组输出流 ByteArrayOutputStream bos = new ByteArrayOutputStream(); DataOutputStream dos = new DataOutputStream(//数据类型处理流 new BufferedOutputStream(//缓冲处理流 bos//建立联系 ) ); //写出的顺序很重要,因为后续在告诉用户的过程中,需要告诉用户第几个参数是什么含义 dos.writeDouble(point); dos.writeLong(num); dos.writeUTF(str); dos.flush(); //获取数据 dest = bos.toByteArray(); //释放资源 dos.close(); return dest; } }
在读取最后的写出结果时,可以直接识别出写出的结果是什么类型的,这样就可以大大的提高运行效率。
2、引用类型(对象) 保留数据+类型
反序列化:输入流:ObjectInputStream readObject()
序列化:输出流:ObjectOutputStream writeObject()
注意:
1)先序列化后反序列化;但序列化的顺序必须和序列化的顺序相同
2)不是所有的对象都可以序列化,需要对象实现接口java.io.Serializable
不是对象的所有属性都需要序列化,可以在不需要序列化的属性前面加上修饰符transient
例如:
1)创建Employee类,实现接口java.io.Serializable,同时利用transient修饰属性name,使得name不进行序列化。
package com.peng.IO.others; /** * 空接口只是一个标识 * */ public class Employee implements java.io.Serializable{//此处是一个空接口 //transient表示该属性不需要序列化 private transient String name; private double salary; public Employee(String name, double salary) { super(); this.name = name; this.salary = salary; } public Employee() { } public String getName() { return name; } public void setName(String name) { this.name = name; } public double getSalary() { return salary; } public void setSalary(double salary) { this.salary = salary; } }
2)对Employee对象进行序列化写出以及反序列化读取
package com.peng.IO.others; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.util.Arrays; /** * 数据类型(不仅限于基本数据类型,可以为引用数据类型,对象)处理流 * 1、序列化------>写出 ObjectOutputStream * 2、反序列化----->读取 ObjectInputStream * * 不是所有的对象都可以序列化 java.io.NotSerializableException * 不是所有的属性都需要序列化 transient * */ public class ObjectDemo01 { public static void main(String[] args) throws IOException, ClassNotFoundException { seri("E:\java学习\test\seri.txt"); read("E:\java学习\test\seri.txt"); } //反序列化 读取 public static void read(String srcPath) throws FileNotFoundException, IOException, ClassNotFoundException { //创建源 File src = new File(srcPath); //选择流 ObjectInputStream ObjectInputStream ois = new ObjectInputStream( new BufferedInputStream( new FileInputStream(src) ) ); //操作 Object obj = ois.readObject(); if (obj instanceof Employee) { Employee temp = (Employee) obj; System.out.println(temp.getName()); System.out.println(temp.getSalary()); } Object obj1 = ois.readObject(); int[] arr = (int[]) obj1; System.out.println(Arrays.toString(arr)); //释放资源 ois.close(); } //序列化 写出 public static void seri(String destPath) throws FileNotFoundException, IOException { Employee temp = new Employee("鹏程万里",1000); int[] arr = {1,2,3,4,5}; //创建源 File dest = new File(destPath); //选择流ObjectOutputStream ObjectOutputStream dos = new ObjectOutputStream( new BufferedOutputStream(//缓冲流 new FileOutputStream(dest)//节点流 ) ); dos.writeObject(temp); dos.writeObject(arr); dos.flush(); dos.close(); } }
3)查看结果
null 1000.0 [1, 2, 3, 4, 5]
由于我们使用了transient对Employee对象的name属性进行了修饰,所以在反序列化(读取)的时候,并没有输出Employee对象的name属性。
注意:
1)数组也属于一个对象,所以在序列化的时候,可以将数组也作为一个对象进行序列化和反序列化。
2)在反序列化(读取)时,读取的顺序必须与序列化(写出)的顺序相同,所以在写出的时候,需要注意写出的对象的顺序,避免后期读取的时候报错。
3)写出的文件内容是给计算机查看的内容,保留有各个属性的类型,所以我们查看路径“E:java学习testseri.txt”的内容时,所看到的是一堆乱码。如下图所示:

三、打印流
1、在java编程中,我们经常使用System.out.println()命令,将我们想要看到的变量打印在控制台上,便于我们看到变量的具体内容。而System.out.println()命令也属于IO流中的一种,也属于处理流中的一种。打印流的使用方法,也和其他的处理流的使用方法相同:
package com.peng.IO.others; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.PrintStream; /** * PrintStream 打印流---->处理流 */ public class PrintStreamDemo01 { public static void main(String[] args) throws FileNotFoundException { System.out.println("test"); PrintStream ps = System.out; ps.println(false); //输出到文件 File src = new File("E:\java学习\test\print.txt"); ps = new PrintStream(//处理流 new BufferedOutputStream(//缓冲流 new FileOutputStream(src)//节点流--- 字节流 ) ); ps.println("peng is very good"); ps.flush(); ps.close(); } }
通过上面的代码可以看出来,打印流(PrintStream)使用的是其自己的方法println方法,将字符串写出到目标文件中。
2、在打印流中,有几个基本的知识点这里提一下:
1)三个常量:
- System.in 输入流—–键盘输入
- System.out 输出流—–控制台输出
- System.err 输出流—–控制台输出 其中,System.out和System.err的功能完全相同,都是将相应的提示信息输出到控制台上,唯一的差别就是在控制台上输出的信息的字体颜色不同,System.out输出到控制台上的字体颜色默认为黑色,而System.err输出到控制台上的字体颜色默认为红色。
2)重定向
由于在上述的三个常量,默认的输入与输出都是在控制台上进行,假如用户需要将信息输出到相应的文本文件中,而不是显示在控制台上,那么就需要使用重定向,将其输入与输出的位置进行重新关联。
例如:
package com.peng.IO.others; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileDescriptor; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.InputStream; import java.io.PrintStream; import java.util.Scanner; public class SystemDemo01 { public static void main(String[] args) throws FileNotFoundException { test(); } public static void test() throws FileNotFoundException { //重定向 System.setOut(new PrintStream(//本质上还是一个输出流 ---- 处理流 new BufferedOutputStream( new FileOutputStream( new File("E:/java学习/test/print.txt") ) ),true ) ); //此时的输出已经不在控制台中了,将所需要输出的字符输出到指定的文件中 System.out.println("test");//控制台----->文件 System.out.println("one produce two, two produce three,three produce all things."); //重新回到控制台 System.setOut(new PrintStream( new BufferedOutputStream( new FileOutputStream( FileDescriptor.out ) ),true ) ); System.out.println("back........."); } }
重定向主要使用三条命令进行实现:SetIn();SetOut();SetErr(),三条命令分别设定输入,输出,以及输出错误信息的位置。
控制台的目标位置同样使用三条命令进行实现:FileDescriptor.in;FileDescriptor.out;FileDescriptor.err,三条命令分别代表输入,输出和错误信息在返回到控制台上的位置。
四、装饰设计模式
1、类与类之间的关系
1)依赖:形参||局部变量
2)关联:属性
聚合:属性 整体与部分 不一致的生命周期 人与手
组合:属性 整体与部分 一致的生命周期 人与大脑
3)继承:父子类关系
4)实现:接口与实现类的关系
2、IO流中使用的属于装饰设计模式
也就是利用不同的处理流,对节点流进行包装处理。比如在老师讲课的时候,使用扩音器,就属于将老师的讲课声音进行了扩大,对原声进行了装饰。
例如:
1)我们先建立一个声音类
package com.peng.IO.pattern; public class Voice { private int voice = 10; public Voice() { } public Voice(int voice) { super(); this.voice = voice; } public int getVoice() { return voice; } public void setVoice(int voice) { this.voice = voice; } public void say() { System.out.println(voice); } }
2)然后建立一个扩音器
package com.peng.IO.pattern; public class Amplifier { private Voice voice; public Amplifier() { } public Amplifier(Voice voice) { super(); this.voice = voice; } public Voice getVoice() { return voice; } public void setVoice(Voice voice) { this.voice = voice; } public void say() { System.out.println(voice.getVoice()*1000); } }
3)最后应用一下
package com.peng.IO.pattern; public class App { public static void main(String[] args) { Voice v =new Voice(20); v.say(); Amplifier am = new Amplifier(v); am.say(); } }
最终的结果当然就是我们将声音的数值扩大了1000倍,这就是简单的使用了装饰模式。类比在IO流的使用上,我们依旧是首先利用处理流,使用处理流对缓冲流进行包装,然后再利用缓冲流对节点流或者字符流进行包装,最后实现我们需要的功能。