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的子类,所以可以传FileInputStream
BufferedInputStream 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.dat
oos.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;
}
@Override
public 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转换为InputStreamReader
InputStreamReader isr = new InputStreamReader(new FileInputStream(filePath), "gbk");
//2、把InputStreamReader 传入 BufferedReader
BufferedReader 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("保存配置文件成功~");
}