《Java面向对象编程》学习笔记16—Java I/O系统

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();
}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值