字符流
字符串中的编码解码问题
编码
- byte[] getBytes():使用平台的默认字符集将该String编码为一系列字节,将结果存储到新的字节数组中;
- byte[] getBytes(String charsetName):使用指定的字符集将该String编码为一系列字节,将结果存储到新的字节数组中;
解码
- String(byte[] bytes):通过使用平台的默认字符集解码指定的字节数组来构造新的String;
- String(byte[] bytes,String charsetName):通过指定的字符集解码指定的字节数组来构造新的String;
字符流写数据
| 方法名 | 说明 |
|---|---|
| void write(int c) | 写一个字符 |
| void write(char[] cbuf) | 写一个字符数组 |
| void write(char[] cbuf,int off,int len) | 写出字符数组的一部分 |
| void write(String str) | 写一个字符串 |
| void write(String str,int off,int len) | 写一个字符串的一部分 |
import java.io.FileWriter;
import java.io.IOException;
public class CharStreamTest {
public static void main(String[] args) throws IOException {
//创建字符输出流对象
//FileWriter fw = new FileWriter(new File("day21\\src\\a.txt"));
FileWriter fw = new FileWriter("day21\\src\\a.txt");
//一次写一个字符
fw.write(97);
fw.write(98);
fw.write(99);
//一次写一个字符数组
char[] arr = {97,98,99,100,101};
fw.write(arr);
//一次写一个字符数组的一部分
fw.write(arr,1,3);
//一次写一个字符串
String s = "一次写一个字符串";
fw.write(s);
//一次写一个字符串的一部分
fw.write(s,5,3);
fw.close();
}
}
注意
- 如果文件不存在,就创建,但是要保证父级路径存在;如果文件存在就清空;
- 写出int类型的整数,实际写出的是整数在码表上的对应的字母;
- 写出字符串数据,是把字符串本身原样写出;
flush和close方法
| 方法名 | 说明 |
|---|---|
| flush() | 刷新流,还可以继续写数据 |
| close() | 关闭流,释放资源,但是在关闭之前会先刷新流,一旦关闭,就不能再写数据 |
字符流读数据
import java.io.FileReader;
import java.io.IOException;
public class ReaderTest {
public static void main(String[] args) throws IOException {
//创建字符输入流对象
FileReader fr = new FileReader("day21\\src\\a.txt");
//读数据
//一次读一个字符
int ch;
while((ch = fr.read()) != -1){
System.out.println((char)ch);
}
//一次读一个字符数组
//创建一个数组
char[] chars = new char[1024];
int len;
//read方法还是读取,但是一次读取多个字符
//他把读到的字符都存入到chars数组
//返回值:表示本次读到了多少个字符
while ((len = fr.read(chars))!=-1){
System.out.println(new String(chars,0,len));
}
//释放资源
fr.close();
}
}
案例:保存键盘录入的数据
将用户键盘录入的用户名和密码保存到本地实现永久化存储;要求用户名和密码各自独占一行。
import java.io.FileWriter;
import java.io.IOException;
import java.util.Scanner;
public class Anli {
public static void main(String[] args) throws IOException {
//实现键盘录入,把用户名和密码录入进来
Scanner sc = new Scanner(System.in);
System.out.println("请录入用户名:");
String username = sc.next();
System.out.println("请录入密码:");
String password = sc.next();
//分别把用户名和密码写到本地文件
FileWriter fw = new FileWriter("day21\\src\\a.txt");
//将用户名和密码写到文件中
fw.write(username);
fw.write("\r\n");
fw.write(password);
//刷新流
fw.flush();
//释放资源
fw.close();
}
}
字符缓冲流:
- BufferedWriter:可以将数据高效的写出;
- BufferedReader:可以将数据高效的读取到内存;
构造方法:
- BufferedWriter(Writer out)
- BufferedReader(Reader in)
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
public class BufferedReaderTest {
public static void main(String[] args) throws IOException {
//字符缓冲输入流
BufferedReader br = new BufferedReader(new FileReader("day21\\src\\a.txt"));
//读取数据
char[] chars = new char[1024];
int len;
while ((len = br.read(chars)) != -1){
System.out.println(new String(chars,0,len));
}
br.close();
}
}
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
public class BufferedWriterTest {
public static void main(String[] args) throws IOException {
//字符缓冲输出流
BufferedWriter bw = new BufferedWriter(new FileWriter("day21\\src\\a.txt"));
//写数据
bw.write(97);
bw.write("\r\n");
char[] chars = {97,98,99,100,101};
bw.write(chars);
bw.write("\r\n");
bw.write(chars,1,3);
bw.write("\r\n");
String s = "字符缓冲输出流";
bw.write(s);
bw.write("\r\n");
bw.write(s,2,5);
bw.close();
}
}
字符缓冲流特有功能
BufferedWriter:
- void newLine():写一行行分隔符,行分隔符字符串由系统属性定义;
BufferedReader:
- public String readLine():读一行文字。结果包含行的内容的字符串,不包括任何行终止符,如果留的结尾已经到达,则为null;
案例:读取文件中的数据排序后再次写到本地
读取文件中的数据,排序后再次写到本地文件;
import java.io.*;
import java.util.Arrays;
public class Anli {
public static void main(String[] args) throws IOException {
//把文件中的数据读取近来
BufferedReader br =new BufferedReader(new FileReader("day21\\src\\a.txt"));
//输出流一定不能写在这里,因为会清空文件里的数据
String s = br.readLine();
System.out.println("读到的数据为:"+s);
br.close();
//将读到的数据进行切割
String[] arr = s.split(" ");
//把字符串数组转换为int数组
int[] arr1 = new int[arr.length];
//遍历数组进行类型转换
for (int i = 0; i < arr.length; i++) {
arr1[i] = Integer.parseInt(arr[i]);
}
//排序
Arrays.sort(arr1);
System.out.println(Arrays.toString(arr1));
//把排序后的结果写回到本地
BufferedWriter bw = new BufferedWriter(new FileWriter("day21\\src\\a.txt",true));
bw.write("\r\n");
for (int i = 0; i < arr1.length; i++) {
bw.write(arr1[i]+" ");
//bw.flush();
}
bw.close();
}
}
其它流
转换流
- 输入流:InputStreamReader
- 输出流:OutputStreamWriter

对象操作流
可以把对象以字节的形式写到本地文件,直接打开文件,是读不懂的,需要再次用对象操作流读到内存中。
- 对象操作输入流:ObjectInputStream
- 对象操作输出流:ObjectOutputStream
对象操作流分为两类:对象操作输入流和对象操作输出流
对象操作输出流(对象序列化流):就是将对象写到本地文件中,或者在网络中传输对象
对象操作输入流(对象反序列化流):把写到本地文件中的对象读到内存中,或者接收网络中传输的对象
对象序列化流:
import java.io.Serializable;
//如果想要这个类的对象能被序列化,那么这个类必须要实现一个接口Serializable
//Serializable接口的意义
//称之为是一个标记性接口,里面没有任何的抽象方法
//只要一个类实现了这个Serializable接口,那么就表示这个类的对象可以被序列化
public class User implements Serializable {
private String username;
private String password;
public User() {
}
public User(String username, String password) {
this.username = username;
this.password = password;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "User{" +
"username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
}
测试类:
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
public class ObjectStreamTest {
public static void main(String[] args) throws IOException {
User user = new User("xiaohua","123456");
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("day21\\src\\a.txt"));
oos.writeObject(user);
oos.close();
}
}
对象反序列化流
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
public class ObjectStreamTest1 {
public static void main(String[] args) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("day21\\src\\a.txt"));
User user = (User) ois.readObject();
System.out.println(user);
ois.close();
}
}
案例:用对象操作流读写多个对象
创建多个Javabean类对象写到文件中,再次读取到内存;
import java.io.Serializable;
public class Student implements Serializable {
private static final long serialVersionUID = 1L;
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.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;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
import java.io.*;
import java.util.ArrayList;
public class StudentTest {
public static void main(String[] args) throws IOException, ClassNotFoundException {
Student st1 = new Student("小花",15);
Student st2 = new Student("小澜",17);
Student st3 = new Student("小雅",16);
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("day21\\src\\a.txt"));
oos.writeObject(st1);
oos.writeObject(st2);
oos.writeObject(st3);
//可以将对象存入集合,再将集合写到本地
// ArrayList<Student> list = new ArrayList<>();
// list.add(st1);
// list.add(st2);
// list.add(st3);
// oos.writeObject(list);
oos.close();
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("day21\\src\\a.txt"));
while (true){
try {
Object o = ois.readObject();
System.out.println(o);
} catch (EOFException e) {
break;
}
}
//读取集合
// ArrayList<Student> list1 = (ArrayList<Student>) ois.readObject();
// for (Student student : list1) {
// System.out.println(student);
// }
ois.close();
}
}
Properties
概述:
- 是一个Map体系的集合类
- Properties中有跟IO相关的方法
- 只存字符串
import java.util.Map;
import java.util.Properties;
import java.util.Set;
public class PropertiesTest {
public static void main(String[] args) {
Properties prop = new Properties();
//增
prop.put("河南","郑州");
prop.put("河北","石家庄");
prop.put("山西","太原");
prop.put("山东","济南");
System.out.println(prop);
//删
prop.remove("山东");
System.out.println(prop);
//改
//put 如果键不存在,那么就添加,如果键存在,那么就覆盖
prop.put("河南","豫州");
System.out.println(prop);
//查
Object value = prop.get("河北");
System.out.println(value);
//遍历
Set<Object> keys = prop.keySet();
for (Object key : keys) {
Object value1 = prop.get(key);
System.out.println(key+"="+value1);
}
System.out.println("------------");
Set<Map.Entry<Object, Object>> entries = prop.entrySet();
for (Map.Entry<Object, Object> entry : entries) {
System.out.println(entry.getKey()+"="+entry.getValue());
}
}
}
Properties作为集合的特有方法
| 方法名 | 说明 |
|---|---|
| Object setProperty(String key,String value) | 设置集合的键和值,都是String类型,底层调用Hashtable方法put |
| String getProperty(String key) | 使用此列表中指定的键搜索属性 |
| Set stringPropertyName() | 从该属性列表中返回一个可修改的健集,其中键及其对应的值是字符串 |
示例:
import java.util.Properties;
import java.util.Set;
public class PropertiesTest2 {
public static void main(String[] args) {
Properties prop = new Properties();
prop.setProperty("河南","郑州");
prop.setProperty("陕西","西安");
prop.setProperty("海南","海口");
prop.setProperty("台湾","台北");
System.out.println(prop);
String value = prop.getProperty("海南");
System.out.println(value);
Set<String> keys = prop.stringPropertyNames();
for (String key : keys) {
String value1 = prop.getProperty(key);
System.out.println(key+"="+value1);
}
}
}
Properties和IO流结合的方法:
| 方法名 | 说明 |
|---|---|
| void load(InputStream inStream) | 从输入字节流读取属性列表(键和元素对) |
| void load(Reader reader) | 从输入字符流读取属性列表(键和元素对) |
| void store(OutputStream out,String comments) | 将此属性列表(键和元素对)写入此Properties表中,以适合于使用load(InoutStream)方法的格式写入输出字节流 |
| void store(Writer writer,String comments) | 将此属性列表(键和元素对)写入此Properties表中,以适合使用load(Reader)方法的格式写入输出字符流 |
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Properties;
public class PropertiesTest3 {
public static void main(String[] args) throws IOException {
//读取
Properties prop = new Properties();
FileReader fr = new FileReader("day21\\prop.properties");
//调用完了load方法之后,文件中的键值对数据已经在集合中了
prop.load(fr);
fr.close();
System.out.println(prop);
prop.put("sansan","123");
prop.put("sisi","456");
prop.put("wuwu","789");
FileWriter fw = new FileWriter("day21\\prop.properties");
prop.store(fw,null);
fw.close();
}
}
练习1
请使用“便捷字符流”配合数组将指定文件(笔者用的是“大地之灯.txt”)的内容打印到控制台,并统计出读文本共使用的时间;
public class Test1 {
public static void main(String[] args) throws IOException {
//获取当前时间
long t1 = System.currentTimeMillis();
//创建字符输入流对象
FileReader fr = new FileReader("day21\\src\\大地之灯.txt", Charset.forName("gbk"));
char[] arr = new char[1024];
int len = 0;
//read方法还是读取,但是一次读取多个字符
//他把读到的字符都存入到arr数组
//返回值:表示本次读到了多少个字符
while ((len=fr.read(arr))!=-1){
String s = new String(arr,0,len);
System.out.print(s);
}
fr.close();
long t2 = System.currentTimeMillis();
System.out.println();
System.out.println(">>>>>>>>>>>>>>>>>>>>");
System.out.println("读取文件共使用了:"+(t2-t1)+"毫秒");
}
}
结果:

练习2
将下列文件中的学生信息按照年龄升序排序后重新写回原文件;要求:使用字符缓冲流完成;每次循环读取一行信息;写数据的时候换行使用newLine方法。
TXT文件内容:
凯亚,18
白山,19
可莉,17
阿七,22
public class Test2 {
public static void main(String[] args) throws IOException {
//创建文件对象和字符缓冲流对象,准备读数据
File f = new File("day21\\src\\1.txt");
BufferedReader br = new BufferedReader(new FileReader(f));
//准备TreeSet集合,用于保存读到的学生对象并排序
TreeSet<Student> set = new TreeSet<Student>((s1,s2)->s1.getAge()-s2.getAge()==0?s1.getName().compareTo(s2.getName()):s1.getAge()-s2.getAge());
String s = null;
while ((s=br.readLine())!=null){
String[] split1 = s.split(",");
//把切分出来的数组的第一个值作为name,第二个值作为age,创建学生对象,并按照年龄排序
Student stu = new Student(split1[0],Integer.parseInt(split1[1]));
set.add(stu);
}
//创建输出流,并把set集合中的学生信息重新写入1.txt文件中;
BufferedWriter bw = new BufferedWriter(new FileWriter("day21\\src\\1.txt"));
for(Student stu : set) {
bw.write(stu.toString());
bw.newLine();//换行,不用考虑系统问题,比写\r\n更通用
}
br.close();
bw.close();
System.out.println("排序已完成");
}
}
public class Student {
private String name;
private int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return name + "," + age;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
if (age != student.age) return false;
return name != null ? name.equals(student.name) : student.name == null;
}
@Override
public int hashCode() {
int result = name != null ? name.hashCode() : 0;
result = 31 * result + age;
return result;
}
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;
}
}










