特殊操作流、标准输入输出流、打印流(有案例)、对象序列化流(三个问题)serialVersionUID&transient、Properties作为Map集合、Properties和IO流相结合的方法
目录
标准输入输出流
System类中有两个静态的成员变量
注:final修饰的class不能被继承也没有子类只有方法,static修饰后可以直接用类名调用方法
- public static final InputStream in:标准输入流。通常该流对于键盘输入或由主机环境或用户指定的另一个输入源
- public static final PrintStream out:标准输出流。通常该流对应于显示输出或由主机环境或用户指定的另一个输出目标
输入流(in):
public abstract class InputStream
extends Object
Implements Closeable
代码示例:
InputStream in=System.in;
int len;
while((len=in.read())!=-1){
//接收一个字节输出
System.out.print((char)len);
//无限输入输出 键盘录入中文时会出现乱码(因为是靠一个一个字节转换为char输出的)
}
//字符缓冲流输出时不会有键盘录入中文就乱码的情况
BufferedReader br = new BufferedReader( new InputStreamReader(System.in) );
String s = br.readLine(); //调用String readLine() 方法返回一个字符串
System.out.print( s );
int i = Integer.parseInt( br.readLine() );
//调用Integer包装类方法parseInt()返回int类型得到一个整数
注:JAVA提供的System.in方法和Scanner类对比,Scanner(System.in)底层逻辑仍是由System.in的标准输入流来设计的
输出流(Out)
public class PrintStream
extends FilterOutputStream
implements Appendable,Closeable
作用:方便打印出各种数据类型的值
代码示例:
PrintStream os=System.out; //多态实现
ps.println("hello"); //和System.out.println();的本质是相同的
ps.print("world"); //调用print()时必须带输出参数,否则编译器会报错。
字节打印流(PrintStream)extends OutputStream
打印流特点:
- 只负责输出,不负责读取数据
- 有字节的特有方法(print)
字节打印流:
- 构造方法:PrintStream(String FileName); //使用指定的文件名创建新的打印流
代码示例:
PrintStream ps = new printStream("file对象文件名");
ps.write(97); //调用OutputStream中的方法写入数据 在文件会中输出a
ps.print(97); //调用特有方法写入数据 在文件中输出97 (写入什么输出什么)
字符打印流(PrintWriter)extends Writer
构造方法:
方法名 | 作用 |
---|---|
PrintWriter(String FlieName) | 使用指定的文件名创建一个新的PrintWriter,而不需要自动执行刷新 |
PrintWriter(Writer out,boolean autoFlush) | 创建一个新的PrintWriter out:字符输出流 autoFlush:一个boolean值,若为真,则println,printf,或format方法将刷新输出缓冲区 |
代码示例:
PrintWriter pw = new PrintWriter("file对象文件名");
pw.write("hello"); //注意:字符流在输出写入后需要调用flush()方法后 才可以在文件中显示写入的数据
pw.write("\r\n"); //windows操作系统换行符\r\n linux操作系统换行符\n
pw.write("world");
pw.flush(); //刷新一下
//pw.close(); 字符流中的close()方法在释放资源前会调用一次flush()方法 字符打印流无需close()
特有方法println()
pw.println("hello");
pw.flush();
pw.println("world");
pw.flush();
//pw.close(); 字符打印流无需close()
简便优化后的字符打印流方法:
PrintWriter( Writer out,boolean autoFlush );
PrintWriter pw = new PrintWriter( new FileWriter("file对象文件名") , true );
pw.println("hello"); //print和println两方法均可实现自动刷新 也就是autoFlush
pw.print("world");
字符打印流复制JAVA文件案例
传统的字符缓冲流复制文件:
BufferedReader br = new BufferedReader( new FileReader("源文件名") );
BufferedWriter bw = new BufferedWriter( new FileWriter("目的地文件名") );
String s;
while( (s = br.readLine())!=-1 ){
bw.write(s);
bw.newLine();
bw.flush();
}
bw.close();
br.close();
打印流改进版:
BufferedReader br = new BufferedReader( new FileReader("源文件名") );
PrintWriter pw = new PrintWriter( new FileWriter("目的地文件名") );
String s;
while( (s=br.readLine())!=-1 ){
pw.println(s); //三步变一步
}
pw.close(); //字符打印流最后同样要释放资源
br.close();
对象序列化流
- 就是将对象保存到磁盘中,或者在网络中传输对象
- 这种机制就是使用一个字节序列表示一个对象,该字节序列包含:对象的类型、对象的数据和对象中储存的属性等信息
- 字节序列写到文件之后,相当于文件中持久保存了一个对象的信息
- 反之,该字节序列还可以从文件中读取回来,重构对象,对它进行反序列化
要实现序列化和反序列化就要使用对象序列化流和对象反序列化流:
- 对象序列化流:ObjectOutputStream
- 对象反序列化流:ObjectInputStream
对象序列化流(ObjectOutputStream)
所在包:java.io
public class ObjectOutputStream
extends OutputStream
implements ObjectOutput,ObjectStreamConstants
注意:
- 一个对象要想被序列化,该对象所属的类必须实现Serializable接口
- Serializable是一个标记接口,实现该接口,不需要重写任何方法
代码示例:
构造方法:
ObjectOutputStream(OutputStream out):创建一个写入指定的OutputStream的ObjectOutputStream
ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream(" 文本文件 "));
//创建对象
Student s=new Student(name:"林青霞",age:30);
//public class Student implements Serializable
//void writeObject(Object obj):将指定的对象写入ObjectOutputStream
oos.writeObject( s );
//注意编写入文件的编码不能直接读取 需要用反序列化流读出数据
//释放资源
oos.close();
反对象序列化流(ObjectInputStream)
所在包:java.io
public class ObjectInputStream
extends InputStream
implements ObjectInput,ObjectStreamContans
ObjectInputStream反序列化先前使用ObjectOutputStream编写的原始数据和对象 //就是从文件中读取对象
构造方法:
- ObjectInputStream(InputStream in): 创建从指定的InputStream读取的ObjectputStream
反序列化对象的方法:
- Object readObject():从ObjectInputStream读取一个对象
代码示例:
ObjectInputStream ois=new ObjectInputStream(new FileInputStream( "需要读取的数据源" ));
Object obj = ois.readObject(); //readObject();的返回值为Object ,创建Object对象接收即可
Student s = (Student) obj; //Student向上转型 再将obj对象传给s
//再控制台输出对象序列化流内容
System.out.print(s.getName()+","+s.getId()+","+s.getScore());
ois.close(); //释放空间
*对象序列化的三个问题
- 用对象序列化流序列化一个对象后,假如我们修改了对象所属的类文件,读取数据会不会出问题呢?(在对象类中修改代码)
答:会出问题,抛出InvalidClassException异常 - 如果报错了,如何解决呢?
答:给对象所属的类添加一个serialVersionUID
private static final long serialVersionUID = 42L; - 如果一个对象中的某个成员变量的值不想被序列化,又该如何实现?
答:给对象成员添加一个transient关键字修饰,该关键字标记的成员变量不参与序列化过程
代码示例:
public static void main(String[] args) throws IOException,ClassNotFoundException{
write();
read();
}
public static void read() throws IOException,ClassNotFoundException{
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("C:\\Users\\23353\\Desktop\\测试案例0\\测试软件\\java.txt"));
Object obj = ois.readObject();
Student s = (Student)obj;
System.out.println(s.getName()+","+s.getId()+","+s.getScore());
ois.close();
}
public static void write() throws IOException{
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("C:\\Users\\23353\\Desktop\\测试案例0\\测试软件\\java.txt"));
Student s = new Student("张三",3,99);
oos.writeObject(s);
oos.close();
}
注意:只有实现了Serializable接口的类才可以被序列化
public class Student implements Serializable{
private static final long serialVersionUID=42L;
//在类中提前给UID赋值避免类自动采用默认UID值导致报错I nvalidClassException
private String Name;
private transient int Id;
//在成员变量前加上修饰符transient 在控制台输出该成员时会输出为赋值的默认值(String默认为null 整型int默认为0)
private int Score;
}
Properties
Properties作为Map集合的使用
所在包:
java.util.Hashtable<Object,Object>
java.util.Properties
public class Properties
extends Hashtable<Object,Object>
概述:
- Proterties是一个Map体系的集合类
- Properties可以保存到流中,或从流中加载出来
Properties作为集合的特有方法:
方法名 | 作用 |
---|---|
Object setProperty(String key,String value) | 设置集合的键和值,都是String类型,底层调用Hashtable方法put |
String getProperty(String key) | 使用此属性列表中指定的键搜索属性 |
Set< String >stringPropertyNames() | 从该属性列表中返回一个不可修改的键集,其中键及其对应的值是字符串 |
代码示例:
Properties作为Map集合的使用
Properties pr=new Properties();
pr.put(K,V);
pr.put(K,V);
Set<Object> keySet=pr.keySet();
for(Object key:keySet){
Object value=pr.get(key);
System.out.println(key+","+value);
}
特有方法示例:
Properties pr = new Properties();
pr.setProperty(K,V);
pr.setProperty(K,V);
Set<String> names=pr.stringPropertyNames(); //stringPropertyNmaes类似keySet()
for(String key : names){
String value = pr.getProperty(key); //注:getProperty();类似Map中get()方法
System.out.print(key+","+value);
}
*Properties和IO流的结合使用
和IO流结合使用的常用方法:
方法名 | 作用 |
---|---|
void load(InputStream isStream) | 从输入字节流读取属性列表(键和元素对) |
void load(Reader reader) | 从输入字符流读取属性列表(键和元素对) |
void store(OutputStream out,Stream comments) | 将此属性列表(键和元素对)写入Properties表中,以适合于使用load(InputStream)方法的格式写入输出字节流 |
void store(Writer writer,String comments) | 将此属性列表(键和元素对)写入此Properties表中,以适合使用load(Reader)方法的格式写入输出字符流 |
代码示例:
public static void main(String[] args) throws IOException{
myStore(); //将集合添加到文件 (把集合中的元素添加到文件中持久化保存)
myload(); //将文件内容添加到集合 (读取文件中持久化保存的数据)
}
private static void myload() throws IOException{
//要使用字符流将文件加载到集合只需要定义FileReader fr = new FileReader();即可
//把文件加载到集合
Properties prop = new Properties();
FileInputStream fr = new FileInputStream("C:\\Users\\23353\\Desktop\\java.txt");
//定义字节流输出对象
prop.load(fr);
fr.close();
System.out.println(prop);
}
private static void myStore() throws IOException{
//把集合添加到文件
Properties prop = new Properties();
FileOutputStream fw = new FileOutputStream("C:\\Users\\23353\\Desktop\\java.txt");
//定义字节流输入对象
prop.setProperty("1","zhang");
prop.setProperty("2","zhang");
prop.setProperty("3","zhang");
prop.store(fw,null);
//用字节流的方式保存集合中的元素(会自动生成一行保存时间)
//例:#Sun Oct 24 14:37:51 CST 2021
fw.close();
}
案例:游戏次数
要求:请写程序实现猜数字小游戏只能玩3次,如果还要玩,提示:试玩已结束请充值
思路:
- 写一个游戏类,里面有猜数字小游戏
- 写一个测试类,测试类中有main()方法,main()方法中按照下面的步骤完成:
- 从文件中读取数据到Properties集合中,用load()方法实现
文件已存在:game.txt
文件中有:count=0 - 通过Properties集合方法获取count键对应的值0为游戏进行的次数
- 判断游戏次数是否进行到3次了
如果已经到3次了则给出提示:试玩已结束请充值
如果没有到3次则:count++,将count=1存入文件表示游戏已经进行过1次了
用store()方法实现将集合写入文件
- 从文件中读取数据到Properties集合中,用load()方法实现
代码示例:
public class game{
//游戏类
public static void start(){
Scanner sc = new Scanner(System.in);
Random r = new Random();
int n = r.nextInt(100)+1;
while(true){
int x = sc.nextInt();
if(x>n){
System.out.println("你输入的数字大了");
}elseif(x<n){
System.out.println("你输入的数字小了");
}else{
System.out.println("恭喜你猜对了");
break;
}
}
}
}
public static void main(String[] args) throws IOException{
//测试类
demo();
}
public static void demo() throws IOException{
Properties prop = new Properties(); //相当于一个Map集合
FileReader fr = new FileReader("C:\Users\23353\Desktop\\java.txt");
prop.load(fr); //将文件添加到集合中
fr.close();
String count = prop.getProperty("count");
int number = Integer.parseInt(count);
if(number>=3){
System.out.println("试玩已结束请充值");
}else{
game.start();
FileWriter fw = new FileWriter("C:\Users\23353\Desktop\\java.txt");
number++;
prop.setProperty("count",String.valueOf(number));
prop.store(fw,null); //将集合中已经更新过的数据添加到文件
fw.close();
}
}