特殊操作流
标准输入输出流
System类中有两个静态的成员变量:
- public static final InputStream in:标准输入流。通常该流对应于键盘输入或由主机环境或用户指定的另一个输入源
- public static final PrintStream out:标准输出流。通常该流对应于显示输出或主机环境或用户指定的另一个输出目标
自己实现键盘录入数据:
- BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
写起来太麻烦,Java就提供了一个类实现键盘录入
- Scanner sc=new Scanner(System.in);
标准输入流的用法:
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Scanner;
/*
public static final InputStream in:标准输入流。通常该流对应于键盘输入或由主机环境或用户指定的另一个输入源
*/
public class SystemInDemo {
public static void main(String[] args) throws IOException {
//public static final InputStream in:标准输入流
// InputStream is = System.in;
// int by;
// while((by=is.read())!=-1) {
// System.out.print((char)by);
// }
/*
a
a
b
b
中
??
*/
//如何把字节流转换为字符流?用转换流
// InputStreamReader isr=;
//使用字符流能不能够实现一次读取一行数据呢?可以
//但是,一次读取一行数据的方法时字符缓冲输入流的特有方法
// BufferedReader br=new BufferedReader(isr);
//用一步来实现
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
System.out.println("请输入一个字符串");
String line = br.readLine();
System.out.println("你输入的字符串是:" + line);
System.out.println("请输入一个整数:");
int i = Integer.parseInt(br.readLine()); //要得到什么类型的字符串,就要用它对应的包装类的方法转换
System.out.println("你输入的整数是:" + i);
//自己实现键盘录入数据太麻烦了,所以Java就提供了一个类供我们使用
// Scanner sc = new Scanner(System.in);
}
}
运行结果:
输出语句的本质:是一个标准的输出流
- PrintStream ps=System.out;
- PrintStream类有的方法,System.out都可以使用
标准输出流的用法:
import java.io.PrintStream;
/*
public static final PrintStream out:标准输出流。通常该流对应于显示输出或主机环境或用户指定的另一个输出目标
*/
public class SystemOutDemo {
public static void main(String[] args) {
// public static final PrintStream out:标准输出流
PrintStream ps = System.out;
// 能够方便地打印各种数据值
// ps.print("hello");
// ps.print(100);
// ps.println("hello");
// ps.println(100);
// System.out的本质是一个字节输出流
System.out.println("hello");
System.out.println(100);
System.out.println();
// System.out.print();//报错
}
}
运行结果:
打印流
打印流分类:
- 字节打印流:PrintStream
- 字符打印流:PrintWriter
打印流特点:
- 只负责输出数据,不负责读取数据
- 有自己的特有方法
字节打印流
- PrintStream(String fileName):使用指定的文件名创建新的打印流
- 使用继承父类的方法写数据,查看的时候会转码;使用自己的特有方法写数据,查看的数据原样输出
字节打印流用法:
import java.io.IOException;
import java.io.PrintStream;
/*
打印流特点:
只负责输出数据,不负责读取数据
有自己的特有方法
字节打印流
PrintStream(String fileName):使用指定的文件名创建新的打印流
*/
public class PrintStreamDemo {
public static void main(String[] args) throws IOException {
// PrintStream(String fileName):使用指定的文件名创建新的打印流
PrintStream ps = new PrintStream("..\\hello java\\ps.txt");
// 写数据
// 字节输出流有的方法
// ps.write(97);//a
// 使用特有方法
// ps.print(97);//97
// ps.println();//换行
// ps.print(98);//98
ps.println(97);
ps.println(98);
// 释放资源
ps.close();
}
}
运行结果:
字符打印流PrintWriter的构造方法:
方法名 | 说明 |
---|---|
PrintWriter (String fileName) | 使用指定的文件名创建一个新的PrintWriter,而不需要自动执行刷新 |
PrintWriter(Writer out,boolean autoFlush) | 创建一个新的PrintWriter 1.out:字符输出流 2.antoFlush:一个布尔值,如果为真,则println,printf,或format方法将刷新输出缓冲区 |
字符打印流用法:
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
/*
字符打印流PrintWriter的构造方法:
PrintWriter (String fileName)使用指定的文件名创建一个新的PrintWriter,而不需要自动执行刷新
PrintWriter(Writer out,boolean autoFlush)创建一个新的PrintWriter
1.out:字符输出流
2.antoFlush:一个布尔值,如果为真,则println,printf,或format方法将刷新输出缓冲区
*/
public class PrintWriterDemo {
public static void main(String[] args) throws IOException {
// PrintWriter (String fileName)使用指定的文件名创建一个新的PrintWriter,而不需要自动执行刷新
// PrintWriter pw=new PrintWriter("..\\hello java\\pw.txt");
// pw.write("hello");
// pw.write("\r\n");//加换行符
// pw.flush();//字符流,需要刷新,否则不出来
//
// pw.write("world");
// pw.write("\r\n");
// pw.flush();
// pw.println("hello");
/*相当于:
pw.write("hello");
pw.write("\r\n");
*/
// pw.flush();//还是需要刷新,比较麻烦
// pw.println("world");
// pw.flush();
// PrintWriter(Writer out,boolean autoFlush)创建一个新的PrintWriter
PrintWriter pw = new PrintWriter(new FileWriter("..\\hello java\\pw.txt"), true);// 实现自动刷新
// PrintWriter pw=new PrintWriter(new FileWriter("..\\hello java\\pw.txt"),false);
pw.println("hello");
/*相当于:
pw.write("hello");
pw.write("\r\n");
pw.flush();
*/
pw.println("world");
pw.close();
}
}
运行结果:
案例:复制Java文件(打印流改进版)
需求:把模块目录下的PrintStreamDemo.java复制到模块目录下的Copy.java
思路:
- 根据数据源创建字符输入流对象
- 根据目的地创建字符输出流对象
- 读写数据,复值文件
- 释放资源
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
/*
案例:复制Java文件(打印流改进版)
需求:把模块目录下的PrintStreamDemo.java复制到模块目录下的Copy.java
思路:
1. 根据数据源创建字符输入流对象
2. 根据目的地创建字符输出流对象
3. 读写数据,复值文件
4. 释放资源
*/
public class CopyJavaDemo {
public static void main(String[] args) throws IOException {
/*//1. 根据数据源创建字符输入流对象
BufferedReader br=new BufferedReader(new FileReader("..\\hello java\\PrintStreamDemo.java"));
//2. 根据目的地创建字符输出流对象
BufferedWriter bw=new BufferedWriter(new FileWriter("..\\hello java\\Copy.java"));
//3. 读写数据,复值文件
String line;
while((line=br.readLine())!=null) {
bw.write(line);
bw.newLine();
bw.flush();
}
//4. 释放资源
br.close();
bw.close();*/
// 1. 根据数据源创建字符输入流对象
BufferedReader br = new BufferedReader(new FileReader("..\\hello java\\PrintStreamDemo.java"));
// 2. 根据目的地创建字符输出流对象
PrintWriter pw = new PrintWriter(new FileWriter("..\\hello java\\Copy.java"), true);
String line;
while ((line = br.readLine()) != null) {
pw.println(line);
}
// 4. 释放资源
br.close();
pw.close();
}
}
运行结果:
对象序列化流
对象序列化:就是将对象保存到磁盘中,或者在网络中传输对象
这种机制就是使用一个字节序列表示一个对象,该字节序列包含:对象的类型、对象的数据和对象中存储的属性等信息
字节序列写到文件之后,相当于文件中持久保存了一个对象的信息
反之,该字节序列还可以从文件中读取回来,重构对象,对它进行反序列化
要实现序列化和反序列化就要使用对象序列化流和对象反序列化流:
- 对象序列化流:ObjectOutputStream
- 对象反序列化流:ObjectInputStream
对象序列化流
对象序列化流:ObjectOutputStream
- 将Java对象的原始数据类型和图形写入OutputStream。可以使用ObjectInputStream读取(重构)对象。可以通过使用流的文件来实现对象的持久存储。如果流是网络套接字流,则可以在另一个主机上或另一个进程中重构对象
构造方法:
- ObjectOutputStream(OutputStream out):创建一个写入指定的OutputStream的ObjectOutputStream
序列化对象的方法:
- void writeObject(Object obj):将指定的对象写入ObjectOutputStream
注意:
- 一个对象要想被序列化,该对象所属的类必须必须实现Serializable接口
- Serializable是一个标记接口,实现该类接口,不需要重写任何方法
对象序列化的用法:
import java.io.Serializable;
public class Student implements Serializable {
private String name;
private int 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;
}
public Student(String name, int age) {
super();
this.name = name;
this.age = age;
}
public Student() {
super();
}
}
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
/*
对象序列化流
构造方法:
- ObjectOutputStream(OutputStream out):创建一个写入指定的OutputStream的ObjectOutputStream
序列化对象的方法:
- void writeObject(Object obj):将指定的对象写入ObjectOutputStream
NotSerializableException:抛出一个实例需要一个Serializable接口。序列化运行时或实例的类可能会抛出此异常
类的序列化由实现java.io.Serializable接口的类启用。不实现此接口的类将不会使用任何状态序列化或反序列化
*/
public class ObjectOutputStreamDemo {
public static void main(String[] args) throws IOException {
// ObjectOutputStream(OutputStream out):创建一个写入指定的OutputStream的ObjectOutputStream
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("..\\hello java\\oos.txt"));
// 创建对象
Student s = new Student("小白", 12);
// void writeObject(Object obj):将指定的对象写入ObjectOutputStream
oos.writeObject(s);
oos.close();
}
}
运行结果:
对象反序列化流:ObjectInputStream
- ObjectInputStream反序列化先前使用ObjectOutputStream编写的原始数据和对象
构造方法:
- ObjectInputStream(InputStream in):创建从指定的InputStream读取的ObjectInputStream
反序列化对象的方法:
- Object readObject():从ObjectInputStream读取一个对象
对象反序列化的用法:
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
/*
对象反序列化流:ObjectInputStream
- ObjectInputStream反序列化先前使用ObjectOutputStream编写的原始数据和对象
构造方法:
- ObjectInputStream(InputStream in):创建从指定的InputStream读取的ObjectInputStream
反序列化对象的方法:
- Object readObject():从ObjectInputStream读取一个对象
*/
public class ObjectInputStreamDemo {
public static void main(String[] args) throws IOException, ClassNotFoundException {
// ObjectInputStream(InputStream in):创建从指定的InputStream读取的ObjectInputStream
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("..\\hello java\\oos.txt"));
// Object readObject():从ObjectInputStream读取一个对象
Object obj = ois.readObject();// 抛出异常
Student s = (Student) obj;// 向下转型
System.out.println(s.getName() + "," + s.getAge());// 小白,12
ois.close();
}
}
运行结果:
三个问题
用对象序列化流序列化了一个对象后,假如我们修改了对象所属的类文件,读取数据会不会出问题呢?
- 会出问题,抛出InvalidClassException异常
如果出问题了,如何解决呢?
- 给对象所属的类加一个seriaLVersionUID
private static final long seriaLVersionUID=42L;
如果一个对象中的某个成员变量的值不想被序列化,又该如何实现呢?
- 给该成员变量加transient关键字修饰,该关键字标记的成员变量不参与序列化过程
import java.io.Serializable;
public class Student implements Serializable {
private static final long serialVersionUID = 42L;
private String name;
// private int age;
private transient int age;// 被transient关键字修饰的成员变量,不参与序列化过程
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;
}
public Student(String name, int age) {
super();
this.name = name;
this.age = age;
}
public Student() {
super();
}
// 修改了类文件
@Override
public String toString() {
return "Student [name=" + name + ", age=" + age + "]";
}
}
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
/*
用对象序列化流序列化了一个对象后,假如我们修改了对象所属的类文件,读取数据会不会出问题呢?
java.io.InvalidClassException:
当序列化运行时检测到类中的以下问题之一时抛出。
类的串行版本与从流中读取的类描述符的类型不匹配
该类包含未知的数据类型
该类没有课访问的无参数构造函数
Contents2.Student; local class incompatible:
stream classdesc serialVersionUID = -7792934128464322290,
local class serialVersionUID = -394614500484104815
序列化版本号不同
如果出问题了,如何解决呢?
给对象所属的类加一个值:private static final long seriaLVersionUID=42L;
如果一个对象中的某个成员变量的值不想被序列化,又该如何实现呢?
private transient int age;
*/
public class ObjectStreamDemo {
public static void main(String[] args) throws IOException, ClassNotFoundException {
// write();
read();
}
// 反序列化
private static void read() throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("..\\hello java\\oos.txt"));
Object obj = ois.readObject();
Student s = (Student) obj;
System.out.println(s.getName() + "," + s.getAge());
ois.close();
}
// 序列化
private static void write() throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("..\\hello java\\oos.txt"));
Student s = new Student("小白", 12);
oos.writeObject(s);
oos.close();
}
}
Properties
Properties概述:
- 是一个Map体系的集合类
- Properties可以保存到流中或从流中加载
练习:Properties作为Map集合的使用
import java.util.Properties;
import java.util.Set;
/*
Properties概述:
- 是一个Map体系的集合类
- Properties可以保存到流中或从流中加载
练习:Properties作为Map集合的使用
*/
public class PorpertiesDemo {
public static void main(String[] args) {
// 创建集合对象
// Properties<String,String> prop=new Properties<String,String>();//错误写法
Properties prop = new Properties();
// 存储元素
prop.put("hello1", "小白");
prop.put("hello2", "小黑");
prop.put("hello3", "小红");
// 遍历集合
Set<Object> keySet = prop.keySet();
for (Object key : keySet) {
Object value = prop.get(key);
System.out.println(key + "," + value);
}
}
}
运行结果:
Properties作为集合的特有方法:
方法名 | 说明 |
---|---|
Object setProperty(String key,String value) | 设置集合的键和值,都是String类型,底层调用Hashtable方法put |
String getProperty(String key) | 使用此属性列表中指定的键搜索属性 |
Set< String > stringPropertyNames() | 从该属性列表中返回一个不可修改的键集,其中键及其对应的值是字符串 |
import java.util.Properties;
import java.util.Set;
/*
Properties作为集合的特有方法:
Object setProperty(String key,String value)
设置集合的键和值,都是String类型,底层调用Hashtable方法put
String getProperty(String key)
使用此属性列表中指定的键搜索属性
Set< String > stringPropertyNames()
从该属性列表中返回一个不可修改的键集,其中键及其对应的值是字符串
*/
public class PorpertiesDemo2 {
public static void main(String[] args) {
// 创建集合对象
Properties prop = new Properties();
// Object setProperty(String key,String value)
// 设置集合的键和值,都是String类型,底层调用Hashtable方法put
prop.setProperty("hello1", "小白");
prop.setProperty("hello2", "小黑");
prop.setProperty("hello3", "小红");
// String getProperty(String key)使用此属性列表中指定的键搜索属性
// System.out.println(prop.getProperty("hello1"));//小白
// System.out.println(prop.getProperty("hello11"));//null
// System.out.println(prop);
// Set< String > stringPropertyNames()从该属性列表中返回一个不可修改的键集,其中键及其对应的值是字符串
Set<String> names = prop.stringPropertyNames();
for (String key : names) {
// System.out.println(key);
String value = prop.getProperty(key);
System.out.println(key + "," + value);
}
}
}
运行结果:
Properties和IO流结合的方法:
方法名 | 说明 |
---|---|
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)方法的格式写入输出字符流 |
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Properties;
/*
Properties和IO流结合的方法:
void Load(Reader reader)
从输入字符流读取属性列表(键和元素对)
void store(Writer writer,String comments)
将此属性列表(键和元素对)写入此Properties表中,以适合使用load(Reader)方法的格式写入输出字符流
*/
public class PorpertiesDemo3 {
public static void main(String[] args) throws IOException {
// 把集合中的数据保存到文件
myStore();
// 把文件中的数据加载到集合
myLoad();
}
private static void myLoad() throws IOException {
Properties prop = new Properties();
// void Load(Reader reader)
FileReader fr = new FileReader("..\\hello java\\fw.txt");
prop.load(fr);
fr.close();
System.out.println(prop);
}
private static void myStore() throws IOException {
Properties prop = new Properties();
prop.setProperty("hello1", "小白");
prop.setProperty("hello2", "小黑");
prop.setProperty("hello3", "小红");
// void store(Writer writer,String comments)
FileWriter fw = new FileWriter("..\\hello java\\fw.txt");
prop.store(fw, null);
fw.close();
}
}
运行结果:
案例:游戏次数
需求:请写程序实现猜数字小游戏只能试玩3次,如果还想玩,提示:游戏试玩已结束,想玩请充值(www.baidu.com)
思路:
- 写一个游戏类,里面有一个猜数字的小游戏
- 写一个测试类,测试类中有main()方法,main()方法中按照下面步骤完成:
- 从文件中读取数据到Properties集合,用load()方法实现
A.件已经存在:game.txt
B.里面有一个数据值:count=0 - 通过Properties集合获取到玩游戏的次数
- 判断次数是否到3次了
A.如果到了,给出提示:游戏试玩已结束,想玩请充值(www.baidu.com)
B.如果不到3次:
玩游戏
次数+1,重新写回文件,用Properties的store()方法实现
import java.util.Random;
import java.util.Scanner;
// 1. 写一个游戏类,里面有一个猜数字的小游戏
public class GuessNumber {
private GuessNumber() {
}
public static void start() {
// 生成随机数1到100
Random r = new Random();
int number = r.nextInt(100) + 1;
while (true) {
// 键盘输入猜的数字
Scanner sc = new Scanner(System.in);
System.out.println("请输入你猜测的数字:");
int gn = sc.nextInt();
// 比较录入的数字和系统产生的数据
if (gn > number) {
System.out.println("猜大了!");
} else if (gn < number) {
System.out.println("猜小了!");
} else {
System.out.println("猜对了!");
break;
}
}
}
}
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Properties;
/*
案例:游戏次数
需求:请写程序实现猜数字小游戏只能试玩3次,如果还想玩,提示:游戏试玩已结束,想玩请充值(www.baidu.com)
思路:
1. 写一个游戏类,里面有一个猜数字的小游戏
2. 写一个测试类,测试类中有main()方法,main()方法中按照下面步骤完成:
1.从文件中读取数据到Properties集合,用load()方法实现:
A.件已经存在:game.txt
B.里面有一个数据值:count=0
2.通过Properties集合获取到玩游戏的次数
3.判断次数是否到3次了:
A.如果到了,给出提示:游戏试玩已结束,想玩请充值(www.baidu.com)
B.如果不到3次:
玩游戏
次数+1,重新写回文件,用Properties的store()方法实现
*/
public class PropertiesTest {
public static void main(String[] args) throws IOException {
// 1.从文件中读取数据到Properties集合,用load()方法实现
Properties prop = new Properties();
FileReader fr = new FileReader("..\\hello java\\game.txt");
prop.load(fr);
fr.close();
// 2.通过Properties集合获取到玩游戏的次数
String count = prop.getProperty("count");
int number = Integer.parseInt(count);// 类型转换
// 3.判断次数是否到3次了
if (number >= 3) {
// A.如果到了,给出提示:游戏试玩已结束,想玩请充值(www.baidu.com)
System.out.println("游戏试玩已结束,想玩请充值(www.baidu.com)");
} else {
// 玩游戏
GuessNumber.start();
// 次数+1,重新写回文件,用Properties的store()方法实现
number++;
prop.setProperty("count", String.valueOf(number));// string中有一个valueOf方法把int类型转为字符串
FileWriter fw = new FileWriter("..\\hello java\\game.txt");
prop.store(fw, null);
fw.close();
}
}
}
运行结果:
第一次运行:
第二次运行:
第三次运行:
第四次运行: