JavaSE——IO流(下)
一、节点流与处理流
节点流:也称为低级流,直接与数据源(如文件、网络等)相连,负责数据的实际读写操作。它们是IO操作的基础。
特点:
- 直接性:节点流直接与数据源相连,因此能够直接访问和操作数据。
- 高效性:由于直接访问数据源,节点流在数据传输时通常具有较高的效率。
- 基础功能:节点流提供了数据读写的基本功能,是IO操作的基础。
处理流:也成为高级流或者包装流,在节点流的基础上进行加工或者拓展。不直接与数据源相连,而是将节点流包装起来,为节点流提供额外的功能或者优化。
特点:
- 功能扩展:处理流为节点流提供了额外的功能,如缓冲、加密、解密、压缩、解压缩等。
- 灵活性:通过使用不同的处理流,我们可以轻松地实现不同的IO功能,而无需修改底层的节点流。
- 易用性:处理流通常具有更友好的API和更简单的使用方法,使得IO操作更加容易。
二、处理流BufferedReader和BufferedWriter
2.1BufferedReader
BufferedReader bfr = new BufferedReader(new NodeReader(PathName));
bfr.readLine();
bfr.close();
readLine方法按行读取文件,当返回null时表示文件读取完毕。 最后关闭流时,只需要关闭外层流,因为底层会自动关闭节点流。
public class BufferedReader_ {
public static void main(String[] args) throws Exception {
String filePath = "D:\\news.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();
}
}
2.2BufferedWriter
new FileWriter(filePath) //表示覆盖方式写入
new FileWriter(filePath,true) //表示追加方式写入
newLine() //表示插入一个和系统有关的换行
public class BufferedWriter_ {
public static void main(String[] args) throws IOException {
String PathName = "D:\\news.txt";
BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(PathName,true));
bufferedWriter.write("hello1" + "哔哩哔哩动画");
bufferedWriter.newLine();
bufferedWriter.write("hello2" + "哔哩哔哩动画");
bufferedWriter.newLine();
bufferedWriter.write("hello3" + "哔哩哔哩动画");
bufferedWriter.close();
}
}
三、处理流BufferedInputStream和BufferedOutputStream
BufferedOutputStream是字节流,实现缓冲的输出流,可以将多个字节写入底层输出流,而不必对每个字节写入调用底层系统。
String srcFilePath = "e:\\a.java";
String destFilePath = "e:\\a3.java";
//创建BufferedInputStream对象
BufferedInputStream bis = null;
//创建BufferedOutputStream对象
BufferedOutputStream bos = null;
try {
//因为 FileInputStream 是 InputStream 子类
bis = new BufferedInputStream(new FileInputStream(srcFilePath));
bos = new BufferedOutputStream(new FileOutputStream(destFilePath));
//循环的读取文件,并写入到 destFilePath
byte[] buff = new byte[1024];
int readLen = 0;
//当返回 -1 时,就表示文件读取完毕
while ((readLen = bis.read(buff)) != -1) {
bos.write(buff, 0, readLen);
}
System.out.println("文件拷贝完毕~~~");
} catch (IOException e) {
e.printStackTrace();
} finally {
//关闭流 , 关闭外层的处理流即可,底层会去关闭节点流
try {
if(bis != null) {
bis.close();
}
if(bos != null) {
bos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
四、对象流ObjectInputStream和ObjectOutputStream
在开发中我们常常有这样的需求:保存数据的同时保存它的数据类型,比如说int 100,我们不仅要保存100这个数字,而是int 100,能够从文件中直接恢复int 100。
序列化:在保存数据时,保存数据的值和数据类型。
反序列化:恢复数据时,恢复数据的值和类型。
要让某个对象支持序列化机制,必须让它是可序列化的,必须实现以下两个接口之一:
Serializable //标记接口,没有方法
Externalizable //该接口有方法需要实现,因此一般使用Serializable
对象流提供了对基本类型或对象类型的序列化和反序列化,ObjectOutputStream提供序列化功能,ObjectInputStream体哦那个反序列化功能。
4.1ObjectInputStream
//指定反序列化的文件
String filePath = "e:\\data.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()); //旺财..
//关闭流, 关闭外层流即可,底层会关闭 FileInputStream 流
ois.close();
4.2ObjectOutputStream
//序列化后,保存的文件格式,不是存文本,而是按照他的格式来保存
String filePath = "e:\\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("数据保存完毕(序列化形式)");
4.3注意事项和细节说明
- 读写顺序要求一致。
- 要求序列化或者反序列化的对象,需要实现Serializable。
- 序列化的类中建议添加SerialVersionUID,以提高版本兼容性。
- 序列化对象时,默认将所有属性都进行序列化,但除了static或transient修饰的成员。
- 序列化对象时,要求属性的类型也实现Serializable接口。
- 序列化具备可继承性,也就是说某类已经实现了序列化,则它的所有子类也默认实现了序列化。
五、标准输入输出流
名称 | 编译类型 | 默认设备 | |
System.in | 标准输入 | InuputStream | 键盘 |
System.out | 标准输出 | OutputStream | 显示器 |
//System 类 的 public final static InputStream in = null;
// System.in 编译类型 InputStream
// System.in 运行类型 BufferedInputStream
// 表示的是标准输入 键盘
System.out.println(System.in.getClass());
//老韩解读
//1. System.out public final static PrintStream out = null;
//2. 编译类型 PrintStream
//3. 运行类型 PrintStream
//4. 表示标准输出 显示器
System.out.println(System.out.getClass());
System.out.println("hello, 哔哩哔哩动画~");
Scanner scanner = new Scanner(System.in);
System.out.println("输入内容");
String next = scanner.next();
System.out.println("next=" + next);
六、转换流InputStreamReader和OutputStreamWriter
- InputStreamReader:Reader的子类,可以将InputStream(字节流)包装成Reader(字符流)。
- OutputStreamWriter:Writer的子类,可以将OutputStream(字节流)包装成Writer(字符流)。
- 在处理纯文本数据时,如果使用字符流效率更高,并且可以有效解决中文问题,所以建议将字节流转换为字符流。
- 可以在使用时指定编码格式。
6.1InputStreamReader
String filePath = "e:\\a.txt";
//解读
//1. 把 FileInputStream 转成 InputStreamReader
//2. 指定编码 gbk
//InputStreamReader isr = new InputStreamReader(new FileInputStream(filePath), "gbk");
//3. 把 InputStreamReader 传入 BufferedReader
//BufferedReader br = new BufferedReader(isr);
//将2 和 3 合在一起
BufferedReader br = new BufferedReader(new InputStreamReader(
new FileInputStream(filePath), "gbk"));
//4. 读取
String s = br.readLine();
System.out.println("读取内容=" + s);
//5. 关闭外层流
br.close();
6.2OutputStreamWriter
String filePath = "e:\\hsp.txt";
String charSet = "utf-8";
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(filePath), charSet);
osw.write("hi, 韩顺平教育");
osw.close();
System.out.println("按照 " + charSet + " 保存文件成功~");
七、打印流PrintStream和PrintWriter
7.1PrintStream
PrintStream out = System.out;
//在默认情况下,PrintStream 输出数据的位置是 标准输出,即显示器
/*
public void print(String s) {
if (s == null) {
s = "null";
}
write(s);
}
*/
out.print("john, hello");
//因为print底层使用的是write , 所以我们可以直接调用write进行打印/输出
out.write("哔哩哔哩,你好".getBytes());
out.close();
//我们可以去修改打印流输出的位置/设备
//1. 输出修改成到 "e:\\f1.txt"
//2. "hello, 哔哩哔哩~" 就会输出到 e:\f1.txt
//3. public static void setOut(PrintStream out) {
// checkIO();
// setOut0(out); // native 方法,修改了out
// }
System.setOut(new PrintStream("e:\\f1.txt"));
System.out.println("hello,哔哩哔哩~");
7.2PrintWriter
//PrintWriter printWriter = new PrintWriter(System.out);
PrintWriter printWriter = new PrintWriter(new FileWriter("e:\\f2.txt"));
printWriter.print("hi, 北京你好~~~~");
printWriter.close();//flush + 关闭流, 才会将数据写入到文件..
八、Properties类
Properties文件是Java支持的一种配置文件类型(所谓支持是因为Java提供了properties类,来读取properties文件中的信息)。文件中以键值对 "键=值"的形式,存储工程中会多次重复使用的配置信息。随后,在需要用到这些配置信息,通过“Properties”类来读取这些信息,以实现“一次编写,多处调用;需要修改配置文件时,只修改一处即可”的效果。
不采用Properties类读取Properties文件:
//读取mysql.properties 文件,并得到ip, user 和 pwd
BufferedReader br = new BufferedReader(new FileReader("src\\mysql.properties"));
String line = "";
while ((line = br.readLine()) != null) { //循环读取
String[] split = line.split("=");
//如果我们要求指定的ip值
if("ip".equals(split[0])) {
System.out.println(split[0] + "值是: " + split[1]);
}
}
br.close();
传统方法也可以读取Properties文件,但是不方便修改、读取某一特定的键值对。
Properties类专门用于读写配置文件的集合类,它要求配置文件的格式为:键=值,键值对不需要有空格,值不需要用引号引起来,默认是String类型。
Properties类读写Properties文件:
//使用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类创建并修改某个KeyValue:
Properties properties = new Properties();
properties.setProperty("charset", "utf8");
properties.setProperty("user", "汤姆");//注意保存时,是中文的 unicode码值
properties.setProperty("pwd", "888888");
properties.setProperty("","123");
properties.setProperty("","456");//123被覆盖为456
System.out.println(properties.getProperty(""));
//将k-v 存储文件中即可
properties.store(new FileOutputStream("src\\mysql2.properties"), null);
System.out.println("保存配置文件成功~");