每次看javaIO的时候就很烦躁,一是因为太繁琐了,看到这么多类,又是字节流又是字符流的,二是我做javaEE开发的时候用的实在不多,过两天就忘了。
流又分为输入流和输出流:这个是以内存为参照的,如果是向内存存入的就是输入流,从内存流出就是输出流。
java的IO分为字符流和字节流,字节输入流每次都是从文件或者内存中的读取都是以字节为单位的,在abstract类InputStream中有一个abstract的read方法
/** Reads the next byte of data from the input stream. The value byte is * returned as an <code>int</code> in the range <code>0</code> to * <code>255</code>. If no byte is available because the end of the stream * has been reached, the value <code>-1</code> is returned */ public abstract int read() throws IOException;//这个方法的的用法是返回一个int类型的,虽然是int但是0~255那么只有一个字节的大小 public int read(byte b[]) throws IOException { return read(b, 0, b.length); } //以上方法是将读取的字节存储到一个字节数组中 public int read(byte b[], int off, int len) throws IOException
在这个类中读取字节的主要方法是一个abstract的,我以为会在子类中得到源码,打开FileInputStream后发现是一个native方法
public native int read() throws IOException; //那么java在底层调用了C++写的代码了
所以我们看不到他的具体实现是什么,不过可以知道的是在读取文件的时候还负责了游标的后移等的操作
在javaIO字节输入流中这个方法是核心,再来看一下DataInputStream,它是能够从文件或者内存中读取入int,char,short,long之类的数据的,字节输出流每次读取的都是字节,那么它是怎么读取返回数据的呢?
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)); } //这上面是一个读取int类型的数据方法,仔细看会发现它实际上每次读取了四个字节,将第一个字节左移三个字节长度, //第二个字节左移两个字节长度,第三个字节左移一个字节长度,然后将他们相加起来就得到了一个int 很相像的还有 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)); } //在这里值得注意的是判断条件ch1 | ch2) < 0 //为什么这么判断呢?是因为读取的是字节那么范围在0~255,这样两者或运算应该是正数,而出现负数只有一种可能那就是读到文件结尾不够了,返回了-1 public final int readUnsignedShort() throws IOException { int ch1 = in.read(); int ch2 = in.read(); if ((ch1 | ch2) < 0) throw new EOFException(); return (ch1 << 8) + (ch2 << 0); }
我一直不清楚的是在字节输入输出流中的布尔类型的数据是这么存储的,看了源码发现是这样的
在DataOutPutStream类中
/** * Writes a <code>boolean</code> to the underlying output stream as * a 1-byte value. The value <code>true</code> is written out as the * value <code>(byte)1</code>; the value <code>false</code> is * written out as the value <code>(byte)0</code>. If no exception is * thrown, the counter <code>written</code> is incremented by * <code>1</code>. * * @param v a <code>boolean</code> value to be written. * @exception IOException if an I/O error occurs. * @see java.io.FilterOutputStream#out */ public final void writeBoolean(boolean v) throws IOException { out.write(v ? 1 : 0); incCount(1); } //就是说java将boolean存储成了一个byte大小的数据 在DataInputStream类中读取boolean是这样的 public final boolean readBoolean() throws IOException { int ch = in.read(); if (ch < 0) throw new EOFException(); return (ch != 0); }
再来看看对象字节流,我们甚至可以使用字节的方式将对象写入文件中,当然该对象的类必须实现了Serializable接口,当我们需要再恢复这个对象的时候,系统不需要再调用构造器来执行初始化。当然了恢复出来的对象与原对象还是不同的对象,除了他们在堆中的地址之外具有其他一样的属性。
package com.gengu.input; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import com.gengu.assist.Book; /** * 这个类测试对象字节输入流 * */ public class TestObjectInput { public static Object objectInput(String filename) throws IOException, ClassNotFoundException{ FileInputStream fin = new FileInputStream("F://新建文件夹 (2)/TestIO/src/com/gengu/input/test.txt"); ObjectInputStream oin = new ObjectInputStream(fin); Object object = oin.readObject(); fin.close(); oin.close(); return object; } //向文件中写入对象 public static void objectOutput(String filename,Object obj) throws IOException{ FileOutputStream fout = new FileOutputStream(filename,false); ObjectOutputStream oout = new ObjectOutputStream(fout); oout.writeObject(obj); oout.close(); fout.close(); } public static void main(String[] args) throws IOException, ClassNotFoundException { Book book = new Book(); TestObjectInput.objectOutput("F://新建文件夹 (2)/TestIO/src/com/gengu/input/test.txt", book); Book book1 = (Book) TestObjectInput.objectInput("F://新建文件夹 (2)/TestIO/src/com/gengu/input/test.txt"); System.out.println(book1.getClass()==book.getClass()); } }