一、Java中有几种流
1、按照流的流向分为:输入流和输出流。
输出流:把程序(内存)中的内容输出到磁盘、光盘等存储设备中。
输入流:读取外部数据到程序中。
2、按处理数据单位不同分为:字节流和字符流。
字节流:每次读取或者写出一个字节,出现中文时会有乱码形成。
字符流:每次读取或者写出两个字节,可正确处理中文。
3、按流的角色分为:节点流和处理流。
节点流:从或者向一个特定的节点读写数据,直接与数据源相连,用于输入或输出。
处理流(缓冲流):对一个已存在的流的连接和封装,通过所封装的流的功能调用实现数据读写。处理流的构造方法总是要带一个节点流的对象做参数。
二、常用方法
1、InputStream
- int read(): 从输入流中读取一个字节的二进制数据,返回的是所读取的字节的int型,该方法为抽象方法
- int read(byte[] b): 将多个字节读取到数组中,填满整个数组,返回传入数组参数个数
- int read(byte[] b, int off, int len): 从输入流中读取长度为len的数据,从数组b中下标为off的位置开始放置读入的数据,读完返回读取的字节数
- void close(): 关闭数据流
- int available(): 返回目前可以从数据流中读取的字节数
- long skip(): 跳过数据流中hiding数量的字节不读取,返回值表示实际跳过的字节数。
另:为什么read()无参方法读取一个字节返回的是一个int类型而不是一个byte类型?
因为字节输入流可以操作任意类型的文件,比如图片音频等,这些文件的底层都是以二进制形式存储的,一个字节是8个二进制,也就是说实际read后的是类似于(11111110,00001010)这样的东西,如果每次读取都返回byte,有可能在读到中间的时候遇到11111111(文件的底层按照补码来存储),那么这11111111是byte类型的-1,而程序遇到-1就会停止,后面的数据就会读不到,所以在读取的时候按照int类型接收,如果出现11111111会在其前面补上24个0凑足4个字节,那么byte类型的-1就会变成int类型的255了,这样可以保证数据被读完。
2、OutputStream
- void write(int i): 将字节i写入到数据流中,它只输出所读入参数的最低8位,该方法是抽象方法,需要在其输出流子类中实现才能使用
- void write(byte[] b): 将数组b中的全部字节写入到数据流
- void write(byte[] b, int off, int len): 将数组b从下标off开始的len个字节写入到数据流
- void close(): 关闭输出流
- void flush(): 刷新此输出流并强制写出所有缓冲的输出字节
三、选择字节流还是字符流?
1、大多数情况下使用字节流会更好,因为大多数IO操作都是直接操作磁盘文件,所以这些流在传输时都是以字节的方式进行的
2、如果对于操作需要通过IO在内存中频繁处理字符串的情况使用字符流会更好一些,因为字符流具有缓冲区,提高了性能
3、什么是缓冲区?
缓冲区就是一段特殊的内存区域,很多情况下当程序需要频繁的操作一个资源性能会变低,所以为了提高性能可以将一部分数据暂时读写到缓冲区,以后直接在该区域读写数据,提高性能
四、序列化
1、序列化
把Java对象转换为字节序列的过程成为对象的序列化,也就是将对象写入到IO流中。
序列化是为了解决在对对象流进行读写操作时所引发的问题。序列化机制允许将实现序列化的Java对象转换为字节序列,这些字节序列可以保存在磁盘上,或通过网络传输,以达到以后恢复成原来的对象。序列化机制使得对象可以脱离程序的运行二独立存在。
要一个对象实现序列化,该对象必须实现Serializable接口。Serializable接口中没有定义任何方法,知识作为一个标记来指示实现该接口的类可以进行序列化。
序列化只能保存对象的非静态成员变量,而不能保存任何成员方法和静态成员变量,并且保存的知识变量的值,变量的修饰符对序列化没有影响。
有一些对象类不具有可持久化性,因为其数据的特性决定了它会经常变化,如Thread对象或者流对象,对于这类对象要使用transient关键字标明,否则编译器会报错,任何使用transient关键字修饰的成员变量都不会被保存,对于一些需要保密的数据就要使用transient关键字修饰。
@Test//对象的序列化
public void Test(){
ObjectOutputStream oos = null;
try {
oos = new ObjectOutputStream(new FileOutputStream("test.dat"));
oos.writeObject(new Student(1,"mxf",18));
oos.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
if(oos!=null){
try {
oos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
此时需要注意Student类
public class Student implements Serializable {
//这个static final long 类型的变量是非常重要的,用来保证序列化和反序列化后的对象一致
private static final long serialVersionUID=3435234657L;
private int id;
private String name;
private int age;
public Student() {
}
..........
2、反序列化
反序列化就是从IO流中恢复对象。
@Test//对象的反序列化
public void Test1(){
ObjectInputStream ois = null;
try {
ois = new ObjectInputStream(new FileInputStream("test.dat"));
//把文件内容读取出来
Object o = ois.readObject();
System.out.println(o);
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} finally {
if(ois!=null){
try {
ois.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
3、序列化的应用
- 所有需要网络传输的对象都需要实现序列化接口。建议所有的javaBean都实现Serializable接口
- 对象的类名、实例变量都会被序列化,方法、类变量、transient实例变量都不会被序列化
- 序列化对象的引用类型成员变量也必须是可序列化的,否则会报错
好的到这里就结束啦!