流类型—下

今日总结:

打印流

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有自动的ush功能 textOut.flushBuffer();
PrintWriter(Writer)
PrintWriter(Writer out, boolean autoFlush)自动刷新----println

PrintWriter(OutputStream out) //参数是一个字节流,但是不需要通过桥接处理
PrintWriter(OutputStream out, boolean autoFlush)
PrintStream(OutputStream)
PrintStream(OutputStream out, boolean autoFlush)

对象流

使用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();

读写一个对象的前提是这个类型的对象是可以被序列化的;
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();

编码细节

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
    BueredOutputStream(newFileOutputStream(“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");//重新获取资源
}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值