`
wdhdmx
  • 浏览: 305043 次
  • 性别: Icon_minigender_1
  • 来自: 山西
博客专栏
D4bbb5f7-9aa4-3e66-8194-f61b3f0241c2
天天编程
浏览量:22022
社区版块
存档分类
最新评论

DataInputStream和DataOutputStream源码理解

阅读更多

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方法。并理解了装饰模式。

 

1
3
分享到:
评论

相关推荐

    Android应用源码之IOStreamSample_Android.zip

    5. DataInputStream和DataOutputStream:用于读写基本数据类型。 6. ObjectInputStream和ObjectOutputStream:用于序列化和反序列化对象。 四、I/O流的使用场景 1. 文件操作:读取和写入文件内容。 2. 网络通信:...

    Java读存二进制大文件块

    本例演示了如何自定义缓存区,并使用不具有缓冲功能的DataInputStream 和DataOutputStream 类来读写大块的数据文件。效果如图所示,当按下“Start”按钮后,程序从源文件“d:\image.jpg”读取指定大小(缓冲区的大小...

    Android 蓝牙串口调试助手源码(保证正确)

    4. **事件监听**:为了实时响应用户操作和蓝牙事件,需要设置各种监听器,例如OnClickListener用于处理按钮点击,BroadcastReceiver监听蓝牙状态变化,以及DataInputStream和DataOutputStream的read()和write()方法...

    Java版飞鸽传书文件传输源码.rar

    【Java版飞鸽传书文件传输源码】是一款基于Java编程语言实现的局域网文件传输工具,其设计目标是提供高效、稳定的文件共享服务。Java作为一种跨平台的语言,...同时,分析和理解这段源码也是一个实践和学习的好机会。

    MiniQQ源码 网络编程

    这个项目对于学习Java网络编程和理解基础的聊天应用架构有着很好的参考价值。 首先,我们要了解Java网络编程的基础概念。在Java中,`java.net`包提供了用于网络通信的类和接口,如Socket和ServerSocket。Socket是...

    Java2核心技术卷I第7版源码

    5. **输入/输出系统**:涵盖File类,以及I/O流的使用,如FileInputStream、FileOutputStream、DataInputStream、DataOutputStream等,用于处理文件操作和数据传输。 6. **文件和目录操作**:源码会展示如何创建、...

    Android应用源码之IOStreamSample.zip

    - DataInputStream和DataOutputStream:提供了读写基本类型数据的能力,如int、float等。 2. **字符流**:Reader和Writer是处理字符流的基类,常用的子类有: - FileReader和FileWriter:专门用于文件的字符读写...

    安卓手机(android)wifi传送文件源码.rar

    2. **Android源码**:源代码是软件开发的基础,它揭示了程序的内部工作原理,对于学习和理解Android应用开发至关重要。 3. **安卓源码**:与“Android源码”同义,指代用Java或Kotlin等语言编写的Android应用原始...

    精选_基于JAVA实现的简易聊天室_源码打包

    3. **输入/输出流**:Java的InputStream和OutputStream接口及其子类用于读取和写入数据,如使用DataInputStream和DataOutputStream处理字符编码和数据格式化。 4. **并发控制**:由于多线程环境下的资源共享,可能...

    Socket案例源码

    例如,使用DataInputStream和DataOutputStream可以方便地处理基本数据类型,或者使用BufferedReader和PrintWriter处理字符串。 - 必须确保发送方和接收方对数据格式有一致的理解,例如是否采用换行符分隔消息,或者...

    JavaSE的项目源码、主要是文件的IO操作! Java学习资料

    文件复制是一个常见的I/O操作,可以使用FileInputStream和FileOutputStream结合DataInputStream和DataOutputStream实现,或者使用NIO(New IO)框架的Channels和Buffers进行高效地复制。 异常处理在文件I/O中非常...

    Java 输入输出流 源码

    7. **过滤流**:FilterInputStream、FilterOutputStream、FilterReader和FilterWriter是装饰器模式的应用,用于增强或修改原有流的功能,如DataInputStream和DataOutputStream增加了读写基本数据类型的能力。...

    Android经典设计源码-IOStreamSample.rar

    3. **DataInputStream与DataOutputStream**:这两个类扩展了InputStream和OutputStream,提供了读写基本数据类型(如int、float)和字符串的便捷方法,简化了数据序列化的过程。 4. **对象序列化与反序列化**:在...

    Java读取和写入二进制大文件的方法.rar

    Java读取和写入二进制大文件的方法,应该说是一个基于二进制的大文件块,演示了如何自定义缓存区,并使用不具有缓冲功能的DataInputStream 和DataOutputStream 类来读写二进制的大文件块数据,运行效果请参见如下...

    Java-se-io学习 Java学习资料源码

    8. 数据流:DataInputStream和DataOutputStream支持基本类型的数据读写,如int、double等。 9. 序列化:Java提供Serializable接口,实现该接口的类可以被序列化,保存其状态。 在这个"Java_se_io-master"资源包中...

    Java-IO流基础例题 & 例题源码 & PPT教学文档(黑马程序员详细版).rar

    Java IO流是Java编程语言中一个非常重要的概念,它提供了数据传输的能力,使得程序能够读取和写入数据到各种输入/输出...通过学习提供的资源,包括例题、源码和PPT,你可以全面掌握Java IO流的使用,提升你的编程能力。

    java局域网文件传输源码

    而DataInputStream和DataOutputStream则用于在网络上传输字节数据。在文件传输中,会使用这些流对象进行文件内容的读取和写入。 4. **Socket编程**:Socket是网络上的两个进程间通信的端点,Java中的Socket类和...

    安卓Android源码——(游戏保存之Stream).zip

    在安卓(Android)平台上开发游戏时,数据保存和读取是一个重要的环节,特别是在涉及游戏进度、用户设置或者高分记录...通过学习和理解这些代码,开发者可以掌握在Android环境中有效地管理和持久化游戏数据的关键技术。

    基于Java的聊天软件Visual Chat v1.91源码.zip

    - 数据传输通常通过InputStream和OutputStream进行,例如使用DataInputStream和DataOutputStream可以方便地读写基本类型的数据。 2. **多线程技术** - 聊天软件需要处理多个并发连接,这就需要用到Java的多线程...

    安卓Android源码——(游戏保存Stream).zip

    4. **DataInputStream与DataOutputStream**:这两类流提供了一种更方便的方式来读写基本类型和字符串。在游戏保存中,它们可以用来直接写入和读取数值,而无需手动转换为字节。 5. **文件路径与权限**:在Android中...

Global site tag (gtag.js) - Google Analytics