第11天 IO流
今日内容介绍
转换流
序列化流
打印流
Properties
今日学习目标
可以阐述编码表的意义
可以使用转换流读取指定编码的文本文件
可以使用转换流写入指定编码的文本文件
可以使用Properties的load方法加载文件中配置信息
可以使用Properties的store方法保存配置信息到文件
可以说出打印流的特色
可以使用序列化流写出对象到文件
可以使用反序列化流读取文件到程序中
可以导入commons-io工具包
可以配置classpath添加jar包到项目中
可以使用FileUtils经常使用方法操做文件java
第1章 字符流
通过前面的学习,咱们基本掌握的文件的读写操做,在操做过程当中字节流能够操做全部数据,但是当咱们操做的文件中有中文字符,而且须要对中文字符作出处理时怎么办呢?
1.1 字节流读取字符的问题
经过如下程序读取带有中文件的文件。程序员
public class CharStreamDemo { public static void main(String[] args) throws IOException { //给文件中写中文 writeCNText(); //读取文件中的中文 readCNText(); } //读取中文 public static void readCNText() throws IOException { FileInputStream fis = new FileInputStream("c:\\cn.txt"); int ch = 0; while((ch = fis.read())!=-1){ System.out.println(ch); } } //写中文 public static void writeCNText() throws IOException { FileOutputStream fos = new FileOutputStream("c:\\cn.txt"); fos.write("a欢迎你".getBytes()); fos.close(); } }
上面程序在读取含有中文的文件时,咱们并无看到具体的中文,而是看到一些数字,这是什么缘由呢?既然看不到中文,那么咱们如何对其中的中文作处理呢?要解决这个问题,咱们必须研究下字符的编码过程。api
1.2 字符编码表
咱们知道计算机底层数据存储的都是二进制数据(二进制了解吗?最高位表示什么?),而咱们生活中的各类各样的数据,如何才能和计算机中存储的二进制数据对应起来呢?
这时老美他们就把每个字符和一个整数对应起来,就造成了一张编码表,老美他们的编码表就是ASCII表。其中就是各类英文字符对应的编码。
编码表:其实就是生活中字符和计算机二进制的对应关系表。
一、ascii: 一个字节中的7位就能够表示。对应的码值都是正数。00000000-0xxxxxxx(其余码表都要兼容它) 0000 0000
二、iso-8859-1:拉丁码表 latin,用了一个字节用的8位。1-xxxxxxx 负数。
三、GB2312:简体中文码表。包含6000-7000中文和符号。用两个字节表示。中文的两个字节都是开头为1 ,即两个字节都是负数。
GBK:目前最经常使用的中文码表,2万的中文和符号。用两个字节表示,其中的一部分文字,第一个字节开头是1,第二字节开头是0
GB18030:最新的中文码表,目前尚未正式使用。やめて
四、unicode:国际标准码表:不管是什么文字,都用两个字节存储。
Java中的char类型用的就是这个码表。char c = 'a';占两个字节。
Java中的字符串是按照系统默认码表来解析的。简体中文版 字符串默认的码表是GBK。
五、UTF-8:基于unicode,一个字节就能够存储的数据,不用两个字节存储,并且这个码表更加的标准化,在每个字节头加入了编码信息(后期到api中查找)。
能识别中文的码表:GBK、UTF-8;正由于识别中文码表不惟一,涉及到了编码解码问题。
对于咱们开发而言;常见的编码 GBK UTF-8 ISO-8859-1
1.GBK:中文2字节
2.UTF-8:中文3字节ide
文字--->(数字) :编码: 就是看能看懂内容,转换成看不懂的内容。a 0110 0001
(数字)--->文字 : 解码: 就是把看不懂的内容,转换成看懂的内容。0110 0001 --a工具
1.3 方便程序员的IO流
在IO开发过程当中,咱们传输最频繁的数据为字符,学习
我爱你---> 10100101010100110010101001--->妹子测试
而以字节方式传输字符须要每次将字符串转换成字节再处理,并且也丧失了程序员对数据内容的判断(由于程序员只认识字符,不认识字节)。因此,为了让程序员方便对字符进行操做,Java提供了专门以字符做为操做单位的类——字符流,其底层仍然为字节流。
显然,字符流只能操做字符,没法操做其余数据,如声音、视频等。
在基础班咱们已经学习过字符流了,这里就进行一个简单的回顾
1.4 字符流回顾
IO流的分类
|- 字节流
|- 字节输入流 InputStream 抽象类
|- FileInputStream 操做文件的字节输入流
|- 字节输出流 OuputStream抽象类
|- FileOutputStream 操做文件的字节输出流
|- 字符流
|- 字符输入流 Reader抽象类
|- FileReader 用来操做文件的字符输入流(简便的流)
|- 字符输出流 Writer抽象类
|- FileWriter 用来操做文件的字符输出流(简便的流)
Java中流的命名 规范 功能+类型ui
第2章 转换流this
在学习字符流(FileReader、FileWriter)的时候,其中说若是须要指定编码和高效区大小时,能够在字节流的基础上,构造一个InputStreamReader或者OutputStreamWriter,这又是什么意思呢?
2.1 字符编码表(重复内容)
咱们知道计算机底层数据存储的都是二进制数据,而咱们生活中的各类各样的数据,如何才能和计算机中存储的二进制数据对应起来呢?
这时老美他们就把每个字符和一个整数对应起来,就造成了一张编码表,老美他们的编码表就是ASCII表。其中就是各类英文字符对应的编码。
编码表:其实就是生活中字符和计算机二进制的对应关系表。
一、ascii: 一个字节中的7位就能够表示。对应的字节都是正数。0-xxxxxxx
二、iso-8859-1:拉丁码表 latin,用了一个字节用的8位。1-xxxxxxx 负数。
三、GB2312:简体中文码表。包含6000-7000中文和符号。用两个字节表示。两个字节都是开头为1 ,两个字节都是负数。
GBK:目前最经常使用的中文码表,2万的中文和符号。用两个字节表示,其中的一部分文字,第一个字节开头是1,第二字节开头是0
GB18030:最新的中文码表,目前尚未正式使用。
四、unicode:国际标准码表:不管是什么文字,都用两个字节存储。
Java中的char类型用的就是这个码表。char c = 'a';占两个字节。
Java中的字符串是按照系统默认码表来解析的。简体中文版 字符串默认的码表是GBK。
五、UTF-8:基于unicode,一个字节就能够存储数据,不要用两个字节存储,并且这个码表更加的标准化,在每个字节头加入了编码信息(后期到api中查找)。
能识别中文的码表:GBK、UTF-8;正由于识别中文码表不惟一,涉及到了编码解码问题。
对于咱们开发而言;常见的编码 GBK UTF-8 ISO-8859-1
文字--->(数字) :编码: 就是看能看懂内容,转换成看不懂的内容。
(数字)--->文字 : 解码: 就是把看不懂的内容,转换成看懂的内容。编码
2.2 OutputStreamWriter类(转换流,快递运输的思想)
查阅OutputStreamWriter的API介绍,OutputStreamWriter 是字符流通向字节流的桥梁:可以使用指定的字符编码表,将要写入流中的字符编码成字节。它的做用的就是,将字符串按照指定的编码表转成字节,在使用字节流将这些字节写出去。
代码演示:
public static void writeCN() throws Exception { //建立与文件关联的字节输出流对象 FileOutputStream fos = new FileOutputStream("c:\\cn8.txt"); //建立能够把字符转成字节的转换流对象,并指定编码 OutputStreamWriter osw = new OutputStreamWriter(fos,"utf-8"); //调用转换流,把文字写出去,实际上是写到转换流的高效区中 osw.write("你好");//写入高效区。 osw.close(); }
OutputStreamWriter流对象,它到底如何把字符转成字节输出的呢?
其实在OutputStreamWriter流中维护本身的高效区,当咱们调用OutputStreamWriter对象的write方法时,会拿着字符到指定的码表中进行查询,把查到的字符编码值转成字节数存放到OutputStreamWriter高效区中。而后再调用刷新功能,或者关闭流,或者高效区存满后会把高效区中的字节数据使用字节流写到指定的文件中。
2.3 InputStreamReader类
查阅InputStreamReader的API介绍,InputStreamReader 是字节流通向字符流的桥梁:它使用指定的字符编码表读取字节并将其解码为字符。它使用的字符集能够由名称指定或显式给定,或者能够接受平台默认的字符集。
代码演示
public class InputStreamReaderDemo { public static void main(String[] args) throws IOException { //演示字节转字符流的转换流 readCN(); } public static void readCN() throws IOException{ //建立读取文件的字节流对象 InputStream in = new FileInputStream("c:\\cn8.txt"); //建立转换流对象 //InputStreamReader isr = new InputStreamReader(in);这样建立对象,会用本地默认码表读取,将会发生错误解码的错误 InputStreamReader isr = new InputStreamReader(in,"utf-8"); //使用转换流去读字节流中的字节 int ch = 0; while((ch = isr.read())!=-1){ System.out.println((char)ch); } //关闭流 isr.close(); } }
注意:在读取指定的编码的文件时,必定要指定编码格式,不然就会发生解码错误,而发生乱码现象。
2.4 转换流和子类区别
发现有以下继承关系:
Writer 字符输出流
|- OutputStreamWriter 转换流(字符流—>字节流)(属于字符输出流, 能够指定字符编码表,用来写入数据到文件)
|--FileWriter 操做文件中字符输出流,采用默认的字符编码表
fw.write(“你好”)
Reader 字符输入流
|- InputStreamReader: 转换流(字节流字符流)(属于字符输入流,能够指定字符编码表,用来从文件中读数据)
|--FileReader操做文件中字符输入流,采用默认的字符编码表
父类和子类的功能有什么区别呢?
OutputStreamWriter和InputStreamReader是字符和字节的桥梁:也能够称之为字符转换流。字符转换流原理:字节流+编码表。
FileWriter和FileReader:做为子类,仅做为操做字符文件的便捷类存在。当操做的字符文件,使用的是默认编码表时能够不用父类,而直接用子类就完成操做了,简化了代码。
InputStreamReader isr = new InputStreamReader(new FileInputStream("a.txt"));//默认GBK字符集。
InputStreamReader isr = new InputStreamReader(new FileInputStream("a.txt"),"GBK");//指定GBK字符集。
FileReader fr = new FileReader("a.txt");
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(“a.txt”));
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(“a.txt”),”GBK”);
FileWriter fw = new FileWriter(“a.txt”)
这三句代码的功能是同样的,其中第三句最为便捷。
注意:一旦要指定其余编码时,绝对不能用子类,必须使用字符转换流。何时用子类呢?
条件:
一、操做的是文件。二、使用默认编码。
总结:
字节--->解码--->字符 : 看不懂的--->看的懂的。 须要读。输入流。 InputStreamReader
字符--->编码--->字节 : 看的懂的--->看不懂的。 须要写。输出流。 OutputStreamWriter
第3章 序列化流与反序列化流
用于从流中读取对象的操做流 ObjectInputStream 称为 反序列化流
用于向流中写入对象的操做流 ObjectOutputStream 称为 序列化流
特色:用于操做对象。能够将对象写入到文件中,也能够从文件中读取对象。
3.1 对象序列化流ObjectOutputStream
ObjectOutputStream 将 Java 对象的基本数据类型和图形写入 OutputStream。可使用 ObjectInputStream 读取(重构)对象。经过在流中使用文件能够实现对象的持久存储。
注意:只能将支持 java.io.Serializable 接口的对象写入流中
代码演示:
public class ObjectStreamDemo { public static void main(String[] args) throws IOException, ClassNotFoundException { /* * 将一个对象存储到持久化(硬盘)的设备上。 */ writeObj();//对象的序列化。 } public static void writeObj() throws IOException { //1,明确存储对象的文件。 FileOutputStream fos = new FileOutputStream("tempfile\\obj.object"); //2,给操做文件对象加入写入对象功能。 ObjectOutputStream oos = new ObjectOutputStream(fos); //3,调用了写入对象的方法。 oos.writeObject(new Person("wangcai",20)); //关闭资源。 oos.close(); } }
Person类
public class Person implements Serializable { private String name; private int age; public Person() { super(); } public Person(String name, int age) { super(); this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "Person [name=" + name + ", age=" + age + "]"; } }
3.2 对象反序列化流ObjectInputStream
ObjectInputStream 对之前使用 ObjectOutputStream 写入的基本数据和对象进行反序列化。支持 java.io.Serializable接口的对象才能从流读取。
代码演示
public class ObjectStreamDemo { public static void main(String[] args) throws IOException, ClassNotFoundException { readObj();//对象的反序列化。 } public static void readObj() throws IOException, ClassNotFoundException { //1,定义流对象关联存储了对象文件。 FileInputStream fis = new FileInputStream("tempfile\\obj.object"); //2,创建用于读取对象的功能对象。 ObjectInputStream ois = new ObjectInputStream(fis); Person obj = (Person)ois.readObject(); System.out.println(obj.toString()); } }
3.3 序列化接口(主要是介绍两个异常)
当一个对象要能被序列化,这个对象所属的类必须实现Serializable接口。不然会发生异常NotSerializableException异常。
同时当反序列化对象时,若是对象所属的class文件在序列化以后进行的修改,那么进行反序列化也会发生异常InvalidClassException。发生这个异常的缘由以下:
该类的序列版本号与从流中读取的类描述符的版本号不匹配
该类包含未知数据类型
该类没有可访问的无参数构造方法
Serializable标记接口。该接口给须要序列化的类,提供了一个序列版本号。serialVersionUID. 该版本号的目的在于验证序列化的对象和对应类是否版本匹配。
代码修改以下,修改后再次写入对象,读取对象测试
public class Person implements Serializable { //给类显示声明一个序列版本号。 private static final long serialVersionUID = 1L; private String name; private int age; public Person() { super(); } public Person(String name, int age) { super(); this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "Person [name=" + name + ", age=" + age + "]"; } }
3.4 瞬态关键字transient
当一个类的对象须要被序列化时,某些属性不须要被序列化,这时不须要序列化的属性可使用关键字transient修饰。只要被transient修饰了,序列化时这个属性就不会被序列化了。
同时静态修饰也不会被序列化,由于序列化是把对象数据进行持久化存储,而静态的属于类加载时的数据,不会被序列化。
代码修改以下,修改后再次写入对象,读取对象测试
public class Person implements Serializable { /* * 给类显示声明一个序列版本号。 */ private static final long serialVersionUID = 1L; private static String name; private transient/*瞬态*/ int age; public Person() { super(); } public Person(String name, int age) { super(); this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "Person [name=" + name + ", age=" + age + "]"; } }
第4章 打印流
4.1 打印流的概述
打印流添加输出数据的功能,使它们可以方便地打印各类数据值表示形式.
打印流根据流的分类:
字节打印流 PrintStream
字符打印流 PrintWriter
方法:
void print(String str): 输出任意类型的数据,
void println(String str): 输出任意类型的数据,自动写入换行操做
代码演示:
/* * 需求:把指定的数据,写入到printFile.txt文件中 * * 分析: * 1,建立流 * 2,写数据 * 3,关闭流 */ public class PrintWriterDemo { public static void main(String[] args) throws IOException { //建立流 //PrintWriter out = new PrintWriter(new FileWriter("printFile.txt")); PrintWriter out = new PrintWriter("printFile.txt"); //2,写数据 for (int i=0; i<5; i++) { out.println("helloWorld"); } //3,关闭流 out.close(); } }
第5章 commons-IO
5.1 导入classpath
加入classpath的第三方jar包内的class文件才能在项目中使用
使用第三方类库的步骤:
1.建立lib文件夹
2.将commons-io.jar拷贝到lib文件夹
3.右键点击commons-io.jar,Build Path→Add to Build Path
5.2 FileUtils
提供文件操做(移动文件,读取文件,检查文件是否存在等等)的方法。
经常使用方法:
readFileToString(File file):读取文件内容,并返回一个String;
writeStringToFile(File file,String content):将内容content写入到file中;
copyFile(File srcFile, File destFile): 文件复制
copyDirectoryToDirectory(File srcDir,File destDir);文件夹复制
代码演示:
/* * 普通方式,完成文件的复制 */ public class CommonsIODemo01 { public static void main(String[] args) throws IOException { //method1("D:\\test.avi", "D:\\copy.avi"); //经过Commons-IO完成了文件复制的功能 FileUtils.copyFile(new File("D:\\test.avi"), new File("D:\\copy.avi")); } //文件的复制 private static void method1(String src, String dest) throws IOException { //1,指定数据源 BufferedInputStream in = new BufferedInputStream(new FileInputStream(src)); //2,指定目的地 BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(dest)); //3,读 byte[] buffer = new byte[1024]; int len = -1; while ( (len = in.read(buffer)) != -1) { //4,写 out.write(buffer, 0, len); } //5,关闭流 in.close(); out.close(); } } /* * 使用commons-io完成文件、文件夹的复制 */ public class CommonsIODemo02 { public static void main(String[] args) throws IOException { //经过Commons-IO完成了文件复制的功能 FileUtils.copyFile(new File("D:\\test.avi"), new File("D:\\copy.avi")); //经过Commons-IO完成了文件夹复制的功能 //D:\基础班 复制到 C:\\abc文件夹下 FileUtils.copyDirectoryToDirectory(new File("D:\\基础班"), new File("C:\\abc")); } }
欢迎关注菜鸟永恒,点滴记录,共同进步。