字符流
为什么会出现字符流
字符流的介绍
由于字节流操作中文不是特别方便,所以java提供字符流
字符流=字节流+编码表
中文的字节存储方式
用字节流复制文本是,文本文件会有中文,汉子在存储时候,无论选择那种编码存储,第一个字节都是负数。
编码表
什么是字符集
-
是一个系统支持的所有字符的集合,包括各国文字,标点符号,图形符号,数字等
计算机要准确的存储和识别各种自己符号,就需要进行字符编码,一套字符集必然至少有一套字符编码,常见字符集有ASCll字符集,GBXXX字符集,Unicode字符集等
常见的字符集
-
ASCll字符集
I是基于拉丁字母的一套电脑编码系统,用于显示现代英语,主要包括控制字符(回车键,推个,换行键等)和可现实字符(英文大小写字符,阿拉伯数字和西文符号)
基本的ASCII字符集,使用7位表示一个字符,其扩展字符集使用8位表示一个字符,共256个字符,方便支持欧洲常用字符,是一个系统支持的所有字符的集合,包括个国家文字,标点符号,图形符号,数字等。
-
GBXXX字符集:
GBK:最常用的中文码表。是在GB2312基础上的扩展规范,使用了双字节流编码方案,共收录了21001个汉字,完全兼容GB312标准,同时支持繁体汉字以及日韩文字等。
-
Unicode字符集
UTF-8编码:可以用来表示UniCode标准中任意字符,他是电子邮件,网页及其他存储或传送文字应用中,优先采用的编码。互联网工程工作小组(IETF)要求所有的互联网协议都必须支持UTF-8编码,他是用一至4个字节为每个字符编码
编码规则:
128个US-ASCLL字符,只需要一个字节编码
拉丁文等字符,需要两个字节编码
大部分常用字(含中文),使用三个字节编码
其他极少使用的UniCode辅助字符,使用四个字节编码
字符串中俄编码解码问题
方法名 | 说明 |
---|---|
byte[] getByte() | 使用平台的默认字符集将该String编码为一系列字节 |
byte[] getBytes(String charsetName) | 使用指定的字符集将String编码为一系列字节 |
String(byte[] bytes) | 使用平台的默认字符集解码指定的字节数组来创建字符集 |
String(byte[] bytes, String charsetName) | 通过指定的爱腹肌解码指定的字节数组来创建字符串 |
代码演示
public static void main(String[] args) throws UnsupportedEncodingException {
String s="菲菲";
byte [] bys=s.getBytes();
byte [] by=s.getBytes("UTF-8");
byte [] bytes=s.getBytes("GBK");
System.out.println(Arrays.toString(bys));
System.out.println(Arrays.toString(by));
System.out.println(Arrays.toString(bytes));
String ss=new String(bys);
String ss1=new String(by,"UTF-8");
String ss2=new String(bytes,"GBK");
System.out.println(ss);
System.out.println(ss1);
System.out.println(ss2);
}
字符流写数据
-
介绍
Writer:用于鞋服字符流的抽象父类
FileWriter:用于写入字符流的常用子类
-
构造方法
方法名 说明 FileWriter(File file) 根据给定的File对象构造一个FileWriter对象 FileWriter(File file,boolean append) 根据给定的File对象构造一个FileWriter对象 FileWriter(String fileName) 根据给定的文件名构造一个FileWriter对象 FileWriter(String fileName,boolean append) 根据给定的文件名以及指示是否附加写入数据的Boolean值来构造FIleWriter对象 -
成员方法
方法名 说明 void write() 写一个字符 void write(char[] cbuf) 写入一个字符数组 void write(char cbuf,int off,int len) 写入字符数组的一部分 void write(String str) 写一个字符串 void write(String str,int off,int len) 写一个字符串的一部分 -
刷新和关闭的方法
方法名 说明 flush() 刷新流,之后还可以继续写数据 close() 关闭流,释放资源,丹斯在关闭之后会刷新流,一旦关闭,就不能再写数据 -
代码演示
public static void main(String[] args) throws IOException { FileWriter fw=new FileWriter("E:\\Java\\IO\\practice\\java.txt"); //写一个字符 //fw.write(98); //写入一个字符串数组 char [] chars={'I','c','h','L','i','e','b','e','D','i','s','h'}; fw.write(chars); //写入一个字符串 fw.write("Minaracastan Sinua"); fw.close(); }
字符流读数据
-
介绍
Reader:用于读取字符流的抽象父类
FileReader:用于读取字符流的常用子类
-
构造方法
方法名 说明 FileReader(File file) 在给定从中读取数据的File的情况下创建一个新的FileReader FileReader(String fileName) 在给定从中读取数据的文件名的情况下创建一个新的FileReader -
成员方法
方法名 说明 int read() 一次读取一个字符数据 int read(char[] cbuf) 一次读取一个字符数组数据 -
代码演示
public static void main(String[] args) throws IOException { FileReader fr=new FileReader("E:\\Java\\IO\\practice\\java.txt"); //一次读取一个数据 int ch; while((ch=fr.read())!=-1){ System.out.println((char)ch); } //一次读取一个字符数组 char [] chars=new char[1024]; int len; while((len=fr.read())!=-1){ System.out.println(new String(chars,0,len)); } fr.close(); }
字符流用户注册案例
-
案例需求
- 将键盘录入的用户名和密码保存到本地实现永久存储
-
实现步骤
- 获取用户输入的用户名和密码
- 将用户输入的用户名和密码写入到本地的文件中
- 关流释放资源
-
代码实现
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("E:\\Java\\IO\\practice\\java1.txt"); //写入信息 fw.write(username); fw.write("\r\n"); fw.write(password); //资源刷新 fw.flush(); //关闭流 fw.close(); }
-
字符缓冲流
-
字符缓冲流介绍
- BufferedWriter:将文本写入字符输出流,缓冲字符,以提供单个字符,数组和字符串的高效写入,可以指定缓冲区的大小,或者可以接受默认大小,默认值足够大,可用于大多数用途
- BufferedReader:从祖父输入流读取文本,缓冲字符,一提供字符,数组和行的高效读取,可以指定缓冲区的大小,默认值足够大,可用于大多数用途
-
构造方法
方法名 说明 BufferedWriter(Writer out) 创建字符缓冲输出流对象 BufferedReader(Reader in) 创建字符缓冲输入流对象 -
代码演示
public static void main(String[] args) throws IOException { BufferedWriter bw=new BufferedWriter(new FileWriter("E:\\Java\\IO\\practice\\java.txt")); bw.write("Ik hou van jou\r\n"); bw.write("Minarastan Sinua"); bw.close(); //读取文件 BufferedReader br=new BufferedReader( new FileReader("E:\\Java\\IO\\practice\\java.txt")); //按字符数组读取 char [] chars=new char[1024]; int len; while((len=br.read())!=-1){ System.out.print(new String(chars,0,len)); } br.close(); }
字符缓冲流特有功能
-
方法介绍
BufferedWriter:
方法名 说明 void newLine() 写一行行分隔符,行分隔字符串有系统属性定义 BufferedReader:
方法名 说明 String readLine() 读一行文字,结果包含行的内容的字符串,不包括任何终止字符如果流的结尾已经到达,则为null -
代码演示
public static void main(String[] args) throws IOException {
//创建字符缓冲流
BufferedWriter bw=new BufferedWriter(
new FileWriter("E:\\Java\\IO\\practice\\java.txt"));
//写数据
for (int i = 0; i < 10; i++) {
bw.write("Ich liebe Dish" +i);
bw.newLine();
bw.flush();
}
//释放资源
bw.close();
//创建字符缓冲输入流
BufferedReader br=new BufferedReader(
new FileReader("E:\\Java\\IO\\practice\\java.txt")
);
String line;
while ((line=br.readLine())!=null){
System.out.println(line);
}
//释放资源
br.close();
}
字符缓冲刘操作文件中数据排序案例
-
案例需求
- 使用字符缓冲流读取文件中的数据,排序后再次写到本地文件
-
实现步骤
- 将文件中的数据读取到程序中
- 对读取到的数据进行处理
- 将处理后的数据添加到集合中
- 对集合中的数据进行排序
- 将排序后的集合中的数据写入到文件中
-
代码实现
public static void main(String[] args) throws IOException { //读取文件 BufferedReader br=new BufferedReader( new FileReader("E:\\Java\\IO\\practice\\java.txt") ); String line=br.readLine(); System.out.println("读取到的数据为:"+line); br.close(); //按照空格进行切割 String [] split=line.split(" "); //把字符串类型的数据变成int类型 int [] arr=new int[split.length]; //遍历split数组,可以进行类型转换 for (int i = 0; i < split.length; i++) { String smallStr=split[i]; int number=Integer.parseInt(smallStr); arr[i]=number; } //排序 Arrays.sort(arr); System.out.println(Arrays.toString(arr)); //排序过后写入本地 BufferedWriter bw=new BufferedWriter( new FileWriter("E:\\Java\\IO\\practice\\java.txt")); for (int i = 0; i < arr.length; i++) { bw.write(arr[i]+" "); bw.flush(); } bw.close(); }
转换流
字符流中的编码解码问题两个相关的两个类
-
InputStreamReader:是从字节流到字符流的桥梁,父类是Reader,它读取字节,并使用指定的编码将其解码为字符
它使用的字符集可以由名称指定,也可以被明确指定,或者可以接受平台的默认字符集
-
OutputStreamWriter:是从字符流到字节流的桥梁,使用指定的编码将写入的字符编码为字节
他使用的字符集可以由名称指定,也可以被明确指定,或者可以接受平台的默认字符集
转换流写数据
构造方法
方法名 | 说明 |
---|---|
InputStreamReader(InputStream in) | 使用默认字符编码创建InputStreamReader对象 |
InputStreamReader(InputStream in String chaset) | 使用指定的字符编码创建InputStreamReader对象 |
OutputStreamWriter(OutputStream out) | 使用默认字符编码创建OutputStreamWriter对象 |
OutputStreamWriter(OutputStream out,String chartset) | 使用指定的字符编码创建OutputStreamWriter对象 |
代码演示
public static void main(String[] args) throws IOException {
OutputStreamWriter osw=new OutputStreamWriter(
new FileOutputStream("E:\\Java\\IO\\practice\\sinua.txt"));
osw.write("Miluji te");
osw.close();
InputStreamReader isr=new InputStreamReader(
new FileInputStream("E:\\Java\\IO\\practice\\sinua.txt"),"GBK");
char [] chars=new char[1024];
int length;
while((length=isr.read())!=-1){
System.out.println(new String(chars,0,length));
}
isr.close();
}
对象操作流
对象序列化流
-
对象序列化介绍
- 对象序列化:将对象保存到磁盘中,或者在网络传输对象
- 这种机智就是使用一个字节序列表示一个对象,该字节序列包含:对象的类型,对象的数据和对象中存储的属性等信息
- 字节序列写到文件后,相当于文件中持久保存了一个对象的信息
- 反之, 该字节序列还可以从文件中读取回来,重构对象,对它进行反序列化
-
对象序列化流:ObjectOutputStream
- 将java对象的原始数据类型和图形写入OutputStream,可以使用ObjectInputStream读取(重构)对象。可以通过使用流的文件来实现对象的持久储存。如果是网络套接字流,则可以在另一进程中重构对象。
-
构造方法
方法名 说明 ObjectOutputStream(OutputStream out) 创建一个写入指定的OutPutStream的ObjecdtOutputStream -
序列化对象方法
方法名 说明 void writeObject(object obj) 将指定的对象写入ObjectOutputStream
对象反序列化流
-
对象反序列化流:ObjectInputStream
- ObjectInputStream反序列化先前使用ObjectOuputStream编写的原始数据和对象
-
构造方法
方法名 说明 ObjectInputStream(InputStream in) 创建从指定的InputStream读取的ObjectInputStream -
反序列化对象的方法
方法名 说明 Object readObject() 从ObjectInputStream读取一个对象
示例代码
OutputStreamWriter osw=new OutputStreamWriter(
new FileOutputStream("E:\\Java\\IO\\practice\\sinua.txt"));
osw.write("Miluji te");
osw.close();
InputStreamReader isr=new InputStreamReader(
new FileInputStream("E:\\Java\\IO\\practice\\sinua.txt"),"GBK");
char [] chars=new char[1024];
int length;
while((length=isr.read())!=-1){
System.out.println(new String(chars,0,length));
}
isr.close();
}
serivalVersionUID&transient
-
servalVersionUID
-
用对象序列化流序列化了一个对象后,加入我们修改了对象所属的类的文件,读取数据会不会出现问题呢?
- 会出问题,会抛出InvalidClassException异常
-
如果出问题了,如何解决呢
-
重新序列化
-
给对象所属的类加一个servalVersionUID
- private static final long servalVersionUID=42L;
-
-
transient
-
如果一个对象中的某个成员变量的值不想被序列化,又该如何实现?
- 给该成员变量加transient官架子修饰,该关键字标记的成员变量不参与序列化过程
-
示例代码
学生类
public class Student implements Serializable { private static final long servalVersionUID=42L; private String name; //private int age; //不想年龄被序列化 private transient int age; public Student() { } public Student(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } @Override public String toString() { return "Student{" + "name='" + name + '\'' + ", age=" + age + '}'; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } }
public class Test { public Test() throws IOException { } public static void main(String[] args) throws IOException, ClassNotFoundException { write(); read(); } private static void write() throws IOException { ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("E:\\Java\\IO\\practice\\java1.txt")); Student s = new Student("佟丽娅", 30); oos.writeObject(s); oos.close(); } private static void read() throws IOException, ClassNotFoundException { //反序列化 ObjectInputStream ois=new ObjectInputStream( new FileInputStream("E:\\Java\\IO\\practice\\java1.txt") ); Object obj=ois.readObject(); Student student=(Student) obj; System.out.println(student.getName()); ois.close(); } }
Properties集合
Properties作为Map集合的使用
-
Properties介绍
- 是一个Map体系的集合类
- Properties可以保存到流中或从流中加载
- 属性列表中的每个键以及对应的值都是一个字符串
-
Properties的基本使用
public static void main(String[] args) { Properties prop =new Properties(); prop.put("法语","Je t'aime"); prop.put("德语","Ich Liebe Dish"); prop.put("芬兰","Minaracastan Sinua"); prop.put("荷兰","Ik hou van jou"); prop.put("捷克","Miluji te"); Set<Map.Entry<Object, Object>> entries = prop.entrySet(); for (Map.Entry<Object, Object> key : entries) { String value=(String)prop.get(key); System.out.println(key); } }
Properties作为Map集合的特有方法
-
特有方法
方法名 说明 Object setProperty(String key,Strinfg value) 设置集合的键和值,都是String类型,底层调用Hashtable方法put String getProperty(String key) 使用此属性列表指定的键搜索属性 Set stringProperty() 从该属性列表中返回一个不可修改的键集,其中键及其对应的值是字符串 示例代码
public static void main(String[] args) { Properties prop =new Properties(); prop.put("法语","Je t'aime"); prop.put("德语","Ich Liebe Dish"); prop.put("芬兰","Minaracastan Sinua"); prop.put("荷兰","Ik hou van jou"); prop.put("捷克","Miluji te"); System.out.println(prop.getProperty("芬兰")); System.out.println(prop.getProperty("捷克")); System.out.println(prop); Set<String> names = prop.stringPropertyNames(); for (String name : names) { String value=prop.getProperty(name); System.out.println(name+","+value); } }
Properties和IO流相结合的方法
和IO结合的方法
方法名 说明 void load(Reader reader) 从输入字符流读取属性列表(键和元素对) void store(Writer writer, String comments) 将此属性列表(键和元素对) 写入此Properties表中,意识和使用load(Reader)方法的格式写入输出字符流 示例代码
public class Exam3 { public static void main(String[] args) throws IOException { myLoad(); //myStore(); } private static void myStore() throws IOException { Properties prop=new Properties(); prop.put("法语","Je t'aime"); prop.put("德语","Ich Liebe Dish"); prop.put("芬兰","Minaracastan Sinua"); prop.put("荷兰","Ik hou van jou"); prop.put("捷克","Miluji te"); FileWriter fw=new FileWriter("E:\\Java\\IO\\practice\\java.txt"); prop.store(fw,null); fw.close(); } private static void myLoad() throws IOException { Properties prop =new Properties(); FileReader fr=new FileReader("E:\\Java\\IO\\practice\\java.txt"); prop.load(fr); fr.close(); System.out.println(prop); } }
-
-