这篇我们聊一聊java中的IO流。
什么是流呢?流是一组有顺序的,有起点和终点的字节集合,是对数据传输的总称或抽象。即数据在两设备间的传输称为流。流的本质是数据的传输,根据数据传输特性将流抽象为各种类,方便更直观的进行数据操作。
IO流的分类
根据处理数据类型的不同分为:字符流和字节流;
根据数据流向不同分为:输入流和输出流。
字符流和字节流
字符流的由来:因为数据编码的不同,而有了对字符进行高效操作的对象。本质其实就是基于字节流读取时,去查了指定的码表。
字节流与字符流的区别:
1)读写单位不同:字节流以字节(8bit)为单位,字符流以字符为单位,根据码表映射字符,一次可以读多个字节;
2)处理对象不同:字节流能处理所有类别的对象(如图片、avi等),而字符流只能处理字符类型的数据;
3)字节流在操作的时候本身是不会用到缓冲区的,是文件本身的直接操作;而字符流在操作的时候是会用到缓冲区的,是通过缓冲区来操作文件。
结论:尽可能使用字节流。硬盘上所有文件都是以字节的形式进行传输或保存的,字符只是在内存中才会形成的,所以在开发中,字节流使用更广泛。
输入输出流
对输入流只能进行读操作,对输出流只能进行写操作。程序中需要根据传输数据的不同特性而使用不同的流。
java流 体系统:
首先我们先看下常用的几个类:
1、输入字节流——InputStream
从上面的体系图我们可以看出来,InputStream是所有输入字节流的父类,它是一个抽象类。
BufferedInputStream、FileInputStream是InputStream常用的子类。
1)FileInputStream:文件字节输入流,一切文件在系统中都是以字节的形式保存的,无论你是文档文件。视频文件、音频文件...这些文件都可以用FileInputStream去读取其保存在存储介质(磁盘等)上的字节序列。FileInputStream在创建时通过把文件名作为构造参数连接到该文件的内容字节,建立起字节流传输通道。
然后通过read(),read(byte[]),read(byte[],int begin,int len)三种方法从字节流中读取一个字节,一组字节。
2)BufferedInputStream:带缓冲的字节流,上面我们知道文件字节输入流读取的时候,是直接同字节流中读取的。由于字节流是与硬件(存储介质)进行的读取,所以速度较慢。CPU需要使用数据时通过read().read(byte[])读取数据时就要受到硬件IO的限制。而且,CPU与内存之间的读写速度比硬件IO快了不止十倍,所以,优化读写的思路就有了。在内存中建立缓冲区,先把存储介质中的字节读取到缓冲区中。CPU需要数据时直接从缓冲区读就行了,缓冲区要足够大,在被读完后又触发fill()函数自动从存储介质的文件字节内容中读取字节存储到缓冲区数组。
BufferedInputStream内部有一个缓冲区,默认大小为8M,每次调用read()方法的时候,它首先尝试从缓冲区里读取数据,若读取失败(缓冲区无可读数据),则选择从物理数据源(譬如文件)读取新数据(这里会尝试尽可能读取多的字节)放入到缓冲区,最后再将缓冲区中的内容返回个用户,由于从缓冲区中读取数据远比直接从存储介质读取速度快,所以BufferedInputStream的效率很高。
一个栗子:
public class Test03 { public static void main(String[] args) throws IOException { String fileName = "D:"+File.separator+"c.txt"; File file = new File(fileName); InputStream in = null; if(!file.exists()){ file.createNewFile(); } try { in = new FileInputStream(file); byte[] b = new byte[(int) file.length()]; in.read(b); System.out.println("文件长度为:"+file.length()); System.out.println(new String(b)); } catch (IOException e) { e.printStackTrace(); }finally{ in.close(); } } }
2、输出字节流——OutputStream
从上面的体系图我们可以看出来,OutputStream是所有输出字节流的父类,它是一个抽象类。
ByteArrayOutputStream、FileOutputStream是两种基本的介质流,它们分别向byte数组,和本地文件中写入数据。
一个栗子:
//向文件中一个字节一个字节写入字符串 public class Test07 { public static void main(String[] args) throws IOException { String fileName = "D:"+File.separator+"bb.txt"; File file = new File(fileName); if(!file.exists()){ try { file.createNewFile(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } OutputStream out = null; try { out = new FileOutputStream(file); String str = "Hello Stream !!"; byte[] b = str.getBytes(); for(int i=0;i<b.length;i++){ out.write(b[i]); } } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); }finally{ out.close(); } } }
3、字符输入流——Reader
从上面的体系图我们可以看出来,Reader是所有字符输入流的父类,它是一个抽象类。
CharReader,StringReader是两种基本的介质流,它们分别从char数组,String数组中读取数据。
一个栗子:
//字符流——循环方式从文件中读取内容 public class Test11 { public static void main(String[] args) throws IOException { String fileName = "D:"+File.separator+"bb.txt"; File file = new File(fileName); char[] ch = new char[100]; Reader reader = new FileReader(file); int temp = 0; int count = 0; while((temp=reader.read())!=-1){ ch[count++]=(char)temp; } reader.close(); System.out.println("内容为:"+ new String(ch,0,count)); } }
4、字符输出流——Writer
从上面的体系图我们可以看出来,Writer是所有输出字符流的父类,它是一个抽象类。
CharArrayWriter,StringWriter是两种基本的介质流,它们分别向Char数组,String中写入对象。
一个栗子:
//字符流——写入数据 public class Test12 { public static void main(String[] args) throws IOException { String fileName = "D:"+File.separator+"cc.txt"; File file = new File(fileName); Writer writer = new FileWriter(file); String str = "Hello R"; writer.write(str); writer.close(); } }
5、字符流与字节流转换:
转换流的特点:
1)是字符流与字节流之间的桥梁;
2)可对读取到的字节数据经过指定编码转换成字符;
3)可对读取到的字符数据经过指定编码转换成字节。
何时使用转换流?
当字节和字符之间有转换动作时;
流操作的数据需要编码或解码时。
具体的对象体现?
InputStreamReader:字节到字符的桥梁;
OutputStreamWriter:字符到字节的桥梁。
这两个流对象是字符体系中的成员,它们有转换作用,本身又是字符流,所以在构造的时候需要传入字节流对象进来。
字节输出流转换为字符输出流栗子:
public class Test13 { public static void main(String[] args) throws IOException { String fileName = "D:"+File.separator+"aa.txt"; File file = new File(fileName); Writer out = new OutputStreamWriter(new FileOutputStream(file)); out.write("HHHBBB"); out.close(); } }
字节输入流转换为字符输入流栗子:
public class Test14 { public static void main(String[] args) throws IOException { String fileName = "D:"+File.separator+"bb.txt"; File file = new File(fileName); Reader reader = new InputStreamReader(new FileInputStream(file)); char[] ch = new char[100]; int len = reader.read(ch); System.out.println(new String(ch,0,len)); reader.close(); } }
File类:
常见方法:
1)创建一个文件:file.createNewFile();
2)删除一个文件:file.delete();
3)创建一个文件夹:file.mkdir();
4)列出目录下的所有文件:file.list(),filelistFiles();
一个栗子:
//list()列出的不是完整路径 public class Test15 { public static void main(String[] args) { String fileName = "D:"+File.separator+"Resources"; File file = new File(fileName); String[] str = file.list(); for(int i = 0;i < str.length;i++){ System.out.println(str[i]); } } } //listFiles()可列出指定目录下所有文件完整路径 public class Test16 { public static void main(String[] args) { String fileName = "D:"+File.separator+"Resources"; File file = new File(fileName); File[] f = file.listFiles(); for(int i=0;i<f.length;i++){ System.out.println(f[i]); } } }
5)判断一个指定路径是否为目录:isDirectory;
RandomAccessFile类:
该对象并不是流体系的一员,其封装了字节流,同时还封装了一个缓冲区(字符数组),通过内部的指针来操作字符数组中的数据。
特点:该对象只能操作文件,所以构造函数接收两种类型的参数:字符串文件路径;File对象。
该对象既可以对文件进行读操作,也可以进行写操作,在进行对象实例化时可指定操作模式(r,rw)。
注意:该对象实例化时,如果操作的文件不存在,会自动创建;如果文件存在,写数据未指定位置,会从头开始写,即覆盖原有内容。可以用于多线程下载会多个线程同时写数据到文件。
一个栗子:
public class Test17 { public static void main(String[] args) throws IOException { String fileName = "D:"+File.separatorChar+"aa.txt"; File file = new File(fileName); RandomAccessFile raf = new RandomAccessFile(file,"rw"); raf.writeBoolean(true); raf.writeBytes("abc"); raf.writeChar('A'); raf.writeFloat(3.14f); raf.writeDouble(6.128); raf.close(); } }
参考时光孤岛博文:https://www.cnblogs.com/QQ846300233/p/6046388.html
来源:https://www.cnblogs.com/Rain1203/p/10770018.html