Nivelle 开拓视野冲破艰险看见世界 身临其境贴近彼此感受生活

java基础(十六)之流

2017-06-10
nivelle

I/O

InputStream

InputStream 的作用是用来表示那些从不同数据源产生输入的类.这些数据源包括:

  • 字节数组
  • String对象
  • 文件
  • “管道”,工作方式与实际管道相似,从一端输入,从另一端输出.
  • 一个由其他种类的流组成的序列,以便我们可以将它们收集合并到一个流内.
  • 其他数据源,如Internet连接等.
功能 使用
ByteArrayInputSream 允许将内存的缓冲区当做inputStream使用 缓存区,字节将从中取出,作为一种数据源,将其与FilterInputStream对象相连以提供有用接口.
StringBufferInpustream 将String转换成InputStream 字符串.底层实现实际使用StringBuffer.作为一种数据源:将其与FilterInputStream对象相连以提供有用接口
FileInputStram 用于从文件中读取信息 字符串,表示文件名,文件或FileDescriptor对象.作为一种数据源将其与FilterInputStream对象相连接以提供有用接口.
PipedInputStream 产生用于写入相关PipedOutput Stream的数据.实现管道化概念. PipedOutputStream 作为多线程中数据源:将其与FilterInputStream对象相连以提供有用接口
SequenceInputStream 将两个或多个InputStream对象转换成单一InputStream 两个InputStream对象或一个容纳InputStream对象的容器Enumeration,作为一种数据源:将其与FilterInputStream对象相连以提供有用接口.
FilterInputStream 抽象类,作为”装饰器”的接口.其中,装饰器为其他的InputStream类提供有用功能.  

OutputStream

该类别的类决定了输出所要去往的目标:

  • 字节数组

  • 文件

  • 管道

功能 使用
ByteArrayOutputStream 内存中创建缓冲区.所有送往”流”的数据都放置在此缓冲区. 缓冲区初始化尺寸(可选的).用于指定数据的目的地:将其与FilterOutputStream对象相连以提供有用接口
FileOutputStream 用于将信息写至文件 字符串,表示文件名,文件或者FileDescriptor对象.指定数据的目的地:将其与FilterOutputStream对象相连以提供有用接口
PipedOutputStream 用于将信息写至文件 字符串,表示文件名,文件或FileDescriptor对象,指定数据的目的地:将其与FilterOutputStream对象相连以提供有用接口
PipedOutputStream 任何写入其中的信息都会自动作为相关PipedInputStream的输出.实现管道概念 PipedInputStream 指定用于多线程的数据的目的地:将其与FilterOutputStream对象相连以提供有用接口
FilterOutputStream 抽象类,作为装饰器的接口,其中装饰器为其他OutputStream提供有用功能  

添加属性和有用的接口

Java I/O类库需要多种不同功能的组合,这正是是使用装饰器模式的理由所在.

这也是java I/O类库里存在filter类的原因所在抽象类filter是所有装饰器类的基类.装饰器必须具有和他所装饰的对象相同的接口,但它也可以拓展接口,而这种情况只发生在个别filter.

FilterInputStream和FilterOutputStream 是用来提供装饰器类接口以控制特定输入流(InputStream)和输出流(OutputStream)的两个类,它们的名字不是很直观.FilterInputStream和FilterOutputStream分别自I/O类库中的基类InputStream和OutputStream派生而来,这两个类是装饰器的必要条件.

通过FilterInputStream从InputStream读取数据

功能 使用
DataInputStream 与DataOutputStream 搭配使用,因此我们可以按照可移植方式从流读取基本数据类型(int,char,long) InputStream 包含用于读取基本类型数据的接口
BufferedInputStream 使用它可以防止每次读取时都得进行实际写操作.代表”使用缓冲区” InputStream,可以指定缓冲区大小,本质上不提供接口,只不过是向进程中添加缓冲区所必需的.与接口对象搭配
LineNumberInputStream 跟踪输入流的行号;可调用getLineNumber()和setLineNumber(int) InputStream 仅增加了行号,因此可能要与接口对象搭配使用
PushbackInputStream 具有”能弹出一个字节的缓冲区”.因此可以将读到的最后一个字符回退 InputStream 通常作为编译器的扫描器,之所以包含在内是因为java编译器的需要,我们可能永远不会用到.

通过FilterOutPutStream向OutputStream写入

与DataInputStream 对应的是DataOutputStream,它可以将各种基本数据类型以及String对象格式化输出到”流”中;这样依赖,任何机器上的任何DataInputStream都能读取它们.

PrintStream最初的目的是为了可视化打印所有的基本数据类型以及String对象.

功能 使用
DataOutputStream 与DataInputStream搭配使用,因此可以按照可移植方式向流中写入基本数据类型 OutPutStream 包含用于写入基本类型数据的全部接口.
PrintStream 用于产生格式化输出,其中DataOutputstream处理数据的存储,PrintlnStream处理显示 OutputStream.可以用boolean值的是否在每次换行时清空缓存区应该是对OutputStream对象的final分装.
BufferedOutputStream 使用它以避免每次发送数据时都要进行实际的写操作.代表”使用缓冲区”.可以调用flush()清空缓冲区. OutPustStream ,可以指定缓冲区大小.本质上不提供接口,只不过是向进程中添加缓冲区所必需的,与接口对象搭配

Reader和Writer

提供兼容Unicode与面向字符的I/O功能,有时我们必须把来自于”字节”层次结构中的类和”字符”层次结构中的类结合起来使用.为了实现这个目标,要用到”适配器”类:InputStreamReader可以把InputStream转换为Reader,而OutputStream转换为Writer.


经典使用方式

缓冲输入文件

如果想打开一个文件用于字符输入,可以使用String或File对象作为文件名的FileInputReader.为了提高素对,我们希望对这个文件进行缓冲,我们将产生的引用传递给一个BufferedReader构造器.由于BufferedReader也提供readLine()方法,所以这是我们最终对象和进行读取的接口.当readLine()将返回null时,你就达到了文件的末尾.

public class BufferedInputFile {
    public static String read(String fileName) throws IOException {
        BufferedReader ind = new BufferedReader(new FileReader(fileName));
        String s;
        StringBuilder sb = new StringBuilder();
        while ((s = ind.readLine()) != null) {
            sb.append(s + "\n");
        }
        ind.close();
        return sb.toString();
    }
    public static  void main(String args[]) throws  IOException{
        System.out.println(read("BufferedInputFile.java"));
    }
}


从内存输入

public class MemoryInput {

    public static  void main(String [] args)throws IOException{
        StringReader  in = new StringReader(BufferedInputFile.read("MemoryInput.java"));
        int c;
        while ((c=in.read())!=-1){
            System.out.print((char)c);
        }
    }
}

对象序列化

Java对象序列胡将那些实现了Seriavle接口的对象转换成一个字节序列,并能够在以后将这个字节序列完全恢复为原来的对象.

对象序列化实现了轻量级的持久性.持久性意味着一个对象的生存周期并不取决于程序是否正在执行;它可以生存与程序的调用之间.之所以称为”轻量级”是因为不能用某种”persistent”关键字来简单地定义一个对象,并让系统自动维护其细节问题,相反,对象必须在程序中显式地序列化和反序列化.

序列化主要是为了支持两种特性:

  • java的远程方法调用.它使存活于其他计算机上的对象使用起来就像存活在本机一样.
  • 使用一个java beans,一般情况下是在设计阶段对它的状态信息进行配置,这种配置信息必须保存下来,并在启动时进行恢复.

要序列化一个对象,首先要创建某些OutputStream对象,然后将其封装在一个ObjectOutputStream对象内.这时,只需要调用writeObject()即可将对象序列化,并将其发送给OutputStream.要反省进行该过程,需要将一个InputStream封装在ObjectInputStream内,然后调用readObject().

public class Worm implements Serializable {
    private static Random random = new Random(47);
    private Data[] d = {new Data(random.nextInt(10)), new Data(random.nextInt(10)), new Data(random.nextInt(10))};
    private Worm next;
    private char c;

    public Worm(int i, char x) {
        System.out.println("worm constructor:" + i);
        c = x;
        if (--i > 0) {
            next = new Worm(i, (char) (x + 1));
        }
    }

    public Worm() {
        System.out.println("default constructor");
    }

    public String toSting() {
        StringBuilder result = new StringBuilder(":");
        result.append(c);
        result.append("(");
        for (Data dat : d) {
            result.append(dat);
        }
        result.append(")");
        if (next != null) {
            result.append("next");
        }
        return result.toString();
    }

    public static void main(String args[]) throws ClassNotFoundException, IOException {
        Worm w = new Worm(6, 'a');
        System.out.println("w=" + w);
        ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("worm.out"));
        out.writeObject("worm storage\n");
        out.writeObject(w);
        out.close();

        ObjectInputStream in = new ObjectInputStream(new FileInputStream("worm.out"));
        String s = (String) in.readObject();
        Worm w2 = (Worm) in.readObject();
        System.out.println(s + "w2=" + w2);
        ByteArrayOutputStream bout = new ByteArrayOutputStream();
        ObjectOutputStream out2 = new ObjectOutputStream(bout);
        out2.writeObject("worm storge\n");
        out2.writeObject(w);
        out2.flush();
        ObjectInputStream in2 = new ObjectInputStream(new ByteArrayInputStream(bout.toByteArray()));
        s = (String) in2.readObject();
        Worm w3 = (Worm) in2.readObject();
        System.out.println(s + "w3=" + w3);


    }
}


结果:

worm constructor:6

worm constructor:5

worm constructor:4

worm constructor:3

worm constructor:2

worm constructor:1

w=Stream.Worm@2aced6e2

worm storage

w2=Stream.Worm@9246bec

worm storge

w3=Stream.Worm@5438ebc3

序列化的控制

如果不希望对象的某一部分被序列化或者一个对象被还原后,某子对象需要重新创建,从而不必将该子对象序列化.

在这些特殊情况下,可通过实现Externalizable接口,代替实现Serializable接口—来对序列化过程进行控制.这个Externalizable接口继承了Serializable接口,同时增添了两个方法:writeExternal()和readExternal().这个两个方法会在序列化和反序列化还原的过程中被自动调用

public class Blip1 implements Externalizable{
    public Blip1(){
        System.out.println("blip1 constructor");
    }
    public void writeExternal(ObjectOutput out)throws IOException{
        System.out.println("blip1.writeExternal");
    }
    public void readExternal(ObjectInput in) throws IOException,ClassNotFoundException{
        System.out.print("blip1.readExternal");
    }
}
class Blip2 implements  Externalizable{
    Blip2(){
        System.out.println("blip2 constructor");
    }
    public void writeExternal(ObjectOutput out)throws IOException{
        System.out.println("blip2.writeExternal");
    }
    public void readExternal(ObjectInput in) throws IOException,ClassNotFoundException{
        System.out.print("blip2.readExternal");
    }
}


public class Blips {
    public static  void main(String args[])throws IOException,ClassNotFoundException{
        System.out.println("Constructing objects:");
        Blip1 b1 = new Blip1();
        Blip2 b2 = new Blip2();
        ObjectOutputStream o = new ObjectOutputStream(new FileOutputStream("Blips.out"));
        System.out.println("saving objects");
        o.writeObject(b1);
        o.writeObject(b2);
        o.close();
        ObjectInputStream in = new ObjectInputStream(new FileInputStream("Blips.out"));
        System.out.println("recovering b1:");
        b1=(Blip1)in.readObject();
    }
}


结果:

Constructing objects:

blip1 constructor

blip2 constructor

saving objects

blip1.writeExternal

blip2.writeExternal

recovering b1:

blip1 constructor

blip1.readExternalrecovering b2:

blip2 constructor

blip2.readExternal

恢复b1后,会调用Blip1默认构造器,这与恢复一个Serializable对象不同.对于Serializable对象,对象完全会以他存储的二进制位为基础构造,而不是调用构造器.对于Externalizable对象,所有普通的默认构造器都会被调用(包括在字段定义时的初始化),然后调用readExternal().必须注意这一点:所有默认的构造器都会被调用,才能使Externalizable对象产生正确的行为.

transient(瞬时)关键字

如果我们操作的是一个Serializable对象,那么所有序列化操作都会自动进行.为了能够予以控制,可以transient(瞬时)关键字逐个字段地关闭序列化,它的意思”不用麻烦你保存或恢复数据”


评论