目录
Java流模型 --- (下)
过滤流类型
过滤流就是在节点流的基础上附加功能。
过滤流使用必须有对应的节点流,因为过滤流是装饰节点流,不是有具体的操作目标。
//Java 内置类 FilterInputStream
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加密:
package IO;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FilterReader;
import java.io.IOException;
import java.io.Reader;
public class D24 {
public static void main(String[] args) throws IOException {
Reader a = new JiaMi(new FileReader("C:/Users/苑/Desktop/JAVA_DY/JiaMi.txt"));
int cc = 0;
while ((cc = a.read()) != -1) {
System.out.print((char) cc);
}
a.close();
}
}
class JiaMi extends FilterReader {// 装饰方实例
protected JiaMi(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字节输出流。
package IO;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
public class D26 {// InputStreamReader用于将一个InputStream类型的输入流自动转换为Reader字符流
public static void main(String[] args) throws IOException {
// InputSteram is=new InputStreamReader(System.in,”iso8859-1”);
Reader a = new InputStreamReader(System.in, "gbk");// System内部类的静态属性 public final static InputStream in = null;
System.out.println("请在控制台进行输入:");
int kk = a.read();// InputStreamReader对象 调用read 在控制台读取
int bb = System.in.read();// 这个是直接用InputStream类型读取
System.out.println((char) kk);// 只能输出一个字符
System.out.println((char) bb);// 只能输出一个字节 实际读取的是"中"的一个字节,输出显示为?
}
}
/*
* 请在控制台进行输入:
* 中国
* 中国
* 中
* ?
*/
缓冲流
- 缓冲流是套接在响应的节点流之上,对续写的数据提供缓冲的功能;
- 提高读写的效率,同时增加了一些新方法
- 以介质是硬盘为例,字节流和字符流的弊端:在每一次读写的时候,都会访问硬盘。 如果读写的频率比较高的时候,其性能表现不佳。
- 为了解决以上弊端,采用缓存流。缓存流在读取的时候,会一次性读较多的数据到缓存中,以后每一次的读取,都是在缓存中访问,直到缓存中的数据读取完毕,再到硬盘中读取。
- 使用缓冲流并没有添加什么额外方法,只是它能够在执行过程中自动引入缓存,从而提高执行效率 。
构造方法
- 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 可以实现代码执行到这里则会阻塞等待,只要输入数据为止。
package IO;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class D27 {
public static void main(String[] args) throws IOException {
BufferedReader a = new BufferedReader(new InputStreamReader(System.in));
System.out.println("输入数据:");
String temp = "";
while ((temp = a.readLine()).trim().length() > 0) {
if ("quit".equals(temp))
break;
System.out.println(temp);
}
}
}
/*
输入数据:
daiyuan'
daiyuan'
sd
sd
quit
*/
package IO;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintStream;
public class D28 {
public static void main(String[] args) throws IOException {
BufferedWriter a = new BufferedWriter(new OutputStreamWriter(System.out));// public final static PrintStream out = null;
a.write("只有缓冲区满才自动进行输出显示");
a.flush(); // 刷新缓冲区,否则看不到输出内容
System.in.read();//控制台输入
a.close(); // 关闭输出时会首先自动进行刷新缓冲区
}
}
- 执行close方法会自动关闭被装饰的对象,所以不需要再关闭FileReader和FileWriter。
- 执行flush会自动刷新数据到节点流上,但是并没有执行关闭流。针对输出流关闭时会自动先flush缓存再执行关闭。
数据流
DataInputStram和DataOutputStream
分别继承自
InputStream
和
OuputStream
,属于过滤流。
需要分别套接在
InputStream
和
OutputStream
类型的节点流上
只有字节流,没有对应的字符流
DataInputStream
和
DataOutputStream
提供了可以存取与机器无关的
Java原始类型数据的方法
DataInputSteram
和
DataOutputStream
构造方法为
- DataInputStream(InputStream) ;
- DataOutputStream(OutputStream)。
package IO;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class D29 {//读取、写出一个double数据到文件中
public static void main(String[] args) throws IOException {
// 使用数据流就可以直接操作简单类型数据
double dd = 123.456789;
FileOutputStream fos = new FileOutputStream("C:/Users/苑/Desktop/JAVA_DY/JAVA2/src/IO/dddd.txt",true);
fos.write((dd + " ").getBytes());// String.getBytes(String decode)方法会根据指定的decode编码返回某字符串在该编码下的byte数组表示:
fos.close();
}
}
需要写一个
double
,然后一个
String
,然后再一个
int
需要将输入内容转换为
String
,并且为了区分数据需要引入特殊符号,例如
@@
,输入数据为
123.456@@shi zxy@@12
。从功能角度上说没问题,但是编码太复杂了,所以引入
Data
类型的输入输出流。
package IO;
import java.io.DataOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class D30 {
public static void main(String[] args) throws IOException {
// 这里不使用OutputStream定义变量的原因是:需要使用DataOutputStream中定义的特殊方法,而不是父类中 定义的通用方法
DataOutputStream dos = new DataOutputStream(new FileOutputStream("C:/Users/苑/Desktop/JAVA_DY/JAVA2/src/IO/dddd.txt", true));
dos.writeDouble(123.456);
dos.writeChars("!");
dos.writeInt(12);
dos.close();
}
}
package IO;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class D32 {
public static void main(String[] args) throws IOException {//读取数据判断文件结束EOFException,这里没有-1 在具体应用中建议针对字串使用readUTF和writeUTF
DataOutputStream dos = new DataOutputStream(new FileOutputStream(("C:/Users/苑/Desktop/JAVA_DY/JAVA2/src/IO/aaaa.txt")));
dos.writeDouble(6666.66);// 读取数据的前提是必须知道数据的结构
String str = "我还年轻吃苦趁现在!";
dos.writeUTF(str);//针对字符串的
dos.writeInt(99);
dos.close();
DataInputStream dis = new DataInputStream(new FileInputStream(("C:/Users/苑/Desktop/JAVA_DY/JAVA2/src/IO/aaaa.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。
println表示输出后自动换行
- PrintWriter和PrintStream的输出操作不会抛出异常,用户通过检测错误状态获取错误信息 ;
- PrintWriter和PrintStream有自动的flush功能 textOut.flushBuffer();
- PrintWriter(Writer) ;
- PrintWriter(Writer out, boolean autoFlush)自动刷新----println 。
PrintWriter(OutputStream out) 参数是一个字节流,但是不需要通过桥接处理。
对象流
使用DataInputStream或者DataOutputStream可以读写对象数据,但是操作比较繁琐 。
SUN
提供了
ObjectInputStream/ObjectOutputStream
可以直接将
Object
写入或读出,实际上还有针对
8
种简单类型及其包装类的操作方法,以及针对
String
类型的操作方法 。
- readObject():Object
- writeObject(Object):void
读写一个对象的前提是这个类型的对象是可以被序列化的;
对象的序列化的实现方便了对象间的克隆,使得复制品实体的变化不会引起原对象实体的变化。
对象序列化【简单来说就是将对象可以直接转换为二进制数据流】
对象的反序列化【可以将二进制数据流转换为对象】,这一般依靠
JVM
实现
编程中只做声明对象序列化的目标是将对象保存到磁盘中,或允许在网络中直接传
输对象。对象序列化机制允许把内存中的
Java
对象转换成平台无关的二进制流,从而允许把这种二进制流持久地保
存在磁盘上,通过网络将这种二进制流传输到另一个网络节点,其他程序一旦获取到这种二进制流,都可以将这种
二进制流恢复成原来的
Java
对象。
- 如何声明对象所属于的类可以进行序列化和反序列化Serializable/Externalizable接口
- 其中的接口没有任何定义,仅仅只起到了说明的作用,这种接口叫做标志接口或者旗标接口 。
- 可以通过ObjectInputStream【readObject():Object】和ObjectOutputStream【writeObject(Object):void】提供的方法直接操作对象 。
package IO;
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.io.Serializable;
public class D33 {
public static void main(String[] args) throws IOException, IOException, ClassNotFoundException {
User user = new User();
user.setId(100);
user.setUsername("zhangsan");
user.setPassword("123456");
//输出对象
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("C:/Users/苑/Desktop/JAVA_DY/JAVA2/src/IO/abcd.txt",true));
oos.writeObject(user);
oos.close();
// 读取对象
ObjectInputStream ois = new ObjectInputStream(
new FileInputStream("C:/Users/苑/Desktop/JAVA_DY/JAVA2/src/IO/abcd.txt"));
Object temp = ois.readObject();
if (temp != null && temp instanceof User) {
User users = (User) temp;
System.out.println(users);
ois.close();
}
}
}
class User implements Serializable {
private long id;
private String Username;
private String Password;
public void setId(long l) {
this.id = l;
}
public void setUsername(String string) {
this.Username = string;
}
public void setPassword(String string) {
this.Password = string;
}
}
关于对象流的实际应用(用户注册登录)
package IO;
import java.io.IOException;
public interface UseDao {// 接口
public boolean login(UserBean userBean) throws IOException;// 登录判定
public void creat(UserBean userBean) throws IOException, ClassNotFoundException;// 创建用戶信息
}
package IO;
import java.io.*;
public class UserDao implements UseDao {
public boolean login(UserBean userBean) throws IOException {// 登录判定
ObjectInputStream ob = new ObjectInputStream(new BufferedInputStream(
new FileInputStream("C:/Users/苑/Desktop/JAVA_DY/JAVA2/src/IO/abcd.txt")));
boolean flag = false;
while (true) {
try {
Object o = ob.readObject();
System.out.println(o);
if (o instanceof UserBean) {
UserBean u = (UserBean) o;
if (u.getId().equals(userBean.getId()) && u.getPassword().equals(userBean.getPassword())) {
flag = true;
break;
}
}
} catch (Exception e) {
break;
}
}
ob.close();
return flag;
}
public void creat(UserBean userBean) throws IOException, ClassNotFoundException {// 注册时调用创建
// ByteArrayOutputStream byo = new ByteArrayOutputStream();
InputStream inputStream = new FileInputStream("C:/Users/苑/Desktop/JAVA_DY/JAVA2/src/IO/abcd.txt");
FileOutputStream fileOutputStream = new FileOutputStream(
"C:/Users/苑/Desktop/JAVA_DY/JAVA2/src/IO/abcd.txt");
int kk;
while ((kk = inputStream.read()) > -1) {
fileOutputStream.write(kk);
}
inputStream.close();
fileOutputStream.close();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new BufferedOutputStream(
new FileOutputStream("C:/Users/苑/Desktop/JAVA_DY/JAVA2/src/IO/abcd.txt", true)));
objectOutputStream.writeObject(userBean);
objectOutputStream.close();
}
}
package IO;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Serializable;
class UserBean implements Serializable {// 声明对象所属于的类可以进行序列化和反序列化Serializable/Externalizable接口
// ,接口没有任何定义,仅仅只起到了说明的作用,这种接口叫做标志接口或者旗标接口 。
private String id; // 私有用户属性
private String password; // 私有密码属性
@Override
public String toString() { // 打印UserBean对象
return "UserBean{" + "id='" + id + '\'' + ", password='" + password + '\'' + '}';
}
public String getId() { // 获取私有id属性
return id;
}
public void setId(String id) { // 设置私有id属性
this.id = id;
}
public String getPassword() { // 获取私有password属性
return password;
}
public void setPassword(String password) { // 设置私有password属性
this.password = password;
}
}
public class UserManager {
private static BufferedReader bf = new BufferedReader(new InputStreamReader(System.in));
private static UserDao userDao = new UserDao();// 静态属性
public static void main(String[] args) throws IOException, ClassNotFoundException {
while (true) {
System.out.println("1---登录");
System.out.println("2---注册");
String s = bf.readLine();// 缓存流接收一个字符输入
switch (s) {
case "1":
login();// 登陆判定
break;
case "2":
creat();// 用来注册登陆需要的信息
break;
case "exit":
System.exit(0);
System.out.println("bye~");
default:
System.out.println("输入不合法");
}
}
}
public static void creat() throws IOException, ClassNotFoundException {// 注册方法
UserBean u = new UserBean();// new 一个UserBean的对象
u.setId(getString("输入用户名:"));// 调用setId方法给私有属性id赋值 ,调用自定义gerString方法,返回一个bf.readLine();的字符串UserName
u.setPassword(getString("输入密码"));// 调用setPassword方法给私有属性id赋值 ,调用自定义gerString方法,返回一个bf.readLine();的字符串UserName
System.out.println(u);
userDao.creat(u);
}
public static void login() throws IOException { // 登录方法
System.out.println("输入登录的用户名(输入exit退出):");
while (true) {
UserBean u = new UserBean();// new 一个UserBean的对象
String username = bf.readLine();
if (username.equals("exit")) {
break;
}
u.setId(username); // bf.readLine();方法给对象u的私有属性id赋值
System.out.println("输入密码:");
String password = bf.readLine();
u.setPassword(password);// bf.readLine();方法给对象u的私有属性password赋值
if (userDao.login(u)) {// 用了UserDao的login方法
System.out.println("登录成功!");
break;
} else {
System.out.println("登录失败");
}
}
}
public static String getString(String s) throws IOException {// 获取userName字符串方法
System.out.println(s);
while (true) {
String userName = bf.readLine();
if (userName.trim().length() > 0 && userName.length() < 21) {
return userName;
}
}
}
}
/*
* 1---登录 2---注册 2 输入用户名: dy 输入密码 12138 UserBean{id='dy', password='12138'}
* 1---登录 2---注册 1 输入登录的用户名(输入exit退出): dy 输入密码: 12138 UserBean{id='dy',
* password='12138'} 登录成功! 1---登录 2---注册
*/
需要通过对象流读写的对象必须实现了序列化接口,否则java.io.NotSerializableException。
所有的序列化和反序列化操作由
JVM
负责实现。
Externalizable
接口定义为
public interface Externalizable extends java.io.Serializable
,这个接口中包含两个方法需要实现
writeExternal
自定义实现对象的序列化,
readExternal
自定义实现对象的反序列化。除非特殊需求一般
不使用
Externalizable
接口,因为没有必要自定义。
一般针对敏感数据不应该进行序列化操作,针对不需要进行序列操作的属性可以添加一个关键字transient,表示该属性不参与序列化和反序列化操作。
class User implements Serializable {private String username ;private transient String password ;private Role role ; // 因为 Role 没有实现序列化接口,所以写出 user 对象时会有报错NotSerializableException 。处理报错的方法有: 1 、可以给 Role 类定义添加序列接口。 2 、在 role 属性上添加 transient 表示这个属性不序列化处理}
如果某个类的属性类型不是基本类型或者String类型,且没有实现可序列化接口,则该类型属性类是不可序列化。