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);
}
4.结束。
这两个类感觉写的比较好懂,思路清晰,没有native方法。并理解了装饰模式。