转换流
字符编码和字符集
字符编码
字符编码:计算机中存储的信息都是二进制数据,而我们看到的数字,英文,符号,汉字等都是二进制数转换之后的结果。
字符编码:就是一套自然语言的字符与二进制之间的对应规则
按照某种规则,将字符存储到计算机中。称为编码
编码:字符(能看懂的) -》 字节(看不懂的)
反之,将存储在计算机中的二进制数按照某种规则解析显示出来,称为解码
解码:字节(看不懂的) -》字符(能看懂的)
字符集:
也叫编码表,是一个系统支持的所有字符的集合,包括各国家文字,标点符号,图形符号,数字等
编码表:生活中文字和计算机中二进制的对应规则
计算机要准确的存储和失败各种字符集符号 需要进行字符编码,一套字符集必然至少有一套字符编码
常见字符集
1.ASCII码 ASCII编码
ASCII码 用于显示现代英语 , 控制字符(回车键,退格,换行符等) 和可显示字符(英文大小写字符 , 阿拉伯数字 西文字符)
基本ASCII码字符集,使用7位表示一个字符,共128个字符 。 第一位使用正负
(拓展ASCII字符使用8位表示一个字符共计256个字符 支持欧洲常用字符)
2.GBK字符集 GBK编码
GBK字符集 国标码 显示中文的一套字符集 两个字节存储一个中文
3.Unicode字符集等 UTF-8编码 UTF16编码 UTF32编码
Unicode字符集 万国码 包含所有国家的任意语言字符而设计的 3个字节存储一个中文
最常用的UTF-8编码 :是电子邮件,网页及其他存储或传送文字的应用中,优先采用的编码
当指定了编码,它所对应的字符集自然就指定了,所以编码才是我们关心的
编码引出的问题
编码不匹配会出现乱码问题
public class Demo01 {
public static void main(String[] args) throws IOException {
FileReader fr = new FileReader("E:\\java\\GBK.txt");
int len = 0;
while ((len = fr.read())!=-1) {
System.out.print((char)len);//锘夸綘濂藉晩 产生乱码
}
fr.close();
}
}
1. OutputStreamWriter:转换输出流
是字符流通向字节流的桥梁:可以使用指定的字节charset 将要写入流中的字符编码成字节(编码)
java.io.OutputStreamWriter extends Writer
继承自父类的共性成员方法
1.void write(int c) 写一个字符
2.void write(char[] cbuf) 写入一个字符数组。
3.abstract void write(char[] cbuf, int off, int len) 写入字符数组的一部分。
4.void write(String str) 写一个字符串
5.void write(String str, int off, int len) 写一个字符串的一部分。
6.abstract void flush() 刷新该流的缓冲。
7.abstract void close() 关闭流,必须先刷新。
构造方法:
OutputStreamWriter(OutputStream out) 创建一个使用默认字符编码的OutputStreamWriter。
OutputStreamWriter(OutputStream out, String charsetName) 创建使用指定字符集的OutputStreamWriter。
参数:
OutputStream out:字节输出流。可以用来写转换之后的字节到文件中
String charsetName : 指定编码表名称 不区分大小写,可以是utf-8/UTF-8 GBK/gbk …
不指定 默认使用utf-8/UTF-8
String charsetName : 指定编码表名称 不区分大小写,可以是utf-8/UTF-8 GBK/gbk …
不指定 默认使用utf-8/UTF-8
使用步骤:
1.创建OutputStreamWriter对象,构造方法中传递字节输出流 和指定的表码表名称
2.使用OutputStreamWriter对象中的方法 write 把字符转换为字节 存储到缓冲区中(编码)
3.使用OutputStreamWriter对象中的方法 flush ,把内存缓冲区中的字节刷新到文件中(使用字节流写字节的过程)
4.资源释放
public class Demo03OutputStreamWriter {
public static void main(String[] args) throws IOException {
//1.创建OutputStreamWriter对象,构造方法中传递字节输出流 和指定的表码表名称
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("E://java//utf.txt"),"GBK");
//2.使用OutputStreamWriter对象中的方法 write 把字符转换为字节 存储到缓冲区中(编码)
osw.write("你好");
//3.使用OutputStreamWriter对象中的方法 flush ,把内存缓冲区中的字节刷新到文件中(使用字节流写字节的过程)
//4.资源释放
osw.close();
}
}
2. InputStreamReader 转换输入流
是从字节流到字符流的桥:它读取字节,并使用指定的charset将其解码为字符(解码:把看不懂的变成能看懂的)
java.io.InputStreamReader extends Reader
继承自父类的方法:
1.int read() 读一个字符 并返回
2.int read(char[] cbuf) 一次读取多个字符,将字符读入数组
3.void close() 关闭流并释放与之相关联的任何系统资源。
构造方法:
InputStreamReader(InputStream in) 创建一个使用默认字符集的InputStreamReader。
InputStreamReader(InputStream in, String charsetName) 创建一个使用指定字符集的InputStreamReader。
参数:
InputStream in:字符输入流,用来读取文件中保存的字节
String charsetName:指定编码表名称 不区分大小写,可以是utf-8/UTF-8 GBK/gbk …
不指定 默认使用utf-8/UTF-8
使用步骤:
1.创建InputStreamReader对象,构造方法中传递字节输入流,和会自动的编码表名称
2.使用InputStreamReader对象中的方法 read 读取文件
3.释放资源
注意事项:构造方法中指定的编码表 要和 文件的编码相同,否则会发生乱码
public class Demo02InputStreamReader {
public static void main(String[] args) throws IOException {
//1.创建InputStreamReader对象,构造方法中传递字节输入流,和会自动的编码表名称
InputStreamReader isr = new InputStreamReader(new FileInputStream("E:\\java\\GBK.txt"),"GBK");
int len = 0;
//2.使用InputStreamReader对象中的方法 read 读取文件
while((len = isr.read()) != -1) {
System.out.println((char)len);
}
//3.释放资源
isr.close();
}
}
案例:将GBK编码的文件 转换为UTF-8编码的文件
1.创建InputStreamReader对象 构造方法中传入字节输入流,和指定编码表名称
2.创建OutputStreamWriter对象 构造方法中传入字节输出流,和指定编码表名称
3.使用InputStreamReader对象中的read方法 读取文件
4.使用OutputStreamWriter对象中的write方法 把读取的数据写人到文件中
5.释放资源
public class Test01 {
public static void main(String[] args) throws IOException {
//1.创建InputStreamReader对象 构造方法中传入字节输入流,和指定编码表名称
InputStreamReader isr = new InputStreamReader(new FileInputStream("E:\\java\\GBK.txt"),"GBK");
//2.创建OutputStreamWriter对象 构造方法中传入字节输出流,和指定编码表名称
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("E:\\java\\utf.txt"),"utf-8");
int len = 0;
//3.使用InputStreamReader对象中的read方法 读取文件
while((len = isr.read())!=-1) {
//4.使用OutputStreamWriter对象中的write方法 把读取的数据写人到文件中
osw.write(len);
}
//5.释放资源
isr.close();
osw.close();
}
}
序列化
概述:java提供了一种对象 序列化 机制。用一个字节序列化可以表示一个对象,该字节序列包含 对象的数据,对象的类型,对象存储的属性等
字节序列写出文件之后,相当于文件中持久保存了一个对象的信息
反之,该字节序列化还可以从文件中读取回来,重构对象,对他进行反序列化。
1 .ObjectOutputStream:对象的序列化流
java.io.ObjectOutputStream extends OutputStream
作用:把对象以流的方式写入到文件中保存
构造方法:
ObjectOutputStream(OutputStream out) 创建一个写入指定的OutputStream的ObjectOutputStream。
参数:OutputStream out :字节输出流
特有的成员方法:
void writeObject(Object obj) 将指定的对象写入ObjectOutputStream。
使用步骤:
1.创建一个ObjectOutputStream对象 构造方法中传递字节输出流
2.使用ObjectOutputStream对象中的方法writeObject 把对象写入到文件中
3.释放资源
注意:
序列化和反序列的时候,会抛出 NotSerializableException:没有序列化异常,
类的序列化由实现java.io.Serializable接口的类启用。 不实现此接口的类将不会使任何状态序列化或反序列化
Serializable:也叫标记型接口
要进行序列化和反序列化的类必须实现Serializable接口,就会给类添加一个标记
当我们进行序列化和反序列化的时候,就会检测类上是否有这个标记
有:就可以序列化和反序列化
没有:就会抛出NotSerializableException 没有序列化异常
类似于:市场卖肉:肉上有一个蓝色章(检测合格)-》放心购买 -》买回来怎么吃 随意
public class Demo01 {
public static void main(String[] args) throws IOException {
//1.创建一个ObjectOutputStream对象 构造方法中传递字节输出流
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("E:\\java\\person.txt"));
oos.writeObject(new Person("小美女",18));
oos.close(); // java.io.NotSerializableException: 未序列化异常
}
}
2. ObjectInputStream:对象的反序列化流
java.io.ObjectInputStream extends InputStream
作用:把文件中保存的对象,以流的方式读取出来使用
构造方法:
ObjectInputStream(InputStream in) 创建从指定的InputStream读取的ObjectInputStream。
参数:InputStream in:字节输入流
特有的成员方法:
Object readObject() 从ObjectInputStream读取一个对象。
使用步骤:
1.创建一个ObjectInputStream对象,构造方法中传递字节输入流
2.使用ObjectInputStream对象中的方法:readObject 读取保存对象的文件
3.释放资源
4.使用读取出来的对象
ClassNotFoundException: class文件找不到异常
当不存在对象的class文件时 抛出异常
反序列前提:
1.类必须实现:Serializable
2.必须存在对应的class文件
transient关键字
transient关键字:瞬态关键字 (如果不想某个成员变量被序列化 加上transient关键字即可)
被transient修饰的成员变量 不能被序列化
序列化出来的对象 值是默认值
static关键字 :静态关键字:静态优先于非静态加载到内存中(静态优先于对象进入到内存中)
被static修饰的成员变量不能被序列化的,序列化的都是对象
序列化出来的对象 值是默认值
public class Demo02 {
public static void main(String[] args) throws ClassNotFoundException, IOException {
//1.创建一个ObjectInputStream对象,构造方法中传递字节输入流
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("E:\\java\\person.txt"));
//2.使用ObjectInputStream对象中的方法:readObject 读取保存对象的文件
Object o = ois.readObject();
//3.释放资源
ois.close();
//4.使用读取出来的对象
System.out.println(o);//Person [name=小美女, age=18]
Person person = (Person)o;
System.out.println(person.getName()+person.getAge());//小美女18
}
}
反序列化操作2:
InvalidClassException 异常
如果可序列化类没有显式声明serialVersionUID,则序列化运行时将根据Java(TM)对象序列化规范中所述的类的各个方面计算该类的默认serialVersionUID值。
但是, 强烈建议所有可序列化的类都明确声明serialVersionUID值,因为默认的serialVersionUID计算对类详细信息非常敏感,这可能会因编译器实现而异,
因此可能会在反InvalidClassException化期间导致InvalidClassException的InvalidClassException。
因此,为了保证不同Java编译器实现之间的一致的serialVersionUID值,一个可序列化的类必须声明一个显式的serialVersionUID值。 还强烈建议,
显式的serialVersionUID声明在可能的情况下使用private修饰符,因为这种声明仅适用于立即声明的类 - serialVersionUID字段作为继承成员无效。
为了防止我们更改对象属性后,出现异常,UID改变 我们可以自己在对象中声明UID值
static final long serialVersionUID = 42L;
案例:序列化集合:
当我们想在文件中保存多个对象的时候
可以把多个对象存储在集合中,
对集合进行序列化和反序列化
分析:
1.定义存储person对象的Arraylist集合
2.往Arraylist集合存储person对象
3.创建一个序列化流 objectOutputstream
4.使用objectOutputstream方法中的ObjectWrite 对集合进行序列化
5.创建一个反序列化:objectInputstream
6.使用objectInputstream对象中的方法 readobject 读取文件中保存的集合
7.把object类型的集合 转换为Arraylist
8.遍历集合
9.释放资源
public class Test01 {
public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException {
//1.定义存储person对象的Arraylist集合
ArrayList<Person> list = new ArrayList<>();
//2.往Arraylist集合存储person对象
list.add(new Person("张三", 18));
list.add(new Person("李四", 20));
list.add(new Person("王五", 17));
list.add(new Person("牛二", 22));
list.add(new Person("赵六", 23));
//3.创建一个序列化流 objectOutputstream
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("E:\\java\\person.txt"));
//4.使用objectOutputstream方法中的ObjectWrite 对集合进行序列化
oos.writeObject(list);
//5.创建一个反序列化:objectInputstream
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("E:\\java\\person.txt"));
//6.使用objectInputstream对象中的方法 readobject 读取文件中保存的集合
Object obj= ois.readObject();
//7.把object类型的集合 转换为Arraylist
ArrayList<Person> list2 = (ArrayList<Person>)obj;
//8.遍历集合
for (Person person : list2) {
System.out.println(person);
}
//9.释放资源
oos.close();
ois.close();
}
}
打印流
平时我们在控制台打印输出,是调用print和println方法完成的,这两个方法其实就是来自java.io.PrintStream 类。
这个类能够方便的打印各种数据类型的值,是一种便捷的输出方式
PrintStream打印流
java.io.PrintStream extends OutputStream
PrintStream特点:
1.只负责数据的输出,不负责数据的读取
2.与其他输出流不同, PrintStream从不抛出IOException
3.有特有的方法print println
void print(任意类型的值)
void println(任意类型的值并换行)
构造方法:
PrintStream(File file) 输出的目的地是一个文件
PrintStream(OutputStream out) 输出的目的地是一个字节输出流
PrintStream(String fileName) 输出的目的地是一个文件路径
继承自父类特有的方法:
void close() 关闭此输出流并释放与此流相关联的任何系统资源。
void flush() 刷新此输出流并强制任何缓冲的输出字节被写出。
void write(byte[] b) 将 b.length字节从指定的字节数组写入此输出流。
void write(byte[] b, int off, int len) 从指定的字节数组写入 len个字节,从偏移 off开始输出到此输出流。
abstract void write(int b) 将指定的字节写入此输出流。
注意:
如果使用继承自父类的write方法写数据,那么查看数据的时候会查询编码表97-a
如果使用自己特有的方法print println写数据,写的数据原样输出
public class Demo01 {
public static void main(String[] args) throws FileNotFoundException {
//1.创建打印流对象,构造方法中绑定要输出的目的地
PrintStream ps = new PrintStream("E:\\java\\x.txt");
//2.如果使用继承自父类的write方法写数据,那么查看数据的时候会查询编码表97-a
//2.如果使用自己特有的方法print println写数据,写的数据原样输出
ps.write(97); //a
ps.println(97);//97
//3.释放资源
ps.close();
}
}
可以改变输出语句的目的地(打印流的流向)
输出语句:默认在控制台输出
使用System.setOut方法改变输出语句的目的地改为参数传递的打印流的目的地
static void setOut(PrintStream out) 重新分配“标准”输出流。
public class Demo2 {
public static void main(String[] args) throws FileNotFoundException {
System.out.println("在控制台输出");//在控制台输出
PrintStream ps = new PrintStream("E:\\java\\arm.txt");
System.setOut(ps); //把输出语句的目的地改为打印流的目的地
System.out.println("我在打印流的目的地输出");
}
}