Java-IO流
第十二章 IO流
File类
引入
【1】文件,目录:
文件:
内存中存放的数据在计算机关机后就会消失。要长久保存数据,就要使用硬盘、光盘、U 盘等设备。为了便于数据的管理和检索,引入了“文件”的概念。一篇文章、一段视频、一个可执行程序,都可以被保存为一个文件,并赋予一个文件名。操作系统以文件为单位管理磁盘中的数据。
一般来说,文件可分为文本文件、视频文件、音频文件、图像文件、可执行文件等多种类别,这是从文件的功能进行分类的。从数据存储的角度来说,所有的文件本质上都是一样的,都是由一个个字节组成的,归根到底都是 0、1 比特串。不同的文件呈现出不同的形态(有的是文本,有的是视频等等)
文件夹(目录):
成千上万个文件如果不加分类放在一起,用户使用起来显然非常不便,因此又引入了树形目录(目录也叫文件夹)的机制,可以把文件放在不同的文件夹中,文件夹中还可以嵌套文件夹,这就便于用户对文件进行管理和使用
【2】查看文件/目录的信息:
右键-属性
【3】在java程序中操纵 文件/目录 ?怎么办?
java程序,最典型的特点,面向对象,java程序最擅长的就是操作对象,盘符上的文件/目录,将它的各种信息进行了封装,封装为一个对象,
java程序最擅长的就是操纵对象,这个对象属于 ---》File类
盘符上的文件---》封装为对象---》对象属于File类的对象--》有了这个对象,我们程序就可以直接操纵这个对象,通过这个对象获取文件的各种信息,还可以对文件进行创建 ,删除。
对文件进行操作
1 package com.llh; 2 3 import java.io.File; 4 import java.io.IOException; 5 6 public class Test01 { 7 //这是一个main方法,是程序的入口: 8 public static void main(String[] args) throws IOException { 9 //将文件封装为一个File类的对象: 10 File f = new File("d:\\test.txt"); 11 File f1 = new File("d:\\test.txt"); 12 File f2 = new File("d:/test.txt"); 13 //File.separator属性帮我们获取当前操作系统的路径拼接符号 14 //在windows,dos下,系统默认用“\”作为路径分隔符 ,在unix,url中,使用“/”作为路径分隔符。 15 File f3 = new File("d:"+File.separator+"test.txt");//建议使用这种 16 //常用方法: 17 System.out.println("文件是否可读:"+f.canRead()); 18 System.out.println("文件是否可写:"+f.canWrite()); 19 System.out.println("文件的名字:"+f.getName()); 20 System.out.println("上级目录:"+f.getParent()); 21 System.out.println("是否是一个目录:"+f.isDirectory()); 22 System.out.println("是否是一个文件:"+f.isFile()); 23 System.out.println("是否隐藏:"+f.isHidden()); 24 System.out.println("文件的大小:"+f.length()); 25 System.out.println("是否存在:"+f.exists()); 26 /*if(f.exists()){//如果文件存在,将文件删除操作 27 f.delete(); 28 }else{//如果不存在,就创建这个文件 29 f.createNewFile(); 30 }*/ 31 System.out.println(f == f1);//比较两个对象的地址 32 System.out.println(f.equals(f1));//比较两个对象对应的文件的路径 33 //跟路径相关的: 34 System.out.println("绝对路径:"+f.getAbsolutePath()); 35 System.out.println("相对路径:"+f.getPath()); 36 System.out.println("toString:"+f.toString()); 37 System.out.println("----------------------"); 38 File f5 = new File("demo.txt"); 39 if(!f5.exists()){ 40 f5.createNewFile(); 41 } 42 //绝对路径指的就是:真实的一个精准的,完整的路径 43 System.out.println("绝对路径:"+f5.getAbsolutePath()); 44 //相对路径:有一个参照物,相对这个参照物的路径。 45 //在main方法中,相对位置指的就是:D:\IDEA_workspace\TestJavaSE 46 //在junit的测试方法中,相对路径指的就是模块位置 47 System.out.println("相对路径:"+f5.getPath()); 48 //toString的效果永远是 相对路径 49 System.out.println("toString:"+f5.toString()); 50 File f6 = new File("a/b/c/demo.txt"); 51 if(!f5.exists()){ 52 f5.createNewFile(); 53 } 54 System.out.println("绝对路径:"+f6.getAbsolutePath()); 55 System.out.println("相对路径:"+f6.getPath()); 56 } 57 }
对目录进行操作
1 package com.llh; 2 3 import java.io.File; 4 5 public class Test02 { 6 //这是一个main方法,是程序的入口: 7 public static void main(String[] args) { 8 //将目录封装为File类的对象: 9 File f = new File("D:\\IDEA_workspace"); 10 System.out.println("文件是否可读:"+f.canRead()); 11 System.out.println("文件是否可写:"+f.canWrite()); 12 System.out.println("文件的名字:"+f.getName()); 13 System.out.println("上级目录:"+f.getParent()); 14 System.out.println("是否是一个目录:"+f.isDirectory()); 15 System.out.println("是否是一个文件:"+f.isFile()); 16 System.out.println("是否隐藏:"+f.isHidden()); 17 System.out.println("文件的大小:"+f.length()); 18 System.out.println("是否存在:"+f.exists()); 19 System.out.println("绝对路径:"+f.getAbsolutePath()); 20 System.out.println("相对路径:"+f.getPath()); 21 System.out.println("toString:"+f.toString()); 22 //跟目录相关的方法: 23 File f2 = new File("D:\\a\\b\\c"); 24 //创建目录: 25 //f2.mkdir();//创建单层目录 26 //f2.mkdirs();//创建多层目录 27 //删除:如果是删除目录的话,只会删除一层,并且前提:这层目录是空的,里面没有内容,如果内容就不会被删除 28 f2.delete(); 29 //查看: 30 String[] list = f.list();//文件夹下目录/文件对应的名字的数组 31 for(String s:list){ 32 System.out.println(s); 33 } 34 System.out.println("========================="); 35 File[] files = f.listFiles();//作用更加广泛 36 for(File file:files){ 37 System.out.println(file.getName()+","+file.getAbsolutePath()); 38 } 39 } 40 }
IO流
引入
【1】File类:封装文件/目录的各种信息,对目录/文件进行操作,但是我们不可以获取到文件/目录中的内容。
【2】引入:IO流:
I/O : Input/Output的缩写,用于处理设备之间的数据的传输。
【3】形象理解:IO流 当做一根 “管”:
【4】IO流的体系结构:
案例:通过java程序完成文件的复制操作
功能分解1:文件--》程序:FileReader
一个字符一个字符的将文件中的内容读取到程序中了:
1 package com.llh; 2 3 import java.io.File; 4 import java.io.FileNotFoundException; 5 import java.io.FileReader; 6 import java.io.IOException; 7 8 public class Test01 { 9 //这是一个main方法,是程序的入口: 10 public static void main(String[] args) throws IOException { 11 //文件--》程序: 12 //1.有一个文件:----》创建一个File类的对象 13 File f = new File("d:\\Test.txt"); 14 //2.利用FileReader这个流,这个“管”怼到源文件上去 ---》创建一个FileReader的流的对象 15 FileReader fr = new FileReader(f); 16 //3.进行操作“吸”的动作 ---》读取动作 17 /*下面的代码我们验证了:如果到了文件的结尾处,那么读取的内容为-1 18 int n1 = fr.read(); 19 int n2 = fr.read(); 20 int n3 = fr.read(); 21 int n4 = fr.read(); 22 int n5 = fr.read(); 23 int n6 = fr.read(); 24 System.out.println(n1); 25 System.out.println(n2); 26 System.out.println(n3); 27 System.out.println(n4); 28 System.out.println(n5); 29 System.out.println(n6);*/ 30 //方式1: 31 /*int n = fr.read(); 32 while(n!=-1){ 33 System.out.println(n); 34 n = fr.read(); 35 }*/ 36 //方式2: 37 int n; 38 while((n = fr.read())!=-1){ 39 System.out.println((char)n); 40 } 41 //4.“管”不用了,就要关闭 ---》关闭流 42 //流,数据库,网络资源,靠jvm本身没有办法帮我们关闭,此时必须程序员手动关闭: 43 fr.close(); 44 } 45 }
想一次性读取五个字符,不够的话下次再读五个字符:
1 package com.llh; 2 3 import java.io.File; 4 import java.io.FileReader; 5 import java.io.IOException; 6 7 public class Test02 { 8 //这是一个main方法,是程序的入口: 9 public static void main(String[] args) throws IOException { 10 //文件--》程序: 11 //1.创建一个File类的对象 12 File f = new File("d:\\Test.txt"); 13 //2.创建一个FileReader的流的对象 14 FileReader fr = new FileReader(f); 15 //3.读取动作 16 //引入一个“快递员的小车”,这个“小车”一次拉5个快递: 17 char[] ch = new char[5];//缓冲数组 18 int len = fr.read(ch);//一次读取五个:返回值是这个数组中 的有效长度 19 while(len!=-1){ 20 //System.out.println(len); 21 //错误方式: 22 /*for (int i = 0 ;i < ch.length;i++){ 23 System.out.println(ch[i]); 24 }*/ 25 //正确方式: 26 /*for (int i = 0 ;i < len;i++){ 27 System.out.println(ch[i]); 28 }*/ 29 //正确方式2:将数组转为String: 30 String str = new String(ch,0,len); 31 System.out.print(str); 32 len = fr.read(ch); 33 } 34 //4.关闭流 35 fr.close(); 36 } 37 }
功能分解2:程序--》文件:FileWriter
一个字符一个字符的向外输出:
1 import java.io.File; 2 import java.io.FileWriter; 3 import java.io.IOException; 4 /** 5 * @author : msb-zhaoss 6 */ 7 public class Test03 { 8 //这是一个main方法,是程序的入口: 9 public static void main(String[] args) throws IOException { 10 //1.有个目标文件: 11 File f = new File("d:\\demo.txt"); 12 //2.FileWriter管怼到文件上去: 13 FileWriter fw = new FileWriter(f); 14 //3.开始动作:输出动作: 15 //一个字符一个字符的往外输出: 16 String str = "hello你好"; 17 for (int i = 0 ;i < str.length();i++){ 18 fw.write(str.charAt(i)); 19 } 20 //4.关闭流: 21 fw.close(); 22 } 23 }
发现:
如果目标文件不存在的话,那么会自动创建此文件。
如果目标文件存在的话:
new FileWriter(f) 相当于对原文件进行覆盖操作。
new FileWriter(f,false) 相当于对源文件进行覆盖操作。不是追加。
new FileWriter(f,true) 对原来的文件进行追加,而不是覆盖。
利用缓冲数组:向外输出(利用缓冲数组:)
1 package com.llh; 2 3 import java.io.File; 4 import java.io.FileWriter; 5 import java.io.IOException; 6 7 8 public class Test03 { 9 //这是一个main方法,是程序的入口: 10 public static void main(String[] args) throws IOException { 11 //1.有个目标文件: 12 File f = new File("d:\\demo.txt"); 13 //2.FileWriter管怼到文件上去: 14 FileWriter fw = new FileWriter(f,true); 15 //3.开始动作:输出动作: 16 //一个字符一个字符的往外输出: 17 String str = "你好中国"; 18 char[] chars = str.toCharArray(); 19 fw.write(chars); 20 //4.关闭流: 21 fw.close(); 22 } 23 }
功能分解3:利用FileReader,FileWriter文件复制
1 package com.llh; 2 3 import java.io.*; 4 5 6 public class Test04 { 7 //这是一个main方法,是程序的入口: 8 public static void main(String[] args) throws IOException { 9 //1.有一个源文件 10 File f1 = new File("d:\\Test.txt"); 11 //2.有一个目标文件: 12 File f2 = new File("d:\\Demo.txt"); 13 //3.搞一个输入的管 怼到源文件上: 14 FileReader fr = new FileReader(f1); 15 //4.搞一个输出的管,怼到目标文件上: 16 FileWriter fw = new FileWriter(f2); 17 //5.开始动作: 18 //方式1:一个字符一个字符的复制: 19 /*int n = fr.read(); 20 while(n!=-1){ 21 fw.write(n); 22 n = fr.read(); 23 }*/ 24 //方式2:利用缓冲字符数组: 25 /*char[] ch = new char[5]; 26 int len = fr.read(ch); 27 while(len!=-1){ 28 fw.write(ch,0,len);//将缓冲数组中有效长度写出 29 len = fr.read(ch); 30 }*/ 31 //方式3:利用缓冲字符数组,将数组转为String写出。 32 char[] ch = new char[5]; 33 int len = fr.read(ch); 34 while(len!=-1){ 35 String s = new String(ch,0,len); 36 fw.write(s); 37 len = fr.read(ch); 38 } 39 //6.关闭流:(关闭流的时候,倒着关闭,后用先关) 40 fw.close(); 41 fr.close(); 42 } 43 }
警告:不要用字符流去操作非文本文件
文本文件:.txt .java .c .cpp ---》建议使用字符流操作
非文本文件:.jpg, .mp3 , .mp4 , .doc , .ppt ---》建议使用字节流操作
利用try-catch-finally处理异常方式
1 package com.llh; 2 3 import java.io.*; 4 5 6 public class Test04 { 7 //这是一个main方法,是程序的入口: 8 public static void main(String[] args) { 9 //1.有一个源文件 10 File f1 = new File("d:\\Test.txt"); 11 //2.有一个目标文件: 12 File f2 = new File("d:\\Demo.txt"); 13 //3.搞一个输入的管 怼到源文件上: 14 FileReader fr = null; 15 FileWriter fw = null; 16 try { 17 fr = new FileReader(f1); 18 //4.搞一个输出的管,怼到目标文件上: 19 fw = new FileWriter(f2); 20 //5.开始动作: 21 char[] ch = new char[5]; 22 int len = fr.read(ch); 23 while(len!=-1){ 24 String s = new String(ch,0,len); 25 fw.write(s); 26 len = fr.read(ch); 27 } 28 } catch (FileNotFoundException e) { 29 e.printStackTrace(); 30 } catch (IOException e) { 31 e.printStackTrace(); 32 } finally { 33 //6.关闭流:(关闭流的时候,倒着关闭,后用先关) 34 try { 35 if(fw!=null){//防止空指针异常 36 fw.close(); 37 } 38 } catch (IOException e) { 39 e.printStackTrace(); 40 } 41 try { 42 if(fr!=null){ 43 fr.close(); 44 } 45 } catch (IOException e) { 46 e.printStackTrace(); 47 } 48 } 49 } 50 }
FileInputStream读取文件中内容
【1】读取文本文件:
1 package com.llh; 2 3 import java.io.File; 4 import java.io.FileInputStream; 5 import java.io.FileNotFoundException; 6 import java.io.IOException; 7 8 9 public class Test01 { 10 //这是一个main方法,是程序的入口: 11 public static void main(String[] args) throws IOException { 12 //功能:利用字节流将文件中内容读到程序中来: 13 //1.有一个源文件: 14 File f = new File("D:\\Test.txt"); 15 //2.将一个字节流这个管 怼 到 源文件上: 16 FileInputStream fis = new FileInputStream(f); 17 //3.开始读取动作 18 /* 19 细节1: 20 文件是utf-8进行存储的,所以英文字符 底层实际占用1个字节 21 但是中文字符,底层实际占用3个字节。 22 细节2: 23 如果文件是文本文件,那么就不要使用字节流读取了,建议使用字符流。 24 细节3: 25 read()读取一个字节,但是你有没有发现返回值是 int类型,而不是byte类型? 26 read方法底层做了处理,让返回的数据都是“正数” 27 就是为了避免如果字节返回的是-1的话,那到底是读入的字节,还是到文件结尾呢。 28 */ 29 int n = fis.read(); 30 while(n!=-1){ 31 System.out.println(n); 32 n = fis.read(); 33 } 34 //4.关闭流: 35 fis.close(); 36 } 37 }
【2】利用字节流读取非文本文件:(以图片为案例:)--》一个字节一个字节的读取:
1 package com.llh; 2 3 import java.io.File; 4 import java.io.FileInputStream; 5 import java.io.IOException; 6 7 8 public class Test02 { 9 //这是一个main方法,是程序的入口: 10 public static void main(String[] args) throws IOException { 11 //功能:利用字节流将文件中内容读到程序中来: 12 //1.有一个源文件: 13 File f = new File("D:\\LOL.jpg"); 14 //2.将一个字节流这个管 怼 到 源文件上: 15 FileInputStream fis = new FileInputStream(f); 16 //3.开始读取动作 17 int count = 0;//定义一个计数器,用来计读入的字节的个数 18 int n = fis.read(); 19 while(n!=-1){ 20 count++; 21 System.out.println(n); 22 n = fis.read(); 23 } 24 System.out.println("count="+count); 25 //4.关闭流: 26 fis.close(); 27 } 28 }
【3】利用字节类型的缓冲数组:
1 import java.io.File; 2 import java.io.FileInputStream; 3 import java.io.IOException; 4 /** 5 * @author : msb-zhaoss 6 */ 7 public class Test03 { 8 //这是一个main方法,是程序的入口: 9 public static void main(String[] args) throws IOException { 10 //功能:利用字节流将文件中内容读到程序中来: 11 //1.有一个源文件: 12 File f = new File("D:\\LOL.jpg"); 13 //2.将一个字节流这个管 怼 到 源文件上: 14 FileInputStream fis = new FileInputStream(f); 15 //3.开始读取动作 16 //利用缓冲数组:(快递员的小车) 17 byte[] b = new byte[1024*6]; 18 int len = fis.read(b);//len指的就是读取的数组中的有效长度 19 while(len!=-1){ 20 //System.out.println(len); 21 for(int i = 0;i<len;i++){ 22 System.out.println(b[i]); 23 } 24 len = fis.read(b); 25 } 26 //4.关闭流: 27 fis.close(); 28 } 29 }
FileInputStream,FileOutputStream完成非文本文件的复制
【1】读入一个字节,写出一个字节:
1 package com.llh; 2 3 import java.io.*; 4 5 6 public class Test04 { 7 //这是一个main方法,是程序的入口: 8 public static void main(String[] args) throws IOException { 9 //功能:完成图片的复制: 10 //1.有一个源图片 11 File f1 = new File("d:\\LOL.jpg"); 12 //2.有一个目标图片: 13 File f2 = new File("d:\\LOL2.jpg"); 14 //3.有一个输入的管道 怼 到 源文件: 15 FileInputStream fis = new FileInputStream(f1); 16 //4.有一个输出的管道 怼到 目标文件上: 17 FileOutputStream fos = new FileOutputStream(f2); 18 //5.开始复制:(边读边写) 19 int n = fis.read(); 20 while(n!=-1){ 21 fos.write(n); 22 n = fis.read(); 23 } 24 //6.关闭流:(倒着关闭流,先用后关) 25 fos.close(); 26 fis.close(); 27 } 28 }
【2】利用缓冲字节数组:
1 package com.llh; 2 3 import java.io.File; 4 import java.io.FileInputStream; 5 import java.io.FileOutputStream; 6 import java.io.IOException; 7 8 9 public class Test05 { 10 //这是一个main方法,是程序的入口: 11 public static void main(String[] args) throws IOException { 12 //功能:完成图片的复制: 13 //1.有一个源图片 14 File f1 = new File("d:\\LOL.jpg"); 15 //2.有一个目标图片: 16 File f2 = new File("d:\\LOL2.jpg"); 17 //3.有一个输入的管道 怼 到 源文件: 18 FileInputStream fis = new FileInputStream(f1); 19 //4.有一个输出的管道 怼到 目标文件上: 20 FileOutputStream fos = new FileOutputStream(f2); 21 //5.开始复制:(边读边写) 22 //利用缓冲数组: 23 byte[] b = new byte[1024*8]; 24 int len = fis.read(b); 25 while(len!=-1){ 26 fos.write(b,0,len); 27 len = fis.read(b); 28 } 29 //6.关闭流:(倒着关闭流,先用后关) 30 fos.close(); 31 fis.close(); 32 } 33 }
缓冲字节流(处理流)-BufferedInputStream ,BufferedOutputStream
【1】读入一个字节,写出一个字节:
【2】利用缓冲字节数组:
【3】利用缓冲区:
想要完成上面的效果,单纯的靠FileInputStream,FileOutputStream是不可以完成的,这个时候就需要功能的加强,
这个加强就需要引入新的流(在FileInputStream,FileOutputStream外面再套一层流):BufferedInputStream ,BufferedOutputStream. ----->处理流
代码:
1 package com.llh; 2 3 import java.io.*; 4 5 6 public class Test06 { 7 //这是一个main方法,是程序的入口: 8 public static void main(String[] args) throws IOException { 9 //1.有一个源图片 10 File f1 = new File("d:\\LOL.jpg"); 11 //2.有一个目标图片: 12 File f2 = new File("d:\\LOL2.jpg"); 13 //3.有一个输入的管道 怼 到 源文件: 14 FileInputStream fis = new FileInputStream(f1); 15 //4.有一个输出的管道 怼到 目标文件上: 16 FileOutputStream fos = new FileOutputStream(f2); 17 //5.功能加强,在FileInputStream外面套一个管:BufferedInputStream: 18 BufferedInputStream bis = new BufferedInputStream(fis); 19 //6.功能加强,在FileOutputStream外面套一个管:BufferedOutputStream: 20 BufferedOutputStream bos = new BufferedOutputStream(fos); 21 //7.开始动作 : 22 byte[] b = new byte[1024*6]; 23 int len = bis.read(b); 24 while(len!=-1){ 25 bos.write(b,0,len); 26 /* bos.flush(); 底层已经帮我们做了刷新缓冲区的操作,不用我们手动完成:底层调用flushBuffer()*/ 27 len = bis.read(b); 28 } 29 //8.关闭流: 30 //倒着关: 31 //如果处理流包裹着节点流的话,那么其实只要关闭高级流(处理流),那么里面的字节流也会随之被关闭。 32 bos.close(); 33 bis.close(); 34 /*fos.close(); 35 fis.close();*/ 36 } 37 }
比对非文本文件复制的三种方法的效率
【1】读入一个字节,写出一个字节:
【2】利用缓冲字节数组:
【3】利用缓冲区:
代码:
1 package com.llh; 2 3 import java.io.*; 4 5 6 public class Test06 { 7 //这是一个main方法,是程序的入口: 8 public static void main(String[] args) throws IOException { 9 //1.有一个源图片 10 File f1 = new File("d:\\LOL.jpg"); 11 //2.有一个目标图片: 12 File f2 = new File("d:\\LOL2.jpg"); 13 //3.有一个输入的管道 怼 到 源文件: 14 FileInputStream fis = new FileInputStream(f1); 15 //4.有一个输出的管道 怼到 目标文件上: 16 FileOutputStream fos = new FileOutputStream(f2); 17 //5.功能加强,在FileInputStream外面套一个管:BufferedInputStream: 18 BufferedInputStream bis = new BufferedInputStream(fis); 19 //6.功能加强,在FileOutputStream外面套一个管:BufferedOutputStream: 20 BufferedOutputStream bos = new BufferedOutputStream(fos); 21 //7.开始动作 : 22 long startTime = System.currentTimeMillis(); 23 byte[] b = new byte[1024]; 24 int len = bis.read(b); 25 while(len!=-1){ 26 bos.write(b,0,len); 27 /* bos.flush(); 底层已经帮我们做了刷新缓冲区的操作,不用我们手动完成:底层调用flushBuffer()*/ 28 len = bis.read(b); 29 } 30 long endTime = System.currentTimeMillis(); 31 System.out.println("复制完成的时间为:"+(endTime-startTime)); 32 //8.关闭流: 33 //倒着关: 34 //如果处理流包裹着节点流的话,那么其实只要关闭高级流(处理流),那么里面的字节流也会随之被关闭。 35 bos.close(); 36 bis.close(); 37 /*fos.close(); 38 fis.close();*/ 39 } 40 }
缓冲字符流(处理流)-BufferedReader,BufferedWriter完成文本文件的复制
1 package com.llh; 2 3 import java.io.*; 4 5 6 public class Test07 { 7 //这是一个main方法,是程序的入口: 8 public static void main(String[] args) throws IOException { 9 //1.有一个源文件: 10 File f1 = new File("d:\\Test.txt"); 11 //2.有一个目标文件: 12 File f2 = new File("d:\\Demo.txt"); 13 //3.需要一个管 怼到 源文件: 14 FileReader fr = new FileReader(f1); 15 //4.需要一根管怼到目标文件: 16 FileWriter fw = new FileWriter(f2); 17 //5.套一根管在输入字符流外面: 18 BufferedReader br = new BufferedReader(fr); 19 //6.套一根管在输出字符流外面: 20 BufferedWriter bw = new BufferedWriter(fw); 21 //7.开始动作: 22 //方式1:读取一个字符,输出一个字符: 23 /*int n = br.read(); 24 while(n!=-1){ 25 bw.write(n); 26 n = br.read(); 27 }*/ 28 //方式2:利用缓冲数组: 29 /*char[] ch = new char[30]; 30 int len = br.read(ch); 31 while(len!=-1){ 32 bw.write(ch,0,len); 33 len = br.read(ch); 34 }*/ 35 //方式3:读取String: 36 String str = br.readLine();//每次读取文本文件中一行,返回字符串 37 while(str!=null){ 38 bw.write(str); 39 //在文本文件中应该再写出一个换行: 40 bw.newLine();//新起一行 41 str = br.readLine(); 42 } 43 //8.关闭流 44 bw.close(); 45 br.close(); 46 } 47 }
转换流-InputStreamReader,OutputStreamWriter
【1】转换流:作用:将字节流和字符流进行转换。
【2】转换流 属于 字节流还是字符流?属于字符流
InputStreamReader :字节输入流 ---》字符的输入流
OutputStreamWriter : 字符输出流 --》字节的输出流
【3】图解:
【4】将输入的字节流转换为输入的字符流,然后完成文件--》程序 :
1 package com.llh; 2 3 import java.io.*; 4 5 6 public class Test01 { 7 //这是一个main方法,是程序的入口: 8 public static void main(String[] args) throws IOException { 9 //文件---》程序: 10 //1.有一个源文件: 11 File f = new File("d:\\Test.txt"); 12 //2.需要一个输入的字节流接触文件: 13 FileInputStream fis = new FileInputStream(f); 14 //3.加入一个转换流,将字节流转换为字符流:(转换流属于一个处理流) 15 //将字节转换为字符的时候,需要指定一个编码,这个编码跟文件本身的编码格式统一 16 //如果编码格式不统一的话,那么在控制台上展示的效果就会出现乱码 17 //InputStreamReader isr = new InputStreamReader(fis,"utf-8"); 18 //获取程序本身的编码--》utf-8 19 InputStreamReader isr = new InputStreamReader(fis); 20 //4.开始动作,将文件中内容显示在控制台: 21 char[] ch = new char[20]; 22 int len = isr.read(ch); 23 while(len!=-1){ 24 //将缓冲数组转为字符串在控制台上打印出来 25 System.out.print(new String(ch,0,len)); 26 len = isr.read(ch); 27 } 28 //5.关闭流: 29 isr.close(); 30 } 31 }
转换流-InputStreamReader,OutputStreamWriter实现文本文件的复制
1 package com.llh; 2 3 import java.io.*; 4 5 6 public class Test02 { 7 //这是一个main方法,是程序的入口: 8 public static void main(String[] args) throws IOException { 9 //1.有一个源文件 10 File f1 = new File("d:\\Test.txt"); 11 //2.有一个目标文件: 12 File f2 = new File("d:\\Demo.txt"); 13 //3.输入方向: 14 FileInputStream fis = new FileInputStream(f1); 15 InputStreamReader isr = new InputStreamReader(fis,"utf-8"); 16 //4.输出方向: 17 FileOutputStream fos = new FileOutputStream(f2); 18 OutputStreamWriter osw = new OutputStreamWriter(fos,"gbk"); 19 //5.开始动作: 20 char[] ch = new char[20]; 21 int len = isr.read(ch); 22 while(len!=-1){ 23 osw.write(ch,0,len); 24 len = isr.read(ch); 25 } 26 //6.关闭流: 27 osw.close(); 28 isr.close(); 29 } 30 }
System类对IO流的支持
【1】System的属性:
System.in : “标准”输入流。---》默认情况下 从键盘输入
System.out :“标准”输出流。 ---》默认情况下,输出到控制台。
【2】System.in :“标准”输入流。---》默认情况下 从键盘输入
1 public class Test01 { 2 //这是一个main方法,是程序的入口: 3 public static void main(String[] args) throws IOException { 4 //得到的是标准的输入流:--》从键盘输入: 5 //InputStream in = System.in; 6 //调用方法: 7 //int n = in.read();//read方法等待键盘的录入,所以这个方法是一个阻塞方法。 8 //System.out.println(n); 9 //以前案例:从键盘录入一个int类型的数据: 10 //从上面的代码证明,键盘录入实际上是:System.in 11 //形象的理解:System.in管,这个管怼到键盘上去了,所以你从键盘录入的话,就从这个管到程序中了 12 //Scanner的作用:扫描器:起扫描作用的,扫键盘的从这根管出来的数据 13 /*Scanner sc = new Scanner(System.in); 14 int i = sc.nextInt(); 15 System.out.println(i);*/ 16 //既然Scanner是扫描的作用,不一定非得扫 System.in进来的东西,还可以扫描其他管的内容: 17 Scanner sc = new Scanner(new FileInputStream(new File("d:\\Test.txt"))); 18 while(sc.hasNext()){ 19 System.out.println(sc.next()); 20 } 21 } 22 }
【3】System.out : 返回的输出流 、 打印流(PrintStream)
1 package com.llh; 2 3 import java.io.PrintStream; 4 5 6 public class Test02 { 7 //这是一个main方法,是程序的入口: 8 public static void main(String[] args) { 9 //写到控制台: 10 PrintStream out = System.out; 11 //调用方法: 12 out.print("你好1");//直接在控制台写出,但是不换行 13 out.print("你好2"); 14 out.print("你好3"); 15 out.print("你好4"); 16 out.println("我是中国人1");//直接在控制台写出,并且换行操作 17 out.println("我是中国人2"); 18 out.println("我是中国人3"); 19 out.println("我是中国人4"); 20 System.out.println("你是"); 21 System.out.print("中国人"); 22 } 23 }
练习:键盘录入内容输出到文件中
【1】解决思路:
【2】代码:
1 package com.llh; 2 3 import java.io.*; 4 5 public class Test03 { 6 //这是一个main方法,是程序的入口: 7 public static void main(String[] args) throws IOException { 8 //1.先准备输入方向: 9 //键盘录入: 10 InputStream in = System.in;//属于字节流 11 //字节流--》字符流: 12 InputStreamReader isr = new InputStreamReader(in); 13 //在isr外面再套一个缓冲流: 14 BufferedReader br = new BufferedReader(isr); 15 //2.再准备输出方向: 16 //准备目标文件 17 File f = new File("d:\\Demo1.txt"); 18 FileWriter fw = new FileWriter(f); 19 BufferedWriter bw = new BufferedWriter(fw); 20 //3.开始动作: 21 String s = br.readLine(); 22 while(!s.equals("exit")){ 23 bw.write(s); 24 bw.newLine();//文件中换行 25 s = br.readLine(); 26 } 27 //4.关闭流: 28 bw.close(); 29 br.close(); 30 } 31 }
数据流-DataInputStream,DataOutputStream
【1】数据流:用来操作基本数据类型和字符串的
【2】
DataInputStream:将文件中存储的基本数据类型和字符串 写入 内存的变量中
DataOutputStream: 将内存中的基本数据类型和字符串的变量 写出 文件中
【3】代码:
利用DataOutputStream向外写出变量:
1 public class Test01 { 2 //这是一个main方法,是程序的入口: 3 public static void main(String[] args) throws IOException { 4 //DataOutputStream: 将内存中的基本数据类型和字符串的变量 写出 文件中 5 /*File f = new File("d:\\Demo2.txt"); 6 FileOutputStream fos = new FileOutputStream(f); 7 DataOutputStream dos = new DataOutputStream(fos);*/ 8 DataOutputStream dos = new DataOutputStream(new FileOutputStream(new File("d:\\Demo2.txt"))); 9 //向外将变量写到文件中去: 10 dos.writeUTF("你好"); 11 dos.writeBoolean(false); 12 dos.writeDouble(6.9); 13 dos.writeInt(82); 14 //关闭流: 15 dos.close(); 16 } 17 }
在Demo2.txt文件中,我们看到:
发现:这个内容我们看不懂,是给程序看的
所以下面我们开始读取的程序:
1 package com.llh; 2 3 import java.io.*; 4 5 6 public class Test02 { 7 //这是一个main方法,是程序的入口: 8 public static void main(String[] args) throws IOException { 9 //DataInputStream:将文件中存储的基本数据类型和字符串 写入 内存的变量中 10 DataInputStream dis = new DataInputStream(new FileInputStream(new File("d:\\Demo2.txt"))); 11 //将文件中内容读取到程序中来: 12 System.out.println(dis.readUTF()); 13 System.out.println(dis.readBoolean()); 14 System.out.println(dis.readDouble()); 15 System.out.println(dis.readInt()); 16 //关闭流: 17 dis.close(); 18 } 19 }
结果:
验证:那个文件,我们看不懂,程序看得懂
要求:
写出的类型跟读入的类型 必须 要匹配!
对象流-ObjectInputStream,ObjectOutputStream
【1】对象流:ObjectInputStream,ObjectInputStream
用于存储和读取基本数据类型数据或对象的处理流。
它的强大之处就是可以把Java中的对象写入到数据源中,也能把对象从数据源中还原回来。
【2】序列化和反序列化:
ObjectOutputStream 类 : 把内存中的Java对象转换成平台无关的二进制数据,从而允许把这种二进制数据持久地保存在磁盘上,或通过网络将这种二进制数据传输到另一个网络节点。----》序列化
用ObjectInputStream类 : 当其它程序获取了这种二进制数据,就可以恢复成原来的Java对象。----》反序列化
【3】代码:操作字符串对象:
首先将一个字符串对象写到文件中去:----》序列化
1 public class Test01 { 2 //这是一个main方法,是程序的入口: 3 public static void main(String[] args) throws IOException { 4 ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(new File("d:\\Demo3.txt"))); 5 //将内存中的字符串写出到文件中: 6 oos.writeObject("你好"); 7 //关闭流: 8 oos.close(); 9 } 10 }
查看文件:
我们看不懂文件的内容,但是程序是可以看懂的,所以可以写一个程序读文件中内容:----》反序列化
1 public class Test02 { 2 //这是一个main方法,是程序的入口: 3 public static void main(String[] args) throws IOException, ClassNotFoundException { 4 //将文件中保存的字符串 读入到 内存: 5 ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File("d:\\Demo3.txt"))); 6 //读取: 7 String s = (String)(ois.readObject()); 8 System.out.println(s); 9 //关闭流: 10 ois.close(); 11 } 12 }
控制台:
【4】代码:操作自定义类的对象:
自定义的Person类:
1 public class Person { 2 private String name; 3 private int age; 4 public String getName() { 5 return name; 6 } 7 public void setName(String name) { 8 this.name = name; 9 } 10 public int getAge() { 11 return age; 12 } 13 public void setAge(int age) { 14 this.age = age; 15 } 16 public Person() { 17 } 18 public Person(String name, int age) { 19 this.name = name; 20 this.age = age; 21 } 22 }
测试类:
1 public class Test01 { 2 //这是一个main方法,是程序的入口: 3 public static void main(String[] args) throws IOException { 4 //序列化:将内存中对象 ---》 文件: 5 //有一个对象: 6 Person p = new Person("lili",19); 7 //有对象流: 8 ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(new File("d:\\Demo4.txt"))); 9 //向外写: 10 oos.writeObject(p); 11 //关闭流: 12 oos.close(); 13 } 14 }
运行的时候发现出现异常:
出现异常的原因:
你想要序列化的那个对象对应的类,必须要实现一个接口:
接口内部,什么都没有,这种接口叫 标识接口。
起到标识作用,标识什么呢?只要实现这个接口的类的对象才能序列化,否则不可以。
解决办法:将Person 实现这个标识接口就可以:
1 public class Person implements Serializable { 2 private String name; 3 private int age; 4 public String getName() { 5 return name; 6 } 7 public void setName(String name) { 8 this.name = name; 9 } 10 public int getAge() { 11 return age; 12 } 13 public void setAge(int age) { 14 this.age = age; 15 } 16 public Person() { 17 } 18 public Person(String name, int age) { 19 this.name = name; 20 this.age = age; 21 } 22 }
测试:发现序列化成功,Person具备了序列化的能力。
这个二进制数据我们看不懂,但是程序可以看懂,所以我们可以用程序实现 反序列化操作:
将这个对象 恢复到内存中来:
1 public class Test02 { 2 //这是一个main方法,是程序的入口: 3 public static void main(String[] args) throws IOException, ClassNotFoundException { 4 ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File("d:\\Demo4.txt"))); 5 //读入内存: 6 Person p = (Person)(ois.readObject()); 7 System.out.println(p/*.toString()*/); 8 //关闭流: 9 ois.close(); 10 } 11 }
结果:
因为我们没有重写toString方法,所以结果为:
证明了反序列化成功: 将二进制数据 --》内存
【5】serialVersionUID:
凡是实现Serializable接口(标识接口)的类都有一个表示序列化版本标识符的静态常量:
➢private static final long serialVersionUID;
➢serialVersionUID用来表明类的不同版本间的兼容性。简言之,其目的是以序列化对象进行版本控制,有关各版本反序加化时是否兼容。
➢如果类没有显示定义这个静态变量,它的值是Java运行时环境根据类的内部细节自动生成的。若类的实例变量做了修改,serialVersionUID 可能发生变化。故建议,显式声明。
➢简单来说,Java的序列化机制是通过在运行时判断类的serialVersionUID来验证版本一致性的。在进行反序列化时,JVM会把传来的字节流中的serialVersionUID与本地相应实体类的serialVersionUID进行比较,如果相同就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异常。(InvalidCastException)
我现在在Person类中加入toString方法:
1 public class Person implements Serializable { 2 private String name; 3 private int age; 4 public String getName() { 5 return name; 6 } 7 public void setName(String name) { 8 this.name = name; 9 } 10 public int getAge() { 11 return age; 12 } 13 public void setAge(int age) { 14 this.age = age; 15 } 16 public Person() { 17 } 18 public Person(String name, int age) { 19 this.name = name; 20 this.age = age; 21 } 22 @Override 23 public String toString() { 24 return "Person{" + 25 "name='" + name + '\'' + 26 ", age=" + age + 27 '}'; 28 } 29 }
再次运行测试类:
出现异常:
出现异常的原因:
解决:给这个类 加入一个 序列号:serialVersionUID
【6】IDEA中配置序列化版本号:
在Person类上:alt+enter:
回车即可生成
【7】序列化细节:
(1)被序列化的类的内部的所有属性,必须是可序列化的 (基本数据类型都是可序列化的)
(2)static,transient修饰的属性 不可以被序列化。
1 public class Person implements Serializable { 2 private static final long serialVersionUID = 8027651838638826533L; 3 private transient String name; 4 private static int age; 5 private Famaily f = new Famaily(); 6 public String getName() { 7 return name; 8 } 9 public void setName(String name) { 10 this.name = name; 11 } 12 public int getAge() { 13 return age; 14 } 15 public void setAge(int age) { 16 this.age = age; 17 } 18 public Person() { 19 } 20 @Override 21 public String toString() { 22 return "Person{" + 23 "name='" + name + '\'' + 24 ", f=" + f + ",age=" + age + 25 '}'; 26 } 27 }
结果:
以上就是Java IO流的介绍。