流模型【二】

今天继续针对流模型进行了深入的学习,了解到了过滤流,桥接流,缓冲流等使用方法。

一、过滤流类型

在这里插入图片描述
过滤流

FilterInputStream/FilterOutputStream和FilterReader/FilterWriter
	public class FilterInputStream extends InputStream {
    
     //典型的装饰模式
		protected volatile InputStream in; //被装饰目标
		protected FilterInputStream(InputStream in) {
    
     //通过构造器组装被装饰对象
		this.in = in;
	       	}
		public int read() throws IOException {
    
    //调用Filter中的read方法时实际操作是由被装饰对象实现的
		return in.read();
		     } 
		}

所谓的过滤流实际上就是类似上面的加密处理,在输入之后(后置处理,被装饰对象先执行)或者输出之前(前置处理,先处理然后被装饰对象执行)进行一下额外的处理,最终实际操作是调用被装饰对象的方法完成工作,依靠这种装饰模式实现在节点流的基础上附加额外功能.当然也允许多个过滤流嵌套从而达到功能累加的目的,FilterInputStream实际上就是一个装饰抽象角色。

  • 自定义流实现循环13加密:
  • 读取数据不变:FileReader—BuàeredReader
  • 写出数据自定义过滤流SecurityWriter(FilterWriter)
public class SecurityWriter extends FilterWriter {
    
    
	protected SecurityWriter(Writer out) {
    
    
		super(out);
	}
	public void write(int c) throws IOException {
    
    
		if (c >= 'a' && c <= 'z') {
    
    
			c = (c - 'a' + 13) % 26 + 'a';
		} else if (c >= 'A' && c <= 'Z') {
    
    
			c = (c - 'A' + 13) % 26 + 'A';
		}
		super.write(c);
	}
}
public class SecurityReader extends FilterReader {
    
    
		protected SecurityReader(Reader in) {
    
    
			super(in);
		}
		public int read() throws IOException {
    
    
			int c = super.read();
			if (c >= 'a' && c <= 'z') {
    
    
				c = (c - 'a' + 13) % 26 + 'a';
			} else if (c >= 'A' && c <= 'Z') {
    
    
				c = (c - 'A' + 13) % 26 + 'A';
			}
			return c;
		}
	}

二、桥接转换流

InputStreamReader和OutputStreamWriter提供了字节流和字符流之间的桥接转换功能,用于与字节数据到字符
数据之间的转换,无需编程实现将字节拼接为字符转换流可以在构造时指定其编码字符集
InputStreamReader用于将一个InputStream类型的输入流自动转换为Reader字符流
OutputStreamWriter用于将一个Writer字符输出流转换为OutputStream字节输出流

InputStreamReader构造器
InputStreamReader(InputStream)
InputStreamReader(InputStream, String)
InputStreamReader(InputStream, Charset)
InputStreamReader(InputStream, CharsetDecorder)

Reader r = new InputStreamReader(System.in);
	int kk=r.read(); // 例如输入的是“中国”,这里实际读取的是"中"
	// 因为这里读取的是一个字节,所以输入"中国",实际读取的是"中"的一个字节,输出显示为?
	kk=System.in.read();System.out.println("输入的是:"+(char)kk);

InputSteram is=new InputStreamReader(System.in,”iso8859-1”);

Reader r=new InputStreamReader(System.in, "gbk");
int kk=r.read(); //例如输入的是"中国",实际读取的是"中"
System.out.println("输入的是:"+(char)kk);

缓冲流是套接在响应的节点流之上,对续写的数据提供缓冲的功能,提高读写的效率,同时增加了一些新方法。
以介质是硬盘为例,字节流和字符流的弊端:在每一次读写的时候,都会访问硬盘。 如果读写的频率比较高的时候,其性能表现不佳。为了解决以上弊端,采用缓存流。
缓存流在读取的时候,会一次性读较多的数据到缓存中,以后每一次的读取,都是在缓存中访问,直到缓存中的数据读取完毕,再到硬盘中读取

构造方法
BuàeredReader(Reader)不定义缓存大小,默认8192
BuàeredReader(Reader in, int size)size为自定义缓冲区的大小
BuàeredWriter(Writer)
BuàeredWriter(Writer out, int size)size为自定义缓冲区的大小
BuàeredInputStream(InputStream)
BuàeredInputStream(InputStream in, int size)size为自定义缓冲区的大小
BuàeredOutputStream(OutputStream)
BuàeredOutputStream(OuputStream out, int size)size为自定义缓冲区的大小
缓冲输入流的方法BuàedReader提供了一个方法readLine():String,但是BuàeredInputStream中并没有这个BuàeredReader提供了readLine方法用于读取一行字符串,以\r或\n分割(换行符)如果读取内容为null,则表示读取到了流的末尾readLine方法会自动剔除本行内容末尾的换行符BuàeredWriter提供了newLine方法用于写入一个行分隔符对于输出的缓冲流,写入的数据会先在内存中缓存,使用Öush方法会使内存中的数据立即写出

键盘录入
System.in:InputStream用于指代系统默认的输入设备—键盘
方法read():int 可以实现代码执行到这里则会阻塞等待,只要输入数据为止

BufferedReader br = new BufferedReader(new InputStreamReader(System.in));System.out.println("输入数据");
	String temp="";
	while((temp=br.readLine()).trim().length()>0){
    
    
		if ("quit".equals(temp))
			break;
		System.out.println(temp);
	}br.close();
BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(System.out));
bw.write("只有缓冲区满才自动进行输出显示");
bw.flush(); //刷新缓冲区,否则看不到输出内容
System.in.read();
bw.close(); //关闭输出时会首先自动进行刷新缓冲区

三、数据流

DataInputStram和DataOutputStream分别继承自InputStream和OuputStream,属于过滤流,需要分别套接在
InputStream和OutputStream类型的节点流上只有字节流,没有对应的字符流
DataInputStream和DataOutputStream提供了可以存取与机器无关的Java原始类型数据的方法
DataInputSteram和DataOutputStream构造方法为
DataInputStream(InputStream)
DataOutputStream(OutputStream)读取、写出一个double数据到文件中

//使用数据流就可以直接操作简单类型数据
double dd=123.4567;
FileOutputStream fos=new FileOutputStream("d:\\a.data");
fos.write((dd+"").getBytes());
fos.close();
//如果不使用数据流,则需要额外编码进行数据类型转换
FileInputStream fis=new FileInputStream("d:/a.data");
byte[] buffer=new byte[8192];
int len=fis.read(buffer);
fis.close();
String str=new String(buffer,0,len);
double dd=Double.parseDouble(str);
System.out.println(dd);

加入需要写一个double,然后一个String,然后再一个int
需要将输入内容转换为String,并且为了区分数据需要引入特殊符号,例如@@,输入数据为123.456@@shi yazhou@@12。从功能角度上说没问题,但是编码太复杂了,所以引入Data类型的输入输出流

//这里不使用OutputStream定义变量的原因是:需要使用DataOutputStream中定义的特殊方法,而不是父类中
定义的通用方法
DataOutputStream dos=new DataOutputStream(new FileOutputStream("d:\\a.data"));
dos.writeDouble(123.456);
dos.writeChars("赵天爱小猴!");
dos.writeInt(12);
dos.close();

由于读取出现问题,针对中间的String数据引入一个额外的数据,其中存储String的char个数
写出数据

double salary=123.456;
String ss="赵天爱小猴,小猿爱小主,小胡招小天";
int age=12;
DataOutputStream dos=new DataOutputStream(new BufferedOutputStream(new
FileOutputStream("d:\\aa.data")));
dos.writeDouble(salary);
dos.writeInt(ss.length());
dos.writeChars(ss);
dos.writeInt(age);
dos.close();

读取数据

double salary=0;
String str="";
int age=0;
//读取数据的前提是必须知道数据的结构
DataInputStream dis=new DataInputStream(new BufferedInputStream(new
FileInputStream("d:\\aa.data")));
salary=dis.readDouble();
StringBuilder sb=new StringBuilder();
int len=dis.readInt();
for(int i=0;i<len;i++) sb.append(dis.readChar());
str=sb.toString();
age=dis.readInt();
System.out.println(salary+"---"+str+"---"+age);

注意:读取数据判断文件结束EOFException,这里没有-1
在具体应用中建议针对字串使用readUTF和writeUTF

DataOutputStream dos=new DataOutputStream(new FileOutputStream("data2.txt"));
dos.writeDouble(1234.56);
String str="猴子愛小終,小終愛信心";
dos.writeUTF(str);
dos.writeInt(99);
dos.close();
DataInputStream dis = new DataInputStream(new FileInputStream("data2.txt"));
double d1 = dis.readDouble();
String ss=dis.readUTF();
int kk = dis.readInt();
System.out.println(d1 + "\t" + ss + "\t" + kk);
dis.close();

四、打印流
PrintStream和PrintWriter都属于输出流,分别针对字节和字符
PrintWriter和PrintStream都提供了重载的print和println方法用于输出多种类型数据
print(Object):void
输出引用类型,实际上是调用对象的toString方法转换为String进行输出

public void println(Object x) {
    
    
		String s = String.valueOf(x); // 调用String类中的静态方法将object类型的数据转换为字符串
		synchronized (this) {
    
    
			print(s);
			newLine(); // print('\n')
		}
	}
	// String中的valueOf方法的定义
	public static String valueOf(Object obj) {
    
    
		return (obj == null) ? "null" : obj.toString(); // 如果输出对象非空,则调用对象的toString方法
	}
}

println表示输出后自动换行
PrintWriter和PrintStream的输出操作不会抛出异常,用户通过检测错误状态获取错误信息
PrintWriter和PrintStream有自动的flush功能 textOut.flushBuffer();

PrintWriter(Writer)
PrintWriter(Writer out, boolean autoFlush)自动刷新----println

五、对象流

使用DataInputStream或者DataOutputStream可以读写对象数据,但是操作比较繁琐

	// 从文件中按照id值查找对应的对象
	int id = dis.readInt(); // 用户id--用户标识
	int len = dis.readInt(); // 用户名称的字符数
	StringBuilder username = new StringBuilder(); // 用户名称
	for (int i = 0; i < len; i++) // 一次读取一个字符,然后拼接成完整的字符串
		username.append(dis.readChar());
	len = dis.readInt();
	StringBuilder password = new StringBuilder(); // 用户口令
	for (int i = 0; i < len; i++)
		password.append(dis.readChar());
	double balance = dis.readDouble(); // 用户余额
	if (dis == id) {
    
    
		res = new Account(); // Account是一个自定义类型,用于封装账户信息
		res.setUsername(username.toString());
		res.setPassword(password.toString());
		res.setBalance(balance);
		break;
	}
}

SUN提供了ObjectInputStream/ObjectOutputStream可以直接将Object写入或读出
这里实际上还有针对8种简单类型及其包装类的操作方法,以及针对String类型的操作方法
readObject():Object
writeObject(Object):void

public static void main(String[] args) {
    
    
		// 简单写法,应该使用try/finally结构或者使用try/resource的写法
		Date now = new Date();
		ObjectOutputStream oos = new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream("data3.txt")));
		oos.writeObject(now);
		oos.close();
		ObjectInputStream ois = new ObjectInputStream(new BufferedInputStream(new FileInputStream("data3.txt")));
		Object obj = ois.readObject();
		if (obj != null && obj instanceof Date) {
    
    
			Date dd = (Date) obj;
			System.out.println(dd);
		}
		ois.close();
	}

读写一个对象的前提是这个类型的对象是可以被序列化的;
NotSerializableException
对象序列化【简单来说就是将对象可以直接转换为二进制数据流】/对象的反序列化【可以将二进制数据流转换为对象】,这一般依靠JVM实现,编程中只做声明对象序列化的目标是将对象保存到磁盘中,或允许在网络中直接传输对象。对象序列化机制允许把内存中的Java对象转换成平台无关的二进制流,从而允许把这种二进制流持久地保存在磁盘上,通过网络将这种二进制流传输到另一个网络节点,其他程序一旦获取到这种二进制流,都可以将这种二进制流恢复成原来的Java对象.

1、如何声明对象所属于的类可以进行序列化和反序列化Serializable/Externalizable接口其中的接口没有任何定义,仅仅只起到了说明的作用,这种接口叫做标志接口或者旗标接口
2、可以通过ObjectInputStream【readObject():Object】和ObjectOutputStream【writeObject(Object):void】提供的方法直接操作对象
3、输出对象

User user = new User();
user.setId(100L);
user.setUsername("zhangsan");
user.setPassword("123456");
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("users.data"));
oos.writeObject(user);
oos.close();

4、读取对象

ObjectInputStream ois = new ObjectInputStream(new FileInputStream("users.data"));
Object temp = ois.readObject();
if (temp != null && temp instanceof User) {
    
    
        User user = (User) temp;
        System.out.println(user);
      }
ois.close();

猜你喜欢

转载自blog.csdn.net/Lecheng_/article/details/113196184