练习学习与收集(io —1)

(三)File
package one;

import java.io.File;
import java.util.ArrayList;
import java.util.Iterator;

public class ChenXiaoJun {
public static void main(String[] args) {
File f = new File("E:\\cxj");
File[] codeFiles = f.listFiles();
ArrayList at = new ArrayList();
for(File child : codeFiles){
if(child.getName().matches("\\w*\\.bmp$")) {
int i = child.getName().length();
String h = child.getName().substring(0,i-4);
at.add(h);
}
}
Iterator it = at.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
}

}

(七)BufferedStreamDemo
package one;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

public class BufferedStreamDemo{
public static void main(String[] ages) {
try {
FileInputStream file = new FileInputStream("E:\\TEST\\bufferInput.txt");
BufferedInputStream bis = new BufferedInputStream(file);
FileOutputStream fos = new FileOutputStream("E:\\TEST\\bufferOutput.txt");
BufferedOutputStream bos = new BufferedOutputStream(fos);
byte[] date = new byte[1];
try {
bis.skip(4);
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
int b;
try {

/* while((b=bis.read()) != -1){
System.out.println((char)b);
}*/
while((bis.read(date)) != -1){
bos.write(date) ;

}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
try {
bos.close();
fos.close();
bis.close();
file.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

}


} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}


}
}
import java.io.*;

public class BufInputStrm {

public static void main(String[] args) throws IOException {
String s = "ABCDEFGHI@JKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
System.out.println(s.length());
byte[] buf = s.getBytes();
ByteArrayInputStream in = new ByteArrayInputStream(buf);
BufferedInputStream bis = new BufferedInputStream(in, 2);
int i = 0;
int k;
bis.read(buf, 0, 2);
do {
k = bis.read();
System.out.print(" "+(char) k + " ");
System.out.print(k);
i++;
if (i == 4) {
bis.mark(6);
}
}while (k != '@');
System.out.println();
bis.reset();
k = bis.read();
while (k != -1) {
System.out.print((char) k + " ");
k = bis.read();
}
}
}

(八) ByteArrayInputStream
-package one;

import java.io.*;

public class ByteArrayTest {
public static void transform(InputStream ips, OutputStream ops) {
int ch = 0;
try {
while ((ch = ips.read()) != -1) {
int upperCh = Character.toUpperCase((char) ch);
ops.write(upperCh);
}

} catch (Exception e) {
e.printStackTrace();
}
}

public static void main(String[] args) {
String str = "kingxip";
byte[] src = str.getBytes();
ByteArrayInputStream baInput = new ByteArrayInputStream(src);
ByteArrayOutputStream baOut = new ByteArrayOutputStream();
transform(baInput, baOut);
byte[] result = baOut.toByteArray();
System.out.println(new String(result));
/* 流的来源或目的地并不一定是文件,也可以是内存中的一块空间,例如一个字节数组。java.io.ByteArrayInputStream、java.io.ByteArrayOutputStream就是将字节数组当作流输入来源、输出目的地的类。
java.io.ByteArrayInputStream将一个字节数组当作流输入的来源,而java.io.ByteArrayOutputStream则可以将一个字节数组当作流输出目的地。
上面的程序就是把字符串转变为字节数组,并作为流输入的来源,用tranform()将字符串中所有的字母都转换为大写的,并将转换后的结果写到ByteArrayOutputStream中。
*/
ByteArrayOutputStream bout = new ByteArrayOutputStream();
DataOutputStream dout = new DataOutputStream(bout);

String name = "prometheus";
int age = 26;
try {
dout.writeUTF(name);
dout.writeInt(age);

byte[] buff = bout.toByteArray();
ByteArrayInputStream bin = new ByteArrayInputStream(buff);
DataInputStream dis = new DataInputStream(bin);

String newName = dis.readUTF();
int newAge = dis.readInt();
System.out.println(newName + ":" + newAge);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

}
}
(九) CharArrayReader
//字符流
class CharArrayReaderDemo {
public static void main(String[] args) throws IOException {
String strTmp = "abcdefghijklmnopqrstuvwxyz";
int intLen = strTmp.length();
char c[] = new char[intLen];
strTmp.getChars(0, intLen, c, 0);
CharArrayReader input1 = new CharArrayReader(c);
CharArrayReader input2 = new CharArrayReader(c, 0, 5);
int i;
System.out.println("input1 is : ");
while ((i = input1.read()) != -1) {
System.out.print((char) i);
}
System.out.println();
System.out.println("input2 is : ");
while ((i = input2.read()) != -1) {
System.out.print((char) i);
}
System.out.println();
}
}
(十)DataInputStream 过时
import java.io.*;
public class DataStreamTest {
public static void main(String[] args) {
FileInputStream fis=null;
FileOutputStream fos=null;
DataInputStream dis=null;
DataOutputStream dos=null;

try {
fos=new FileOutputStream("d:\\liaojianguo\\data.dat");
dos=new DataOutputStream(fos);
for(int i=0;i<10;i++){
double d=Math.random();
dos.writeDouble(d);
System.out.println(d);
}
dos.flush();
} catch (IOException e) {
e.printStackTrace();
}finally{
if(dos!=null)try{dos.close();}catch(IOException e){}
if(fos!=null)try{fos.close();}catch(IOException e){}
}
System.out.println("***************************");
try {
fis=new FileInputStream("d:\\liaojianguo\\data.dat");
dis=new DataInputStream(fis);
for(int i=0;i<10;i++){
double d=dis.readDouble();
System.out.println(d);
}
} catch (IOException e) {
e.printStackTrace();
}finally{
if(dis!=null)try{dis.close();}catch(IOException e){}
if(fis!=null)try{fis.close();}catch(IOException e){}
}


}

}
(十一) FileInputStream
package one;

import java.io.FileInputStream;
import java.io.File;
import java.io.FileOutputStream;

public class TestFileInputStream {

/**
* 1. File类 1 ) File 类介绍(《 core java 》 638 页) File 类封装了对用户机器的文件系统进行操作的功能。
* 例如,可以用 File 类获得文件上次修改的时间移动, 或者对文件进行删除、重命名。换句话说,流类关注的是文件内容, 而 File
* 类关注的是文件在磁盘上的存储 。 File 类的主要方法有:
* getName(),getCanonicalFile(),lastModified(),
* isDerector(),isFile(),getPath() 等; 2 ) File 类与 FileInputStream 类的区别:
* 流类关注的是文件内容,而 File 类关注的是文件在磁盘上的存储。 File 不属于文件流 , 只能代表一个文件或是目录的路径名而已。 提示:(《
* core java 》 639 页) 如果处理文件或者目录名,就应该使用 File 对象,而不是字符串。 例如, File 类的 equals
* 方法知道一些 文件系统对大小写是敏感的,目录尾的“ / ”字符无关紧要。 自己的领会: FileInputStream 类或者
* FileReader 类的构造函数有多个, 其中典型的两个分别为:一个使用 File 对象为 参数;而另一个使用表示路径的 String
* 对象作为参数; 自己以前一直觉得直接用了 String 指定路径就可以 了,一直不明白为什么很多人都先构造一个 File 对象,
* 现在终于明白了,“如果处理文件或者目录名,就应 该使用 File 对象,而不是字符串。”! 2. FileInputStream 类 1 )
* FileInputStream 类介绍: 以字节为单位(非 unicode )的流处理。 字节序列即:二进制数据。与编码无关,不存在乱码问题。
* FileInputStream 类的主要方法有: Read (), read ( byte[] b ), read ( byte[],int
* off,int len ) ,available(); 2 ) FileInputStream 类与 FileReader 类的区别:
* 两个类的构造函数的形式和参数都是相同的, 参数为 File 对象或者表示路径的 String ,它们到底有何区别 * 呢? Readers and
* Writers work only on line based character data, so plain text files. For
* anything else, you MUST use Streams. File类:主要提供了对平台磁盘上文件的操作,
* 一些参考资料上也说这个类是与流无关的。里面的方法很简单, 直接查文档就可以了。注意mkdirs方法,
* 可以创建多层目录,还有一个length属性,下面创建byte[]的length使用它就可以。 FileInputStream
* 类:提供了对文件读取流的操作。由于InputStream,OutputStream是
* 针对字节流的,所以读取文件以前先要定义byte[],然后把文件流读取到byte[]里面。 FileOutputStream类:文件输出流操作。
* 不管是FileInputStream还是FileOutputStream,
* 流已经打开是不会自动关闭的,所以一定要使用手动关闭流。方法是close();
* 你也看到了,在FileInputStream里有一个说明是说此方法将阻塞, 意思就是说在你读一个文件输入流的时候,当读到某个位置的时候,
* 如果做一些其他处理(比如说接受一部分字节做一些处理等等) 这个时候输入流在什么位置就是什么位置,不会继续往下读,
* 而BufferedInputStream虽然也有一个read方法, 但是从名字就可以看出,它带有一个缓冲区,它是一个非阻塞的方法
* ,在你读到某个位置的时候,做一些处理的时候,输入流可能还会继续读入字节, 这样就达到了缓冲的效果。
* 对于性能要求不高的时候,用哪个都无所谓,但是如果有性能要求, 建议你还是用BufferedInputStream。
* 本贴来自ZDNetChina中文社区 http://bbs.zdnet.com.cn
* ,本贴地址:http://bbs.zdnet.com.cn/viewthread.php?tid=636567
* 一个是字节流(FileOutputStream),一个是字符流(OutputStreamWriter) 如果用OutputStreamWrite
* 请有InputStreamReader获取字节流OutputStreamWriter 用来将字符溜转换成字节流
* 一个用来处理字节码,一个用来处理unicode码 FileInputStream fis = new FileInputStream(new
* File("test.txt")); ByteArrayOutputStream baos = new
* ByteArrayOutputStream(); byte[] buffer = new byte[128]; int iLength = 0;
* while((iLength = fis.read(buffer)) != -1) { baos.write(buffer, 0,
* iLength); } fis.close(); baos.close();
*
*
*
* private static void copyfile(String str,String str1) throws
* FileNotFoundException { try { File file=new File(str); InputStream
* is=null; OutputStream os=null; byte[] b; File f=new File(str1);
* if(!file.isDirectory()) { is=new FileInputStream(str); b=new
* byte[is.available()]; is.read(b); os=new FileOutputStream(str1);
* os.write(b); is.close(); os.close(); return; } else if(!f.exists())
* f.mkdirs(); File[] filename=file.listFiles(); for(int i=0;i<filename.length;i++) {
* copyfile(filename[i].getAbsolutePath(),str1+"/"+filename[i].getName()); } }
* catch(IOException ex) { filecopycount++;
* System.out.println("filecopycount:"+String.valueOf(filecopycount));
* if(filecopycount<=5) copyfile(str,str1);
* System.err.println("err:"+ex.toString()); } }
*
*/
public static void main(String[] args) throws Exception {
File f = new File("d:\\temp\\read.txt");
FileInputStream fis = new FileInputStream(f);

byte[] b = new byte[(int) f.length()];
fis.read(b);

for (int i = 0; i < b.length; i++) {
System.out.print(b[i]);
}

String bb = new String(b);
System.out.println(bb);
boolean a = true;
File ff = new File("d:\\temp\\newread2.txt");
FileOutputStream fos = new FileOutputStream(ff, a);
fos.write(b);
String abc = "abc";
byte[] ab = abc.getBytes();
fos.write(ab);
fis.close();
fos.close();
}

}
(十二)FileRead
package one;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;

public class IoTest {
public static void main(String[] args) {
String dirName = "E:\\TEST";
String fileName ="testcreatetxtfile.txt";
File file = new File(dirName);
if(!file.exists()){
file.mkdir();
}
File f = new File(file,fileName);
try {
f.createNewFile();

FileInputStream fis = new FileInputStream(f);
FileOutputStream fos = new FileOutputStream(f);
OutputStreamWriter osw = new OutputStreamWriter(fos);
for (char i = 'A'; i < 'Z'; i++) {
osw.write(i);
}
osw.write("\n");
for (char i = 'a'; i < 'z'; i++) {
osw.write(i);
}
osw.close();
InputStreamReader isr = new InputStreamReader(fis);
BufferedReader br = new BufferedReader(isr);
String strline = br.readLine();
System.out.println(strline);
System.out.println("##########");
while(br.read()!=-1){
System.out.print( (char)br.read());
}
isr.close();

} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
InputStreamReader 是字节流通向字符流的桥梁:它使用指定的 charset 读取字节并将其解码为字符。它使用的字符集可以由名称指定或显式给定,否则可能接受平台默认的字符集。
每次调用 InputStreamReader 中的一个 read() 方法都会导致从基础输入流读取一个或多个字节。要启用从字节到字符的有效转换,可以提前从基础流读取更多的字节,使其超过满足当前读取操作所需的字节。
为了达到最高效率,可要考虑在 BufferedReader 内包装 InputStreamReader。例如:
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));

java.io包中用于处理字符流的最基本的类,用来在字节流和字符流之间作为中介。

◇ 生成流对象
  public InputStreamReader(InputStream in);
  /*in是字节流,而InputStreamReader是字符流,但是其来源是字节流in,
  因此InputStreamReader就可以把字节流in转换成字符流处理。/*

  public InputStreamReader(InputStream in,String enc) throws UnsupportedEncodingException;
  /*enc是编码方式,就是从字节流到字符流进行转换时所采用的编码方式,
   例如 ISO8859-1,UTF-8,UTF-16等等*/

  public OutputStreamWriter(OutputStream out);
  /*out是字节流,而OutputStreamReader是字符流 */

  public OutputStreamWriter(OutputStream out,String enc) throws UnsupportedEncodingException; //enc是编码方式

  InputStreamReader和OutputStreamWriter的方法:

  ◇ 读入和写出字符
  基本同Reader和Writer。

  ◇ 获取当前编码方式
  public String getEncoding();

  ◇ 关闭流
  public void close() throws IOException;

(十三)Io
从InputStream到ByteArrayInputStream
本篇主要分析:1.如何将byte数组适配至ByteArrayInputStream,对应与IO部分的适配器模式;2.BufferedInputStream的工作原理,对应于IO的装饰器模式,会首先研究InputStream和FilterInputStream的源代码,同时会将要谈谈软件设计中的缓存相关的知识。后面专门一章分析PipedInputStream和PipedOutStream,简单谈谈管道相关的知识,以及软件架构的想法。
1 InputStream
InputStream 是输入字节流部分,装饰器模式的顶层类。主要规定了输入字节流的公共方法。

package java.io;
public abstract class InputStream implements Closeable {
private static final int SKIP_BUFFER_SIZE = 2048; //用于skip方法,和skipBuffer相关
private static byte[] skipBuffer; // skipBuffer is initialized in skip(long), if needed.

public abstract int read() throws IOException; //从输入流中读取下一个字节,
//正常返回0-255,到达文件的末尾返回-1
//在流中还有数据,但是没有读到时该方法会阻塞(block)
//Java IO和New IO的区别就是阻塞流和非阻塞流
//抽象方法哦!不同的子类不同的实现哦!

//将流中的数据读入放在byte数组的第off个位置先后的len个位置中
//放回值为放入字节的个数。
public int read(byte b[], int off, int len) throws IOException { //
if (b == null) {
throw new NullPointerException();
} else if (off < 0 || len < 0 || len > b.length - off) {
throw new IndexOutOfBoundsException();
} else if (len == 0) {
return 0;
} //检查输入是否正常。一般情况下,检查输入是方法设计的第一步
int c = read(); //读取下一个字节
if (c == -1) { return -1; } //到达文件的末端返回-1
b[off] = (byte)c; //放回的字节downcast
int i = 1; //已经读取了一个字节
try {
for (; i < len ; i++) { //最多读取len个字节,所以要循环len次
c = read(); //每次循环从流中读取一个字节
//由于read方法阻塞,
//所以read(byte[],int,int)也会阻塞
if (c == -1) { break; } //到达末尾,理所当然放回-1
b[off + i] = (byte)c; //读到就放入byte数组中
}
} catch (IOException ee) { }
return i;
//上面这个部分其实还有一点比较重要,int i = 1;在循环的外围,或许你经常见到,
//或许你只会在循环是才声明,为什么呢?
//声明在外面,增大了变量的生存周期(在循环外面),所以后面可以return返回
//极其一般的想法。在类成员变量生命周期中使用同样的理念。
//在软件设计中,类和类的关系中也是一样的。
} //这个方法在利用抽象方法read,某种意义上简单的Templete模式。

public int read(byte b[]) throws IOException {
return read(b, 0, b.length);
} //利用上面的方法read(byte[] b)

public long skip(long n) throws IOException {
long remaining = n; //方法内部使用的、表示要跳过的字节数目,
//使用它完成一系列字节读取的循环
int nr;
if (skipBuffer == null)
skipBuffer = new byte[SKIP_BUFFER_SIZE]; //初始化一个跳转的缓存
byte[] localSkipBuffer = skipBuffer; //本地化的跳转缓存
if (n <= 0) { return 0; } //检查输入参数,应该放在方法的开始
while (remaining > 0) { //一共要跳过n个,每次跳过部分,循环
nr = read(localSkipBuffer, 0, (int) Math.min(SKIP_BUFFER_SIZE, remaining));
//利用上面的read(byte[],int,int)方法尽量读取n个字节
if (nr < 0) { break; } //读到流的末端,则返回
remaining -= nr; //没有完全读到需要的,则继续循环
}
return n - remaining;//返回时要么全部读完,要么因为到达文件末端,读取了部分
}

public int available() throws IOException { //查询流中还有多少可以读取的字节
return 0;
}
//该方法不会block。在java中抽象类方法的实现一般有以下几种方式:
//1.抛出异常(java.util);2.“弱”实现。象上面这种。子类在必要的时候覆盖它。
//3.“空”实现。下面有例子。

public void close() throws IOException {}
//关闭当前流、同时释放与此流相关的资源

public synchronized void mark(int readlimit) {}
//在当前位置对流进行标记,必要的时候可以使用reset方法返回。
//markSupport可以查询当前流是否支持mark

public synchronized void reset() throws IOException {
throw new IOException("mark/reset not supported");
}
//对mark过的流进行复位。只有当流支持mark时才可以使用此方法。
//看看mark、available和reset方法。体会为什么?!

public boolean markSupported() { //查询是否支持mark
return false;
} //绝大部分不支持,因此提供默认实现,返回false。子类有需要可以覆盖。

}

2 FilterInputStream
这是字节输入流部分装饰器模式的核心。是我们在装饰器模式中的Decorator对象,主要完成对其它流装饰的基本功能。下面是它的源代码:
package java.io;

//该类对被装饰的流进行基本的包裹。不增加额外的功能。
//客户在需要的时候可以覆盖相应的方法。具体覆盖可以在ByteInputStream中看到!
public class FilterInputStream extends InputStream {
protected volatile InputStream in; //将要被装饰的字节输入流

protected FilterInputStream(InputStream in) { //通过构造方法传入此被装饰的流
this.in = in;
}
//装饰器的代码特征:被装饰的对象一般是装饰器的成员变量
//上面几行可以看出。

//下面这些方法,完成最小的装饰――0装饰,只是调用被装饰流的方法而已

public int read() throws IOException {
return in.read();
}

public int read(byte b[]) throws IOException {
return read(b, 0, b.length);
}

public int read(byte b[], int off, int len) throws IOException {
return in.read(b, off, len);
}

public long skip(long n) throws IOException {
return in.skip(n);
}

public int available() throws IOException {
return in.available();
}

public void close() throws IOException {
in.close();
}

public synchronized void mark(int readlimit) {
in.mark(readlimit);
}

public synchronized void reset() throws IOException {
in.reset();
}

public boolean markSupported() {
return in.markSupported();
}
//以上的方法,都是通过调用被装饰对象in完成的。没有添加任何额外功能
//装饰器模式中的Decorator对象,不增加被装饰对象的功能。
//它是装饰器模式中的核心。更多关于装饰器模式的理论请阅读博客中的文章。
}

以上分析了所有字节输入流的公共父类InputStream和装饰器类FilterInputStream类。他们是装饰器模式中两个重要的类。更多细节请阅读博客中装饰器模式的文章。下面将讲解一个具体的流ByteArrayInputStream,不过它是采用适配器设计模式。

3 ByteArray到ByteArrayInputStream的适配
// ByteArrayInputStream内部有一个byte类型的buffer。
//很典型的适配器模式的应用――将byte数组适配流的接口。
//下面是源代码分析:

package java.io;

public class ByteArrayInputStream extends InputStream {
protected byte buf[]; //内部的buffer,一般通过构造器输入
protected int pos; //当前位置的cursor。从0至byte数组的长度。
//byte[pos]就是read方法读取的字节
protected int mark = 0; //mark的位置。
protected int count; //流中字节的数目。不一定与byte[]的长度一致???

public ByteArrayInputStream(byte buf[]) {//从一个byte[]创建一个ByteArrayInputStream
this.buf = buf; //初始化流中的各个成员变量
this.pos = 0;
this.count = buf.length; //count就等于buf.length
}

public ByteArrayInputStream(byte buf[], int offset, int length) { //构造器
this.buf = buf;
this.pos = offset; //与上面不同
this.count = Math.min(offset + length, buf.length);
this.mark = offset; //与上面不同
}

public synchronized int read() { //从流中读取下一个字节
return (pos < count) ? (buf[pos++] & 0xff) : -1; //返回下一个位置的字节
//流中没有数据则返回-1
}

//下面这个方法很有意思!从InputStream中可以看出其提供了该方法的实现。
//为什么ByteArrayInputStream要覆盖此方法呢?
//同样的我们在Java Collections Framework中可以看到:
//AbstractCollection利用iterator实现了Collecion接口的很多方法。但是,
//在ArrayList中却有很多被子类覆盖了。为什么如此呢??

public synchronized int read(byte b[], int off, int len) {
if (b == null) { //首先检查输入参数的状态是否正确
throw new NullPointerException();
} else if (off < 0 || len < 0 || len > b.length - off) {
throw new IndexOutOfBoundsException();
}
if (pos >= count) { return -1; }
if (pos + len > count) { len = count - pos; }
if (len <= 0) { return 0; }
System.arraycopy(buf, pos, b, off, len); //java中提供数据复制的方法
pos += len;
return len;
}
//出于速度的原因!他们都用到System.arraycopy方法。想想为什么?
//某些时候,父类不能完全实现子类的功能,父类的实现一般比较通用。
//当子类有更有效的方法时,我们会覆盖这些方法。这样可是不太OO的哦!

//下面这个方法,在InputStream中也已经实现了。
//但是当时是通过将字节读入一个buffer中实现的,好像效率低了一点。
//看看下面这段代码,是否极其简单呢?!
public synchronized long skip(long n) {
if (pos + n > count) { n = count - pos; } //当前位置,可以跳跃的字节数目
if (n < 0) { return 0; } //小于0,则不可以跳跃
pos += n; //跳跃后,当前位置变化
return n;
} //比InputStream中的方法简单、高效吧!

public synchronized int available() {
return count - pos;
}
//查询流中还有多少字节没有读取。
//在我们的ByteArrayInputStream中就是当前位置以后字节的数目。

public boolean markSupported() {
return true;
} //ByteArrayInputStream支持mark所以返回true

public void mark(int readAheadLimit) {
mark = pos;
}
//在流中当前位置mark。
//在我们的ByteArrayInputStream中就是将当前位置赋给mark变量。
//读取流中的字节就是读取字节数组中当前位置向后的的字节。

public synchronized void reset() {
pos = mark;
}
//重置流。即回到mark的位置。

public void close() throws IOException { }
//关闭ByteArrayInputStream不会产生任何动作。为什么?仔细考虑吧!!
}

上面我们分3小节讲了装饰器模式中的公共父类(对应于输入字节流的InputStream)、Decorator(对应于输入字节流的FilterInputStream)和基本被装饰对象(对应于输入字节流的媒体字节流)。下面我们就要讲述装饰器模式中的具体的包装器(对应于输入字节流的包装器流)。
4 BufferedInputStream
4.1原理及其在软件硬件中的应用
1.read――read(byte[] ,int , int)
2.BufferedInputStream
3.《由一个简单的程序谈起》
4. Cache
5.Pool
6.Spling Printer
(最近比较忙,不讲了!)
4.2 BufferedInputStream源代码分析

package java.io;

import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;

//该类主要完成对被包装流,加上一个缓存的功能
public class BufferedInputStream extends FilterInputStream {
private static int defaultBufferSize = 8192; //默认缓存的大小
protected volatile byte buf[]; //内部的缓存
protected int count; //buffer的大小
protected int pos; //buffer中cursor的位置
protected int markpos = -1; //mark的位置
protected int marklimit; //mark的范围

//原子性更新。和一致性编程相关
private static final
AtomicReferenceFieldUpdater<BufferedInputStream, byte[]> bufUpdater =
AtomicReferenceFieldUpdater.newUpdater (BufferedInputStream.class, byte[].class, "buf");

private InputStream getInIfOpen() throws IOException { //检查输入流是否关闭,同时返回被包装流
InputStream input = in;
if (input == null) throw new IOException("Stream closed");
return input;
}

private byte[] getBufIfOpen() throws IOException { //检查buffer的状态,同时返回缓存
byte[] buffer = buf;
if (buffer == null) throw new IOException("Stream closed"); //不太可能发生的状态
return buffer;
}

public BufferedInputStream(InputStream in) { //构造器
this(in, defaultBufferSize); //指定默认长度的buffer
}

public BufferedInputStream(InputStream in, int size) { //构造器
super(in);
if (size <= 0) { //检查输入参数
throw new IllegalArgumentException("Buffer size <= 0");
}
buf = new byte[size]; //创建指定长度的buffer
}

//从流中读取数据,填充如缓存中。
private void fill() throws IOException {
byte[] buffer = getBufIfOpen(); //得到buffer
if (markpos < 0)
pos = 0; //mark位置小于0,此时pos为0
else if (pos >= buffer.length) //pos大于buffer的长度
if (markpos > 0) {
int sz = pos - markpos; //
System.arraycopy(buffer, markpos, buffer, 0, sz);
pos = sz;
markpos = 0;
} else if (buffer.length >= marklimit) { //buffer的长度大于marklimit时,mark失效
markpos = -1; //
pos = 0; //丢弃buffer中的内容
} else { //buffer的长度小于marklimit时对buffer扩容
int nsz = pos * 2;
if (nsz > marklimit) nsz = marklimit;//扩容为原来的2倍,太大则为marklimit大小
byte nbuf[] = new byte[nsz];
System.arraycopy(buffer, 0, nbuf, 0, pos); //将buffer中的字节拷贝如扩容后的buf中
if (!bufUpdater.compareAndSet(this, buffer, nbuf)) {
//在buffer在被操作时,不能取代此buffer
throw new IOException("Stream closed");
}
buffer = nbuf; //将新buf赋值给buffer
}
count = pos;
int n = getInIfOpen().read(buffer, pos, buffer.length - pos);
if (n > 0) count = n + pos;
}

public synchronized int read() throws IOException { //读取下一个字节
if (pos >= count) { //到达buffer的末端
fill(); //就从流中读取数据,填充buffer
if (pos >= count) return -1; //读过一次,没有数据则返回-1
}
return getBufIfOpen()[pos++] & 0xff; //返回buffer中下一个位置的字节
}

private int read1(byte[] b, int off, int len) throws IOException { //将数据从流中读入buffer中
int avail = count - pos; //buffer中还剩的可读字符
if (avail <= 0) { //buffer中没有可以读取的数据时
if (len >= getBufIfOpen().length && markpos < 0) { //将输入流中的字节读入b中
return getInIfOpen().read(b, off, len);
}
fill(); //填充
avail = count - pos;
if (avail <= 0) return -1;
}
int cnt = (avail < len) ? avail : len; //从流中读取后,检查可以读取的数目
System.arraycopy(getBufIfOpen(), pos, b, off, cnt); //将当前buffer中的字节放入b的末端
pos += cnt;
return cnt;
}


public synchronized int read(byte b[], int off, int len)throws IOException {
getBufIfOpen(); // 检查buffer是否open
if ((off | len | (off + len) | (b.length - (off + len))) < 0) { //检查输入参数是否正确
throw new IndexOutOfBoundsException();
} else if (len == 0) {
return 0;
}
int n = 0;
for (;;) {
int nread = read1(b, off + n, len - n);
if (nread <= 0) return (n == 0) ? nread : n;
n += nread;
if (n >= len) return n;
// if not closed but no bytes available, return
InputStream input = in;
if (input != null && input.available() <= 0) return n;
}
}


public synchronized long skip(long n) throws IOException {
getBufIfOpen(); // 检查buffer是否关闭
if (n <= 0) { return 0; } //检查输入参数是否正确
long avail = count - pos; //buffered中可以读取字节的数目
if (avail <= 0) { //可以读取的小于0,则从流中读取
if (markpos <0) return getInIfOpen().skip(n); //mark小于0,则mark在流中
fill(); // 从流中读取数据,填充缓冲区。
avail = count - pos; //可以读的取字节为buffer的容量减当前位置
if (avail <= 0) return 0;
}
long skipped = (avail < n) ? avail : n;
pos += skipped; //当前位置改变
return skipped;
}

public synchronized int available() throws IOException {
return getInIfOpen().available() + (count - pos);
}
//该方法不会block!返回流中可以读取的字节的数目。
//该方法的返回值为缓存中的可读字节数目加流中可读字节数目的和

public synchronized void mark(int readlimit) { //当前位置处为mark位置
marklimit = readlimit;
markpos = pos;
}

public synchronized void reset() throws IOException {
getBufIfOpen(); // 缓冲去关闭了,肯定就抛出异常!程序设计中经常的手段
if (markpos < 0) throw new IOException("Resetting to invalid mark");
pos = markpos;
}

public boolean markSupported() { //该流和ByteArrayInputStream一样都支持mark
return true;
}

//关闭当前流同时释放相应的系统资源。
public void close() throws IOException {
byte[] buffer;
while ( (buffer = buf) != null) {
if (bufUpdater.compareAndSet(this, buffer, null)) {
InputStream input = in;
in = null;
if (input != null) input.close();
return;
}
// Else retry in case a new buf was CASed in fill()
}
}
}

(十四)SequenceInputStream
package one;

import java.util.*;
import java.io.*;

public class SequenceStreamDemo {
/**
* 若要将一个文件分割为数个文件,再将之组合还原为一个文件,最基本的作法是使用数个FileInputStream来打开分割后的文件,然后一个一个文件的读取,并使用同一个FileOutputStream实例写到同一个文件中。必须要自行判断每一个分割文件的读取是否完毕,如果完毕就读取下一个文件。
*
* 如果使用java.io.SequenceInputStream就不用这么麻烦,SequenceInputStream可以看作是数个
* InputStream对象的组合。当一个InputStream对象的内容读取完毕后,它就会取出下一个InputStream对象,直到所有的
* InputStream对象都读取完毕为止。
*
* 范例14.11是SequenceInputStream的使用示范。可以将指定的文件进行分割,也可以将分割后的文件还原为一个文件。
*
*
*/
public static void main(String[] args) {
try {
// args[0]: 指定分割(s)或连接(c)
switch (args[0].charAt(1)) {
case 's':
// args[1]: 每个分割文件的大小
int size = Integer.parseInt(args[1]);
// args[2]: 指定要被分割的文件名称
seperate(args[2], size);
break;
case 'c':
// args[1]: 指定要被组合的文件个数
int number = Integer.parseInt(args[1]);
// args[2]: 组合后的文件名称
concatenate(args[2], number);
break;
}
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("Using: java UseSequenceStream [-s/-c]"
+ " (size/number) filename");
System.out.println("-s: 分割文件\n-c: 组合文件");
} catch (IOException e) {
e.printStackTrace();
}
}

// 分割文件
public static void seperate(String filename, int size) throws IOException {
FileInputStream fileInputStream = new FileInputStream(
new File(filename));
BufferedInputStream bufInputStream = new BufferedInputStream(
fileInputStream);
byte[] data = new byte[1];
int count = 0;
// 还原文件大小及指定分割的大小
// 决定要分割为几个文件
if (fileInputStream.available() % size == 0)
count = fileInputStream.available() / size;
else
count = fileInputStream.available() / size + 1;

// 开始进行分割
for (int i = 0; i < count; i++) {
int num = 0;
// 分割的文件加上下划线与编号
File file = new File(filename + "_" + (i + 1));
BufferedOutputStream bufOutputStream = new BufferedOutputStream(
new FileOutputStream(file));

while (bufInputStream.read(data) != -1) {
bufOutputStream.write(data);
num++;
if (num == size) { // 分割出一个文件
bufOutputStream.flush();
bufOutputStream.close();
break;
}
}

if (num < size) {
bufOutputStream.flush();
bufOutputStream.close();
}
}

System.out.println("分割为" + count + "个文件");
}

// 连接文件
public static void concatenate(String filename, int number)
throws IOException {
// 收集文件用的List
List<InputStream> list = new ArrayList<InputStream>();

for (int i = 0; i < number; i++) {
// 文件名必须为下划线加上编号
File file = new File(filename + "_" + (i + 1));
list.add(i, new FileInputStream(file));
}

final Iterator<InputStream> iterator = list.iterator();

// SequenceInputStream 需要一个Enumeration对象来构建
Enumeration<InputStream> enumation = new Enumeration<InputStream>() {
public boolean hasMoreElements() {
return iterator.hasNext();
}

public InputStream nextElement() {
return iterator.next();
}
};

// 建立SequenceInputStream
// 并使用BufferedInputStream
BufferedInputStream bufInputStream = new BufferedInputStream(
new SequenceInputStream(enumation), 8192);
BufferedOutputStream bufOutputStream = new BufferedOutputStream(
new FileOutputStream(filename), 8192);
byte[] data = new byte[1];
// 读取所有文件数据并写入目的地文件
while (bufInputStream.read(data) != -1)
bufOutputStream.write(data);
bufInputStream.close();
bufOutputStream.flush();
bufOutputStream.close();
System.out.println("组合" + number + "个文件 OK!!");
}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值