特殊操作流
1. 标准输入流 & 标准输出流
1.1 概述
- System类中有两个静态的成员变量
- public static final InputStream in:标准输入流。通常该流对应于键盘输入或由主机环境或用户指定的 另一个输入源
- public static final PrintStream out:标准输出流。通常该流对应于显示输出或由主机环境或用户指定的 另一个输出目标
1.2 示例
- 标准输入流
//自己实现键盘录入数据过于麻烦,所以Java体统了一个类供我们使用
Scanner sc = new Scanner(System.in);
-
标准输出流
//System.out的本质是一个字节输出流 System.out.print(); //不换行 System.out.println(); //换行
2. 字节打印流 & 字符打印流
2.1 概述
- PrintStream:字节打印流
- PrintWriter:字符打印流
- 使用继承父类的方法写数据,查看的时候会转码;使用自己的特有方法写数据,查看的数据原样输出
- 可以改变输出语句的目的地
- public static void setOut(PrintStream out):重新分配“标准”输出流
2.2 特点
- 只负责输出数据,不负责读取数据
- 永远不会抛出IOException
- 有自己特有的方法
2.3 构造方法
2.3.1 字节打印流
方法名 | 说明 |
---|---|
PrintStream(String fileName) | 使用指定的文件名创建新的打印流 |
2.3.2 字符打印流
方法名 | 说明 |
---|---|
PrintWriter(String fileName) | 使用指定的文件名创建一个新的PrintWriter,不会自动执行刷新 |
PrintWriter(Writer out, boolean autoFlush) | 创建一个新的PrintWriter out:字符输出流 autoFlush: 一个布尔值,如果 为真,则println 、print 或format方法将刷新输出缓冲区 |
2.4 代码示例
2.4.1 字节打印流
import java.io.IOException;
import java.io.PrintStream;
//字节打印流
public class Demo01 {
public static void main(String[] args) throws IOException {
PrintStream ps = new PrintStream("iodemo\\demo.txt");
//字节输出流的方法 写数据
ps.write(97); //写入的是a
//字节打印流 特有方法写数据
ps.print(97); // 写的是多少 写入的就是多少
ps.println(98);//换行
ps.print(99);
//释放资源
ps.close();
}
}
2.4.2 字符打印流
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
public class Demo02 {
public static void main(String[] args) throws IOException {
/* //第一种构造方法
PrintWriter pw = new PrintWriter("iodemo\\demo.txt");
pw.write("hello");
pw.write("\r\n");
pw.flush();
//println 自动换行
pw.println("world");
pw.flush();*/
//第二种构造方法 加一个参数必须为true 这种构造方法能实现自动刷新
PrintWriter pw = new PrintWriter(new FileWriter("iodemo\\demo.txt"),true);
pw.println("hello");
/* 这一步相当于 第一种构造方法中
pw.write("hello");
pw.write("\r\n");
pw.flush();
*/
pw.println("world");
pw.close();
}
}
2.5 复制Java文件(打印流改进版)
import java.io.*;
//复制Java文件 (打印流改进)
/*
1.根据数据源创建字符缓冲输入流对象
2.根据目的地创建字符打印流对象
3.读写数据,复制文件
4.释放资源
*/
public class Test01 {
public static void main(String[] args) throws IOException {
//由于打印流只能写数据 不能读数据 所以读数据还是使用 字符缓冲输入流来进行
BufferedReader br = new BufferedReader(new FileReader("iodemo\\src\\com\\wang\\test\\Test01.java"));
//创建打印流对象
PrintWriter pw = new PrintWriter(new FileWriter("iodemo\\Test.java"),true);
//读数据仍用 字符缓冲输入流的方法
String line;
while ((line = br.readLine()) != null){
//写数据采用打印字符流
pw.println(line);
/*
bw.write(String);
bw.nerLine();
bw.flush();
*/
}
//释放资源
br.close();
pw.close();
}
}
3. 对象序列化流 & 对象反序列化流
3.1 概述
-
对象序列化:就是将对象保存到磁盘中,或者在网络中传输对象
-
这种机制就是使用一个字节序列表示一个对象,该字节序列包含:对象的类型、对象的数据和对象中存储的属性等信息字节序列写到文件之后,相当于文件中持久保存了一个对象的信息
-
反之,该字节序列还可以从文件中读取回来,重构对象,对它进行 反序列化
-
要实现序列化和反序列化就要使用对象序列化流和对象反序列化流
- ObjectOutputStream:对象序列化流
- ObjectInputStream:对象反序列化流
3.2 对象序列化流
3.2.1 概述
- 将Java对象的原始数据类型和图形写入OutputStream。 可以使用ObjectInputStream读取(重构)对象。可以通过使用流的文件来实现对象的持久存储。 如果流是网络套接字流,则可以在另一个主机上或另一个进程中重构对象
3.2.2 构造方法 & 方法
构造方法 | 说明 |
---|---|
ObjectOutputStream(OutputStream out) | 创建一个写入指定的OutputStream的 ObjectOutputStream |
方法名 | 说明 |
void writeObject(Object obj) | 将指定的对象写入ObjectOutputStream |
3.2.3 代码示例
- 测试类
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
public class Demo03 {
public static void main(String[] args) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("iodemo\\demo.txt"));
Student03 s1 = new Student03("张三", 18);
oos.writeObject(s1);
oos.close();
}
//NotSerializableException 抛出一个实例需要一个Serializable接口,序列化运行时或实例的类可能会抛出此异常
}
/* 写入结果
�� sr com.wang.specialdemo.Student03������� I ageL namet Ljava/lang/String;xp t 张三
*/
- 学生类
import java.io.Serializable;
//如何解决NotSerializableException异常
//Serializable接口 仅仅是一个标识接口 没有方法需要重写
public class Student03 implements Serializable {
private String name;
private int age;
public Student03() {
}
public Student03(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;
}
}
3.2.4 注意事项
- 一个对象要想被序列化,该对象所属的类必须必须实现Serializable 接口
- Serializable是一个标记接口,实现该接口,不需要重写任何方法
3.3 对象反序列化流
3.3.1 前提
- ObjectInputStream反序列化前 先使用ObjectOutputStream编写的原始数据和对象
3.3.2 构造方法 & 方法
构造方法 | 说明 |
---|---|
ObjectInputStream(InputStream in) | 创建从指定的InputStream读取的ObjectInputStream |
方法名 | 说明 |
Object readObject() | 从ObjectInputStream读取一个对象 |
3.3.3 代码示例
- 测试类
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
//对象反序列化流
public class Demo04 {
public static void main(String[] args) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("iodemo\\demo.txt"));
Object obj = ois.readObject();
//向下转型
Student03 s = (Student03) obj;
System.out.println(s.getName() + "," + s.getAge());
//释放资源
ois.close();
}
}
- 学生类
import java.io.Serializable;
//如何解决NotSerializableException异常
//Serializable接口 仅仅是一个标识接口 没有方法需要重写
public class Student03 implements Serializable {
private String name;
private int age;
public Student03() {
}
public Student03(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;
}
}
3.4 三个问题
3.4.1 serialVersionUID & transient的应用
-
用对象序列化流序列化了一个对象后,假如我们修改了对象所属的类文件,读取数据会不会出问题呢?
- 会出现问题,抛出InvalidClassException异常
-
如果出现了问题,应该如何解决?
-
重新序列化
-
给对象所属的类加一个 serialVersionUID
-
private static final long serialVersionUID = 42L;
-
-
-
如何一个对象中的某个成员变量的值不想被系列化,又该如何实现?
-
给该成员变量加transient关键字修饰,该关键字标记的成员变量不参与序列化过程
-
private transient int age;
-
-
不参与序列化过程的成员变量,输出默认值
-
4. Properties 集合
4.1 概述
- 是一个Map体系的集合类
- Properties可以保存到流中或从流中加载
- 属性列表中的每个键及其对应的值都是一个字符串
4.2 作为Map集合使用<代码示例>
import java.util.Properties;
import java.util.Set;
// Properties作为Map集合的使用
public class Test02 {
public static void main(String[] args) {
//创建集合对象 Properties 木有泛型 是一个Map体系的集合类
Properties prop = new Properties();
//存储元素 key value
prop.put("11111","张三");
prop.put("22222","李四");
prop.put("33333","王五");
//遍历集合 输出集合元素
Set<Object> keySet = prop.keySet();
for (Object key : keySet) {
Object value = prop.get(key);
System.out.println(key + "," + value);
}
}
}
4.3 特有方法
方法名 | 说明 |
---|---|
Object setProperty(String key, String value) | 设置集合的键和值,都是String类型,底层调用 Hashtable方法 put |
String getProperty(String key) | 使用此属性列表中指定的键搜索属性 |
Set stringPropertyNames() | 从该属性列表中返回一个不可修改的键集,其中键及其对应的值是字符串 |
4.3.1 代码示例
import java.util.Properties;
import java.util.Set;
public class Demo05 {
public static void main(String[] args) {
Properties prop = new Properties();
//Object setProperty(String key, String value) 底层调用的是put方法
prop.setProperty("11111","张三");
prop.setProperty("22222","李四");
prop.setProperty("33333","王五");
System.out.println(prop); //{33333=王五, 11111=张三, 22222=李四}
//String getProperty(String key) 键找值
System.out.println(prop.getProperty("11111")); //张三
//Set<String> stringPropertyNames() 输出键集
Set<String> names = prop.stringPropertyNames();
for (String key : names) {
String value = prop.getProperty(key);
System.out.println(key + "," + value);
}
}
4.4 结合IO流的方法
4.4.1 方法
方法名 | 说明 |
---|---|
void load(InputStream inStream) | 从输入字节流读取属性列表(键和元素对) |
void load(Reader reader) | 从输入字符流读取属性列表(键和元素对) |
void store(OutputStream out, String comments) | 将此属性列表(键和元素对)写入此 Properties表中,以适合于使用 load(InputStream)方法的格式写入输出字节流 |
void store(Writer writer, String comments) | 将此属性列表(键和元素对)写入此 Properties表中,以适合使用 load(Reader)方法的格式写入输出字符流 |
4.4.2 代码示例
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Properties;
public class Demo06 {
public static void main(String[] args) throws IOException{
myStore();
myLoad(); //{33333=王五, 11111=张三, 22222=李四}
}
private static void myLoad() throws IOException{
Properties prop = new Properties();
FileReader fr = new FileReader("iodemo\\demo.txt");
prop.load(fr);
fr.close();
System.out.println(prop);
}
private static void myStore() throws IOException {
Properties prop = new Properties();
prop.setProperty("11111","张三");
prop.setProperty("22222","李四");
prop.setProperty("33333","王五");
FileWriter fw = new FileWriter("iodemo\\demo.txt");
//store();方法 集合元素 写入文件
prop.store(fw,null);
fw.close();
}
4.5 案例
4.5.1 游戏次数
- 游戏类
import java.util.Random;
import java.util.Scanner;
//猜数字游戏
public class GuessGame {
public static void game(){
Random r = new Random();
int i = r.nextInt(100) + 1;
while (true){
Scanner sc = new Scanner(System.in);
System.out.println("请输入你猜的数字");
int i1 = sc.nextInt();
if (i > i1){
System.out.println("小了");
}else if (i < i1){
System.out.println("大了");
}else{
System.out.println("恭喜你猜对了,正确答案为:" + i);
break;
}
}
}
}
- 测试类
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Properties;
//游戏次数
/*
1.写一个游戏类,里面有一个猜数字的小游戏
2.写一个测试类
3.从文件中读取数据到Properties集合,用load()方法实现
文件已经存在:game.txt
里面有一个数据值:count=0
4.通过Properties集合获取到玩游戏的次数
5.判断次数是否到到2次了
如果到了,给出提示:游戏试玩已结束,想玩请充值(www.itcast.cn)
如果不到3次:
次数+1,重新写回文件,用Properties的store()方法实现
玩游戏
*/
public class Test {
public static void main(String[] args) throws IOException {
Properties pt = new Properties();
FileReader fr = new FileReader("iodemo\\demo.txt");
//load();方法 将文件内容存储到Properties集合中
pt.load(fr);
//释放资源
fr.close();
//getProperty();方法 键找值
String count = pt.getProperty("count");
//值是数字 将String转成Int类型
int number = Integer.parseInt(count);
if (number>=2){
System.out.println("你的免费游戏次数不足,请充值648继续畅玩");
}else {
GuessGame.game();
number++;
//将元素存储到集合
pt.setProperty("count",String.valueOf(number));
FileWriter fw = new FileWriter("iodemo\\demo.txt");
//store();方法 将集合中元素写入文件
pt.store(fw,null);
fw.close();
}
}
}