io学习一

IO流的大体结构

pipes 中的四个流提供了线程之间通信的能力。java中的管道不同于Linux中的管道,前者只能只能是同一进程下的两个线程通信,后者是不同进程可通过管道通信

package pipes;

import java.io.IOException;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.util.concurrent.TimeUnit;

public class Example {
	public static void main(String[] args) throws Exception {
		PipedOutputStream pos = new PipedOutputStream();
		PipedInputStream pis = new PipedInputStream(pos);
		Thread t1 = new Thread(new Runnable() {
			public void run() {
				try {
					pos.write("hello pipes".getBytes());
					TimeUnit.MILLISECONDS.sleep(2000);
				} catch (IOException e) {
					System.out.println("
OutputStream Execption:");
					e.printStackTrace();
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		});
		Thread t2 = new Thread(new Runnable() {
			@Override
			public void run() {
				try {
					int temp = pis.read();
					while (temp != -1) {
						System.out.print((char) temp);
						temp = pis.read();
					}

				} catch (IOException e) {
					System.out.println("
Input Exception:");
					e.printStackTrace();
				}
			}
		});
		t1.start();
		t2.start();
	}
}

这里需要注意一点的是:如果去掉 sleep(),会报:java - IOException: Write end dead”的异常,同时与之相同的异常还有:“java - IOException: Read end dead” 出现这些异常的原因是:
在利用管道读写数据时,必须保证利用管道读写数据的线程都不能退出。

package pipes;

import java.io.IOException;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.io.PipedReader;
import java.io.PipedWriter;
import java.util.concurrent.TimeUnit;

public class Example2 {
	public static void main(String[] args) throws Exception {
		PipedReader reader = new PipedReader();
		PipedWriter writer = new PipedWriter();
		reader.connect(writer); // 可以不通过构造函数 连接 ,可以通过此函数连接两个流

		Thread t1 = new Thread(new Runnable() {
			public void run() {
				try {
					writer.write(" hello pipedwriter");
					TimeUnit.MILLISECONDS.sleep(2000);
				} catch (IOException e) {
					System.out.println("
OutputStream Execption:");
					e.printStackTrace();
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		});

		Thread t2 = new Thread(new Runnable() {
			@Override
			public void run() {
				try {
					int temp = reader.read();
					while (temp != -1) {
						System.out.print((char) temp);
						temp = reader.read();
					}

				} catch (IOException e) {
					System.out.println("
Input Exception:");
					e.printStackTrace();
				}
			}
		});
		t1.start();
		t2.start();
	}
}

这里使用了 connect 方法来连接两个流
注意在使用管道流的时候一定要注意 分配给两个不同的线程 ,read和write方法的调用会导致流阻塞,这意味着在一个线程中同时进行读写可能会导致线程死锁。
Arrays 允许流读取数组的能力:

package bytes;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.util.Arrays;

public class Example {
	public static void main(String[] args) throws Exception {
		InputStream in = new ByteArrayInputStream("hello ByteStream".getBytes());
		int temp = in.read();
		while (temp != -1) {
			System.out.print((char) temp);
			temp = in.read();
		}
		// 
		ByteArrayOutputStream out=new ByteArrayOutputStream();
		out.write("hello test".getBytes());
		System.out.println();
		System.out.println(Arrays.toString(out.toByteArray()));
	}
}
package bytes;

import java.io.CharArrayReader;
import java.io.CharArrayWriter;

public class Example2 {
	public static void main(String[] args) throws Exception {
		CharArrayWriter writer = new CharArrayWriter();
		writer.write("String".toCharArray());
		System.out.println(writer.toCharArray());

		CharArrayReader reader = new CharArrayReader("chararray reader test".toCharArray());
		int temp = reader.read();
		while (temp != -1) {
			System.out.print((char) temp);
			temp = reader.read();
		}

	}
}

标准输入输出流 错误流 System.out/in/error

这些都可以设置 System.setOut 设置。这里的 out 其实是一个 PrintStream 记得要在jvm关闭之前 flush 刷新。
这里延伸一下 什么时候要flush 以及 ?为什么要flush ?:

  • 多数时候,如果你最后会调用一次close方法,flush方法是可以不使用的,除非你明确的想使数据尽早写到磁盘或者网络上。
  • 为什么要flush: http://www.tuicool.com/articles/yqauEjB

需要注意的一点是当 Buffer流的 缓冲区被数据满了的时候 它会自动写入 ,没有写满Buffer流 缓冲区的数据需要flush 或者close 才能刷新进入目的地
java默认缓冲区大小 8192 字节

 package flush;

import java.io.FileOutputStream;
import java.io.FileWriter;

public class flushTest {
	public static void main(String[] args) throws Exception {
		FileOutputStream fout = new FileOutputStream("test.txt");
		fout.write("test".getBytes()); // 没有缓冲区直接可以写入
										// java.net.SocketOutputStream也是一样
										// 这里文件可以直接写进值
		FileWriter fwriter = new FileWriter("test2.txt");
		// 缓冲区的大小默认是 8192
		StringBuilder sb = new StringBuilder(8192);
		for (int i = 0; i < 8192; i++) {
			sb.append("s");
		}
		// 状况一 :下面 sb.append("b");
		// writer.flush();
		// fwriter.close(); 都被注释 test2.txt文件中没有任何值

		// 状况二 : 下面 writer.flush();
		// fwriter.close(); 都被注释 文件中 8192 个s 但是没有b

		// 状况三 下面都没有被注释 close 和flush 随便留一个
		// 文件中 8192 个 s 和1个 b

		sb.append("b");
		fwriter.write(sb.toString().toCharArray()); // 这个有缓冲区需要 flush

		// fwriter.flush();
		// fwriter.close();
	}
}

DataIn/OutputStream 提供读取、写入数据(int/float/double)的能力

package data;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;

public class Example {
	public static void main(String[] args) throws Exception {
		OutputStream out = new FileOutputStream("data.out");
		try (DataOutputStream dataOut = new DataOutputStream(out)) {
			dataOut.writeInt(200);
			dataOut.writeLong(2000);
			dataOut.writeDouble(2.22);
		}
		// jdk 1.7 的新语法 try-with-resouces 只要是实现了 AutoClose 接口的都可以这样自动关闭资源
		// 你可能觉得 这里只关闭了 DataOutputStream , FileOutputStream 还没有被关闭
		// 其实在 DataOutputStream关闭的时候它也会将 FileOutputStream关闭

		InputStream in = new FileInputStream("data.out");
		try (DataInputStream dataIn = new DataInputStream(in)) {
			System.out.println(dataIn.readInt());
			System.out.println(dataIn.readLong());
			System.out.println(dataIn.readDouble());
		}
	}
}

DataOutputStream 继承于FilterOutputStream ,这是其关闭close方法:
它也是使用的 jdk7 的方法 try-with-resouces 方法来关闭 流的

PushbackReader/Stream 允许重新推回流中的能力

它的引用场景可以用在替换字符串的中的部分字符。
你肯定有疑问这个问题为什么不用String.repalce解决?

You could use the String.replace() method, but for large amounts of data, and high number of replacements, this performs badly. Why?

The String.replace() method creates a new String instance - which is a copy of the original String with the replacements applied. If the String is 1 MB large, you end up with two strings each 1 MB large. If you have to perform 5 replacements, you have to call replace() 5 times, each time on the string returned by the last replace() call, like this:

String data = "1234567890";   // imagine a large string loaded from a file

data.replace("12", "ab")
    .replace("34", "cd")
    .replace("56", "ef")
    .replace("78", "gh")
    .replace("90", "ij")

The result would be 5 copies of the original string and a total memory consumption of 5 times the original data. As you can imagine, this method performs badly, and doesn't scale very well. The O notation of using the String.replace() method is:
O(N * M)
where N = the size of the strinTokenReplacingReader Performance

The TokenReplacingReader does not use as much memory as the String.replace() method. The data is modified while read, so all the data is copied once (but no more). Since the data is copied character by character, the memory consumption is not much larger than the buffer / stream of data you are reading.

The speed of the replacement of tokens depends on your implementation of the ITokenResolver interface.

The O notation of the TokenReplacingReader is:

O(N + M) 

where N is the size of the data to replace tokens in, and M is the number of replacements.

This is faster than the O(N * M) of the String.replace() method.g, and M = the number of replacements to perform

这些简单的英语还是应该能看懂的。

package token;

import java.io.IOException;
import java.io.PushbackReader;
import java.io.Reader;
import java.nio.CharBuffer;

public class TokenReplacingReader extends Reader {

	protected PushbackReader pushbackReader = null;
	protected ITokenResolver tokenResolver = null;
	protected StringBuilder tokenNameBuffer = new StringBuilder();
	protected String tokenValue = null;
	protected int tokenValueIndex = 0;

	public TokenReplacingReader(Reader source, ITokenResolver resolver) {
		this.pushbackReader = new PushbackReader(source, 2);
		this.tokenResolver = resolver;
	}

	public int read(CharBuffer target) throws IOException {
		throw new RuntimeException("Operation Not Supported");
	}

	public int read() throws IOException {
		if (tokenValue != null) {
			if (tokenValueIndex < tokenValue.length()) {
				return tokenValue.charAt(tokenValueIndex++);
			}
			tokenValue = null;
			tokenValueIndex = 0;
		}
		int data = pushbackReader.read();
		if (data != '$') {
			return data;
		}
		data = pushbackReader.read();
		if (data != '{') {
			pushbackReader.unread(data);
			return '$';
		}
		// 清空 tokenNamebuffer
		this.tokenNameBuffer.delete(0, tokenNameBuffer.length());
		data=pushbackReader.read();
		
		if(data==-1){
			throw new RuntimeException("error expression language!");
		}
		
		while (data != '}') {
			this.tokenNameBuffer.append((char) data);
			data = pushbackReader.read();
			if(data==-1){
				throw new RuntimeException("error expression language!");
			}
		}

		tokenValue = tokenResolver.resolveToken(tokenNameBuffer.toString());
		if (tokenValue == null) {
			tokenValue = "${" + tokenNameBuffer + "}";
		}
		if (tokenValue.length() == 0) {
			return read();
		}
		return tokenValue.charAt(tokenValueIndex++);

	}

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

	public int read(char cbuf[], int off, int len) throws IOException {
		int readCout = 0;
		for (int i = 0; i < len; i++) {
			int readChar = read();
			if (readChar == -1) {
				if(readCout==0){
					return -1;
				}
				break;
			}
			readCout=i+1;
			cbuf[off+i]=(char)readChar;
		}
		return readCout;
	}

	public void close() throws IOException {
		this.pushbackReader.close();
	}

	public long skip(long n) throws IOException {
		throw new RuntimeException("Operation Not Supported");
	}

	public boolean ready() throws IOException {
		return this.pushbackReader.ready();
	}

	public boolean markSupported() {
		return false;
	}

	public void mark(int readAheadLimit) throws IOException {
		throw new RuntimeException("Operation Not Supported");
	}

	public void reset() throws IOException {
		throw new RuntimeException("Operation Not Supported");
	}

}
package token;

public interface ITokenResolver {

	public String resolveToken(String tokenName);
}
package token;

import java.util.HashMap;
import java.util.Map;

public class MapTokenResolver implements ITokenResolver {

	protected Map<String, String> tokenMap = new HashMap<String, String>();

	public MapTokenResolver(Map<String, String> tokenMap) {
		this.tokenMap = tokenMap;
	}

	public String resolveToken(String tokenName) {
		return this.tokenMap.get(tokenName);
	}

}
package token;

import java.io.Reader;
import java.io.StringReader;
import java.util.HashMap;
import java.util.Map;

public class Example {
	public static void main(String[] args) throws Exception {
		Map<String, String> tokens = new HashMap<String, String>();
		tokens.put("token1", "value1");
		tokens.put("token2", "JJ ROCKS!!!");

		MapTokenResolver resolver = new MapTokenResolver(tokens);

		//Reader source = new StringReader("1234567890${token1}abcdefg${token2}XYZ$000${"); // error expression language
		Reader source = new StringReader("1234567890${token1}abcdefg${token2}XYZ$000");
		Reader reader = new TokenReplacingReader(source, resolver);

		int data = reader.read();
		while (data != -1) {
			System.out.print((char) data);
			data = reader.read();
		}
	}
}
原文地址:https://www.cnblogs.com/joeCqupt/p/6872765.html