zoukankan      html  css  js  c++  java
  • 【Java学习笔记】I/O体系

    作者:gnuhpc
    出处:http://www.cnblogs.com/gnuhpc/

    1.基本概念和基本情况:

    a.流:一组有序的数据序列。

    b.字节流:数据流中最小的数据单元是字节。

    c.字符流:数据流中最小的数据单元是字符。

    d.java.io.InputStream和java.io.OutputStream分别表示字节输入流和字节输出流。都是抽象类,不能被实例化。所有的字节操作流都是这两个类的直接或间接子类。read()读取的是一个8位字节,write()写入的是一个8位字节。

    e.java.io.Reader和java.io.Writer分别表示字符输入流和字符输出流。都是抽象类,不能被实例化。所有的字符操作流都是这两个类的直接或间接子类。

     

    2.字节输入流:

    image_thumb2

     

    1)字节数组输入流:ByteArrayInputStream类

    从内存中的字节数组中读取数据,其数据源是一个字节数组。该类本身采用了适配器设计模式,将字节数组类型转化为输入流,完成对数组的读操作,读到的每一个字节类型的元素都会自动转换为int。

    使用如下:

    import java.io.ByteArrayInputStream;
    import java.io.IOException;

    public class Test {
        public static void main(String[] args){
            byte buff[]= new byte[]{2,15,67,-1,-9,9};
            ByteArrayInputStream inputStream = new ByteArrayInputStream(buff,1,4);
            int data = inputStream.read();
            while (data!=-1) {
                System.out.println(data + " ");
                data = inputStream.read();
            }
            try {
                inputStream.close();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
       }
    }

    这里的值会在前边补0而导致变化,但是请注意在byte类型赋值给int类型时,取值不会变。

     

    2)文件输入流:FileInputStream类

    从文件中读取数据流。注意系统的默认编码格式。

    3)字符串输入流:StringBufferInputStream类。(已被StringReader类代替而废弃)

    从一个字符串中读取。本身采用了适配器设计模式。

    4)管道输入流PipedInputStream类

    通常用于线程间通信,A执行管道输入流的read方法,暂时没有数据的时候这个线程就会被阻塞,只有当线程B向管道输入流写了新的数据后A才会执行。

    5)顺序输入流:SequenceInputStream类

    用于将几个输入流串联在一起,合并为一个输入流。当类通过这个类来读取数据时,它会依次(以输入流加入为序) 从所有被串联的输入流中读取数据。关闭输入流不需要先依次关闭各个子输入流。

    6)过滤输入流:FilterInputStream类

    这是装饰器,它本身继承了InputStream类,还可以用来修饰其他的输入流类。其构造方法为protected访问级别,因此外部程序不能创建这个类本身的实例。

    关于装饰器模式,在这摘录一个网友(江苏 无锡 缪小东 )的文字,我把一些调侃的话删掉了,留其本质内容:

    “三明治”的例子:三明治必不可少的是两块面包片,然后可以在夹层里加上蔬菜、沙拉、咸肉等等,外面可以涂上奶油之类的。假如现在你要为一个三明治小店构造一个程序,其中要设计各种三明治的对象。可能你已经创建了一个简单的Sandwich对象,现在要产生带蔬菜的就是继承原有的Sandwich添加一个蔬菜的成员变量,看起来很“正点”的做法,以后我还要带咸肉的、带奶油的、带蔬菜的又分为带青菜的、带芹菜的、生菜的……还是一个一个继承是吧!假如我们还需要即带蔬菜又带其它肉类,设置我们还要求这些添加成分的任意组合,那你就慢慢继承吧!下面我们就使用装饰器模式来设计这个库吧!下图是我们的设计图:

    下面是以上各个类的意义:

    1. Ingredient(成分):所有类的父类,包括它们共有的方法,一般为抽象类且方法都有默认的实现,也可以为接口。它有Bread和Decorator两个子类。这种实际不存在的,系统需要的抽象类仅仅表示一个概念,图中用红色表示。

    2. Bread(面包):就是我们三明治中必须的两片面包。它是系统中最基本的元素,也是被装饰的元素,和IO中的媒质流(原始流)一个意义。在装饰器模式中属于一类角色,所以其颜色为紫色。

    3. Decorator(装饰器):所有其它成分的父类,这些成分可以是猪肉、羊肉、青菜、芹菜。这也是一个实际不存在的类,仅仅表示一个概念,即具有装饰功能的所有对象的父类。图中用蓝色表示。

    4. Pork(猪肉):具体的一个成分,不过它作为装饰成分和面包搭配。

    5. Mutton(羊肉):同上。

    6. Celery(芹菜):同上。

    7. Greengrocery(青菜):同上。

    总结一下装饰器模式中的四种角色:1.被装饰对象(Bread);2.装饰对象(四种);3.装饰器(Decorator);4.公共接口或抽象类(Ingredient)。其中1和2是系统或者实际存在的,3和4是实现装饰功能需要的抽象类。

    写段代码体会其威力吧!(程序很简单,但是实现的方法中可以假如如何你需要的方法,意境慢慢体会吧!)

    //Ingredient.java

    public abstract class Ingredient {

    public abstract String getDescription();

    public abstract double getCost();

    public void printDescription(){

    System.out.println(" Name "+ this.getDescription());

    System.out.println(" Price RMB "+ this.getCost());

    }

    }

    所有成分的父类,抽象类有一个描述自己的方法和一个得到价格的方法,以及一个打印自身描述和价格的方法(该方法与上面两个方法构成模板方法哦!)

    //Bread.java

    public class Bread extends Ingredient {

    private String description ;

    public Bread(String desc){

    this.description=desc ;

    }

    public String getDescription(){

    return description ;

    }

    public double getCost(){

    return 2.48 ;

    }

    }

    面包类,因为它是一个具体的成分,因此实现父类的所有的抽象方法。描述可以通过构造器传入,也可以通过set方法传入。同样价格也是一样的,我就很简单地返回了。

    //Decorator.java

    public abstract class Decorator extends Ingredient {

    Ingredient ingredient ;

    public Decorator(Ingredient igd){

    this.ingredient = igd;

    }

    public abstract String getDescription();

    public abstract double getCost();

    }

    装饰器对象,所有具体装饰器对象父类。它最经典的特征就是:1.必须有一个它自己的父类为自己的成员变量;2.必须继承公共父类。 这是因为装饰器也是一种成分,只不过是那些具体具有装饰功能的成分的公共抽象罢了。在我们的例子中就是有一个Ingredient作为其成员变量。Decorator继承了Ingredient类。

    //Pork.java

    public class Pork extends Decorator{

    public Pork(Ingredient igd){

    super(igd);

    }

    public String getDescription(){

    String base = ingredient.getDescription();

    return base +"/n"+"Decrocated with Pork !";

    }

    public double getCost(){

    double basePrice = ingredient.getCost();

    double porkPrice = 1.8;

    return basePrice + porkPrice ;

    }

    }

    具体的猪肉成分,同时也是一个具体的装饰器,因此它继承了Decorator类 。猪肉装饰器装饰可以所有的其他对象,因此通过构造器传入一个Ingredient的实例,程序中调用了父类的构造方法,主要父类实现了这样的逻辑关系。同样因为方法是具体的成分,所以getDescription得到了实现,不过由于它是具有装饰功能的成分,因此它的描述包含了被装饰成分的描述和自身的描述。价格也是一样的。价格放回的格式被装饰成分与猪肉成分的种价格哦!

    从上面两个方法中我们可以看出,猪肉装饰器的功能得到了增强,它不仅仅有自己的描述和价格,还包含被装饰成分的描述和价格。主要是因为被装饰成分是它的成员变量,因此可以任意调用它们的方法,同时可以增加自己的额外的共同,这样就增强了原来成分的功能。

    //Mutton.java

    public class Mutton extends Decorator{

    public Mutton(Ingredient igd){

    super(igd);

    }

    public String getDescription(){

    String base = ingredient.getDescription();

    return base +"/n"+"Decrocated with Mutton !";

    }

    public double getCost(){

    double basePrice = ingredient.getCost();

    double muttonPrice = 2.3;

    return basePrice + muttonPrice ;

    }

    }

    羊肉的包装器。

    //Celery.java

    public class Celery extends Decorator{

    public Celery(Ingredient igd){

    super(igd);

    }

    public String getDescription(){

    String base = ingredient.getDescription();

    return base +"/n"+"Decrocated with Celery !";

    }

    public double getCost(){

    double basePrice = ingredient.getCost();

    double celeryPrice =0.6;

    return basePrice + celeryPrice ;

    }

    }

    芹菜的包装器。

    //GreenGrocery.java

    public class GreenGrocery extends Decorator{

    public GreenGrocery (Ingredient igd){

    super(igd);

    }

    public String getDescription(){

    String base = ingredient.getDescription();

    return base +"/n"+"Decrocated with GreenGrocery !";

    }

    public double getCost(){

    double basePrice = ingredient.getCost();

    double greenGroceryPrice = 0.4;

    return basePrice + greenGroceryPrice ;

    }

    }

    青菜的包装器。

    下面我们就领略装饰器模式的神奇了!我们有一个测试类,其中建立夹羊肉的三明治、全蔬菜的三明治、全荤的三明治。
    public class DecoratorTest{

    public static void main(String[] args){

    Ingredient compound = new Mutton(new Celery(new Bread("Master24's Bread")));

    compound.printDescription();

    compound = new Celery(new GreenGrocery(new Bread("Bread with milk")));

    compound.printDescription();

    compound = new Mutton(new Pork(new Bread("Bread with cheese")));

    compound.printDescription();

    }

    }

    以上就是一个简单的装饰器类。

    a.DataInputStream类

    实现了DataInput接口,用于读取基本类型数据。如int,long,float,double,boolean。其中的readUTF()方法还能读取UTF-8编码的字符串。其应该与DataOutputStream配套使用。也就是说,用DataInputStream读取由DataOutputStream写出的数据,才能保证获得正确的数据。

     

     

    b.LineNumberInputStream类:已被废弃

     

    c.BufferedInputStream类

    覆盖了被装饰的输入流的读数据行为,利用buffer来提供效率。一般在读取数据文件或者键盘输入时都使用其进行装饰。

     

    d.PushbackInputStream类

    很少用到。

     

    3.字节输出流

    image_thumb4

     

    1)字节数组输出流:ByteArrayOutputStream类

    向内存中的字节数组写数据,使用toByteArray方法获得字节数组。

     

    2)文件输出流:FileOutputStream类

    向文件中写数据。注意默认下使用则是覆盖文件,必须使用FileOutputStream(String name,boolean append),在后边设置为true则是在追加。

     

    3)过滤输出流:FilterOutputStream类

    和过滤输入流的特性一样。

    a)DataOutputStream类:

    用于向输出流写基本数据类型。

    b)BufferedOutputStream类:

    缓冲区提高效率。在超过缓冲区时会将缓冲区的内的数据写入输出流,若没有超过则不会自动写入,否则使用flush()方法刷新,或者关闭输出流。

    c)PrintStream类:

    采用本地OS默认的字符编码输出格式化数据。每一个Print()方法都与一个println()方法对应。使用checkError()方法判断写数据是否成功。它也带有缓冲区,不过用户可以自己设置刷新规则(见其构造函数的一个重载)。

    4.字符输入流

    image_thumb8

    注意BufferedReader不是FilterReader的子类。

     

    1)字符数组输入流:CharArrayReader类

    从内存中的字符数组中读取字符。

    2)字符串输入流:StringReader类

    数据源是字符串。

    3)InputStreamReader类:

    image_thumb10

    主要是解决Unicode字符编码和用户指定编码的变换问题。为了提高效率则可以使用BufferedReader来修饰。它有个子类是FileReader类,但是其只能按照本地平台的字符编码来读取数据,用户不能指定编码类型。

    4)BufferedReader类:

    带有缓冲区的字符输入流。

     

    5.字符输出流

    image_thumb12

     

    1)字符数组输出流:CharArrayWriter类

    将内存中的字符中写入字符数组。

    2)BufferedWriter类:带有缓冲。

    3)OutputStreamWriter类:

    为了提高效率则可以使用BufferedWriter来修饰。它有个子类是FileWriter类,但是其只能按照本地平台的字符编码来写入数据,用户不能指定编码类型。

    4)PrintWriter类:能输出格式化的数据。在输出字符数据的场合,应该优先考虑用PrintWriter。

     

    6.标准IO

    三个标准输入输出流:

    System.in:InputStream类,代表标准输入流,默认为键盘。常用套路是InputStreamReader适配器把System.in转换为Reader类型,再用BufferedReader装饰它。

    System.out:PrintStream类,代表标准输出流,默认为控制台。通常转化为PrintWriter类型。

    System.err:PrintStream类,代表标准错误输出流,默认为控制台。

    重定向使用System类中的,setIN(),setOut(),setErr()方法。

     

    7.随机访问文件类:RandomAccessFile

    可以从任意位置对文件进行读写。实现了DataInput和DataOutput接口,可以读写格式化的数据。

     

    8.新IO库的部分介绍

    java.nio包中,目的在于提高IO操作效率。缓冲器Buffer类:减少了实际物理读写次数,减少动态分配和回收内存区域的次数。这个类可以使得程序直接控制和运用缓冲区。抽象类,有8个具体的缓冲区类,都有能返回自身实例的静态工厂方法。

        image_thumb15

        缓冲区的几个属性(也提供了改变这三个属性的方法):

    容量:缓冲区的大小。

    极限:缓冲区的当前终点。这个点即为实际的数据区的终点。

    位置:缓冲区的下一个读写单元。

    image_thumb14

     

    Channel是用来连接缓冲区与数据源的。通道在创建时被打开,一旦关闭就不能再打开了。

    有两个常用方法:一个是flip(),确保后续操作只针对在缓冲区中的当前数据,而clear()方法则把缓冲区的极限设为容量值,为后续向缓冲区填入更多的数据做准备。

     

    2)字符编码Charset类

    其中的每个实例都代表特定的字符编码类型。

     

    3)FileChannel读写文件

    放一个例子吧,从书上摘的:

    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.RandomAccessFile;
    import java.nio.ByteBuffer;
    import java.nio.channels.FileChannel;
    import java.nio.charset.Charset;

    public class Test{
        public static void main(String[] args) throws IOException{
            final int SIZE = 1024;
            /*向文件中写数据*/
            FileChannel fcChannel = new FileOutputStream("c://test.txt").getChannel();
            fcChannel.write(ByteBuffer.wrap("你好,".getBytes()));//静态方法wrap为包装为一个ByteBuffer对象
            fcChannel.close();
            /*向文件末尾添加数据*/
            fcChannel = new RandomAccessFile("c://test.txt","rw").getChannel();
            fcChannel.position(fcChannel.size());//定位到文件尾
            fcChannel.write(ByteBuffer.wrap("世界!".getBytes()));
            fcChannel.close();
            /*读数据*/
            fcChannel = new FileInputStream("c://test.txt").getChannel();
            ByteBuffer buffer = ByteBuffer.allocate(SIZE);//创建ByteBuffer对象
            fcChannel.read(buffer);//将文件中的数据读入缓冲区中
            buffer.flip();//将极限设为当前位置值,再把position设为0,这使得接下来的cs.decode(buff)方法仅仅操作刚刚写入缓冲区的数据。
            Charset cs = Charset.defaultCharset();//获得本地OS的字符编码
            System.out.println(cs.decode(buffer));//转换为Unicode字符编码
            fcChannel.close();
        }
    }

    作者:gnuhpc
    出处:http://www.cnblogs.com/gnuhpc/

  • 相关阅读:
    Java Class/Method
    Data Types in Java
    Java overview && JVM
    Interface Vs. Abstract Class
    【javascript】Object三种定义方式
    【C#】导出Excel
    【javascript】简单原型链、借用构造函数
    Mysql表编码查看修改
    asp.net的mvc?
    连表
  • 原文地址:https://www.cnblogs.com/gnuhpc/p/2844077.html
Copyright © 2011-2022 走看看