前言
上回简单的介绍了IO流,并且顺便把File给介绍了,今天我们好好的介绍字节流。
介绍
字节流顾名思义就是操作字节的输入输出流。我们通过它可以读取硬盘中的字节数据放入内存中的字节数组,并将这些字节取出输出到硬盘上。
而字节流的顶层是两个抽象类:InputStream、OutputStream。这两个是无法实例化的,所以就有不同形式的子类了,接下来一一介绍。
FileInputStream、FileOutputStream
这是关于操作文件的字节流,在我们拿到文件的File对象后,可以以此拿到文件的字节流
//流的使用格式
InputStream in = new FileOutputStream(new File("c:/aaa.txt"));//建立文件字节输入流
OutputStream out = new FileOutputStream(new File("c:/bbb.txt"),"utf-8");//建立文件字节输出流,可以指定编码格式
//流的读写。。。。。。。。。。。。。。。。。
//关闭流
in.close();
out.close();
建立流后的读写(用完记得关闭流哦)
//很low的读写方式
int read = in.read('a');//此处读写读到的是int型的ASCII码,并且是一次一次的读的,写的时候记得强转为char类型
out.write('a');//无返回值,一次读写一个字符
//常用的读写方式
in.read('b');//一次一次读实在太麻烦了,我们可以选择一次读入byte数组,这样可以很高提高效率
int data = -1;
byte[] bytes = new byte[1024 * 10];
while ((data = in.read(bytes)) != -1) {// 此处读入返回值为实际读取内容的大小,-1意味着读取结束
out.write(bytes);//将字节数组输出
out.write(bytes, 0, data);
}
BufferedInputStream、BufferedOutputStream
- 用于包装字节流的缓冲字节流,有人说用缓冲流包文件字节流可以提高读写效率,如果你的文件字节流选择的是一个一个读写的确可以提高效率,但是通常情况下我们都是读写字节数组,在这种情况下缓冲流不会提高效率的。
- 在我们读写流的时候,是往后按顺序读写的。但是使用了缓冲流,只要流未关闭的情况下,我们可以跳转指定位置重新读写。
//流的使用格式
// 缓冲输入流
InputStream is = new FileInputStream(new File("c:/aaa.txt"));
is = new BufferedInputStream(is);
boolean flag = is.markSupported();// 判断是否具有缓冲功能
int data = is.read();
is.mark(0);// 做标记,参数随意
int data1 = is.read();
int data2 = is.read();
is.reset();// 重置,此时跳转到mark(0)位置读入
int data3 = is.read();
// 缓冲输出流
OutputStream os = new FileOutputStream(new File("c:/d.txt"));
os = new BufferedOutputStream(os, 2);// 规定缓冲大小,在缓冲流中当flush、close、满缓冲区才会输出
os.write('a');
os.write('b');//只会输出'a','b'
os.write('c');
is.close();
os.close();
实际演示
ObjectInputStream、ObjectOutputStream
- 对象流可以将对象转为字节,并将字节转为对象。将对象转为字节的过程我们称之为序列化(Serializable),将字节转为对象的过程我们称之为反序列化。我们平常创建对象是在内存中创建的,有了序列化技术我们就可以将对象固定的存储在硬盘中存放了,这个技术真nb。
- 要实现序列化技术,需要让我们要序列化的对象实现序列化,也就是实现Serializable接口,我们的八大基本数据类型的包装类和String已经实现了序列化接口,它们是不用手动实现的了。
手动实现序列化
class Stu implements Serializable {
public int age;
public transient String name;// 瞬时修饰符,不让其序列化
public Stu() {
}
public Stu(int age, String name) {
this.age = age;
this.name = name;
}
}
对象流使用,之前我的声明都是多态的,但是由于要使用特有方法所以引用对象类型是对象流
ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream(new File("c:/d.txt")));
String s1 = new String();// String和八个基本类型的包装类已经实现序列化接口了,它们都是final的,不可以被继承
Stu stu = new Stu(11, "jjy");
// writeObject是特有方法
os.writeObject(s1);
os.writeObject(stu);
os.close();
// 反序列化deSerializable
ObjectInputStream is = new ObjectInputStream(new FileInputStream(new File("c:/d.txt")));
Object readObject = is.readObject();
Object readObject2 = is.readObject();
// 判断类型
if (readObject2 instanceof Stu) {
stu = (Stu) readObject2;
}
System.out.println(readObject);
System.out.println(stu + " " + stu.name + " " + stu.age);
is.close();
ByteArrayInputStream、ByteArrayOutputStream
字节数组流我感觉平常真的用不到,但是该讲还是得讲,不过字节数组输出流中的toByteArray()方法很好用啊。
字节数组流跟一般流不同,在构造自己之后,它还需要传入别的流中的构造,因为用这个流的目的就是为了获取流中的字节数组,所以需要别的流。
//流的使用
public static Object copyObj(Object srcObject) {
ByteArrayInputStream bis = null;
ObjectInputStream ois = null;
if (srcObject != null) {
// 如果对象不为空则进行复制操作
try (ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
// ????
) {
// 序列化,将对象变为字节写入字节数组
oos.writeObject(srcObject);
// 取出字节数组
byte[] bs = bos.toByteArray();
// 取出对象
bis = new ByteArrayInputStream(bs);
ois = new ObjectInputStream(bis);
return ois.readObject();
} catch (Exception e) {
} finally {
if (bis != null && ois != null) {
try {
ois.close();// 会顺便把bis关了
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
} else {
throw new RuntimeException("给定对象为空!");
}
return null;
}
DataInputStream、DataOutputStream
数据字节流主要用于传输数据的,针对于八大基本类型和String
// data流用于传输数据(包括八种基本数据类型,和String)
DataOutputStream os = new DataOutputStream(new FileOutputStream(new File("c:/d.txt")));
os.writeByte(11);// Byte的范围-2的7次方-1到2的7次方,1个字节是八位,有一位是符号位不算
os.writeShort(3);// short 2字节
os.writeInt(123);// int 4字节
os.writeLong(12131241);// long 8字节
os.writeBoolean(true);// boolean 1位、1字节 实际没有规定大小
os.writeChar('a');// char 2字节,英文字符实际占1字节
os.writeFloat(2.4f);// float 4字节
os.writeDouble(2.4);// double 8字节
os.close();
DataInputStream is = new DataInputStream(new FileInputStream(new File("c:/d.txt")));
System.out.println(is.read());
is.close();
异常处理
关于IO操作,异常处理是不可避免的,如果不知道如何处理异常的可以看看我之前关于异常的博客,这里简单写一下关于流的异常处理。
有了try-with-resources,我们处理流的异常就很简单了,我们只需要声明不需要关闭了,try-with-resources隐式地实现了AutoColoseable接口,所以会自动关闭了资源。注意要jdk1.7的版本以后实现。
try (FileOutputStream out = new FileOutputStream(new File("c:/aa.txt"))){
}
catch (IOException e) {
e.printStackTrace();
}
原始的异常处理(捕捉或声明)
声明就是方法直接 throws IOException
捕捉则要麻烦一点
FileOutputStream out =null;
try {
out = new FileOutputStream(new File("c:/aa.txt"))
}catch (IOException e) {
e.printStackTrace();
}finally{
if (out!=null) {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
介绍
啊,终于介绍完了字节流,写了一个上午感觉对于字节流感悟更深了,后面会介绍字符流的。。。