1000字范文,内容丰富有趣,学习的好帮手!
1000字范文 > IO流 输入流 输出流 字节流 字符流 转换流 及Properties类

IO流 输入流 输出流 字节流 字符流 转换流 及Properties类

时间:2024-03-12 05:00:52

相关推荐

IO流 输入流 输出流 字节流 字符流 转换流 及Properties类

IO流

1、IO概述

生活中,你肯定经历过这样的场景。当你编辑一个文本文件,忘记了ctrl+s ,可能文件就白编了。当你电脑上插入一个U盘,可以把一个视频,拷贝到你的电脑硬盘里。那么数据都是在哪些设备上的呢?键盘、内存、硬盘、外接设备等等。【系统内存存储的数据,只要关机,系统内存的数据就会被释放!】

我们把这种数据的传输,可以看做是一种数据的流动,按照流动的方向,以内存为基准,分为输入input 和输出output ,即流向内存是输入流,流出内存的输出流

Java中I/O操作主要是指使用java.io包下的内容,进行输入、输出操作。输入也叫做读取数据,输出也叫做作写出数据。

注意:流的关闭原则为,先开后关,后开先关。

2、IO的分类

根据数据的流向分为:输入流和输出流。

输入流 :把数据从其他设备上读取到内存中的流。

输出流 :把数据从内存 中写出到其他设备上的流。根据数据的类型分为:字节流和字符流。

字节流 :以字节为单位,读写数据的流。

字符流 :以字符为单位,读写数据的流。顶级父类(均为抽象类)

字节输入流:InputStream

字节输出流:OutputStream

字符输入流:Reader

字符输出流:Writer

3、IO资源处理

3.1 JDK7之前的处理方式

将关闭流资源的代码写在finally语句体中,需要将流对象提到try…cath…finally的外部 【麻烦!】

/*以字符输出流为例!说明JDK7之前的处理io资源的问题!*/public class Test01BeforeJDK7 {public static void main(String[] args) {// 创建一个字符输出流对象FileWriter fw = null;try {fw = new FileWriter("day11_io/src/fw.txt");fw.write("黑马程序员");} catch (IOException e) {e.printStackTrace();}finally {try {fw.close();} catch (IOException e) {e.printStackTrace();}}}}

3.2 JDK7之后的处理方式

为了代码的操作方便,采取了新的语法措施!

* 语法:try(io流资源的创建操作) { // 意味着后面不用写finally,它会关闭! 【多个io流资源之间使用“;”隔开!】// io流读取操作的代码!} catch (IOException e) {// 异常处理的代码!}

/*以字符输出流为例!说明JDK7之后的处理io资源的问题!*/public class Test02AfterJDK7 {public static void main(String[] args) {// 创建一个字符输出流对象try (FileWriter fw = new FileWriter("day11_io/src/fw.txt");//FileReader fr = new FileReader("day11_io/src/fr.txt");) {fw.write("黑马程序员");} catch (IOException e) {e.printStackTrace();}}}

测试结果发现fw.txt文件中有数据,说明流关闭了(没有关闭,数据在缓冲区)

Properties类

1、 Properties类的概述

java.util.Properties 继承于Hashtable ,来表示一个持久的属性集。它使用键值结构存储数据,每个键及其对应值都是一个字符串。该类也被许多Java类使用,比如获取系统属性时,System.getProperties 方法就是返回一个Properties对象。【更多时候用于读取配置文件】

Properties的底层是一个Map结构

2、 构造方法

Properties() //创建一个没有默认值的空属性列表。

3、 基本存取功能方法

public Object setProperty(String key, String value) : // 保存一对属性。 =====>>>map.put(key,value);public String getProperty(String key) : // 使用此属性列表中指定的键搜索属性值。======>>> map.get(key);public Set<String> stringPropertyNames() : // 所有键的名称的集合。 ======>>map.keySet();

/*Properties类的构造方法和基本存取功能方法【就是将其当做Map(Hashtable类型的)功能来使用】*/public class Test01 {public static void main(String[] args) {// 创建Properties类的对象Properties pp = new Properties();// 存数据pp.setProperty("driverClass","com.mysql.jdbc.Driver");pp.setProperty("url","http://localhost:3306/day11_db");pp.setProperty("username","root");pp.setProperty("password","root");// 取数据(所有的key)Set<String> set = pp.stringPropertyNames();// 非空判断if(!set.isEmpty()){// 遍历for (String key : set) {System.out.println("key:" + key + ", value:"+ pp.getProperty(key));}}}}

4、 Properties类读取配置文件

Properties类除了能够当做Map使用,还可以与文件(可以是txt文件、可以是properties)进行关联,获得文件里面的数据!

void load(InputStream inStream) // 从输入字节流读取属性列表(键和元素对)。

要求:将文件放在src目录(类的根路径)下面!

src下的资源文件fis.properties内容:

driverClass=com.mysql.jdbc.Driverurl=jdbc:mysql://localhost:3306/day11_dbpassword=rootusername=root

/*读取配置文件(text类型的)*/public class Test02 {public static void main(String[] args) {// 创建Properties类的对象Properties prop = new Properties();// 创建资源文件对应的字节输入流try (//FileInputStream fis = new FileInputStream("day11_io/src/fis.txt");FileInputStream fis = new FileInputStream("day11_io/src/fis.properties");) {// 让文件对应的的输入流与prop关联prop.load(fis);// 获得所有的keySet<String> set = prop.stringPropertyNames();// 判断if (!set.isEmpty()) {// 遍历for (String key : set) {// 获得值String value = prop.getProperty(key);// 打印输出System.out.println(key + "对应的值是:" + value);}}} catch (Exception e) {e.printStackTrace();}}}

扩展:使用ResourceBundle类读取properties配置文件

核心方法:

static ResourceBundle getBundle(String baseName) // 通过读取在src下面的properties文件的名称(不要扩展名),得到一个ResourceBundle对象!String getString(String key) // 获得指定key对应的value值 【driverClass=com.mysql.jdbc.Driver,只要将driverClass作为参数,就可以得到值!】

/*ResourceBundle类:读取properties配置文件!*/public class Test03 {public static void main(String[] args) {// 通过ResourceBundle类的静态方法,得到ResourceBundle类的对象ResourceBundle bundle = ResourceBundle.getBundle("fis"); // 只要给定properties配置文件的名称,不要扩展名!// 获得指定参数的值String driverClass = bundle.getString("driverClass");String url = bundle.getString("url");String username = bundle.getString("username");String password = bundle.getString("password");System.out.println(driverClass);System.out.println(url);System.out.println(username);System.out.println(password);}}

一、字节流

1.1 一切皆为字节

一切文件数据(文本、图片、视频等)在存储时,都是以二进制数字的形式保存,都一个一个的字节,那么传输时一样如此。所以,字节流可以传输任意文件数据。在操作流的时候,我们要时刻明确,无论使用什么样的流对象,底层传输的始终为二进制数据。

1.2 字节输出流FileOutputStream

1.2.1 抽象父类

java.io.OutputStream抽象类是表示字节输出流的所有类的超类,将指定的字节信息写出到目的地(外置设备)。它定义了字节输出流的基本共性功能方法。

public void close() :// 关闭此输出流并释放与此流相关联的任何系统资源。public void flush() :// 刷新此输出流并强制任何缓冲的输出字节被写出。public void write(byte[] b):// 将 b.length字节从指定的字节数组写入此输出流。public void write(byte[] b, int off, int len) :// 从指定的字节数组写入 len字节,从偏移量 off开始输出到此输出流。public abstract void write(int b) :// 将指定的字节输出流。

close方法,当完成流的操作时,必须调用此方法,释放系统资源。

OutputStream有很多子类,我们从最简单的一个子类开始。

1.2.2 构造方法

java.io.FileOutputStream类是文件输出流,用于将数据从内存写出到文件。

* 构造方法:public FileOutputStream(File file):创建文件输出流以写入由指定的 File对象表示的文件。public FileOutputStream(String name): 创建文件输出流以指定的名称写入文件。

当你创建一个流对象时,必须传入一个文件路径。该路径下,如果没有这个文件,会创建该文件。如果有这个文件,会清空这个文件的数据。

public class Test01 {public static void main(String[] args) throws FileNotFoundException {// 创建File对象File file = new File("day10_io/src/a.txt"); // a.txt文件目前是不存在的,但是不影响file对象的创建// 创建字节输出流对象(通过File对象构建的)FileOutputStream fos = new FileOutputStream(file);System.out.println(fos);// 创建字节输出流对象(通过字符串路径构建的)FileOutputStream fos1 = new FileOutputStream("day10_io/src/b.txt");System.out.println(fos1);}}

1.2.3 写出字节数据

① 写出字节:write(int b) 方法,每次可以写出一个字节数据[97 98 99]

1. 虽然参数为int类型四个字节,但是只会保留一个字节的信息写出。2. 流操作完毕后,必须释放系统资源,调用close方法,千万记得。

public class Test02 {public static void main(String[] args){FileOutputStream fos = null;try {// 创建字节输出流的对象fos = new FileOutputStream("day10_io/src/os.txt");// 写出字节数据fos.write(97);fos.write(98);fos.write(99);}catch (FileNotFoundException e){e.printStackTrace();}catch (IOException e){e.printStackTrace();}finally {// 关闭流(释放资源)try {fos.close();} catch (IOException e) {e.printStackTrace();}}}}

② 写出字节数组:write(byte[] b),每次可以写出数组中的数据[黑马程序员]

public class Test03 {public static void main(String[] args) throws IOException {// 创建字节输出流对象FileOutputStream fos = new FileOutputStream("day10_io/src/os.txt");// 准备字节数组//byte[] b = {97, 98, 99, 100};byte[] b = "黑马程序员".getBytes(); // 每次新写出数据,都会将文件中原来的数据给清空!// 调用write方法,将字节数组的数据从内存中写出到指定文件。fos.write(b);// 关闭资源fos.close();}}

③ 写出指定长度字节数组:write(byte[] b, int off, int len) ,每次写出从off索引开始,len个字节[abcde]

/*FileOutputStream字节输出流写出字节数组中指定的数据:void write(byte[] b, int off, int len) 将 len字节从指定的字节数组开始,从偏移量 off开始写入此文件输出流。off:指定起始位置!len:指定个数!*/public class Test04 {public static void main(String[] args) throws IOException {// 创建字节输出流对象FileOutputStream fos = new FileOutputStream("day10_io/src/os.txt");// 准备字节数组byte[] b = "abcde".getBytes(); // 每次新写出数据,都会将文件中原来的数据给清空!// 调用write方法,将字节数组的数据从内存中写出到指定文件。fos.write(b,0,b.length); // abcde// 关闭资源fos.close();}}

1.2.4 数据追加续写

经过以上的演示,每次程序运行,创建输出流对象,都会清空目标文件中的数据。如何保留目标文件中数据,还能继续添加新数据呢?

public FileOutputStream(File file, boolean append): // 创建文件输出流以写入由指定的File对象表示的文件。public FileOutputStream(String name, boolean append): // 创建文件输出流以指定的名称写入文件。

这两个构造方法,参数中都需要传入一个boolean类型的值,true 表示追加数据,false 表示清空原有数据。这样创建的输出流对象,就可以指定是否追加续写了

/*FileOutputStream字节输出流追加续写!在FileOutputStream构造方法的参数位置再添加一个boolean类型的值true即可!【默认为false,不追加续写,清掉之前的数据,写入新数据】*/public class Test05 {public static void main(String[] args) throws IOException {// 创建字节输出流对象FileOutputStream fos = new FileOutputStream("day10_io/src/os.txt",true);// 准备字节数组byte[] b = "黑马程序员".getBytes(); // 每次新写出数据,都会将文件中原来的数据给清空!// 调用write方法,将字节数组的数据从内存中写出到指定文件。fos.write(b,0,b.length); // abcde// 关闭资源fos.close();}}

1.2.5 写出换行

* 系统中的换行:Windows系统里,每行结尾是 回车+换行 ,即\r\n; 【win10里面换行 \n 也行!】Unix系统里,每行结尾只有 换行 ,即\n;Mac系统里,每行结尾是 回车 ,即\r。从 Mac OS X开始与Linux统一。* 回车符\r和换行符\n :回车符:回到一行的开头(return)。换行符:下一行(newline)。

需求:写出字节数组byte[] words = {97,98,99,100,101},写出单个字节数据后换行

public class Test06 {public static void main(String[] args) throws IOException {// 创建字节输出流对象FileOutputStream fos = new FileOutputStream("day10_io/src/os.txt",true);// 准备字节数组byte[] words = {97,98,99,100,101};// 遍历for (byte word : words) {// 先写出单个字节数据fos.write(word);// 再换行fos.write("\r\n".getBytes()); // 在win10系统中 \n也可以完成换行!}// 关闭资源fos.close();}}

1.3 字节输入流FileInputStream

1.3.1 抽象父类

java.io.InputStream抽象类是表示字节输入流的所有类的超类,可以读取字节信息到内存中。它定义了字节输入流的基本共性功能方法。

public void close() :// 关闭此输入流并释放与此流相关联的任何系统资源。public abstract int read(): // 从输入流读取数据的下一个字节。public int read(byte[] b): // 从输入流中读取一些字节数,并将它们存储到字节数组 b中 。

注意:close方法,当完成流的操作时,必须调用此方法,释放系统资源。

1.3.2 构造方法

FileInputStream(File file): // 通过打开与实际文件的连接来创建一个 FileInputStream ,该文件由文件系统中的 File对象 file命名。FileInputStream(String name): // 通过打开与实际文件的连接来创建一个 FileInputStream ,该文件由文件系统中的路径名 name命名。

注意:当你创建一个流对象时,必须传入一个文件路径。该路径下,如果没有该文件,会抛出FileNotFoundException

/*FileInputStream字节输入流的构造方法:FileInputStream(File file): // 通过打开与实际文件的连接来创建一个 FileInputStream ,该文件由文件系统中的 File对象 file命名。FileInputStream(String name): // 通过打开与实际文件的连接来创建一个 FileInputStream ,该文件由文件系统中的路径名 name命名。注意:给定路径的文件不存在,报错!FileNotFoundException*/public class Test01 {public static void main(String[] args) throws FileNotFoundException {// FileInputStream(File file):File file = new File("day10_io/src/os.txt");// 创建字节输入流(file对象作为参数的构造方法)FileInputStream fis1 = new FileInputStream(file);System.out.println(fis1);// 创建字节输入流(文件路径作为参数的构造方法)FileInputStream fis2 = new FileInputStream("day10_io/src/is.txt");System.out.println(fis2);}}

1.3.3 读取字节数据

① 读取字节:read方法,每次可以读取一个字节的数据,提升为int类型,读取到文件末尾,返回-1

/*FileInputStream字节输入流读单个字节数据int read() 从该输入流读取一个字节的数据。*/public class Test02 {public static void main(String[] args) throws IOException {// 创建字节输入流(文件路径作为参数的构造方法)FileInputStream fis = new FileInputStream("day10_io/src/is.txt"); // 数据为abcdef// 读单个字节数据int r1 = fis.read();System.out.println((char)r1); // aint r2 = fis.read();System.out.println((char)r2); // bint r3 = fis.read();System.out.println((char)r3); // cint r4 = fis.read();System.out.println((char)r4); // dint r5 = fis.read();System.out.println((char)r5); // eint r6 = fis.read();System.out.println((char)r6); // fint r7 = fis.read();System.out.println(r7); // 读到文件末尾是-1// 释放资源fis.close();}}

循环改进:

/*FileInputStream字节输入流读单个字节数据使用循环改进!*/public class Test03 {public static void main(String[] args) throws IOException {// 创建字节输入流(文件路径作为参数的构造方法)FileInputStream fis = new FileInputStream("day10_io/src/is.txt"); // 数据为abcdef// 记录每次读取的单个字节数据!int r;// 使用循环改进while((r = fis.read())!=-1){System.out.println((char)r); // abcdef}// 释放资源fis.close();}}

* 注意:1. 虽然读取了一个字节,但是会自动提升为int类型。2. 流操作完毕后,必须释放系统资源,调用close方法,千万记得。

② 使用字节数组读取:read(byte[] b),每次读取b的长度个字节到数组中,返回读取到的有效字节个数,读取到末尾时,返回-1[abcde]

public class Test05 {public static void main(String[] args) throws IOException {// 创建字节输入流(文件路径作为参数的构造方法)FileInputStream fis = new FileInputStream("day10_io/src/is.txt"); // 数据为abcde// 定义字节数组byte[] bytes = new byte[2];// 读取到字节数组数据的长度!int len;while((len=fis.read(bytes))!=-1){// 只要没有读到文件末尾就一直读!// 每次读取的数据都放在字节数组System.out.print(new String(bytes));}// 释放资源fis.close();}}// 结果数据: abcded

错误数据d,是由于最后一次读取时,只读取一个字节e,数组中,上次读取的数据cd没有被完全替换,所以要通过len ,获取有效的字节

public class Test05 {public static void main(String[] args) throws IOException {// 创建字节输入流(文件路径作为参数的构造方法)FileInputStream fis = new FileInputStream("day10_io/src/is.txt"); // 数据为abcde// 定义字节数组byte[] bytes = new byte[2];// 读取到字节数组数据的长度!int len;while((len=fis.read(bytes))!=-1){// 只要没有读到文件末尾就一直读!// 每次读取的数据都放在字节数组System.out.print(new String(bytes,0,len)); // 从数组0索引位置到指定长度(读取的数据的长度,并不是数组的长度!)}// 释放资源fis.close();}}

二、字符流

当使用字节流读取文本文件时,可能会有一个小问题。就是遇到中文字符时,可能不会显示完整的字符,那是因为一个中文字符可能占用多个字节存储。所以Java提供一些字符流类,以字符为单位读写数据,专门用于处理文本文件。

2.1 字符输入流FileReader

2.1.1 抽象父类

java.io.Reader抽象类是表示用于读取字符流的所有类的超类,可以读取字符信息到内存中。它定义了字符输入流的基本共性功能方法。

public void close() :// 关闭此流并释放与此流相关联的任何系统资源。public int read(): // 从输入流读取一个字符。public int read(char[] cbuf): // 从输入流中读取一些字符,并将它们存储到字符数组 cbuf中 。

2.1.2 构造方法

java.io.FileReader类是读取字符文件的便利类。构造时使用系统默认的字符编码和默认字节缓冲区。

1. 字符编码:字节与字符的对应规则。Windows系统的中文编码默认是GBK编码表。idea中UTF-82. 字节缓冲区:一个字节数组,用来临时存储字节数据。

FileReader(File file): 创建一个新的 FileReader ,给定要读取的File对象。FileReader(String fileName): 创建一个新的 FileReader ,给定要读取的文件的名称。

当你创建一个流对象时,必须传入一个文件路径。类似于FileInputStream 。【传入的文件不存在,出现异常:FileNotFoundException】

/*字符输入流FileReader的构造方法:FileReader(File file): 创建一个新的 FileReader ,给定要读取的File对象。FileReader(String fileName): 创建一个新的 FileReader ,给定要读取的文件的名称。注意:文件路径字符串构建FileReader对象是,文件不存在,报错!FileNotFoundException*/public class Test01 {public static void main(String[] args) throws FileNotFoundException {// 创建File对象File file = new File("day10_io/src/is.txt");FileReader fr = new FileReader(file);System.out.println(fr);// 文件路径的字符串构建字符输入流FileReader fileReader = new FileReader("day10_io/src/is.txt"); // 若文件不存在,异常!System.out.println(fileReader);}}

2.1.3 读取字符数据

① 读取字符:read方法,每次可以读取一个字符的数据,提升为int类型,读取到文件末尾,返回-1,循环读取[黑马程序员]

/*字符输入流FileReader读单个字符数据:int read() 读一个字符*/public class Test02 {public static void main(String[] args) throws IOException {// 文件路径的字符串构建字符输入流FileReader fr = new FileReader("day10_io/src/is.txt");int r;while((r = fr.read())!=-1){System.out.println((char) r);}// 关闭资源fr.close();}}

② 使用字符数组读取:read(char[] cbuf),每次读取b的长度个字符到数组中,返回读取到的有效字符个数,读取到末尾时,返回-1

/*字符输入流FileReader读字符数组数据:int read​(char[] cbuf) 将字符读入数组。*/public class Test03 {public static void main(String[] args) throws IOException {// 文件路径的字符串构建字符输入流FileReader fr = new FileReader("day10_io/src/is.txt"); // 黑马程序员// 定义字符数组char[] chars = new char[2];// 读取到字符数组中有效的字符数据的个数int len;while((len = fr.read(chars))!=-1){// 包含了无效的数据!System.out.println(new String(chars));}// 关闭资源fr.close();}}// 运行结果://黑马//程序//员序

获得有效字符改进:

public class Test04 {public static void main(String[] args) throws IOException {// 文件路径的字符串构建字符输入流FileReader fr = new FileReader("day10_io/src/is.txt");// 定义字符数组char[] chars = new char[2];// 读取到字符数组中有效的字符数据的个数int len;while((len = fr.read(chars))!=-1){// 每次都是从数组的0索引位置到你读取到数组中有效长度的数据来构建字符串数据System.out.println(new String(chars,0,len));}// 关闭资源fr.close();}}

2.2 字符输出流FileWriter

2.2.1 抽象父类

java.io.Writer抽象类是表示用于写出字符流的所有类的超类,将指定的字符信息写出到目的地。它定义了字节输出流的基本共性功能方法。

public abstract void close() :// 关闭此输出流并释放与此流相关联的任何系统资源。public abstract void flush() :// 刷新此输出流并强制任何缓冲的输出字符被写出。public void write(int c) :// 写出一个字符。public void write(char[] cbuf):// 将 b.length字符从指定的字符数组写出此输出流。public abstract void write(char[] b, int off, int len) :// 从指定的字符数组写出 len字符,从偏移量 off开始输出到此输出流。public void write(String str) :// 写出一个字符串。

2.2.2 构造方法

java.io.FileWriter类是写出字符到文件的便利类。构造时使用系统默认的字符编码和默认字节缓冲区。

FileWriter(File file): // 创建一个新的 FileWriter,给定要读取的File对象。FileWriter(String fileName): // 创建一个新的 FileWriter,给定要读取的文件的名称。

当你创建一个流对象时,必须传入一个文件路径,类似于FileOutputStream。

/*字符输出流FileWriter的构造方法:FileWriter(File file): // 创建一个新的 FileWriter,给定要读取的File对象。FileWriter(String fileName): // 创建一个新的 FileWriter,给定要读取的文件的名称。注意:文件路径字符串构建FileWriter对象时,若路径中的文件不存在,不会报错,它会自动创建!*/public class Test01 {public static void main(String[] args) throws IOException {// 创建File对象File file = new File("day10_io/src/fw.txt");FileWriter fw = new FileWriter(file);System.out.println(fw);// 路径文件字符串构建FileWriter对象FileWriter fw1 = new FileWriter("day10_io/src/fw1.txt");System.out.println(fw1);}}

2.2.3 写出字符数据

① 写出字符:write(int b) 方法,每次可以写出一个字符数据[97,b,c,30000]

/*FileWriter写出单个字符数据 【97,b,c,30000】*/public class Test02 {public static void main(String[] args) throws IOException {// 创建对象FileWriter fw = new FileWriter("day10_io/src/fw.txt");// 写出单个字符数据fw.write(97);fw.write('b');fw.write('c');fw.write(22269);// 释放资源fw.close(); // 没有这行代码,那么数据不会到文件中!(还在内存的缓冲区!)}}

1. 虽然参数为int类型四个字节,但是只会保留一个字符的信息写出。2. 未调用close方法,数据只是保存到了缓冲区,并未写出到文件中。

② 关闭和刷新

因为内置缓冲区的原因,如果不关闭输出流,无法写出字符到文件中。但是关闭的流对象,是无法继续写出数据的。如果我们既想写出数据,又想继续使用流,就需要flush 方法了。

flush :// 刷新缓冲区,流对象可以继续使用。close :// 关闭流,释放系统资源。关闭前会刷新缓冲区。

public class Test02 {public static void main(String[] args) throws IOException {// 创建对象FileWriter fw = new FileWriter("day10_io/src/fw.txt");// 写出单个字符数据fw.write(97);fw.write('b');fw.write('c');fw.write(22269);// 先刷新内存中缓冲区的数据,然后关闭!后面不能再使用这个流对象,否则报错!Stream closed//fw.close();// 只刷新缓冲区数据,不关闭流!后面可以继续使用流对象!fw.flush();fw.write(100);fw.flush();}}

③ 写出字符数组 :write(char[] cbuf) 和 write(char[] cbuf, int off, int len) ,每次可以写出字符数组中的数据

/*FileWriter写出字符数组数据void write​(char[] cbuf) 写入一个字符数组。*/public class Test03 {public static void main(String[] args) throws IOException {// 创建对象FileWriter fw = new FileWriter("day10_io/src/fw.txt");// 创建字符数组char[] chars = {'a','b','c','d'};// 写出字符数组数据fw.write(chars); // abcd// 释放资源!fw.close();}}

/*FileWriter写出字符数组指定数据void write​(char[] cbuf, int off, int len) 写入字符数组的一部分。 */public class Test04 {public static void main(String[] args) throws IOException {// 创建对象FileWriter fw = new FileWriter("day10_io/src/fw.txt");// 创建字符数组char[] chars = {'a','b','c','d'};// 写出字符数组数据fw.write(chars,2,2); // cd// 释放资源!fw.close();}}

2.2.4 写出字符串

write(String str) 和 write(String str, int off, int len) ,每次可以写出字符串中的数据

/*FileWriter写出字符串数据,以及字符串指定数据void write​(String str) 写一个字符串void write​(String str, int off, int len) 写一个字符串的一部分。*/public class Test05 {public static void main(String[] args) throws IOException {// 创建对象FileWriter fw = new FileWriter("day10_io/src/fw.txt");// 直接写出字符串数据fw.write("黑马程序员!");// 已经具备续写能力!fw.write("666么么哒!",0,3);// 释放资源!fw.close();}}

2.2.5 续写和换行

操作类似于FileOutputStream,在构造方法后面加一个boolean类型的参数,取值为true即可!

/*FileWriter写出字符串数据,以及字符串指定数据void write​(String str) 写一个字符串void write​(String str, int off, int len) 写一个字符串的一部分。*/public class Test05 {public static void main(String[] args) throws IOException {// 创建对象FileWriter fw = new FileWriter("day10_io/src/fw.txt",true);// 直接写出字符串数据fw.write("\r\n");fw.write("黑马程序员!");fw.write("\r\n");// 已经具备续写能力!fw.write("666么么哒!",0,3);// 释放资源!fw.close();}}

三、缓冲流

3.1 缓冲流概述

缓冲流,也叫高效流,是对4个基本的FileXxx 流的增强,所以也是4个流,按照数据类型分类:

* 4个基本流:字节输入流:FileInputStream字节输出流:FileOutputStream字符输入流:FileReader字符输出流:FileWriter* 4个高效流字节缓冲流:字节输入缓冲流:BufferedInputStream字节输出缓冲流:BufferedOutputStream字符缓冲流:字符输入缓冲流:BufferedReader字符输出缓冲流:BufferedWriter

缓冲流的基本原理,是在创建流对象时,会创建一个内置的默认大小的缓冲区数组,通过缓冲区读写,减少系统IO次数,从而提高读写的效率

3.2 字节缓冲流

构造方法:

public BufferedInputStream(InputStream in) :// 创建一个 新的缓冲输入流。【通过一个基本的字节流得到一个缓冲流】public BufferedOutputStream(OutputStream out):// 创建一个新的缓冲输出流。

功能方法与普通的字节流一致!

测试1:使用普通字节流复制文件!

/*普通字节流复制文件!将D盘io目录下面的 jdk8.exe文件拷贝到day11_io/src/copy.exe 【文件大小为202MB】*/public class Test01 {public static void main(String[] args) {// 创建2个流对象try (FileInputStream fis = new FileInputStream("D:/io/jdk8.exe");FileOutputStream fos = new FileOutputStream("day11_io/src/copy.exe");) {long start = System.currentTimeMillis();// 创建字节数组byte[] bt = new byte[1024];int len;while ((len = fis.read(bt)) != -1) {// 写出去fos.write(bt, 0, len);}long end = System.currentTimeMillis();System.out.println("复制完毕,消耗时间为:" + (end - start) + "毫秒!");} catch (IOException e) {e.printStackTrace();}}}// 测试结果:1723毫秒!

测试2:使用缓冲流复制文件!

/*高效字节流复制文件!将D盘io目录下面的 jdk8.exe文件拷贝到day11_io/src/copy.exe 【文件大小为202MB】*/public class Test02 {public static void main(String[] args) {// 创建2个流对象try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream("D:/io/jdk8.exe"));BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("day11_io/src/copy.exe"));) {long start = System.currentTimeMillis();// 创建字节数组byte[] bt = new byte[1024];int len;while ((len = bis.read(bt)) != -1) {// 写出去bos.write(bt, 0, len);}long end = System.currentTimeMillis();System.out.println("复制完毕,消耗时间为:" + (end - start) + "毫秒!");} catch (IOException e) {e.printStackTrace();}}}// 测试结果:413毫秒!

3.3 字符缓冲流

① 构造方法

public BufferedReader(Reader in) :// 创建一个 新的缓冲输入流。public BufferedWriter(Writer out): // 创建一个新的缓冲输出流。

② 额外功能方法

BufferedReader:public String readLine() : // 读一行文字。BufferedWriter:public void newLine() : // 写一行行分隔符,由系统属性定义符号。

/*高效字符流构造方法:public BufferedReader(Reader in) :// 创建一个 新的缓冲输入流。public BufferedWriter(Writer out): // 创建一个新的缓冲输出流。高效字符流读一行、写一行(到下一行)操作:BufferedReader:public String readLine() : // 读一行文字。BufferedWriter:public void newLine() : // 写一行行分隔符,由系统属性定义符号。*/public class Test03 {public static void main(String[] args) {// 创建2个流对象try (BufferedReader br = new BufferedReader(new FileReader("day11_io/src/br.txt"));BufferedWriter bw = new BufferedWriter(new FileWriter("day11_io/src/bw.txt"));) {// 读到的具体某行数据!String line;while((line = br.readLine())!=null){//System.out.println(line);// 将读到的一行数据写到bw.txt文件中bw.write(line);// 写一行就换行!bw.newLine(); // 等价于:bw.write("\r\n");}} catch (IOException e) {e.printStackTrace();}}}

四、打印流

4.1 打印流概述

平时我们在控制台打印输出,是调用print方法和println方法完成的,这两个方法都来自于java.io.PrintStream类,该类能够方便地打印各种数据类型的值,是一种便捷的输出方式。

4.2 相关方法

* 构造方法:【System.out得到的这个对象,默认打印到控制台,但是可以通过构造方法的参数指定打印到指定的某个文件中去(开发基本不用的!)】public PrintStream(String fileName): 使用指定的文件名创建一个新的打印流。* 功能方法:void print(Object obj) 打印一个对象。 void println(Object x) 打印一个对象,然后终止该行。

public class Test01 {public static void main(String[] args) throws IOException {// 通过System类的属性获得对象PrintStream ps = System.out;// 调用方法打印数据到控制台ps.print("黑马"); // 不换行!// 调用方法打印数据到控制台ps.println("666"); // 换行!// 通过PrintStream的构造方法来指定打印数据的位置PrintStream psm = new PrintStream("day11_io/src/ps.txt");// 向指定位置的文件中写出数据!psm.write("黑马666".getBytes());}}

五、装饰者设计模式

5.1 装饰者设计模式介绍

在我们今天所学的缓冲流中涉及到java的一种设计模式,叫做装饰者模式,我们来认识并学习一下这个设计模式。

装饰模式指的是在不改变原类, 不使用继承的基础上,动态地扩展一个对象的功能。

* 装饰模式遵循原则:①装饰类和被装饰类必须实现相同的接口或者继承同一个父类!②在装饰类中必须传入被装饰类的引用【构造方法】③在装饰类中对需要扩展的方法进行扩展④在装饰类中对不需要扩展的方法调用被装饰类中的同名方法

① 装饰类和被装饰类必须实现相同的接口或者继承同一个父类!

装饰类:BufferedInputStream

被装饰类:FileInputStream

② 在装饰类中必须传入被装饰类的引用【构造方法】源码分析如下:

③ 在装饰类中对需要扩展的方法进行扩展

可以使用被装饰类的对象,调用原有的方法,完成一些基础性的功能,然后再添加额外的代码完成增强的功能!

④ 在装饰类中对不需要扩展的方法调用被装饰类中的同名方法

5.2 装饰者代码演示

5.2.1 定义父接口

/*明星接口![类似于InputStream]*/public interface Star {// 定义抽象方法void sing();// 定义默认方法default public void method(){System.out.println("不老男神!");}}

5.2.2 定义被装饰类

/*明星:歌手刘德华 【类似于FileInputStream】 被包装的类!*/public class LiuDeHua implements Star {// 重写接口的抽象方法@Overridepublic void sing() {System.out.println("啊哈,给我一杯忘情水~");}// 重写覆盖父接口的默认方法@Overridepublic void method() {System.out.println("华仔人真好!");}}

5.2.3 定义装饰类

/*装饰类:类似于BufferedInputStream1. 装饰类与被装饰类需要实现同一个父接口或者继承同一个父类!2. 在装饰类中需要有被装饰类对象的引用3. 在需要增强的方法内部,添加新增的代码4. 在不需要增强的方法内部,直接调用被装饰类原有功能的方法即可!*/public class LiuDeHuaWrapper implements Star{// 拥有了被装饰类对象的引用private LiuDeHua liuDeHua;// 通过装饰类的构造方法为被装饰类对象的引用赋值!public LiuDeHuaWrapper(LiuDeHua liuDeHua) {this.liuDeHua = liuDeHua;}// 要增强的方法!@Overridepublic void sing() {// 通过被装饰类对象的引用,调用原有功能!liuDeHua.sing();// 添加增强功能的代码System.out.println("换我一夜不流泪~");}// 不需要增强!@Overridepublic void method() {// 对于不需要增强的方法,直接调用被装饰类原有功能即可liuDeHua.method();}}

5.2.4 测试类

public class Test {public static void main(String[] args) {// 创建歌手刘德华对象(被装饰类)LiuDeHua ldh = new LiuDeHua();// 调用功能//ldh.sing();// 创建装饰类的对象(需要传入被装饰类的对象)LiuDeHuaWrapper ldhw = new LiuDeHuaWrapper(ldh);// 调用方法(增强了的方法)ldhw.sing();// 调用被装饰类原有功能的方法ldhw.method();}}

总结:对于一个类中的方法进行增强,可以使用继承,还可以使用装饰者设计模式。

六、转换流

6.1 字符编码和字符集

* 字符编码Character Encoding : 就是一套自然语言的字符与二进制数之间的对应规则。计算机中储存的信息都是用二进制数表示的,而我们在屏幕上看到的数字、英文、标点符号、汉字等字符是二进制数转换之后的结果。按照某种规则,将字符存储到计算机中,称为编码 。反之,将存储在计算机中的二进制数按照某种规则解析显示出来,称为解码 。比如说,按照A规则存储,同样按照A规则解析,那么就能显示正确的文本f符号。反之,按照A规则存储,再按照B规则解析,就会导致乱码现象。【只要编码和解码的码表一致就不会出现乱码问题!】* 字符集 Charset:也叫编码表。是一个系统支持的所有字符的集合,包括各国家文字、标点符号、图形符号、数字等。计算机要准确的存储和识别各种字符集符号,需要进行字符编码,一套字符集必然至少有一套字符编码。常见字符集有ASCII字符集、GBK字符集、Unicode字符集等。

可见,当指定了编码,它所对应的字符集自然就指定了,所以编码才是我们最终要关心的

1. ASCII字符集 :ASCII(American Standard Code for Information Interchange,美国信息交换标准代码)是基于拉丁字母的一套电脑编码系统,用于显示现代英语,主要包括控制字符(回车键、退格、换行键等)和可显示字符(英文大小写字符、阿拉伯数字和西文符号)。基本的ASCII字符集,使用7位(bits)表示一个字符,共128字符。ASCII的扩展字符集使用8位(bits)表示一个字符,共256字符,方便支持欧洲常用字符。2. ISO-8859-1字符集:拉丁码表,别名Latin-1,用于显示欧洲使用的语言,包括荷兰、丹麦、德语、意大利语、西班牙语等。ISO-5559-1使用单字节编码,兼容ASCII编码。3. GBxxx字符集:GB就是国标的意思,是为了显示中文而设计的一套字符集。①GB2312:简体中文码表。一个小于127的字符的意义与原来相同。但两个大于127的字符连在一起时,就表示一个汉字,这样大约可以组合了包含7000多个简体汉字,此外数学符号、罗马希腊的字母、日文的假名们都编进去了,连在ASCII里本来就有的数字、标点、字母都统统重新编了两个字节长的编码,这就是常说的"全角"字符,而原来在127号以下的那些就叫"半角"字符了。②GBK:最常用的中文码表。是在GB2312标准基础上的扩展规范,使用了双字节编码方案,共收录了21003个汉字,完全兼容GB2312标准,同时支持繁体汉字以及日韩汉字等。③GB18030:最新的中文码表。收录汉字70244个,采用多字节编码,每个字可以由1个、2个或4个字节组成。支持中国国内少数民族的文字,同时支持繁体汉字以及日韩汉字等。4. Unicode字符集 :Unicode编码系统为表达任意语言的任意字符而设计,是业界的一种标准,也称为统一码、标准万国码。它最多使用4个字节的数字来表达每个字母、符号,或者文字。有三种编码方案,UTF-8、UTF-16和UTF-32。最为常用的UTF-8编码。UTF-8编码,可以用来表示Unicode标准中任何字符,它是电子邮件、网页及其他存储或传送文字的应用中,优先采用的编码。互联网工程工作小组(IETF)要求所有互联网协议都必须支持UTF-8编码。所以,我们开发Web应用,也要使用UTF-8编码。它使用一至四个字节为每个字符编码,编码规则:①128个US-ASCII字符,只需一个字节编码。②拉丁文等字符,需要二个字节编码。③大部分常用字(含中文),使用三个字节编码。④其他极少使用的Unicode辅助字符,使用四字节编码。

小结:①字符集里面包含了字符编码![一套字符集至少包含了一种字符编码!] 【字符集:不同的字符集规定了里面能够出现的字符(文字)】拉丁字符集:只能存储欧美国家的文字字符!GBK字符集:只能存中国(日韩)等国家的文字字符!Unicode字符集:可以存储全世界各国文字字符!②每种字符集里面出现的字符数据最终是要存储到计算机!【它会使用对应的规则来存储: 将文字字符====>>> 二进制数据】gbk字符集将文字数据弄到计算机中!后面取出数据要保证不乱吗,要么解码就需要使用gbk!每一种存取的方式就是一种字符编码!

要想开发中能够存储所有的文字,使用utf-8编码!要想开发不出现乱码,保证编码和解码的码表一致就行!

6.2 编码引出的问题

在IDEA中,使用FileReader 读取项目中的文本文件。由于IDEA的设置,都是默认的UTF-8编码,所以没有任何问题。但是,当读取Windows系统中创建的文本文件时,由于Windows系统的默认是GBK编码,就会出现乱码。

/*读取数据gbk.txt产生中文乱码:文件编码码表:使用的是gbk程序读取码表:使用的是utf-8*/public class Test01 {public static void main(String[] args) {try (FileReader fr = new FileReader("day11_io/src/gbk.txt");) {char[] ch = new char[1024];int len;while ((len = fr.read(ch)) != -1) {System.out.println(new String(ch, 0, len));}} catch (IOException e) {e.printStackTrace();}}}

得到的结果数据:

6.3 InputStreamReader类

转换流java.io.InputStreamReader,是Reader的子类,是从字节流到字符流的桥梁。它读取字节,并使用指定的字符集将其解码为字符。它的字符集可以由名称指定,也可以接受平台的默认字符集。

* 构造方法:InputStreamReader(InputStream in) : // 创建一个使用默认字符集的字符流。InputStreamReader(InputStream in, String charsetName) : // 创建一个指定字符集的字符 【能够处理中文乱码问题】

指定编码读取

/*读取数据gbk.txt产生中文乱码:文件编码码表:使用的是gbk程序读取码表:使用的是utf-8我们使用转换流对象:InputStreamReader*/public class Test02 {public static void main(String[] args) {try (// 没有指定解码的码表,默认使用utf-8的!//InputStreamReader isr = new InputStreamReader(new FileInputStream("day11_io/src/gbk.txt"));// 指定解码码表为utf-8,依旧会出现乱码!【gbk.txt文件里面使用的是gbk码表来编码的!】InputStreamReader isr = new InputStreamReader(new FileInputStream("day11_io/src/gbk.txt"),"gbk");) {char[] ch = new char[1024];int len;while ((len = isr.read(ch)) != -1) {System.out.println(new String(ch, 0, len));}} catch (IOException e) {e.printStackTrace();}}}

6.4 OutputStreamWriter类

转换流java.io.OutputStreamWriter ,是Writer的子类,是从字符流到字节流的桥梁。使用指定的字符集将字符编码为字节。它的字符集可以由名称指定,也可以接受平台的默认字符集。

* 构造方法:OutputStreamWriter(OutputStream in) : 创建一个使用默认字符集的字符流。OutputStreamWriter(OutputStream in, String charsetName) : 创建一个指定字符集的字符流。

指定编码写出

public class Test03 {public static void main(String[] args) {try (// 文件默认给定的码表是gbk,idea使用的是utf-8. 将代码写出数据的码表指定为gbk即可!OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("day11_io/src/gbk.txt"),"gbk")) {osw.write("中国Niubility");} catch (IOException e) {e.printStackTrace();}}}

默认我们在idea中使用FileReader和FileWriter操作数据是没有乱码的(代码和文件的码表都是utf-8),讲解上面的前提(文件采用的是gbk)

七、序列化流

7.1 序列化与反序列化

Java 提供了一种对象序列化的机制。用一个字节序列可以表示一个对象,该字节序列包含该对象的数据、对象的类型和对象中存储的属性等信息。字节序列写出到文件之后,相当于文件中持久保存了一个对象的信息。反之,该字节序列还可以从文件中读取回来,重构对象,对它进行反序列化。对象的数据、对象的类型和对象中存储的数据信息,都可以用来在内存中创建对象。

简言之:内存的数据到硬盘就是序列化!硬盘的数据到内存就是反序列化!

7.2 ObjectOutputStream类

java.io.ObjectOutputStream 类,将Java对象的原始数据类型写出到文件,实现对象的持久存储。

* 构造方法:public ObjectOutputStream(OutputStream out): // 创建一个指定OutputStream的ObjectOutputStream。* 功能方法:public final void writeObject (Object obj) : //将指定的对象写出。

要想保证对象从内存正常的序列化到硬盘中,需要确保2点:

1. 必须实现Serializable接口!【Serializable接口里面啥也没有!它是一个标记接口!能生成一个序列号出来!】2. 要序列的数据(成员变量)不能被transient(瞬时)修饰!

/*要想这个类的对象能够正常完成序列化操作,那么:1. 必须实现Serializable接口!【Serializable接口里面啥也没有!它是一个标记接口!能生成一个序列号出来!】2. 要序列的数据(成员变量)不能被transient(瞬时)修饰!*/public class Student implements Serializable {private static final long serialVersionUID = -2519870842411754342L;private String name;private int age;private transient String address;public Student(String name, int age, String address) {this.name = name;this.age = age;this.address = address;}public Student() {}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;}public String getAddress() {return address;}public void setAddress(String address) {this.address = address;}}

/*序列化测试类:*/public class StudentDemo {public static void main(String[] args) throws IOException {// 创建对象Student student = new Student();student.setName("jack");student.setAge(38);student.setAddress("tianjin");// 创建序列化流对象ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("D:/io/student.txt"));oos.writeObject(student);// 关闭oos.close();}}

若类没有实现Serializable接口,会出现异常:NotSerializableException

7.3 ObjectInputStream类

ObjectInputStream反序列化流,将之前使用ObjectOutputStream序列化的原始数据恢复为对象。

* 构造方法:public ObjectInputStream(InputStream in): 创建一个指定InputStream的ObjectInputStream。* 功能方法:public final Object readObject () : 读取一个对象。

/*反序列化测试类:*/public class StudentTest {public static void main(String[] args) throws IOException, ClassNotFoundException {// 创建反序列化对象ObjectInputStream ois = new ObjectInputStream(new FileInputStream("D:/io/student.txt"));// 读数据Student s = (Student) ois.readObject();// 测试数据System.out.println(s.getName());System.out.println(s.getAge());System.out.println(s.getAddress());// 释放资源ois.close();}}

测试结果:

反序列化不成功,可能出现的问题!

* 字节码文件不存在(可能被删除了)! 【反序列化是需要根据对应的字节码文件来完成的】:出现异常: ClassNotFoundException* 在序列化成功之后,你又修改了原来的字节码文件的数据(修改字节码对应的java文件,然后重新编译) 出现异常:InvalidClassException* 有字节码,但是不是反序列化需要的那个字节码文件!出现异常: ClassNotFoundException* 对应的异常名称:① ClassNotFoundException② InvalidClassException

八、commons-io工具包

目前我们接触的IO流对象有:

1. inputStream2. outputStream3. reader4. writer5. FileInputStream6. FileOutputStream7. FileReader8. FileWriter9. BufferedInputStream10. BufferedOutputStream11. BufferedReader12. BufferedWriter13. PrintWriter14. InputStreamReader15. OutputStreamWriter16. ObjectOutputStream17. ObjectInputStream

commons-io是apache开源基金组织提供的一组有关IO操作的类库,可以挺提高IO功能开发的效率。commons-io工具包提供了很多有关io操作的类,见下表:

8.1基本使用

8.1.1 使用步骤

1. 下载commons-io相关jar包;/proper/commons-io/2. 把commons-io-2.6.jar包复制到指定的Module的lib目录中3. 将commons-io-2.6.jar加入到classpath中

8.1.2 常用API

commons-io提供了一个工具类 mons.io.IOUtils,封装了大量IO读写操作的代码。其中有两个常用方法:

// 把input输入流中的内容拷贝到output输出流中,返回拷贝的字节个数(适合文件大小为2GB以下)public static int copy(InputStream in, OutputStream out)// 把input输入流中的内容拷贝到output输出流中,返回拷贝的字节个数(适合文件大小为2GB以上) public static long copyLarge(InputStream in, OutputStream out)

代码演示:

public class Test01 {public static void main(String[] args) {try(// 创建2个字节流对象InputStream is = new FileInputStream("D:/io/jdk8.exe");OutputStream os = new FileOutputStream("D:/io/xxx/copy.exe");){long start = System.currentTimeMillis();// 复制文件IOUtils.copy(is,os);long end = System.currentTimeMillis();System.out.println("消耗时间:" + (end-start) +"毫秒!");}catch (IOException e){e.printStackTrace();}}}

commons-io还提供了一个工具类mons.io.FileUtils,封装了一些对文件操作的方法:

// 复制文件到另外一个目录下。public static void copyFileToDirectory(final File srcFile, final File destFile)// 复制file1目录到file2位置。public static void copyDirectoryToDirectory( file1 , file2 );

代码演示:

public class Test02 {public static void main(String[] args) throws IOException {//FileUtils.copyFileToDirectory(new File("day11_io/src/fis.properties"),new File("d:/io/xxx"));FileUtils.copyDirectoryToDirectory(new File("D:/video"),new File("d:/io/xxx"));}}

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。