IO流
1、文件
1.1、概念
1.1.1、什么是文件
文件,对我们并不陌生,文件是保存数据的地方,比如大家经常使用的word文档,txt文件,exce文件......都是文件。它既可以保存一张图片,也可以保持视频,声音......
1.1.2、文件流
文件在程序中是以流的形式来操作的
流:数据在数据源(文件)和程序(内存)之间经历的路径
输入流:数据从数据源(文件)到程序(内存)的路径
输出流:数据从程序(内存)到数据源(文件)的路径
1.2、常用操作
1.2.1、创建文件对象相关构造器和方法
new File(String pathname) //根据路径构建一个File对象
new File(File parent,String child) //根据父目录文件+子路径构建
new File(String parent,String child) //根据父目录+子路径构建
注意:createNewFile 创建新文件
1.2.1——应用案例演示
FileCreate.java
请在e盘下,创建文件news1.txt、news2.txt、 news3.txt,用三种不同方式创建。
package com.fy; import org.junit.jupiter.api.Test; import java.io.File; import java.io.IOException; //创建文件 public class FileCreate { public static void main(String[] args) { } //方式1:new File(String pathname) //根据路径构建一个File对象 @Test public void create01() { String filePath = "d:\\news1.txt"; File file = new File(filePath); try { //真正创建文件,在这里,如果没有 file.createNewFile(),是不会创建文件的 file.createNewFile(); System.out.println("文件创建成功"); } catch (IOException e) { e.printStackTrace(); } } //方式2:new File(File parent,String child) //根据父目录文件+子路径构建 //d:\\news2.txt @Test public void create02() { File parentFile = new File("d:\\"); String fileName = "news2.txt"; File file = new File(parentFile,fileName); try { file.createNewFile(); System.out.println("文件创建成功~"); } catch (IOException e) { e.printStackTrace(); } } //方式3:new File(String parent,String child) //根据父目录+子路径构建 @Test public void create03() { String parentPath = "d:\\"; String filePath = "news3.txt"; File file = new File(parentPath,filePath); try { file.createNewFile(); System.out.println("创建成功~"); } catch (IOException e) { e.printStackTrace(); } } }
1.2.2、获取文件的相关信息
getName、getAbsolutePath、getParent、length、exists、isFile、isDirectory
1.2.2——应用案例演示
FileInformation.java
如何获取到文件的大小、文件名、路径、父File、是文件还是目录(目录本质也是文件,一种特殊的文件)、是否存在。
package com.fy; import org.junit.jupiter.api.Test; import java.io.File; public class FileInformation { public static void main(String[] args) { } //获取文件的信息 @Test public void info() { //先创建文件对象 File file = new File("d:\\news1.txt"); //调用相应的方法,得到对应信息 System.out.println("文件名字="+file.getName()); //getName、getAbsolutePath、getParent、length、exists、isFile、isDirectory System.out.println("文件绝对路径="+file.getAbsolutePath()); System.out.println("文件父级目录="+file.getParent()); System.out.println("文件大小(字节)="+file.length()); System.out.println("文件是否存在="+file.exists()); System.out.println("是不是一个文件="+file.isFile()); System.out.println("是不是一个目录="+file.isDirectory()); } }
1.2.3、目录的操作和文件删除
mkdir创建一级目录、mkdirs创建多级目录、delete删除空目录或文件。
1.2.3——应用案例演示
判断d:\news1.txt是否存在,如果存在就删除。
判断D:\demo02是否存在,存在就删除,否则提示不存在。
判断D:\demo\a\b\c目录是否存在,如果存在就提示已经存在,否则就创建。
package com.fy; import org.junit.jupiter.api.Test; import java.io.File; public class Directory { public static void main(String[] args) { } //判断d:\\news1.txt是否存在,如果存在就删除 @Test public void m1() { String filePath = "d:\\news1.txt"; File file = new File(filePath); if (file.exists()) { if (file.delete()) { System.out.println(filePath+"删除成功"); }else { System.out.println(filePath+"删除失败"); } }else { System.out.println("文件不存在"); } } //判断D:\\demo02是否存在,存在就删除,否则提示不存在 //这里我们需要体会到,在Java编程中,目录也被当成文件来对待 @Test public void m2() { String filePath = "D:\\demo02"; File file = new File(filePath); if (file.exists()) { if (file.delete()) { System.out.println(filePath+"删除成功"); }else { System.out.println(filePath+"删除失败"); } }else { System.out.println("该目录不存在"); } } //判断D:\\demo\\a\\b\\c目录是否存在,如果存在就提示已经存在,否则就创建 @Test public void m3() { String directoryPath = "D:\\demo\\a\\b\\c"; File file = new File(directoryPath); if (file.exists()) { System.out.println("该目录已存在"); }else { if (file.mkdirs()) { //创建一级目录使用mkdir(),创建多级目录使用mkdirs() System.out.println(directoryPath+"该目录创建成功"); }else { System.out.println(directoryPath+"该目录创建成功"); } } } }
2、IO流原理及流的分类
2.1、Java I0流原理
-
I/O是Input/Output的缩写,I/O技术是非常实用的技术,用于处理数据传输。如读/写文件,网络通讯等。
-
Java程序中,对于数据的输入/输出操作以"流(stream)"的方式进行。
-
java.io包 下提供了各种“流”类和接口,用以获取不同种类的数据,并通过方法输入或输出数据。
-
输入input:读取外部数据(磁盘、光盘等存储设备的数据)到程序(内存)中。
-
输出output:将程序(内存)数据输出到磁盘、光盘等存储设备中。
2.2、流的分类
-
按操作数据单位不同分为:字节流(8 bit),字符流(按字符)
-
按数据流的流向不同分为:输入流,输出流
-
按流的角色的不同分为:节点流,处理流/包装流
1)Java的IO流共涉及40多个类,实际上非常规则,都是从如上4个抽象基类派生的。
2)由这四个类派生出来的子类名称都是以其父类名作为子类名后缀。
-
节点流和处理流:
节点流可以从一个特定的数据源读写数据,如FileReadet、 FileWriter
处理流(也叫包装流)是"连接”在已存在的流(节点流或处理流)之上,为程序提供更为强大的读写功能,也更加的灵活,如BufferedReader、BufferedWriter(BufferedReader类中,有属性Reader,既可以封装一个节点流,该节点流可以是任意的,只要是Reader的子类都可以,类似于祖宗类Object)
-
节点流和处理流一览图:
-
节点流和处理流的区别和联系:
-
节点流是底层流/低级流,直接跟数据源相接。
-
处理流(包装流)包装节点流,既可以消除不同节点流的实现差异,也可以提供更方便的方法来完成输入输出。[源码理解]
-
处理流(也叫包装流)对节点流进行包装,使用了修饰器设计模式,不会直接与数据源相连。[模拟修饰器设计模式]
-
处理流的功能主要体现在以 下两个方面:
-
性能的提高:主要以增加缓冲的方式来提高输入输出的效率。
-
操作的便捷:处理流可能提供了一系列便捷的方法来一次输入输出大批量的数据,使用更加灵活方便。
模拟修饰器设计模式:io-study下的xiushiqishipeiqimoshi包
3、字节流和字符流
3.1、输入流
3.1.1、InputStream:字节输入流
-
InputStream 抽象类是所有类字节输入流的超类
-
InputStream 常用的子类:
-
FilelnputStream:文件输入流
-
BufferedInputStream:缓冲字节输入流
-
ObjectInputStream:对象字节输入流
-
3.1.1.1、FileInputStream(节点流)
3.1.1.1——应用案例
FilelnputStream.java
要求:请使用 FileInputStream 读取 hello.txt 文件,并将文件内容显示到控制台。
package com.fy.inputStream; import org.junit.jupiter.api.Test; import java.io.FileInputStream; import java.io.IOException; //演示FileInputStream的使用(字节输入流,文件————>程序) public class FileInputStreamTest { public static void main(String[] args) { } /** * 演示读取文件 *单个字节读取,效率比较低 *-->使用 read(byte[] b) */ @Test public void readFile01() { String filePath = "d:\\hello.txt"; int readDate = 0; FileInputStream fileInputStream = null; try { //创建 FileInputStream 对象,用于读取文件 fileInputStream = new FileInputStream(filePath); //从该输入流读取一个字节的数据,如果没有输入可用,此方法将阻止 //如果返回 -1 ,表示读取完比 while ((readDate = fileInputStream.read()) != -1) { System.out.print((char) readDate); //转成char显示 } } catch (IOException e) { e.printStackTrace(); } finally { //关闭文件,流释放资源 try { fileInputStream.close(); } catch (IOException e) { e.printStackTrace(); } } } /** * 使用 read(byte[] b) 读取文件,提高效率 */ @Test public void readFile02() { String filePath = "d:\\hello.txt"; //字节数组 byte[] buf = new byte[8]; //一次读取8个字节 int readLen = 0; FileInputStream fileInputStream = null; try { //创建 FileInputStream 对象,用于读取文件 fileInputStream = new FileInputStream(filePath); //从该输入流读取最多 b.length 字节的数据到字节数组。此方法将阻塞,知道某些输入可用 //如果返回 -1 ,表示读取完比 //如果读取正常,返回实际读取的字节数 while ((readLen = fileInputStream.read(buf)) != -1) { System.out.print(new String(buf,0,readLen)); //字符显示 } } catch (IOException e) { e.printStackTrace(); } finally { //关闭文件,流释放资源 try { fileInputStream.close(); } catch (IOException e) { e.printStackTrace(); } } } }
3.1.1.2、BufferedInputStream(处理流)
3.1.1.2——应用案例
要求:编程完成图片/音乐的拷贝(要求使用Buffered...流)
package com.fy.outputStream; import java.io.*; //演示使用:BufferedInputStream 和 BufferedOutputStream //二进制文件拷贝 //思考:字节流可以操作二进制文件,那可以操作文本文件吗? //可以,反过来就不行(字符流不能操作二进制文件) public class BufferedCopy02 { public static void main(String[] args) { String srcFilePath = "d:\\h.png"; String desFilePath = "d:\\h02.png"; //创建BufferedInputStream 和 BufferedOutputStream BufferedInputStream bis = null; BufferedOutputStream bos = null; try { //因为 FileInputStream 是 InputStream 子类 bis = new BufferedInputStream(new FileInputStream(srcFilePath)); bos = new BufferedOutputStream(new FileOutputStream(desFilePath)); //循环读取文件,并写入到 desFilePath byte[] buf = new byte[1024]; //提高读取效率,做一个缓冲 int readLen = 0; //当返回 -1 时,表示文件读取完毕 while ((readLen = bis.read(buf)) != -1) { bos.write(buf,0,readLen); } } catch (IOException e) { e.printStackTrace(); } finally { //关闭流 try { if (bis != null) { bis.close(); } if (bos != null) { bos.close(); } } catch (IOException e) { e.printStackTrace(); } } } }
3.1.1.3、ObjectInputStream(处理流)
ObjectInputStream——处理流中的对象流
功能:
ObjectOutputStream:提供了对基本类型或对象类型的 序列化方法
ObjectInputStream:提供了对基本类型或对象类型的 反序列化方法
-
看一个需求:
-
将 int num = 100 这个int数据保存到文件中,注意不是100数字,而是int 100,并且,能够从文件中直接恢复 int 100。
-
将 Dog dog = new Dog( "小黄”,3) 这个dog对象保存到文件中,并且能够从文件恢复。
上面的要求,就是能够将基本数据类型或者对象进行序列化和反序列化操作。
-
序列化和反序列化
-
序列化就是在保存数据时,保存数据的值和数据类型
-
反序列化就是在恢复数据时,恢复数据的值和数据类型
-
需要让某个对象支持序列化机制,则必须让其类是可序列化的,为了让某个类是可序列化的,该类必须实现如下两个接口之一:
1)Serializable //这是一个标记接口(声明性质),没有方法
2)Externalizable //该接口有方法需要实现,因此,我们一般使用上面的 Serializable 接口
-
注意事项和细节说明
-
读写顺序要一致
-
要求序列化或反序列化对象,需要实现Serializable
-
序列化的类中建议添加SerialVersionUID,为了提高版本的兼容性
-
序列化对象时,默认将里面所有属性都进行序列化,除了 static 或 transient 修饰的成员
-
序列化对象时,要求里面属性的类型也需要实现序列化接口
-
序列化具备可继承性,也就是如果某类已经实现了序列化,则它的所有子类也已经默认实现了序列化
3.1.1.3——应用案例
要求:使用 ObjectInputStream 读取Date.dat 并反序列化恢复数据
package com.fy.inputStream; import com.fy.outputStream.Dog; import java.io.FileInputStream; import java.io.IOException; import java.io.ObjectInputStream; public class ObjectInputStreamTest { public static void main(String[] args) throws IOException, ClassNotFoundException { //指定反序列化文件 String filePath = "d:\\Date.dat"; ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filePath)); //读取 //1.读取(反序列化)的顺序要和你保存数据(序列化)的顺序一致 //2.否则会出现异常 System.out.println(ois.readInt()); System.out.println(ois.readBoolean()); System.out.println(ois.readChar()); System.out.println(ois.readDouble()); System.out.println(ois.readUTF()); //dog 的编译类型是 Object , dog 的运行类型是 Dog Object dog = ois.readObject(); System.out.println("运行类型="+dog.getClass()); System.out.println("dog信息="+dog); //底层会把 Object -->(转成) Dog //这里是特别重要的细节: //1.如果我们希望调用Dog的方法,需要向下转型 //2.需要将Dog类的定义,放在可以引用的位置 Dog dog2 = (Dog) dog; System.out.println(dog2.getName()); //旺财 //关闭流 ois.close(); } }
3.1.2、Reader:字符输入流
3.1.2.1、FileReader(节点流)
FileReader 相关方法:
-
new FileReader(File/String)
-
read:每次读取单个字符,返回该字符,如果到文件末尾返回-1
-
read(char[]):批量读取多个字符到数组,返回读取到的字符数,如果到文件末尾返回-1
相关API:
-
new String(char[]):将char[]转换成String
-
new String(charl[,off,len):将char[]的指定部分转换成String
3.1.2.1——应用案例
要求:使用FileReader从story.txt读取内容,并显示。
package com.fy.reader; import com.sun.xml.internal.ws.api.model.wsdl.WSDLOutput; import org.junit.jupiter.api.Test; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; public class FileReaderTest { public static void main(String[] args) { //单个字符读取 String filePath = "d:\\story.txt"; FileReader fileReader = null; int data = ' '; try { //创建FileReader对象 fileReader = new FileReader(filePath); //循环读取 使用read while ((data = fileReader.read()) != -1) { System.out.print((char) data); } } catch (IOException e) { e.printStackTrace(); } finally { try { if (fileReader!=null) { fileReader.close(); } } catch (IOException e) { e.printStackTrace(); } } } //字符数组读取文件 @Test public void readFile02() { System.out.println("------------read02-------------"); String filePath = "d:\\story.txt"; FileReader fileReader = null; int readLen = 0; char[] buf = new char[8]; try { fileReader = new FileReader(filePath); //循环读取,使用read(buf),返回的是实际读取到的字符数 //如果返回-1,说明读到文件结束 while ((readLen = fileReader.read(buf)) != -1) { System.out.print(new String(buf,0,readLen)); } } catch (IOException e) { e.printStackTrace(); } finally { try { if (fileReader != null) { fileReader.close(); } } catch (IOException e) { e.printStackTrace(); } } } }
3.1.2.2、BufferedReader(处理流)
3.1.2.2——应用案例
要求:使用 BufferedReader 读取文本文件,并显示在控制台
package com.fy.reader; import java.io.BufferedReader; import java.io.FileReader; public class BufferedReaderTest { public static void main(String[] args) throws Exception { String filePath = "d:\\note.txt"; //创建BufferedReader BufferedReader bufferedReader = new BufferedReader(new FileReader(filePath)); //读取 String line; //按行读取,效率高 //说明 //1.bufferedReader.readLine() 是按行读取文件 //2.当返回null时,表示文件读取完毕 while ((line = bufferedReader.readLine()) != null) { System.out.println(line); } //关闭流,这里只需要关闭 BufferedReader, 因为底层会自动关闭节点流 bufferedReader.close(); } }
3.1.2.3、InputStreamReader(处理流)
InputStreamReader——处理流中的转换流
-
介绍
-
InputStreamReader:Reader的子类,可以将InputStream(字节流)包装成Reader(字符流)
-
OutputStreamWriter:Writer的子类,实现将OutputStream(字节流)包装成Writer(字符流)
-
当处理纯文本数据时,如果使用字符流效率更高,并且可以有效解决中文问题,所以建议将字节流转换成字符流
-
可以在使用时指定编码格式(比如utf- 8,gbk,gb2312,IS08859-1等)
功能:把字节流转换成字符流
方法:InputStreamReader(InputStream, Charset)
InputStream:传入对象,Charset:指定编码
3.1.2.3——应用案例1
package com.fy.transformation; import java.io.BufferedReader; import java.io.FileReader; import java.io.IOException; //看一个中文乱码问题 public class CodeQuestion { public static void main(String[] args) throws IOException { //读取 d:\\hello.txt 文件到程序中 //思路 //1. 创建字符输入流 BufferedReader [处理流] //2. 使用 BufferedReader 对象读取 d:\\hello.txt //3. 默认情况下,读取文件是按照 utf-8 编码读取,如果读取编码不一样,就可能会出现乱码问题 String filePath = "d:\\hello.txt"; BufferedReader bufferedReader = new BufferedReader(new FileReader(filePath)); String s = bufferedReader.readLine(); System.out.println("读取到的内容:" + s); bufferedReader.close(); } }
3.1.2.3——应用案例2
package com.fy.transformation; import java.io.*; //使用 InputStreamReader 转换流解决中文乱码问题 //将字节流(FileInputStream)转成字符流(InputStreamReader) public class InputStreamReaderTest { public static void main(String[] args) throws IOException { String filePath = "d:\\hello.txt"; //1. 把 FileInputStream 转成 InputStreamReader //2. 指定编码 gbk //InputStreamReader inputStreamReader = new InputStreamReader(new FileInputStream(filePath),"gbk"); //3. 把 InputStreamReader 传入 BufferedReader //BufferedReader bufferedReader = new BufferedReader(inputStreamReader); //将 2 和 3 合在一起写 BufferedReader bufferedReader = new BufferedReader(new InputStreamReader( new FileInputStream(filePath),"gbk")); //4. 读取 String str = bufferedReader.readLine(); System.out.println("读取内容:" + str); //5.关闭流 bufferedReader.close(); } }
3.2、输出流
3.2.1、OutputStream:字节输出流
3.2.1.1、FileOutputStream(节点流)
3.2.1.1——应用案例1
演示使用 FileOutputStream 将数据写到文件中,如果该文件不存在,则创建该文件。
package com.fy.outputStream; import org.junit.jupiter.api.Test; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.nio.charset.StandardCharsets; public class FileOutputStreamTest { public static void main(String[] args) { } /** * 演示使用 FileOutputStream 将数据写到文件中 * 如果该文件不存在,则创建该文件 */ @Test public void writeFile() { //创建 FileOutputStream 对象 String filePath = "d:\\h.txt"; FileOutputStream fileOutputStream = null; try { //得到一个 FileOutputStream 对象 // new FileOutputStream(filePath) 创建方式,当写入内容时,会覆盖原来的内容 // new FileOutputStream(filePath,true) 创建方式,当写入内容时,会追加到原来内容的末尾 fileOutputStream = new FileOutputStream(filePath,true); //写入一个字节 //fileOutputStream.write('a'); //写入字符串 String str = "hello,world"; //str.getBytes(StandardCharsets.UTF_8) 可以把字符串-->字节数组 //fileOutputStream.write(str.getBytes()); //write(byte[] b,int off,int len) 将字节数组的 off 到 len 之间的字节写入此文件的输入流 fileOutputStream.write(str.getBytes(),0,5); //某个位置~某个位置 } catch (IOException e) { e.printStackTrace(); } finally { try { fileOutputStream.close(); } catch (IOException e) { e.printStackTrace(); } } } }
3.2.1.1——应用案例2
要求:编程完成图片/音乐的拷贝。
package com.fy.outputStream; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; public class FileCopy { public static void main(String[] args) { //完成文件拷贝,将d:\\h.png 拷贝 d:\\hx.png //思路分析: //1. 创建文件的输入流,将文件读入程序 //2. 创建文件的输出流,将读取到的文件数据,写入到指定的文件 String srcFilePath = "d:\\h.png"; String destFilePath = "d:\\hx.png"; FileInputStream fileInputStream = null; FileOutputStream fileOutputStream = null; try { fileInputStream = new FileInputStream(srcFilePath); fileOutputStream = new FileOutputStream(destFilePath); //定义一个字节数组,提高读取效率 byte[] buf = new byte[1024]; int readLen = 0; while ((readLen = fileInputStream.read(buf)) != -1) { //读取到后,就写入到文件 通过 fileOutputStream //即,一边读,一边写 fileOutputStream.write(buf,0, buf.length); //一定要用这个方法 } System.out.println("拷贝成功~"); } catch (Exception e) { e.printStackTrace(); } finally { try { if (fileInputStream != null) { fileInputStream.close(); } if (fileOutputStream != null) { fileOutputStream.close(); } } catch (IOException e) { e.printStackTrace(); } } } }
3.2.1.2、BufferedOutputStream(处理流)
BufferedOutputStream 是字节流,实现缓冲的输出流,可以将多个字节写入底层输出流中,而不必对每次字节写入调用底层系统。
3.2.1.2——应用案例
要求:编程完成图片/音乐的拷贝(要求使用Buffered...流)
package com.fy.outputStream; import java.io.*; //演示使用:BufferedInputStream 和 BufferedOutputStream //二进制文件拷贝 //思考:字节流可以操作二进制文件,那可以操作文本文件吗? //可以,反过来就不行(字符流不能操作二进制文件) public class BufferedCopy02 { public static void main(String[] args) { String srcFilePath = "d:\\h.png"; String desFilePath = "d:\\h02.png"; //创建BufferedInputStream 和 BufferedOutputStream BufferedInputStream bis = null; BufferedOutputStream bos = null; try { //因为 FileInputStream 是 InputStream 子类 bis = new BufferedInputStream(new FileInputStream(srcFilePath)); bos = new BufferedOutputStream(new FileOutputStream(desFilePath)); //循环读取文件,并写入到 desFilePath byte[] buf = new byte[1024]; //提高读取效率,做一个缓冲 int readLen = 0; //当返回 -1 时,表示文件读取完毕 while ((readLen = bis.read(buf)) != -1) { bos.write(buf,0,readLen); } } catch (IOException e) { e.printStackTrace(); } finally { //关闭流 try { if (bis != null) { bis.close(); } if (bos != null) { bos.close(); } } catch (IOException e) { e.printStackTrace(); } } } }
3.2.1.3、ObjectOutputStream(处理流)
ObjectOutputStream——处理流中的对象流
功能:
ObjectOutputStream:提供了对基本类型或对象类型的 序列化方法
ObjectInputStream:提供了对基本类型或对象类型的 反序列化方法
3.2.1.3——应用案例
要求:使用 ObjectOutputStream 序列化基本数据类型和一个Dog对象(name, age),并保存到 data.dat 文件中。
package com.fy.outputStream; import java.io.FileOutputStream; import java.io.ObjectOutputStream; import java.io.Serializable; public class ObjectOutputStreamTest { public static void main(String[] args) throws Exception { //序列化后,保存的文件格式,不是存文本,而是按照他的格式来保存 String filePath = "d:\\Date.dat"; ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(filePath)); //序列化数据到d:\Date.dat oos.write(100); //int -> Integer (int 自动装箱成 Integer,Integer实现了 Serializable) oos.writeBoolean(true); //boolean -> Boolean (实现了 Serializable) oos.writeChar('a'); //char -> Character (实现了 Serializable) oos.writeDouble(9.5); //double -> Double (实现了 Serializable) oos.writeUTF("学习IO流中的 ObjectOutputStream"); //String //保存一个对象 oos.writeObject(new Dog("旺财",10)); oos.close(); System.out.println("数据保存完毕(序列化形式)"); } }
package com.fy.outputStream; import java.io.Serializable; //如果需要序列化某个类的对象,需要实现 Serializable 接口 public class Dog implements Serializable { private String name; private int age; public Dog(String name, int age) { this.name = name; this.age = age; } @Override public String toString() { return "Dog{" + "name='" + name + '\'' + ", 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; } }
3.2.2、Writer:字符输出流
3.2.2.1、FileWriter(节点流)
FileWriter 常用方法:
-
new FileWriter(File/String):覆盖模式,相当于流的指针在首端
-
new FileWriter(File/String,true):追加模式,相当于流的指针在尾端
-
write(int):写入单个字符
-
write(char[]):写入指定数组
-
write(char[],off,len):写入指定数组的指定部分
-
write(string):写入整个字符串
-
write(string,off,len):写入字符串的指定部分
相关API:String类:toCharArray:将String转换成char[]
注意:FileWriter使用后,必须要关闭(close)或刷新(flush),否则写入不到指定的文件!
3.2.2.1——应用案例
要求:使用 FileWriter 将 “风雨之后,定见彩虹” 写入到note.txt文件中,注意细节。
package com.fy.writer; import java.io.FileWriter; import java.io.IOException; public class FileWriterTest { public static void main(String[] args) { String filePath = "d:\\note.txt"; FileWriter fileWriter = null; char[] chars = {'a','b','c'}; try { fileWriter = new FileWriter(filePath); //在数据量大的情况下,可以循环操作 //3) write(int):写入单个字符 fileWriter.write('H'); //4) write(char[]):写入指定数组 fileWriter.write(chars); //5) write(char[],off,len):写入指定数组的指定部分 fileWriter.write("韩老师",0,3); //6) write(string):写入整个字符串 fileWriter.write("风雨之后,定见彩虹"); //7) write(string,off,len):写入字符串的指定部分 fileWriter.write("你好大连~",0,2); } catch (IOException e) { e.printStackTrace(); } finally { //对于FileWriter,一定要关闭流,或者flush才能真正的把数据写入到文件中 try { //fileWriter.flush(); //关闭文件流,等价 flush() + 关闭 fileWriter.close(); } catch (IOException e) { e.printStackTrace(); } } System.out.println("程序结束"); } }
3.2.2.2、BufferedWriter(处理流)
3.2.2.2——应用案例1
要求:使用 BufferedWriter 将“hello,韩顺平”写入文件中
package com.fy.writer; import java.io.BufferedWriter; import java.io.FileWriter; public class BufferedWriterTest { public static void main(String[] args) throws Exception{ String lifePath = "d:\\note.txt"; //创建 BufferedWriter //说明: //1. new FileWriter(lifePath,true) 表示以追加的方式写入 //2. new FileWriter(lifePath) 表示以覆盖的方式写入 BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(lifePath)); bufferedWriter.write("hello1,我在学IO流"); bufferedWriter.newLine(); //插入一个和系统相关的换行 bufferedWriter.write("hello2,我在学IO流"); bufferedWriter.newLine(); bufferedWriter.write("hello3,我在学IO流"); bufferedWriter.newLine(); //关闭流,这里只需要关闭 BufferedWriter, 因为底层会自动关闭节点流 //即:关闭外层流即可 bufferedWriter.close(); } }
3.2.2.2——应用案例2
要求:综合使用 BufferedReader 和 BufferedWriter 完成文本文件拷贝
package com.fy.writer; import java.io.*; public class BufferedCopy { public static void main(String[] args) { //BufferedReader 和 BufferedWriter 是安装字符操作 //不要去操作二进制文件[声音、视频、doc、pdf...],可能会造成文件损坏 String srcFilePath = "d:\\hello.txt"; String desFilePath = "d:\\hello-BufferedCopy.txt"; BufferedReader bufferedReader = null; BufferedWriter bufferedWriter = null; String line; //按行读取 try { bufferedReader = new BufferedReader(new FileReader(srcFilePath)); bufferedWriter = new BufferedWriter(new FileWriter(desFilePath)); //说明:readLine 读取一行内容,没有换行符 while ((line = bufferedReader.readLine()) != null) { //每读取一行,就写入 bufferedWriter.write(line); //插入换行符 bufferedWriter.newLine(); } } catch (IOException e) { e.printStackTrace(); } finally { //关闭流 try { if (bufferedReader != null) { bufferedReader.close(); } if (bufferedWriter != null) { bufferedWriter.close(); } } catch (IOException e) { e.printStackTrace(); } } } }
3.2.2.3、OutputStreamWriter(处理流)
OutputStreamWriter——处理流中的转换流
3.2.2.3——应用案例
要求:编程将字节流 FileOutputStream 包装成(转换成)字符流 OutputStreamWriter,对文件进行写入(按照gbk格式,可以指定其他,比如utf-8)
package com.fy.transformation; import java.io.*; //演示 OutputStreamWriter 的使用 //把字节流(FileOutputStream),转成字符流(OutputStreamWriter) public class OutputStreamWriterTest { public static void main(String[] args) throws IOException { String filePath = "d:\\hello.txt"; String charSet = "utf-8"; OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(filePath),charSet); osw.write("hello,我在学"); osw.close(); System.out.println("按照" + charSet + "保存文件成功"); } }
3.3、标准输入输出流、打印流
3.3.1、System.in And System.out
标准输入:System.in 标准输出:System.out
类型:InputStream PrintStream
默认设备:键盘 显示器
package com.fy.standard; import java.util.Scanner; public class InputAndOutput { public static void main(String[] args) { //System 类的 public final static InputStream in = null; //System.in 编译类型 InputStream //System.in 运行类型 BufferedInputStream //表示标准输入 键盘 System.out.println(System.in.getClass()); //System 类的 public final static PrintStream out = null; //编译类型 PrintStream //运行类型 PrintStream //表示标准输出 显示器 System.out.println(System.out.getClass()); System.out.println("hello,每天学习Java~"); System.out.println("键盘输入内容"); String next = new Scanner(System.in).next(); System.out.println("scanner=" + next); } }
3.3.2、PrintStream(字节流)和 PrintWrite(字符流)
打印流只有输出流,没有输入流
package com.fy.printstream; import java.io.IOException; import java.io.PrintStream; //演示 PrintStream (字节打印流/输出流) public class PrintStreamTest { public static void main(String[] args) throws IOException { PrintStream out = System.out; //在默认情况下,PrintStream 输出数据的位置是,标准输出,即显示器 /* public void print(String s) { if (s == null) { s = "null"; } write(s); } */ out.println("hello,你好~"); //因为 print 底层使用的是 write,所以我们可以直接调用 write 进行打印/输出 out.write("hello,你好!".getBytes()); out.close(); //我们可以修改打印流输出的位置/设备 //1. 输出修改到 "d:\\hello.txt" //2. "你好,hello" 就会输出到 "d:\\hello.txt" System.setOut(new PrintStream("d:\\hello.txt")); System.out.println("你好,hello"); } }
package com.fy.printstream; import java.io.FileWriter; import java.io.IOException; import java.io.PrintWriter; //演示 PrintWrite public class PrintWriteTest { public static void main(String[] args) throws IOException { //标准输出:显示器(控制台) //PrintWriter printWriter = new PrintWriter(System.out); //输出位置:d:\hello.txt PrintWriter printWriter = new PrintWriter(new FileWriter("d:\\hello.txt")); printWriter.print("hi,北京你好!"); // = flush + 关闭流 ,写入+关闭,真正的将数据写入文件 printWriter.close(); }
4、Properties类
-
看一个需求
如下一个配置文件 mysql.properties
ip = 192.168.0.13
user = root
pwd = 12345
请向编程读取 ip、user 和 pwd 的值是多少
分析:
-
传统的方法
-
使用 Properties 类可以方便实现
package com.fy.properties; import java.io.*; public class Properties01 { public static void main(String[] args) throws IOException { //读取 mysql.properties 文件,并得到 ip、user、pwd BufferedReader br = new BufferedReader(new FileReader("src\\mysql.properties")); String line = null; while ((line = br.readLine()) != null) { //循环读取 String[] split = line.split("="); //如果我们指定ip值 if ("ip".equals(split[0])) { System.out.println(split[0] + "值是:" + split[1]); } } br.close(); } }
-
基本介绍
-
专门用于读写配置文件的集合类
配置文件的格式:
键=值
键=值
-
注意:键值对不需要有空格,值不需要用引号引起来。默认类型是String
-
Properties的常见方法
1)Ioad:加载配置文件的键值对到 Properties 对象
2)lis:将数据显示到指定设备(流对象)
3)getProperty(key):根据键获取值
4)SetProperty(key,value):设置键值对到 Properties 对象
5)stor:将 Properties 中的键值对存储到配置文件,在idea中,保存信息到配置文件,如果含有中文,会存储为 unicode 码
-
Unicode编码转换 - 站长工具 unicode码查询工具
4——应用案例
-
使用 Properties 类完成对 mysql.properties 的读取
package com.fy.properties; import java.io.FileReader; import java.io.IOException; import java.util.Properties; public class Properties02 { public static void main(String[] args) throws IOException { //使用 Properties 类完成对 mysql.properties 的读取 //1. 创建 Properties 对象 Properties properties = new Properties(); //2. 加载指定配置文件 properties.load(new FileReader("src\\mysql.properties")); //3. 把 k-v 显示到控制台、 properties.list(System.out); //4. 根据键(key)获取对应的值 String user = properties.getProperty("user"); String pwd = properties.getProperty("pwd"); System.out.println("用户名:" + user); System.out.println("密码:" + pwd); } }
-
使用 Properties 类添加 key-val 到新文件 mysql2.properties 中并修改某个key-val
package com.fy.properties; import java.io.FileOutputStream; import java.io.IOException; import java.util.Properties; public class Properties03 { public static void main(String[] args) throws IOException { //使用 Properties 类来创建配置文件、修改配置文件内容 Properties properties = new Properties(); //创建 //修改配置文件时: //1. 如果该文件没有 key,就是创建 //2. 如果该文有 key,就是修改 properties.setProperty("charset", "utf-8"); properties.setProperty("user", "汤姆"); //保存注意时,是中文的 unicode 码值 properties.setProperty("pwd", "123456"); //将 K-V 存储到文件中 //properties.store(new FileOutputStream("src\\mysql2.properties"), "Hello World"); //null 注释的意思 properties.store(new FileOutputStream("src\\mysql2.properties"), null); //null 注释的意思 System.out.println("保存配置文件成功~"); } }
练习题
-
编程题 Homework01.java 5min
(1) 在判断d盘下是否有文件夹mytemp,如果没有就创建mytemp
(2) 在d:\mytemp目录下,创建文件hello.txt
(3) 如果hello.txt已经存在,提示该文件已经存在,就不要再重复创建了
(4) 并且在hello.txt文件中,写入hello,world~
package com.fy; import java.io.BufferedWriter; import java.io.File; import java.io.FileWriter; import java.io.IOException; public class Test01 { public static void main(String[] args) throws IOException { /* (1) 在判断d盘下是否有文件夹mytemp,如果没有就创建mytemp (2) 在d:\\mytemp目录下,创建文件hello.txt (3) 如果hello.txt已经存在,提示该文件已经存在,就不要再重复创建了 (4) 并且在hello.txt文件中,写入hello,world~ */ String directoryPath = "d:\\mytemp"; String filePath = directoryPath + "\\hello.txt"; String str = "hello,world~ 哈哈哈哈"; File file1 = new File(directoryPath); File file2 = new File(filePath); if (!file1.exists()) { //创建 if (file1.mkdirs()) { System.out.println("创建 " + directoryPath + " 创建成功"); }else { System.out.println("创建 " + directoryPath + " 创建失败"); } } if (file2.exists()) { System.out.println("文件 " + file2 + " 已存在,不再重复创建~"); }else { if (file2.createNewFile()) { System.out.println("文件 " + file2 + "创建成功~"); BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(file2)); bufferedWriter.write(str); bufferedWriter.close(); }else { System.out.println("文件 " + file2 + "创建失败~"); } } } }
-
编程题 Homework02.java
要求:使用BufferedReader读取一个文本文件,为每行加上行号,再连同内容一并输出到屏幕上。
package com.fy; import java.io.*; public class Test02 { public static void main(String[] args) throws IOException { /* 要求:使用BufferedReader读取一个文本文件,为每行加上行号,再连同内容一并输出到屏幕上。 */ String filePath = "d:\\mytemp\\hello.txt"; String str = null; int lineNum = 0; BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(new FileInputStream(filePath),"gbk")); //BufferedReader bufferedReader = new BufferedReader(new FileReader(filePath)); while ((str = bufferedReader.readLine()) != null) { System.out.println("第" + ++lineNum + "行:" + str); } if (bufferedReader != null) { bufferedReader.close(); } } }
-
编程题 Homework03.java
(1) 要编写一个 dog.properties
name=tom
age=5
color=red
(2) 编写Dog类(name,age,color)创建一个dog对象,读取dog.properties用相应的内容完成属性初始化,并输出
(3) 将创建的Dog对象,序列化到文件dog.dat文件
package com.fy; import org.junit.jupiter.api.Test; import java.io.*; import java.util.Properties; public class Test03 { public static void main(String[] args) throws IOException { /* (1) 要编写一个 dog.properties name=tom age=5 color=red (2) 编写Dog类(name,age,color)创建一个dog对象,读取dog.properties用相应的内容完成属性初始化,并输出 (3) 将创建的Dog对象,序列化到文件dog.dat文件 */ Properties properties = new Properties(); properties.load(new FileReader("src\\dog.properties")); String name = (String) properties.get("name"); //Object-->String int age = Integer.parseInt((String) properties.get("age")); //Object-->int String color = (String) properties.get("color"); //Object-->String Dog dog = new Dog(name, age, color); System.out.println(dog); //将创建的Dog对象,序列化到文件dog.dat文件 String serFilePath = "d:\\dog.dat"; ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(serFilePath)); oos.writeObject(dog); oos.close(); System.out.println("序列化成功~"); } //反序列化 @Test public void fy() throws IOException, ClassNotFoundException { String serFilePath = "d:\\dog.dat"; ObjectInputStream ois = new ObjectInputStream(new FileInputStream(serFilePath)); //向下转型 Dog dog = (Dog) ois.readObject(); ois.close(); System.out.println("反序列化成功~"); System.out.println(dog); } } class Dog implements Serializable{ private String name; private int age; protected String color; public Dog(String name, int age, String color) { this.name = name; this.age = age; this.color = color; } @Override public String toString() { return "Dog{" + "name='" + name + '\'' + ", age=" + age + ", color='" + color + '\'' + '}'; } }