转换流
字符编码
编码:字符(能看懂的)–字节(看不懂的)
解码:字节(看不懂的)–>字符(能看懂的)
InputStreamReader类
转换流java.io.InputStreamReader
,是Reader的子类,是从字节流到字符流的桥梁。它读取字节,并使用指定的字符集将其解码为字符。
构造方法
InputStreamReader(InputStream in)
: 创建一个使用默认字符集的字符流。InputStreamReader(InputStream in, String charsetName)
: 创建一个指定字符集的字符流。
指定编码读取
public class ReaderDemo2 {
public static void main(String[] args) throws IOException {
// 定义文件路径,文件为gbk编码
String FileName = "E:\\file_gbk.txt";
// 创建流对象,默认UTF8编码
InputStreamReader isr = new InputStreamReader(new FileInputStream(FileName));
// 创建流对象,指定GBK编码
InputStreamReader isr2 = new InputStreamReader(new FileInputStream(FileName) , "GBK");
// 定义变量,保存字符
int read;
// 使用默认编码字符流读取,乱码
while ((read = isr.read()) != -1) {
System.out.print((char)read); // ��Һ�
}
isr.close();
// 使用指定编码字符流读取,正常解析
while ((read = isr2.read()) != -1) {
System.out.print((char)read);// 大家好
}
isr2.close();
}
}
OutputStreamWriter类
转换流java.io.OutputStreamWriter
,是Writer的子类,是从字符流到字节流的桥梁。使用指定的字符集将字符编码为字节。
构造方法
OutputStreamWriter(OutputStream in)
: 创建一个使用默认字符集的字符流。OutputStreamWriter(OutputStream in, String charsetName)
: 创建一个指定字符集的字符流。
指定编码写出
public class OutputDemo {
public static void main(String[] args) throws IOException {
// 定义文件路径
String FileName = "E:\\out.txt";
// 创建流对象,默认UTF8编码
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(FileName));
// 写出数据
osw.write("你好"); // 保存为6个字节
osw.close();
// 定义文件路径
String FileName2 = "E:\\out2.txt";
// 创建流对象,指定GBK编码
OutputStreamWriter osw2 = new OutputStreamWriter(new FileOutputStream(FileName2),"GBK");
// 写出数据
osw2.write("你好");// 保存为4个字节
osw2.close();
}
}
数据流
如果要在程序中直接处理Java的基础数据类型,怎么办呢?
String name = “巫师”;
int age = 300;
char gender = ‘男’;
int energy = 5000;
double price = 75.5;
boolean relive = true;
案例:
public class TestData {
public void save() throws IOException{
String name = "巫师";
int age = 300;
char gender = '男';
int energy = 5000;
double price = 75.5;
boolean relive = true;
DataOutputStream dos = new DataOutputStream(new FileOutputStream("game.dat"));
dos.writeUTF(name);
dos.writeInt(age);
dos.writeChar(gender);
dos.writeInt(energy);
dos.writeDouble(price);
dos.writeBoolean(relive);
dos.close();
}
public void reload()throws IOException{
DataInputStream dis = new DataInputStream(new FileInputStream("game.dat"));
String name = dis.readUTF();
int age = dis.readInt();
char gender = dis.readChar();
int energy = dis.readInt();
double price = dis.readDouble();
boolean relive = dis.readBoolean();
System.out.println(name+"," + age + "," + gender + "," + energy + "," + price + "," + relive);
dis.close();
}
}
读写属性顺序要保持一致
对象流与序列化
Java 提供了一种对象序列化的机制。用字节序列可以表示一个对象,该字节序列包含该对象的类型
和对象中存储的属性
等信息。字节序列写出到文件之后,相当于文件中持久保存了一个对象的信息。
反之,该字节序列还可以从文件中读取回来,重构对象,对它进行反序列化。对象的数据
、对象的类型
和对象中存储的数据
信息,都可以用来在内存中创建对象。
ObjectOutputStream类
java.io.ObjectOutputStream
类,将Java对象的原始数据类型写出到文件,实现对象的持久存储。
构造方法
public ObjectOutputStream(OutputStream out)
: 创建一个指定OutputStream的ObjectOutputStream。
序列化操作
- 该类必须实现
java.io.Serializable
接口,Serializable
是一个标记接口,不实现此接口的类将不会使任何状态序列化或反序列化,会抛出NotSerializableException
。- 如果对象的某个属性也是引用数据类型,那么如果该属性也要序列化的话,也要实现
Serializable
接口
- 如果对象的某个属性也是引用数据类型,那么如果该属性也要序列化的话,也要实现
- 该类的所有属性必须是可序列化的。如果有一个属性不需要可序列化的,则该属性必须注明是瞬态的,使用
transient
关键字修饰。(比如网络中传输时,考虑安全因素银行卡字段可以使用transient不进行序列化) - 静态变量的值不会序列化(静态变量的值不属于某个对象的数据,而是属于类的数据)
写出对象方法
public final void writeObject (Object obj)
: 将指定的对象写出。
ObjectInputStream类
ObjectInputStream
反序列化流,将之前使用ObjectOutputStream
序列化的原始数据恢复为对象。
构造方法
public ObjectInputStream(InputStream in)
: 创建一个指定InputStream的ObjectInputStream。
反序列化操作1
如果能找到一个对象的class文件,我们可以进行反序列化操作,调用ObjectInputStream
读取对象的方法:
public final Object readObject ()
: 读取一个对象。
对于JVM可以反序列化对象,它必须是能够找到class文件的类。如果找不到该类的class文件,则抛出一个 ClassNotFoundException
异常。
反序列化操作2
当JVM反序列化对象时,能找到class文件,但是class文件在序列化对象之后发生了修改,那么反序列化操作也会失败,抛出一个InvalidClassException
异常。发生这个异常的原因如下:
- 该类的序列版本号与从流中读取的类描述符的版本号不匹配
- 该类包含未知数据类型
Serializable
接口给需要序列化的类,提供了一个序列版本号。serialVersionUID
该版本号的目的在于验证序列化的对象和对应类是否版本匹配。
public class Employee implements java.io.Serializable {
// 加入序列版本号
private static final long serialVersionUID = 1L;
public String name;
public String address;
// 添加新的属性 ,重新编译, 可以反序列化,该属性赋为默认值.
public int eid;
IDEA自动生成serialVersionUID
案例:
public class SerTest {
public static void main(String[] args) throws Exception {
// 创建 学生对象
Student student = new Student("老王", "laow");
Student student2 = new Student("老张", "laoz");
Student student3 = new Student("老李", "laol");
ArrayList<Student> arrayList = new ArrayList<>();
arrayList.add(student);
arrayList.add(student2);
arrayList.add(student3);
// 序列化操作
serializ(arrayList);
// 反序列化
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("list.txt"));
// 读取对象,强转为ArrayList类型
ArrayList<Student> list = (ArrayList<Student>)ois.readObject();
for (int i = 0; i < list.size(); i++ ){
Student s = list.get(i);
System.out.println(s.getName()+"--"+ s.getPwd());
}
}
private static void serializ(ArrayList<Student> arrayList) throws Exception {
// 创建 序列化流
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("list.txt"));
// 写出对象
oos.writeObject(arrayList);
// 释放资源
oos.close();
}
}
打印流与标准输入输出流
打印流PrintStream与PrintWriter
打印流只有输出没有输入
构造方法
public PrintStream(String fileName)
: 使用指定的文件名创建一个新的字节打印流。public PrintWriter(String fileName)
:使用指定的文件名创建一个新的字符打印流。PrintWriter(OutputStream out, boolean autoFlush)
:基于字符输出流创建一个自动刷新的字符打印流
标准输入/输出流
System类中有三个属性字段:
Modifier and Type | Field and Description |
---|---|
static PrintStream | err The “standard” error output stream. |
static InputStream | in The “standard” input stream. |
static PrintStream | out The “standard” output stream. |
System.in 标准输入流,本质是一个字节输入流,默认接受键盘录入的数据(不要用Junit单元测试,键盘录入)。
System.out 标准输出流,本质是一个字节输出流,默认输出数据到控制台。
public class PrintDemo {
public static void main(String[] args) throws IOException {
// 调用系统的打印流,控制台直接输出97
System.out.println(97);
// 创建字节打印流,指定文件的名称
PrintStream ps = new PrintStream("ps.txt");
// 设置系统的打印流流向,输出到ps.txt
System.setOut(ps);
// 调用系统的打印流,ps.txt中输出97
System.out.println(97);
}
}
Properties类
Properties 类本质是一个集合类 但是一般用它加载配置文件 所以又表示一个持久的属性集合。
Properties 可保存在流中或从流中加载。属性列表中每个键及其对应值都是一个字符串。
加载磁盘的属性文件
@Test
public void test03() throws IOException {
//创建属性集对象
Properties prop = new Properties();
//使用流加载文件
InputStream is = new FileInputStream("D:/message.properties") ;
prop.load(is);
//根据key获取值
String value = prop.getProperty("uname");
System.out.println("value = " + value);
}
加载src下的属性文件
@Test
public void test04() throws IOException {
//创建属性集对象
Properties prop = new Properties();
//使用流加载文件
InputStream is = PropertiesTest.class.getClassLoader().getResourceAsStream("jdbc.properties");
prop.load(is);
//展示所有的数据
prop.list(System.out);
}
src下的配置文件不管在哪个模块下只要名称对应就可以加载到
JDK1.7之后引入新try…catch
try(需要关闭的资源对象的声明){
业务逻辑代码,可能发生异常的代码
}catch(异常类型 e){
处理异常代码
}catch(异常类型 e){
处理异常代码
}
它没有finally,也不需要程序员去关闭资源对象,无论是否发生异常,都会关闭资源对象
IOUtils工具类(commons-io包)
IOUtils是Apache出品的一个方便IO操作的工具类,简化了IO流的读、写、复制及关闭流等操作(使用前需要导包)
@Test
public void test2() throws IOException {
IOUtils.copy(new FileInputStream("ps.txt"), new FileOutputStream("pw.txt"));//复制文件
IOUtils.write("hello world",new FileOutputStream("ps.txt"),"UTF-8");//写数据到文件
}