前言
"IO流"(Input/Output stream)指的是Java中用于处理输入输出(I/O)数据的机制。在Java中,所有的输入和输出都被抽象为“流”对象,并通过输入流读取数据、输出流写入数据。
Java的I/O包提供了丰富的类和方法来支持不同类型的流,输入流和输出流之间可以自由地进行转换。它们分别主要包括字节流和字符流两种类型。其中,字节流是操作二进制数据的流,可以处理任何类型的数据,常见的例如InputStream和OutputStream;字符流则是处理字符数据的流,其主要使用Reader和Writer接口。
Java中的IO流可以对文件、网络套接字等的数据进行读写操作,这些操作涉及到打开、关闭、查找、修改和删除文件、创建及维护网络连接等等一系列活动。因此,掌握Java中IO流的知识,有助于我们实现更加高效准确地操作文件、网络数据等各种实用应用。
一、IO流的分类
可以从三个不同的维度进行分类:
• 1、按照流的方向(输出输入都是站在程序所在内存的角度划分的)
• 输入流:只能从中读取数据【主要由InputStream和Reader作为基类】
• 输出流:只能向其写入数据【主要由outputStream和Writer作为基类】
• 2、按照流的操作颗粒度划分
• 字节流:以字节为单元,可操作任何数据【主要由InputStream和outPutStream作为基类】
• 字符流:以字符为单元,只能操作纯字符数据,比较方便【主要由Reader和Writer作为基类】
• 3、按照流的角色划分
• 节点流:可以从/向一个特定的IO设备(如磁盘,网络)读/写数据的流,也叫【低级流,主要由】
• 处理流:用于对一个已存在的流进行连接和封装,通过封装后的流来实现数据的读/写功能,也叫【高级流】
二、流的原理及流的数量
• 1、流的原理解析
流其实我们可以想象成一个“水管”,源端和目的端就是两个“水桶”,数据是通过这个“水管”进行流动传输的,以InputStream和Reader为例,水管的每个“水滴”就是具体的数据,如果是字节流,那么一个“水滴”就是一个字节,如果是字符流,那么一个“水滴”就是一个字符。
当创建一个流对象的时候,如fis=new FileInputStream(“…\xx\xx.txt”),记录指针来表示当前正准备从哪个“水滴”开始读取,每当程序从InputStream或者Reader里面取出一个或者多个“水滴”后,记录指针自定向后移动;除此之外,InputStream和Reader里面都提供了一些方法来控制记录指针的移动。
2. java的流的图结构:
三、Java IO流对象
1. 输入字节流InputStream
定义和结构说明:
InputStream 是所有的输入字节流的父类,它是一个抽象类。
ByteArrayInputStream、StringBufferInputStream、FileInputStream 是三种基本的介质流,它们分别从Byte 数组、StringBuffer、和本地文件中读取数据。PipedInputStream 是从与其它线程共用的管道中读取数据,与Piped 相关的知识后续单独介绍。
ObjectInputStream 和所有FilterInputStream的子类都是装饰流(装饰器模式的主角)。
意思是FileInputStream类可以通过一个String路径名创建一个对象,FileInputStream(String name)。而DataInputStream必须装饰一个类才能返回一个对象,DataInputStream(InputStream in)。
【案例 】读取文件内容
/** * 字节流 * 读文件内容 * */ import java.io.*; class hello{ public static void main(String[] args) throws IOException { String fileName="D:"+File.separator+"hello.txt"; File f=new File(fileName); InputStream in=new FileInputStream(f); byte[] b=new byte[1024]; in.read(b); in.close(); System.out.println(new String(b)); } }
注意:
该示例中由于b字节数组长度为1024,如果文件较小,则会有大量填充空格。我们可以利用in.read(b);的返回值来设计程序,如下案例:
/** * 字节流 * 读文件内容 * */ import java.io.*; class hello{ public static void main(String[] args) throws IOException { String fileName="D:"+File.separator+"hello.txt"; File f=new File(fileName); InputStream in=new FileInputStream(f); byte[] b=new byte[1024]; int len=in.read(b); in.close(); System.out.println("读入长度为:"+len); System.out.println(new String(b,0,len)); } }
注意:
观察上面的例子可以看出,我们预先申请了一个指定大小的空间,但是有时候这个空间可能太小,有时候可能太大,我们需要准确的大小,这样节省空间,那么我们可以这样做:
/** * 字节流 * 读文件内容,节省空间 * */ import java.io.*; class hello{ public static void main(String[] args) throws IOException { String fileName="D:"+File.separator+"hello.txt"; File f=new File(fileName); InputStream in=new FileInputStream(f); byte[] b=new byte[(int)f.length()]; in.read(b); System.out.println("文件长度为:"+f.length()); in.close(); System.out.println(new String(b)); } }
注意:
有时候我们不知道文件有多大,这种情况下,我们需要判断是否独到文件的末尾。
/** * 字节流 *读文件 * */ import java.io.*; class hello{ public static void main(String[] args) throws IOException { String fileName="D:"+File.separator+"hello.txt"; File f=new File(fileName); InputStream in=new FileInputStream(f); byte[] b=new byte[1024]; int count =0; int temp=0; while((temp=in.read())!=(-1)){ b[count++]=(byte)temp; } in.close(); System.out.println(new String(b)); } }
2. 输出字节流OutputStream
定义和结构说明:
OutputStream 是所有的输出字节流的父类,它是一个抽象类。
ByteArrayOutputStream、FileOutputStream是两种基本的介质流,它们分别向Byte 数组、和本地文件中写入数据。PipedOutputStream 是向与其它线程共用的管道中写入数据,
ObjectOutputStream 和所有FilterOutputStream的子类都是装饰流。具体例子跟InputStream是对应的。
1.向文件中写入字符串
/** * 字节流 * 向文件中写入字符串 * */ import java.io.*; class hello{ public static void main(String[] args) throws IOException { String fileName="D:"+File.separator+"hello.txt"; File f=new File(fileName); OutputStream out =new FileOutputStream(f); String str="Hello World"; byte[] b=str.getBytes(); out.write(b); out.close(); } }
2.向文件中追加新内容
/** * 字节流 * 向文件中追加新内容: * */ import java.io.*; class hello{ public static void main(String[] args) throws IOException { String fileName="D:"+File.separator+"hello.txt"; File f=new File(fileName); OutputStream out =new FileOutputStream(f,true);//true表示追加模式,否则为覆盖 String str="Rollen"; //String str="\r\nRollen"; 可以换行 byte[] b=str.getBytes(); for (int i = 0; i < b.length; i++) { out.write(b[i]); } out.close(); } }
3. 复制文件
/** * 文件的复制 * */ import java.io.*; class hello{ public static void main(String[] args) throws IOException { if(args.length!=2){ System.out.println("命令行参数输入有误,请检查"); System.exit(1); } File file1=new File(args[0]); File file2=new File(args[1]); if(!file1.exists()){ System.out.println("被复制的文件不存在"); System.exit(1); } InputStream input=new FileInputStream(file1); OutputStream output=new FileOutputStream(file2); if((input!=null)&&(output!=null)){ int temp=0; while((temp=input.read())!=(-1)){ output.write(temp); } } input.close(); output.close(); } }
4.缓存流的使用(BufferedInputStream/BufferedOutputStream,BufferedReader/BufferedWriter)
import java.io.*; public class TestBufferStream { public static void main (String[] args) throws IOException{ BufferedInputStream bis=null; BufferedOutputStream bos=null; try { FileInputStream fis = new FileInputStream("C:\\Users\\41639\\Desktop\\java\\FileText\\src\\TestFileImportStream.java"); FileOutputStream fos = new FileOutputStream("C:\\Users\\41639\\Desktop\\java\\temp\\out2.java"); bis = new BufferedInputStream(fis); bos = new BufferedOutputStream(fos); byte[] b = new byte[1024]; int off=0; while ((off=bis.read(b))>0) { bos.write(b,0,off); } bis.close(); bos.close(); }catch (IOException e) { e.printStackTrace(); }finally { bis.close(); bos.close(); } } }
他们最基本的其实也是FileInputStream和FileOutputStream,在这个“流”的基础上,又加了缓存的功能流BufferedInputStream和BufferedOutputStream。
5.转换流的使用(InputStreamReader/OutputStreamWriter)
字面意思理解,转化流就是用来转化的,那么到底是什么转什么呢?我们可以通过以下的例子来熟悉。读取键盘输入的每一行内容,并写入到文本中,直到遇到over行结束输入
public static void main(String[] args) throws IOException { BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); BufferedWriter bw = new BufferedWriter(new FileWriter("C:\\Users\\41639\\Desktop\\java\\temp\\test1031.txt")); String line =null; while ((line=br.readLine())!=null) { if ("over".contentEquals(line)) { break; } bw.write(line); bw.newLine(); bw.flush(); } bw.close(); br.close(); }
6.对象流的使用(FileInputStream/ObjectOutputStream)
import java.io.*; public class ObjectStreamTest { public static void main(String[] args) throws Exception{ try { Person P=new Person("Jeccica",26); FileOutputStream fos=new FileOutputStream("C:\\Users\\admin\\Desktop\\Java\\temp\\22.txt"); ObjectOutputStream oos=new ObjectOutputStream(fos); oos.writeObject(P); oos.flush(); oos.close(); }catch(FileNotFoundException e) { e.printStackTrace(); }catch(IOException e) { e.printStackTrace(); } FileInputStream fis=new FileInputStream("C:\\Users\\admin\\Desktop\\Java\\temp\\22.txt"); ObjectInputStream ois=new ObjectInputStream(fis); Person P2=(Person)ois.readObject(); System.out.println(P2.name+"的年龄为"+P2.age); } } class Person implements Serializable{ String name=null; int age=0; Person(String _name,int _age){ name=_name; age=_age; } }
字节数组流的使用(ByteArrayInputStream/ByteArrayOutputStream)【通常结合数据流DataInputStream/DataOutputStream】
public static void main(String[] args) { ByteArrayOutputStream baos=new ByteArrayOutputStream();//创建字节数组流,同时会在内存里面创建数组 DataOutputStream dos=new DataOutputStream(baos);//对字节数组流外封装成数据处理流 try { dos.writeDouble(Math.random());//利用数据流里面的写入方法,写一个Double类型的随机数据 dos.writeBoolean(true); ByteArrayInputStream bias=new ByteArrayInputStream(baos.toByteArray());//toByteArray()方法是创建一个新分配的字节数组。数组的大小和当前输出流的大小。这里指的是baos这个字节数组 System.out.println(bias.available()); DataInputStream dis=new DataInputStream(bias); System.out.println(dis.readDouble()); System.out.println(dis.readBoolean()); dos.close(); dis.close(); }catch (IOException e) { e.printStackTrace(); } }