IO流
1、文件
(1)文件概念
文件就是保存数据的地方。例如word文档,txt文件,execl文件等等。
(2)文件流
文件在程序中是以流的形式来操作的。
- 流:数据在数据源(文件)和程序(内存)之间经历的路径
- 输入流:数据从数据源(文件)到程序(内存)的路径
- 输出流:数据从程序(内存)到数据源(文件)的路径
2、常用的文件操作
(1)创建文件构造器的相关方法
new File(String pathname) //根据路径构建一个File对象
new File(File parent, String child) //根据父目录文件+子路径构建
new File(String parent, String child) //根据父目录+子路径构建
createNewFile 创建新文件
举例:
方式一:
//方式1 new File(String pathname)
public void create01() throws IOException {//两个斜杠是防止一个变成转义字符String filePath = "d:\\news1.txt";File file = new File(filePath); //这里file只是一个对象,并没有创建文件file.createNewFile(); //执行了createNewFile才会真正的,在磁盘创建文件System.out.println("创建文件成功!");
}
方式二:
//方式2 new File(File parent, String child) 根据父目录文件+子路径构建
public void create02() throws IOException {//两个斜杠是防止一个变成转义字符File parentFile = new File("d:\\");String fileName = "news2.txt";File file = new File(parentFile, fileName);file.createNewFile();System.out.println("创建文件成功!");
}
方式三:
//方式2 new File(File parent, String child) 根据父目录文件+子路径构建
public void create02() throws IOException {//两个斜杠是防止一个变成转义字符File parentFile = new File("d:\\");String fileName = "news2.txt";File file = new File(parentFile, fileName);file.createNewFile();System.out.println("创建文件成功!");
}
//IO流的四个抽象类:InputStream, OutputStream, Reader, Writer
(2)获取文件的相关信息
getName,getAbsolutePath,getParent,length,exists,isFile,isDirectory
举例:
public void info(){//先创建文件对象File file = new File("d:\\new1.txt");//调用对应的方法,获取相关信息System.out.println("文件名字="+file.getName());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());
}
(3) 目录的操作和文件删除
mkdir //创建一级目录
mkdirs //创建多级目录
delete //删除空目录或文件
举例:
public void directoryMethod() throws IOException {String fileName1 = "d:\\new1.txt"; //删除文件File file = new File(fileName1);if(file.exists()){file.delete();System.out.println("文件已经删除"+(file.exists() == false));}String fileName2 = "d:\\demo02"; //删除空目录File file2 = new File(fileName2);if(file2.exists()){file2.delete();System.out.println("文件已经删除"+(file2.exists() == false));}else{System.out.println("文件不存在");}String fileName3 = "d:\\demo\\a\\b\\c"; //创建多级目录File file3 = new File(fileName3);if(file3.exists()){System.out.println("文件存在");}else{System.out.println("文件不存在,创建该文件");file3.mkdirs();}
}
3、IO流原理以及流的分类
(1)Java IO流原理
- I/O 是Input/Output 的缩写,I/O 技术是非常实用的技术,用于处理传输数据。如读/x写文件,网络通讯等。
- Java程序中,对于数据的输入/输出以“流(stream)“的方式进行
- Java.io包提供了各种“流”类和接口,用于获取不同种类的数据,并通过方法输入或输出数据
- 输入input:读取外部数据(磁盘、光盘等存储设备的数据)到程序(内存)中
- 输出output:将程序(内存)数据输出到磁盘、光盘等存储设备中
(2)分类方法
按操作数据单位分为: | 字节流(8 bit)二进制文件 | 字符流(按字符)文本文件 |
---|---|---|
按数据流流向分为: | 输入流 | 输出流 |
按流的角色分为: | 节点流 | 处理流/包装流 |
四个抽象类:(IO流的40多个流都是由这个抽象基类派生的)
(抽象基类) | 字节流 | 字符流 |
---|---|---|
输入流 | InputStream | Reader |
输出流 | OutputStream | Writer |
4、IO流体系图
(1)字节流
InputStream
OutputStream
(2)字符流
Reader:
Writer:
5、IO流类各类实践
(1)FileInputStream
函数接口:(只列举常用方法)
FileInputStream(File file) // 构造函数1:创建“File对象”对应的“文件输入流”
FileInputStream(String path) // 构造函数3:创建“文件(路径为path)”对应的“文件输入流”void close() // 关闭“文件输入流”
int read() // 返回“文件输入流”的下一个字节
int read(byte[] b) // 从该输入流读取最多 b.length个字节的数据为字节数组。
方法举例:
public void readFile01(){//读取一个字节String filePath = "d:\\test.txt";int readData = 0;FileInputStream fileInputStream = null;try {fileInputStream = new FileInputStream(filePath);//read,读取一个字节,直到-1结束while((readData = fileInputStream.read()) != -1){System.out.print((char) readData);}} catch (IOException e) {e.printStackTrace();}finally {//关闭文件流,释放资源,文件流是一种资源,需要主动关闭try {fileInputStream.close();} catch (IOException e) {e.printStackTrace();}}
}public void readFile02(){//读取多个字节String filePath = "d:\\test.txt";byte readData[] = new byte[16]; //一次读取16个字节int readLen = 0;FileInputStream fileInputStream = null;try {fileInputStream = new FileInputStream(filePath);//read(byte),返回实际读取的字符数while(((readLen = fileInputStream.read(readData)) != -1)){System.out.println(new String(readData, 0, readLen));}} catch (IOException e) {e.printStackTrace();}finally {//关闭文件流,释放资源,文件流是一种资源,需要主动关闭try {fileInputStream.close();} catch (IOException e) {e.printStackTrace();}}
}
(2)FileOutputStream
功能:将数据写入到文件中,如果文件不存在,就会创建这个文件。
方法举例:
public void FileOutput01() throws IOException {//如果文件不存在,就会创建这个文件String filePath = "d:\\test.txt";FileOutputStream fileOutputStream = null;fileOutputStream = new FileOutputStream(filePath); //写入内容会覆盖原来的内容
// fileOutputStream = new FileOutputStream(filePath,true); //写入内容会增加到当前文件的末尾fileOutputStream.write('s'); //写一个字节String str = "hello, you are so beautiful";//str.getBytes() 可以将字符串转为字符数组fileOutputStream.write(str.getBytes());fileOutputStream.write(str.getBytes(), 0,3); //off为偏移量,3为要写入的长度fileOutputStream.close(); //关闭文件流}
(3)FileReader
方法介绍:
- new FileReader(File/String)
- read() 每次读取单个字符,返回该字符,如果读到文件末尾返回-1
- read(char[]) 批量读取多个字符到数组中能够,返回读取到字符数量,如果读到文件末尾返回-1
方法举例:
public void FileRead01() throws IOException{String filePath = "d:\\test.txt";FileReader fileReader = new FileReader(filePath);int data; //读取单个字符
// while((data = fileReader.read()) != -1){
// System.out.print((char)data);
// }char read[] = new char[10]; //每次读取10个字符,注释是因为上次的流已经到文件末尾了,所以要么重新更新流的指向,要么注释掉,这里选择注释int readLen = 0;while((readLen = fileReader.read(read) )!= -1){System.out.println(new String(read));}fileReader.close();}
(4)FileWriter
方法介绍:
- new FileReader(File/String) 覆盖模式,覆盖掉当前文件
- new FileReader(File/String,true) 追加模式,流的指针在末尾
- write() 写入单个字符
- write(char[]),写入指定数组
- write(char[], off, len) 写入指定数组的某个部分
- write(string) 写入整个字符串
- write(string, off, len) 写入字符串的指定部分
方法举例:
public void fileWrite01() throws IOException{String dest = "风雨之后,定见彩虹";String filePath = "d:\\test.txt";FileWriter fileWriter = new FileWriter(filePath);fileWriter.write(dest); //写入字符串fileWriter.write(dest.toCharArray()); //转换为字符数组写入fileWriter.write(dest.toCharArray(),0,4); //从0开始写入字符fileWriter.close(); //close 等价于 flush() + 关闭,执行close的时候底层才执行写文件的操作}
5、节点流和处理流
(1)介绍
- 节点流可以从一个特定的数据源读写数据,比如FilReader,FileWriter等
- 处理流(包装流)是“连接”在已存在的流(节点流或处理流)之上,为程序提供更强大的读写功能,更加灵活。比如:BufferedReader,BufferedWriter,创建对象时,需要传入一个节点流进行实现
节点流与处理流分类:
节点流与处理流的区别与联系:
- 节点流是底层流/低级流,直接跟数据源相接
- 处理流(包装流)包装节点流,既可以消除不同节点之间的实现差异,也可以提供更方便的方法来完成输入输出
- 处理流(包装流)对节点流进行包装,使用了修饰器设计模式,不会直接与数据源相接。
处理流的功能主要体现在以下两个方面:
- 性能的提高,主要增加缓冲的方式来提高输入输出的效率
- 操作的便捷:处理流可能提供了一系列的方法来一次输入输出大量的数据,使用更急灵活方便
(2)处理流-BufferedReader和BufferedWriter
BufferedReader和BufferedWriter属于字符流,是按照字符来读取数据的
会创建一个内部缓冲区的数组,可以直接将多个字节写入底层输出流当中,不必对每次字节写入调用底层系统,提高了效率。
关闭时处理流,只需要关闭外层流即可。
举例:
//BufferedReader和BufferedWriter是安装字符操作,不能操作二进制文件[声音、视频、doc,pdf等等],可能造成文件损坏
public void ReaderAndWriter() throws IOException{String filePath = "d:\\FileReadStream01.java";//创建bufferedReader,里面要传一个与源文件直接相关的节点流BufferedReader br = new BufferedReader(new FileReader(filePath));String line; //可以直接按行读取,效率较高while((line = br.readLine()) != null){ //readLine不会读取换行符System.out.println(line);}//关闭流只需要关闭外层流即可,底层会自动关闭节点流br.close();String writePath = "d:\\test.txt";BufferedWriter bw = new BufferedWriter(new FileWriter(writePath));bw.write("hello, how are you");bw.newLine(); //换行bw.write("hello2,how old are you");bw.close(); //底层自动关闭FileWriter
}
(3)处理流-BufferedInputStream和BufferedOutputStream
BufferedInputStream和BufferedOutputStream属于字节流,是按照字节来读取数据的,可以读取二进制文件,比如图像,声音,视频等
关闭时处理流,只需要关闭外层流即可。
举例:
public void BufferedInputAndOutput() throws IOException{String src1 = "d:\\bp.jpg";String src2 = "d:\\FileReadStream01.java";String dest1 = "d:\\bp1.jpg";String dest2 = "d:\\FileReadStream02.java";//创建对象,因为 FileInputStream是InputStream的子类,所以可以传FileInputStreamBufferedInputStream bis = new BufferedInputStream(new FileInputStream(src1));BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(dest1));byte buf[] = new byte[1024];int readLen = 0;while((readLen = bis.read(buf)) != -1){
// bos.write(buf, 0, readLen);bos.write(buf);}bis.close();bos.close();}
(4)对象流-ObjectInputStream和ObjectOutputStream
对象流在进行输入输出的过程中,可以保存数据的值和数据类型
序列化和反序列化:
- 序列化就是在保存数据时,保存数据的值和数据类型
- 反序列化就是在恢复数据时,恢复数据的值和数据类型
- 需要让某个对象支持序列化机制,则必须让其类是可以序列化的,为了让某个类是可序列化的,该类必须实现两个接口
- Serializable //这是一个标记接口,没有方法,不需要实现- Externalizable //该接口有方法需要实现,**因为我们一般实现上面的 Serializable接口**
ObjectOutputStream 提供序列化功能
ObjectInputStream 提供反序列化功能
举例:
public class ObjectInputAndOutputStream {public static void main(String[] args) throws IOException, ClassNotFoundException {//序列化后,保存的文件格式,不是存文本,而是按照他的格式来保存//序列化String filePath = "d:\\data.dat";ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(filePath));//序列化数据到 e:\data.datoos.writeInt(100);// int -> Integer (实现了 Serializable)oos.writeBoolean(true);// boolean -> Boolean (实现了 Serializable)oos.writeChar('a');// char -> Character (实现了 Serializable)oos.writeDouble(9.5);// double -> Double (实现了 Serializable)oos.writeUTF("韩顺平教育");//String//保存一个dog 对象oos.writeObject(new Dog("旺财", 10));oos.close();System.out.println("数据保存完毕(序列化形式)");//反序列化// 1.创建流对象ObjectInputStream ois = new ObjectInputStream(new FileInputStream("d:\\data.dat"));// 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());System.out.println((Dog)ois.readObject());// 3.关闭ois.close();System.out.println("以反序列化的方式读取(恢复)ok~");}
}class Dog implements Serializable{ //必须实现序列化接口才能调用Object对象流String name;int age;public Dog(String name, int age) {this.name = name;this.age = age;}@Overridepublic String toString() {return "Dog{" +"name='" + name + '\'' +", age=" + age +'}';}
}
注意事项和细节说明
- 读写顺序要一致
- 要求序列化或反序列化对象,需要实现Serializable接口
- 序列化的类建议添加SerialVersionUID,可以提高版本的兼容性
- 序列化对象中,默认将里面所有属性都进行序列化,但除了static或transient修饰的成员
- 序列化对象中,要求里面属性的类型也需要实现序列化接口
- 序列化具备可继承性,如果某个类已经实现了序列化,则它的子类也默认实现了序列化
(5)标准输入输出流
类型 | 默认设备 | |
---|---|---|
System.in 标准输入 | InputStream | 键盘 |
System.out 标准输入 | OutputStream | 显示器 |
案例:
传统的Scanner是从标准输入键盘中获取数据的
public static void main(String[] args) {// System.in 类为: public final static InputStream in = null;//编译类型:InputStream//运行类型:BufferedInputStream//表示标准输入:键盘System.out.println(System.in.getClass());// System.in 类为: public final static PrintStream out = null;//编译类型:PrintStream//运行类型:PrintStream//表示标准输出:显示屏System.out.println(System.out.getClass());Scanner scanner = new Scanner(System.in); //传入的是BufferedInputStream
}
(6)转换流InputStreamReader和OutputStreamWriter
介绍:
- InputStreamReader:是Reader的子类,可以将InputStream(字节流)包装成(转换成)Reader(字符流)
- OutputStreamWriter:是Writer的子类,可以将OutputStream(字节流)包装成Writer(字符流)
- 当处理纯文本数据时,如果使用字符流的效率更高,并且可以有效解决中文问题,建议将字节流转换成字符流
- 可以在使用时指定编码格式(比如:UTF-8,gbk,gb2312,ISO8859-1等)
举例:
public static void main(String[] args) throws IOException {//InputStreamReader,可以将字节流转换为字符流,然后还可以指定编码,比较方便String filePath = "d:\\test.txt";//1、把FileInputStream转换为InputStreamReaderInputStreamReader isr = new InputStreamReader(new FileInputStream(filePath), "gbk");//2、把InputStreamReader 传入 BufferedReaderBufferedReader br = new BufferedReader(isr);//3、读取String s = br.readLine();System.out.println("读取内容:"+s);br.close();//OutputStreamWriter,写文件,可以指定编码方式//1、创建流对象OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(filePath), "gbk");//2、写入osw.write("hello,你好吗!");//3、关闭osw.close();
}
(7)打印流 PrintStream和PrintWriter
打印流只有输出流,没有输入流
举例:
public static void main(String[] args) throws IOException {PrintStream out = System.out;//默认情况下,输出数据的位置是标准输出,就是显示器out.println("hack,hello");//因为print 底层使用的是write , 所以我们可以直接调用write 进行打印/输出out.write("你好".getBytes());out.close();//我们可以去修改打印流输出的位置/设备System.setOut(new PrintStream("d:\\text1.txt"));System.out.println("hello, 你好哇");System.out.close();PrintWriter pw = new PrintWriter(System.out); //这里是在显示屏中输出pw.print("hi,这里是PrintWriter");pw.close();PrintWriter pw1 = new PrintWriter(new FileWriter("d:\\text1.txt")); //这里是在文件中输出pw1.println("hi,这里是PrintWriter");pw1.close();
}
6、PROPERTIES 类
(1)基本介绍
- 专门用于读写配置文件的集合类
- 配置文件的格式为:
- 键=值
- 键与值不需要空格,值不需要引号一起来。默认类型是String
(2)常见方法
- load:加载配置文件的键值对到Properties对象
- list:将数据显示到指定的设备
- getProperty(key):根据键获取值
- setProperty(key,value):设置键值对到Properties对象中
- store:将Properties中的键值对存储到配置文件,在idea中,保存信息到配置文件,如果含有中文,会存储为unicode编码
举例:
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 类来创建 配置文件, 修改配置文件内容//创建//1.如果该文件没有key 就是创建//2.如果该文件有key ,就是修改properties.setProperty("charset", "utf8");properties.setProperty("user", "汤姆");//注意保存时,是中文的 unicode 码值properties.setProperty("pwd", "888888");//将k-v 存储文件中即可properties.store(new FileOutputStream("src\\mysql2.properties"), null);System.out.println("保存配置文件成功~");
}