过滤流类型

过滤流类型

过滤流就是在节点流的基础上附加功能

处理类型字符流字节流
缓存BufferedReader、BufferedWriterBufferedInputStream、 BufferedOutputStream
过滤处理FilterReader、FilterWriterFilterInputStream、FilterOutputStream
桥接处理InputStreamReader OutputStreamWriter
对象序列化处理ObjectInputStream、ObjectOutputStream
数据转换DataInputStream、 DataOutputStream
行数统计LineNumberReaderLineNumberInputStream
回滚处理PushbackReaderPushbackInputStream
打印功能PrintWriterPrintStream

过滤流

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—BufferedReader

写出数据自定义过滤流SecurityWriter(FilterWriter)

package com.kang;

import java.io.FilterWriter;
import java.io.IOException;
import java.io.Writer;

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);
	}
}

package com.kang;

import java.io.FilterReader;
import java.io.IOException;
import java.io.Reader;

public class SercuityReader extends FilterReader {

	protected SercuityReader(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);

缓冲流###

缓冲流是套接在响应的节点流之上,对续写的数据提供缓冲的功能,提高读写的效率,同时增加了一些新方法

以介质是硬盘为例,字节流和字符流的弊端:在每一次读写的时候,都会访问硬盘。 如果读写的频率比较高的时候,其性能表现不佳。为了解决以上弊端,采用缓存流。

缓存流在读取的时候,会一次性读较多的数据到缓存中,以后每一次的读取,都是在缓存中访问,直到缓存中的数据读取完毕,再到硬盘中读取。

构造方法

BufferedReader(Reader)不定义缓存大小,默认8192

BufferedReader(Reader in, int size)size为自定义缓冲区的大小

BufferedWriter(Writer)

BufferedWriter(Writer out, int size)size为自定义缓冲区的大小

BufferedInputStream(InputStream)

BufferedInputStream(InputStream in, int size)size为自定义缓冲区的大小

BufferedOutputStream(OutputStream)

BufferedOutputStream(OuputStream out, int size)size为自定义缓冲区的大小

缓冲输入流的方法

BuffedReader提供了一个方法readLine():String,但是BufferedInputStream中并没有这个

  • BufferedReader提供了readLine方法用于读取一行字符串,以\r或\n分割(换行符)

如果读取内容为null,则表示读取到了流的末尾

readLine方法会自动剔除本行内容末尾的换行符

  • BufferedWriter提供了newLine方法用于写入一个行分隔符

对于输出的缓冲流,写入的数据会先在内存中缓存,使用flush方法会使内存中的数据立即写出

键盘录入

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);

打印流

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
//简单写法,应该使用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();

编码细节

1、需要通过对象流读写的对象必须实现了序列化接口,否则java.io.NotSerializableException

class User implements Serializable

2、Serializable接口是标志接口,没有需要实现的方法,所有的序列化和反序列化操作由VM负责实现。

Externalizable接口定义为public interface Externalizable extends java.io.Serializable,这个接口中包含两个方法需要实现writeExternal自定义实现对象的序列化,readExternal自定义实现对象的反序列化。除非特殊需求一般不使用Externalizable接口,因为没有必要自定义

class User implements Externalizable { 
    @Override public void writeExternal(ObjectOutput out) throws IOException { // 写出对象的操作
        System.out.println("现在需要写出对象:" + this); out.writeLong(this.id); 
        out.writeUTF(this.username); out.writeUTF(this.password); }
    @Override public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { // 读取对象的操作 
        this.id = in.readLong();
        this.username = in.readUTF(); 
        this.password = in.readUTF(); 
    }
    public User(){}

3、类型转换问题:

Object temp = ois.readObject();
if (temp != null && temp instanceof User) { 
    User user = (User) temp;
    System.out.println(user); 
}

4、private static Õnal long serialVersionUID = 6889840055394511246L

如果不添加序列号,则会有警告信息,但是不是错误信息

一般选择Add generated serial version ID会生成一个在项目中永不重复的的序列版本编号

序列版本号可以不用添加,这个序列版本号是一种序列化和反序列化中快速识别类型的简单方法,比不加序列号的识

别效率高。引入的功能是如果版本号不对应,不会进行类型识别,而是直接报异常InvalidClassException

5、一般针对敏感数据不应该进行序列化操作,针对不需要进行序列操作的属性可以添加一个关键字transient,表示该属性不参与序列化和反序列化操作

class User implements Serializable {
    private transient String password; //transient用于声明该属性不支持序列化操作
class User implements Serializable {
    private String username;
    private transient String password; 
    private Role role;//因为Role没有实现序列化接口,所以写出user对象时会有报错 NotSerializableException。处理报错的方法有:1、可以给Role类定义添加序列接口。2、在role属性上添加 transient表示这个属性不序列化处理

6、读文件的判断:读取文件时可以通过EOFException异常来判断文件读取结束

try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("users.data"));) {
    while (true) {
        try {
            Object temp = ois.readObject(); 
            if (temp != null && temp instanceof User) {
                User user = (User) temp; System.out.println(user); 
            }
        } catch (EOFException ex) { // 这个异常是用于判断文件结尾,所以不需要进行处理 
            break; 
        }
    }
}

已经向文件中写入数据后,继续追加存储,则读取数据会出现StreamCorruptedException

  • ObjectOutputStream oos=new ObjectOutputStream(new BuàeredOutputStream(new

FileOutputStream(“users.data”,true)));

// 向文件中追加新数据 true
// 首先读取数据,然后再统一写入 
Object[] arr = new Object[100];
int counter = 0;
File ff = new File("user.data");
if (ff.exists()) { 
    ObjectInputStream ois = new ObjectInputStream(new FileInputStream("user.data")); 
    while (true) {
        try {
            Object temp = ois.readObject();
            arr[counter++] = temp; 
        } catch (EOFException e) { 
            break; 
        } 
    }
    ois.close(); 
} // 追加数据 
User user = new User();
user.setId(299L); 
user.setUsername("name299"); 
user.setPassword("pwd299");
arr[counter++] = user; // 然后统一写出到文件中 
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("user.data"));
for (int i = 0; i < arr.length; i++) {
    Object temp = arr[i]; 
    if (temp != null) 
        oos.writeObject(temp);
}
oos.close();

问题:如果某个类的属性类型不是基本类型或者String类型,且没有实现可序列化接口,则该类型属性类是不可序列化

针对于对象中的InputStream/OutputStream之类的资源类型的属性,不仅不能进行序列化操作,而且在序列化之前应该释放资源,在反序列化后应该重新创建资源链接。Externalizable

class User implements Externalizable {
    private Long id; // 要求自增长
    private String username;
    private String password; 
    private InputStream is; //输入流不能被序列
    @Override 
    public void writeExternal(ObjectOutput out) throws IOException {
        //writeObject方法时调用 
        is.close(); //释放资源 
        out.writeLong(this.id);
        out.writeUTF(this.username);
    }
    @Override 
    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
        //readObject方法时执行
        this.id=in.readLong();
        this.username=in.readUTF();
        is=new FileInputStream("ddd.txt");//重新获取资源 } }
  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值