转载:http://wdhdmx.iteye.com/blog/1256318
1.FilterInputStream简介
列出主要的内容。
- public class FilterInputStream extends InputStream {
- //对象引用
- protected volatile InputStream in;
- protected FilterInputStream(InputStream in) {
- this.in = in;
- }
- public int read() throws IOException {
- return in.read();
- }
- //这个地方的read没用用对象引用in,感觉这个地方用的特别好。
- 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);
- }
- }
FilterInputStream是标准的装饰模式,动态的增加对象的功能 。
2.DataInputStream
- public class DataInputStream extends FilterInputStream implements DataInput {}
继承的接口是DataInput,API文档上说是从二进制流中读取字节,具体内容就不看了,直接看DataInputStream
2.1 构造函数
- public DataInputStream(InputStream in) {
- //在上面的FilterInputStream中给变量赋值
- super(in);
- }
2.2 read方法
这个类中没有写read()方法,直接调用传入构造函数InputStream对象的read方法,如:FileInputStream的话就是读文件的方法。
下面的两个read带参方法
- //这两个方法没什么内容
- public final int read(byte b[], int off, int len) throws IOException {
- return in.read(b, off, len);
- }
- public final int read(byte b[]) throws IOException {
- return in.read(b, 0, b.length);
- }
2.3 readFully方法
从输入流中读取一些字节,并将它们存储在缓冲区数组 b
中。
- public final void readFully(byte b[]) throws IOException {
- readFully(b, 0, b.length);
- }
- public final void readFully(byte b[], int off, int len) throws IOException {
- if (len < 0)
- throw new IndexOutOfBoundsException();
- int n = 0;
- //首先read返回的是读了几个字节,这个方法在这里没什么意义,直接读入byte数组b。
- //如果b读不满的话就会继续一个循环,然后报错。也就是说传入的b必须读满
- while (n < len) {
- int count = in.read(b, off + n, len - n);
- if (count < 0)
- throw new EOFException();
- n += count;
- }
- }
2.4 skipBytes
- public final int skipBytes(int n) throws IOException {
- int total = 0;
- int cur = 0;
- //in.skip这个方法返回的是跳过了多少字节,返回的是long类型,这里可以随便转是因为n-total是int型,所以。。
- //好奇的是为什么不直接返回,还要多一个这样的while循环?想不通。有可能是担心in所对应的对象被覆写,呵呵。
- while ((total<n) && ((cur = (int) in.skip(n-total)) > 0)) {
- total += cur;
- }
- return total;
- }
2.5 各种read类型
直接看
- //读一个boolean
- public final boolean readBoolean() throws IOException {
- int ch = in.read();
- if (ch < 0)
- throw new EOFException();
- //这一步可以看出boolean的真实情况,在多数情况下返回的结果是true
- return (ch != 0);
- }
- //读一个字节,即8位
- public final byte readByte() throws IOException {
- int ch = in.read();
- if (ch < 0)
- throw new EOFException();
- return (byte)(ch);
- }
- //读一个short类型(16位)
- public final short readShort() throws IOException {
- int ch1 = in.read();
- int ch2 = in.read();
- if ((ch1 | ch2) < 0)
- throw new EOFException();
- //进行以为拼装,证实一个字节是八位。后面的一个怎么需要移位0呢,多此一举?还是什么原因?
- return (short)((ch1 << 8) + (ch2 << 0));
- }
- //读一个字符,也需要拼装(16位)
- public final char readChar() throws IOException {
- int ch1 = in.read();
- int ch2 = in.read();
- if ((ch1 | ch2) < 0)
- throw new EOFException();
- return (char)((ch1 << 8) + (ch2 << 0));
- }
- //读一个int类型,很明显是32位
- public final int readInt() throws IOException {
- int ch1 = in.read();
- int ch2 = in.read();
- int ch3 = in.read();
- int ch4 = in.read();
- if ((ch1 | ch2 | ch3 | ch4) < 0)
- throw new EOFException();
- return ((ch1 << 24) + (ch2 << 16) + (ch3 << 8) + (ch4 << 0));
- }
下面是一些占地大的类型
- private byte readBuffer[] = new byte[8];
- //读long类型,64位,8字节
- public final long readLong() throws IOException {
- //看来这里是为什么读不满就报错的原因。
- readFully(readBuffer, 0, 8);
- //转成long类型相加
- return (((long)readBuffer[0] << 56) +
- ((long)(readBuffer[1] & 255) << 48) +
- ((long)(readBuffer[2] & 255) << 40) +
- ((long)(readBuffer[3] & 255) << 32) +
- ((long)(readBuffer[4] & 255) << 24) +
- ((readBuffer[5] & 255) << 16) +
- ((readBuffer[6] & 255) << 8) +
- ((readBuffer[7] & 255) << 0));
- }
- //读float,和int一样,4字节,32位
- public final float readFloat() throws IOException {
- return Float.intBitsToFloat(readInt());
- }
- //和long一样的double
- public final double readDouble() throws IOException {
- return Double.longBitsToDouble(readLong());
- }
2.6 readLine 和readUTF
readLine是一个被抛弃的方法,不看了
readUTF是一个自身对应的方法,和writeUTF需要一起看,在下面一起看
3.DataOutputStream
继承关系
- public class DataOutputStream extends FilterOutputStream implements DataOutput { }
FilterOutputStream也是一个标准的装饰模式, 动态的增加对象的功能 。
3.1 各种write方法
- //写一个布尔,当然1是真,0是假
- public final void writeBoolean(boolean v) throws IOException {
- out.write(v ? 1 : 0);
- incCount(1);
- }
- //写一个byte,这个也是占一个字节
- public final void writeByte(int v) throws IOException {
- out.write(v);
- incCount(1);
- }
- //写short,两个字节,移位后强取后八位。
- public final void writeShort(int v) throws IOException {
- out.write((v >>> 8) & 0xFF);
- out.write((v >>> 0) & 0xFF);
- incCount(2);
- }
- //再看两个write
- public final void writeInt(int v) throws IOException {
- out.write((v >>> 24) & 0xFF);
- out.write((v >>> 16) & 0xFF);
- out.write((v >>> 8) & 0xFF);
- out.write((v >>> 0) & 0xFF);
- incCount(4);
- }
- //这个强转byte和强取后八位是一样的效果。
- private byte writeBuffer[] = new byte[8];
- public final void writeLong(long v) throws IOException {
- writeBuffer[0] = (byte)(v >>> 56);
- writeBuffer[1] = (byte)(v >>> 48);
- writeBuffer[2] = (byte)(v >>> 40);
- writeBuffer[3] = (byte)(v >>> 32);
- writeBuffer[4] = (byte)(v >>> 24);
- writeBuffer[5] = (byte)(v >>> 16);
- writeBuffer[6] = (byte)(v >>> 8);
- writeBuffer[7] = (byte)(v >>> 0);
- out.write(writeBuffer, 0, 8);
- incCount(8);
- }
- //这个就是每一次写操作中添加的次数,这个返回
- public final int size() {
- return written;
- }
有空再看看移位的相关知识。
3.2 UTF相关
- static int writeUTF(String str, DataOutput out) throws IOException {
- int strlen = str.length();
- int utflen = 0;
- int c, count = 0;
- //计算字节数,看来String的每一个字符都是两个字节,16位,也就是4x4
- for (int i = 0; i < strlen; i++) {
- c = str.charAt(i);
- if ((c >= 0x0001) && (c <= 0x007F)) {
- //看来这些是英文等简单字符,只占一个字节
- utflen++;
- } else if (c > 0x07FF) {
- //这个占三个字节,有点想不通那个。(这个和规则有关)
- utflen += 3;
- } else {
- //这个占两个字节。
- utflen += 2;
- }
- }
- //一次只能写入么多。65537是最大值,
- if (utflen > 65535)
- throw new UTFDataFormatException(
- "encoded string too long: " + utflen + " bytes");
- //分配大小
- byte[] bytearr = null;
- if (out instanceof DataOutputStream) {
- DataOutputStream dos = (DataOutputStream)out;
- if(dos.bytearr == null || (dos.bytearr.length < (utflen+2)))
- dos.bytearr = new byte[(utflen*2) + 2];
- bytearr = dos.bytearr;
- } else {
- bytearr = new byte[utflen+2];
- }
- //这个相当于写头信息。我感觉。里面记录这次记录的内容所占字节长度。
- bytearr[count++] = (byte) ((utflen >>> 8) & 0xFF);
- bytearr[count++] = (byte) ((utflen >>> 0) & 0xFF);
- int i=0;
- //开始连续的简单字符,都是按一个字节存储的。
- for (i=0; i<strlen; i++) {
- c = str.charAt(i);
- if (!((c >= 0x0001) && (c <= 0x007F))) break;
- bytearr[count++] = (byte) c;
- }
- for (;i < strlen; i++){
- c = str.charAt(i);
- if ((c >= 0x0001) && (c <= 0x007F)) {
- bytearr[count++] = (byte) c;
- } else if (c > 0x07FF) {
- //移位都是移6位,每次都要求在对应范围内。
- // 大于1110 0000 小于 1110 1111
- bytearr[count++] = (byte) (0xE0 | ((c >> 12) & 0x0F));
- // 大于1000 0000 小于 10111111 ,差不多就是这个规则。
- bytearr[count++] = (byte) (0x80 | ((c >> 6) & 0x3F));
- bytearr[count++] = (byte) (0x80 | ((c >> 0) & 0x3F));
- } else {
- //这个大于 11000000 小于 11011111
- bytearr[count++] = (byte) (0xC0 | ((c >> 6) & 0x1F));
- bytearr[count++] = (byte) (0x80 | ((c >> 0) & 0x3F));
- }
- }
- //将包含两个头信息的字符都写入。
- out.write(bytearr, 0, utflen+2);
- return utflen + 2;
- }
read的就比写难多了
- public final static String readUTF(DataInput in) throws IOException {
- //读前两个字符,得到有多少个字符需要转。
- int utflen = in.readUnsignedShort();
- byte[] bytearr = null;
- char[] chararr = null;
- if (in instanceof DataInputStream) {
- DataInputStream dis = (DataInputStream)in;
- //计算空间够不够,为什么需要申请两倍的空间?
- if (dis.bytearr.length < utflen){
- dis.bytearr = new byte[utflen*2];
- dis.chararr = new char[utflen*2];
- }
- chararr = dis.chararr;
- bytearr = dis.bytearr;
- } else {
- bytearr = new byte[utflen];
- chararr = new char[utflen];
- }
- int c, char2, char3;
- int count = 0;
- int chararr_count=0;
- //在已读两个字节后读utflen长度的字节。读满
- in.readFully(bytearr, 0, utflen);
- //读前面的简单字符。
- while (count < utflen) {
- c = (int) bytearr[count] & 0xff;
- if (c > 127) break;
- count++;
- chararr[chararr_count++]=(char)c;
- }
- //读正常的内容,对应前面的规则。具体就不看了。大致懂就行
- while (count < utflen) {
- c = (int) bytearr[count] & 0xff;
- switch (c >> 4) {
- case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
- count++;
- chararr[chararr_count++]=(char)c;
- break;
- case 12: case 13:
- count += 2;
- if (count > utflen)
- throw new UTFDataFormatException(
- "malformed input: partial character at end");
- char2 = (int) bytearr[count-1];
- if ((char2 & 0xC0) != 0x80)
- throw new UTFDataFormatException(
- "malformed input around byte " + count);
- chararr[chararr_count++]=(char)(((c & 0x1F) << 6) |
- (char2 & 0x3F));
- break;
- case 14:
- /* 1110 xxxx 10xx xxxx 10xx xxxx */
- count += 3;
- if (count > utflen)
- throw new UTFDataFormatException(
- "malformed input: partial character at end");
- char2 = (int) bytearr[count-2];
- char3 = (int) bytearr[count-1];
- if (((char2 & 0xC0) != 0x80) || ((char3 & 0xC0) != 0x80))
- throw new UTFDataFormatException(
- "malformed input around byte " + (count-1));
- chararr[chararr_count++]=(char)(((c & 0x0F) << 12) |
- ((char2 & 0x3F) << 6) |
- ((char3 & 0x3F) << 0));
- break;
- default:
- throw new UTFDataFormatException(
- "malformed input around byte " + count);
- }
- }
- // The number of chars produced may be less than utflen
- return new String(chararr, 0, chararr_count);