惯例引用百科的回答
流是一种抽象概念,它代表了数据的无结构化传递。按照流的方式进行输入输出,数据被当成无结构的字节序或字符序列。从流中取得数据的操作称为提取操作,而向流中添加数据的操作称为插入 操作 。用来进行输入输出操作的流就称为IO流。换句话说,IO流就是以流的方式进行输入输出 [1] .
我对IO流的理解就是"你的程序和系统之间读写文件的操作就是IO操作,和系统之间读写用的东西就是IO流"。
JAVA IO流就是JAVA程序和操作系统之间通信用的方法。
给大家看下JAVA IO的脑图
自己画的JAVA的脑图,如果有需要原文件的去我公众号,发送:JAVA IO,就可以得到脑图的原文件了,带黄色文件夹标注的是我自己写的注释嗷。
什么你还不知道我公众号,微信搜索,千珏(jue),就可以关注到我了。
在这里由于篇幅的原因我只讲解JAVA IO流中的字节流和字符流,别的就等以后再写,比如:NIO AIO BIO这些,以后有时间抽出时间出来写一篇,要是想看的记得点个关注哦。
下面进入正题:
字节流和字符流操作的本质区别只有一个:字节流是原生的操作,字符流是经过处理后的操作。
画个图,字节流在操作时不会用到缓冲区,也就是不会用到内存,文件本身直接操作的,而字符流在操作时使用了缓冲区,通过缓冲区再操作文件,看下图:
我相信有些读者心里肯定要问这个问题,我刚开始学习的时候也想过这个问题,为什么不直接用字节流解决呢,还非要搞个字符流出来呢。
我的理解就是字节流处理 多个字节表示的东西 的时候有可能会出现乱码的问题,比如汉字,用字节流读取的时候有可能因为一位字节没有读到就变成了乱码,字符流呢就完美解决了这个问题,字符流你们可以这样理解, 字节流和编码表的组合就是字符流 。因为有了编码表所以可以确定这个汉字有多少个字节,这样字节流就可以根据位数准确的读写汉字了。
以上纯为个人理解,如有不对的地方请在评论区给我留言哦。
字节流顾名思义就是通过字节直接操作字符,更底层一些。
字节流最基础的两个类就是 InputStream
和 OutputStream
,根据这两个派生而来类都含有 read()
和 write()
的基本方法,用于读写单个字节或者字节数组。
InputStream类是一个抽象类 ,是所有字节输入流类的父类。
OutputStream类是一个抽象类,是所有字节输出流的父类
InputStream的常见子类有:
OutputStream的常见子类有:
我们一个一个过要不然怎么能叫一文带你看懂JAVA IO流了呢,那样不就是标题党了吗[滑稽]。
FileInputStream是文件字节输入流,就是对文件数据以字节的方式来处理,如音乐、视频、图片等。
FileOutPutStream是文件字节输出流,
//通过文件的名字来创建一个对象 public FileInputStream(String name) throws FileNotFoundException{} //通过File对象来创建一个对象 public FileInputStream(File file) throws FileNotFoundException{} /** * 通过FileDescriptor来创建一个对象 * FileDescriptor是一个文件描述符号 * 有in,out,err三种类型 * in:标准输入描述符,out:标准输出的描述符,err:标准错误输出的描述号 */ public FileInputStream(FileDescriptor fdObj){} //打开指定的文件进行读取 ,是java和c之间进行操作的api 我们并不会用到 private native void open0(String name){} //打开指定的文件进行读取,我们并不会用到 因为在构造方法里面帮我们打开了这个文件 private void open(String name){} //从输入流中读取一个字节的数据,如果到达文件的末尾则返回-1 public int read() throws IOException{} //读取一个字节数组 private native int readBytes(byte b[], int off, int len) throws IOException; private native int read0() throws IOException; //从输入流中读取b.length的数据到b中 public int read(byte b[]) throws IOException{} //从输入流中读取off到len之间的数据到b中 public int read(byte b[], int off, int len) throws IOException{} //跳过并丢弃输入流中的n个数据 public long skip(long n) throws IOException{} private native long skip0(long n) throws IOException; //可以从此输入流中读取的剩余字节数 public int available() throws IOException {} private native int available0() throws IOException; //关闭此文件输入流并释放与该流关联的所有系统资源 public void close() throws IOException {} //返回FileDescriptor对象 public final FileDescriptor getFD() throws IOException{} //该方法返回与此文件输入流关联的通道 NIO中会用到 本文不会提及 public FileChannel getChannel(){} private static native void initIDs(); private native void close0() throws IOException; //没有更多引用时,调用此方法来关闭输入流 一般不使用 protected void finalize() throws IOException {} 复制代码
由于篇幅起见FileOutputStream代码里面的方法我就不仔细的带你们看了(我不会说我是因为懒才不带你们看的,溜。
一般常用的方法就几个,举个例子,往D盘下面hello.txt里面输入“hello world”
public class Test { public static void main(String []args) throws IOException { //根据文件夹的名字来创建对象 FileOutputStream fileOutputStream = new FileOutputStream("D://hello.txt"); //往文件里面一个字节一个字节的写入数据 fileOutputStream.write((int)'h'); fileOutputStream.write((int)'e'); fileOutputStream.write((int)'l'); fileOutputStream.write((int)'l'); fileOutputStream.write((int)'o'); String s = " world"; //入文件里面一个字节数组的写入文件 fileOutputStream.write(s.getBytes()); fileOutputStream.close(); //传文件夹的名字来创建对象 FileInputStream fileInputStream = new FileInputStream("D://hello.txt"); int by = 0; //一个字节一个字节的读出数据 while((by = fileInputStream.read()) != -1){ System.out.println((char)by); } //关闭流 fileInputStream.close(); //通过File对象来创建对象 fileInputStream = new FileInputStream("new File("D://hello.txt")"); byte []bytes = new byte[10]; //一个字节数组的读出数据 while ((by = fileInputStream.read(bytes)) != -1){ for(int i = 0; i< by ; i++){ System.out.print((char) bytes[i]); } } //关闭流 fileInputStream.close(); } } 复制代码
常用的就上述代码里面的三种方法。
ByteArrayInputStream是字节数组输入流,它里面包含一个内部的缓冲区(就是一个字节数组 ),该缓冲区含有从流中读取的字节。
ByteArrayOutputStream是字节数组输出流
//通过byte数组来创建对象 public ByteArrayInputStream(byte buf[]) {} //通过byte数组,并给定开始下标和结束下标来创建对象 public ByteArrayInputStream(byte buf[], int offset, int length){} //从这个输入流读取下一个字节 末尾会返回 public synchronized int read(){} //从输入流中读取off到len之间的数据到b中 public synchronized int read(byte b[], int off, int len){} //跳过并丢弃输入流中的n个数据 public synchronized long skip(long n){} //可以从此输入流中读取的剩余字节数 public synchronized int available(){} //判断这个输入流是否支持标记,他一直返回true public boolean markSupported(){} //将mark的值设置为当前读取的下标,readAheadLimit这个参数没有意义,因为没用到 public void mark(int readAheadLimit){} //将当前的下标设置为mark一般和mark()方法一起使用 public synchronized void reset(){} //关闭这个输入流,因为ByteArrayInputStream操作的是数组所以没有必要关闭流 public void close() throws IOException{} 复制代码
由于篇幅起见ByteArrayOutputStream代码里面的方法我就不仔细的带你们看了(我不会说我是因为懒才不带你们看的,溜
举个例子,从一个字符串读取数组
public class Test { public static void main(String[] args) throws IOException { //创建一个字节输出流对象 ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); //一个字节一个字节的写入数据 byteArrayOutputStream.write('h'); byteArrayOutputStream.write('e'); byteArrayOutputStream.write('l'); byteArrayOutputStream.write('l'); byteArrayOutputStream.write('o'); //一个字节数组的写入数据 byteArrayOutputStream.write(" world".getBytes()); ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray(); //从这个流中读取数据 int b = 0; //从这个流中一个字节一个字节的读数据 while ((b = byteArrayInputStream.read()) != -1) { System.out.println((char) b); } byteArrayInputStream = new ByteArrayInputStream(bytes); byte[] bs = new byte[10]; //从这个流中一次性读取bs.length的数据 while ((b = byteArrayInputStream.read(bs)) != -1) { for (int i = 0; i < b; i++) { System.out.print((char) bs[i]); } System.out.println(); } } } 复制代码
如上代码所示,我平时常用的也就这几个方法。
1)概念
ObjectInputStream是反序列化流,一般和ObjectOutputStream配合使用。
用ObjectOutputStream将java对象序列化然后存入文件中,然后用ObjectInputStream读取出来
这个类的作用,我的理解是有些类在这个程序生命周期结束后,还会被用到所以要序列化保存起来
常用的其实就两个方法
public final Object readObject(){} public final void writeObject(Object obj) throws IOException{} 复制代码
class Data implements Serializable { private int n; public Data(int n){ this.n=n; } @Override public String toString(){ return Integer.toString(n); } } public class Test { public static void main(String[] args) throws IOException, ClassNotFoundException { Data w=new Data(2); ObjectOutputStream out=new ObjectOutputStream(new FileOutputStream("worm.out")); //序列化对象,把对象写到worm.out里面 out.writeObject("Worm storage/n"); //序列化对象,把对象写到worm.out里面 out.writeObject(w); out.close(); //从worm.out里面读取对象 ObjectInputStream in=new ObjectInputStream(new FileInputStream("worm.out")); //读取String对象 String s=(String)in.readObject(); //读取Data对象 Data d=(Data)in.readObject(); System.out.println(s+"Data = "+d); } } 复制代码
FilterInputStream和FilteOutputStream分别是过滤输入流和过滤输出流,他们的作用是为基础流提供一些额外的功能
FilterInputStream常用子类
FilterOutputStream常用子类
DataInputStream基本类型写入方法。
// 将一个 byte 值写入流中。 void writeByte(int v) // 将一个 short 值写入流中 void writeShort(int v) //将一个 int 值写入流中 void writeInt(int v) // 将一个 long 值写入流中 void writeLong(long v) //使用 Float 类中的 floatToIntBits 方法将 float 参数转换为一个 int 值,然后该int值写入流中 void writeFloat(float v) //使用Double 类中的 doubleToLongBits 方法将 double 参数转换为一个 long 值,然后将该long写入到流中。 void writeDouble(double v) //写入一个char void writeChar(int v) //将一个 boolean 值写入流。 void writeBoolean(boolean v) //将字节数组写入流中 void write(byte[] b, int off, int len) //将字符串写出到基础输出流中。 oid writeBytes(String s) //采用UTF-16be方式写入,也就是java字符串的编码 void writeChars(String s) // 以utf-8形式写入一个字符串 void writeUTF(String str) //清空此数据输出流,写入文件 void flush() 复制代码
测试一下方法试试
public class Test { public static void main(String[] args) throws IOException, ClassNotFoundException { DataOutputStream dataOutputStream = new DataOutputStream(new FileOutputStream("D://hello.txt")); // 写入byte类型数据 dataOutputStream.writeByte(20); // 写入short类型数据 dataOutputStream.writeShort(30); // 写入int类型 dataOutputStream.writeInt(900); // 写入float类型 dataOutputStream.writeFloat(12.3f); // 写入long类型 dataOutputStream.writeLong(800L); // 写入double类型 dataOutputStream.writeDouble(14.23); //写入boolean类型 dataOutputStream.writeBoolean(true); // 写入char类型 dataOutputStream.writeChar('中'); dataOutputStream.close(); DataInputStream dataInputStream = new DataInputStream(new FileInputStream("D://hello.txt")); System.out.println(dataInputStream.readByte()); System.out.println(dataInputStream.readShort()); System.out.println(dataInputStream.readInt()); System.out.println(dataInputStream.readFloat()); System.out.println(dataInputStream.readLong()); System.out.println(dataInputStream.readDouble()); System.out.println(dataInputStream.readBoolean()); System.out.println(dataInputStream.readChar()); dataInputStream.close(); //创建一个对象 PrintStream printStream = new PrintStream("D://hello.txt"); //写入一个字节数组 printStream.write("helloworld".getBytes()); //写入一个换行符号 printStream.println(); //格式化写入数据 printStream.format("文件名称:%s","hello.txt"); printStream.println(); printStream.append("abcde" ); printStream.close(); } } 复制代码
BufferedInputStream和BufferedOutputStream我另开一篇文章写,里面要介绍的东西很多,一篇文章介绍不完。
emmm,还有字符流下篇文章写,今天是完不成了,觉得这篇文章还可以想看下一篇文章的关注我呀,或者可以关注我公众号:千珏呀,后台留言给我呀,是千珏(jue),不是千钰。