Java IO流
Java IO流梳理
一、流的概念和作用
学习Java IO流,我是真的有点无语啊。。。!相信很多学习Java的朋友在起初学习Java IO的时候都会觉得很简单,可是真正用的使用的时候又措手不及,这是一个比较正常的现象,这不能怪你,因为Java设计者在设计Java IO这个类库时,就已然注定了这样蛋疼的一个局面。Java IO的学习之难,主要有两点:其一是构建一个实例需要借助其他的一个或多个实例,其次是类多而杂。你需要以一种俯瞰的视角来对待Java IO,只要你了解了Java IO类库的层次关系,你就可以很好的使用它了,是不是很激动...^_^,下面让我们来看看流的概念:
流:是一组有顺序的,有起点和终点的字节集合,是对数据传输的总称和抽象。即数据在两设备间的传输成为流,流的本质是数据传输,根据数据传输特性将流抽象为各种类,方便更直观的进行数据操作。
二、流的分类
根据处理数据类型的不同分:字符流和字节流
根据数据流向不同分为:输入流和输出流
三、字符流和字节流
字符流的由来:因为数据编码的不同,而有了对字符进行高效操作的流对象。本质其实就是基于字节流读取时,去查了指定的码表。
字节流和字符流的区别:
(1)读写单位不同:字节流以字节(8bit)为单位,字符流以字符(16bit)为单位,根据码表映射字符。
(2)处理对象不同:字节流能处理所有类型的数据(如:图片、音乐、视频等媒体介质),而字符流只能处理字符类型的数据。
(3)字节流在操作的时候本身是不会用到缓冲区的,是文件本身的直接操作的;而字符流在操作的时候是会用到缓冲区的,是通过缓冲区来操作文件。
建议:当我们在处理媒体介质时,请选择字节流;当我们在处理字符介质时,请选择字符流;当我们不明确操作的介质的类型时,请选择字节流。
四、输入流和输出流
对输入流,只能进行读(read)操作;对输出流,只能进行写(write)操作,程序中需要根据待传输数据的不同特性而使用不停用的流。
五、Java IO流对象
1、定义和结构说明:
InputStream是所有输入字节流的父类,它是一个抽象(abstract)类。
ByteArrayInputStream/StringBufferInputStream/FoleInputStream是三种几倍的介质流,它们分别从Byte数组、StringBuffer、和本地文件中读取数据。其中的StringBufferInputStream已经过时了,因为此类不能正确的将字符转换成字节。
PipedInputStream是一个跟多线程有关的流。
ObjectInputStream和所有的FilterInputStream的子类都是装饰流。所谓装饰流:必须通过其他流的实例才能创建自己的实例。如:DataInputStream dis = new DataInputStream(InputStream is);
案例演示1:
1 private static final String pathname = "D:" + File.separatorChar + "HelloWorld.java"; 2 private static final File filePath = new File(pathname); 3 public static void start(){ 4 try(InputStream is = new java.io.FileInputStream(filePath)) { 5 if(!filePath.isFile()) 6 return; 7 byte[] bytes = new byte[(int) filePath.length()]; 8 is.read(bytes); 9 String fileContext = new String(bytes); 10 System.out.println(fileContext); 11 } catch (Exception e) { 12 // TODO: handle exception 13 e.printStackTrace(); 14 }; 15 }
仔细观察上面代码的第7行,我们创建了一个字节数组,其长度为指定文件的长度:这意味这,我们设定了一个字节缓冲区,这个缓冲区的长度等于文件的长度,然后我们可以通过第8行is.read(bytes);代码将指定文件一次性读入到这个字节缓冲区中,所以我们不再需要循环的去读了。下面请看下面的案例2:
案例演示2
1 public static void startVariant1(){ 2 try(InputStream is = new java.io.FileInputStream(filePath)) { 3 if(!filePath.isFile()) 4 return; 5 byte[] bytes = new byte[1024]; 6 int temp = is.read(bytes); 7 String fileContext = new String(bytes, 0, temp); 8 System.out.println(fileContext); 9 } catch (Exception e) { 10 // TODO: handle exception 11 e.printStackTrace(); 12 }; 13 }
这里,我们指定字节缓冲区的长度为1024:这意味着,我们通过is.read(bytes);每次从文件中读取1024个长度的数据到字节缓冲区中,然后通过String的构造器将缓冲区的0-temp个长度字节转换成了字符串。(temp:实际读取的字节数)这里就有一个问题了——“如果我们的文件长度大于1024,那么我们得到的结果是不正确的?”,所以请接着看案例演示3:
案例演示3:
1 public static void startVariant2(){ 2 File filePath = new File("D:" + File.separatorChar + "pcssinfo.log"); 3 StringBuffer sb = new StringBuffer(); 4 try(InputStream is = new java.io.FileInputStream(filePath)) { 5 if(!filePath.isFile()) 6 return; 7 byte[] bytes = new byte[1024]; 8 int temp = is.read(bytes); 9 while(temp != -1){ 10 sb.append(new String(bytes, 0, temp)); 11 temp = is.read(bytes); 12 } 13 System.out.println(sb.toString()); 14 } catch (Exception e) { 15 // TODO: handle exception 16 e.printStackTrace(); 17 }; 18 }
在案例2的基础上,我们加了一个while循环,每次从文件中读取1024个字节到字节缓冲区中,当temp == -1时意味着已经到达了文件末尾,则跳出循环;这样无论文件有多大,都无所谓了。其实上面的while循环还可以优化下,如下:
byte[] bytes = new byte[1024]; int temp = 0; while((temp = is.read(bytes)) != -1){ sb.append(new String(bytes, 0, temp)); }
当然了,我们可以选择逐字节读取,但是那样就很慢很慢了,所以大家还是尽量选择使用缓冲区。
2、我们来看看ObjectInputStream/ObjectOutputStream
1 /** 2 * ObjectInputStream 对使用 ObjectOutputStream 写入的基本数据和对象进行反序列化 3 * 4 */ 5 public class ObjectStream { 6 7 /** 8 * 向指定文件里面写入Student对象信息 9 * @param targetFile 10 */ 11 public static void write(String targetFile){ 12 ObjectOutputStream oos = null; 13 try { 14 oos = new ObjectOutputStream(new FileOutputStream(targetFile)); 15 oos.writeObject(new Student("yangkang", 22, 1)); 16 oos.writeObject(new Student("liming", 28, 1)); 17 oos.writeObject(new Student("tanglu", 23, 0)); 18 } catch (FileNotFoundException e) { 19 // TODO Auto-generated catch block 20 e.printStackTrace(); 21 } catch (IOException e) { 22 // TODO Auto-generated catch block 23 e.printStackTrace(); 24 } finally{ 25 if(oos != null){ 26 try { 27 oos.close(); 28 oos = null; 29 } catch (IOException e) { 30 // TODO Auto-generated catch block 31 e.printStackTrace(); 32 } 33 } 34 } 35 } 36 37 /** 38 * 从指定文件里面反序列化出对象 39 * @param targetFile 40 */ 41 public static void read(String targetFile){ 42 ObjectInputStream ois = null; 43 int flag = 3; 44 List<Student> studs = new ArrayList<Student>(); 45 try { 46 ois = new ObjectInputStream(new FileInputStream(targetFile)); 47 while(flag-- > 0){ 48 try { 49 Student stud = (Student) ois.readObject(); 50 studs.add(stud); 51 System.out.println(stud); 52 } catch (ClassNotFoundException e) { 53 // TODO Auto-generated catch block 54 e.printStackTrace(); 55 } 56 57 } 58 59 } catch (FileNotFoundException e) { 60 // TODO Auto-generated catch block 61 e.printStackTrace(); 62 } catch (IOException e) { 63 // TODO Auto-generated catch block 64 e.printStackTrace(); 65 } finally{ 66 if(ois != null){ 67 try { 68 ois.close(); 69 ois = null; 70 } catch (IOException e) { 71 // TODO Auto-generated catch block 72 e.printStackTrace(); 73 } 74 } 75 } 76 } 77 78 public static void main(String[] args) { 79 ObjectStream.write("object.txt"); 80 ObjectStream.read("object.txt"); 81 } 82 83 }
3、PushBackInputStream
这是一个回退流,主要用在编辑器检查代码的语法、拼写等是否符合约定,基本上你不可能用到这个流,就不再赘述。
六、输出字节流OutputStream
1、定义和结构说明:
OutputStream是所有的输出字节流的父类,它是一个抽象类。
ByteArrayOutputStream/FileOutputStream是两种基本的介质类,他们分别向Byte数组/本地文件中写入数据。
PipedOutputStream是用与多线程的流,由于涉及到多线程,这里就不做介绍了。
ObjectOutputStream和所有FilterOutputStream的子类都是装饰流,具体的和上面ObjectInputStream对应着看下。
案例演示4:
1 class WriteFile{ 2 private static final String pathname = "D:" + File.separatorChar + "onew.txt"; 3 private static final File filePath = new File(pathname); 4 public static void start(){ 5 if(!filePath.exists()) 6 try { 7 filePath.createNewFile(); 8 } catch (IOException e1) { 9 // TODO Auto-generated catch block 10 e1.printStackTrace(); 11 } 12 try(OutputStream os = new FileOutputStream(filePath)) { 13 StringBuffer sb = new StringBuffer(); 14 for (int i = 0; i < 1000; i++) { 15 sb.append("附近的思考你发的空间分地方能打开年饭的你发的开始能付款双方的可能发"); 16 } 17 byte[] bytes = sb.toString().getBytes(); 18 for (int i = 0; i < bytes.length; i++) { 19 os.write(bytes[i]); 20 } 21 } catch (Exception e) { 22 // TODO: handle exception 23 e.printStackTrace(); 24 } 25 } 26 public static void startVariant1(){ 27 if(!filePath.exists()) 28 try { 29 filePath.createNewFile(); 30 } catch (IOException e1) { 31 // TODO Auto-generated catch block 32 e1.printStackTrace(); 33 } 34 try(OutputStream os = new FileOutputStream(filePath)) { 35 StringBuffer sb = new StringBuffer(); 36 for (int i = 0; i < 1000; i++) { 37 sb.append("附近的思考你发的空间分地方能打开年饭的你发的开始能付款双方的可能发"); 38 } 39 byte[] bytes = sb.toString().getBytes(); 40 os.write(bytes); 41 } catch (Exception e) { 42 // TODO: handle exception 43 e.printStackTrace(); 44 } 45 } 46 }
案例演示5:边读边写
/** * 边读边写文件 * @author yangkang * */ class ReadAndWriteFile{ private static final File from = new File("D:" + File.separatorChar + "onew.txt"); private static final File to = new File("D:" + File.separatorChar + "onewCopy.txt"); public static void readAndWrite(){ try(InputStream is = new java.io.FileInputStream(from)) { byte[] bytes = new byte[1021]; int temp = 0; while((temp = is.read(bytes)) != -1){ try(OutputStream os = new FileOutputStream(to, true)) { os.write(bytes, 0, temp); } catch (Exception e) { // TODO: handle exception e.printStackTrace(); } } } catch (Exception e) { // TODO: handle exception e.printStackTrace(); } } }

浙公网安备 33010602011771号