Java——Contents3(特殊操作流、Object、Properties)

特殊操作流

标准输入输出流

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

思路:

  1. 根据数据源创建字符输入流对象
  2. 根据目的地创建字符输出流对象
  3. 读写数据,复值文件
  4. 释放资源

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)

思路:

  1. 写一个游戏类,里面有一个猜数字的小游戏
  2. 写一个测试类,测试类中有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();

		}

	}
}

运行结果:
第一次运行:
在这里插入图片描述
在这里插入图片描述
第二次运行:
在这里插入图片描述
在这里插入图片描述
第三次运行:
在这里插入图片描述
在这里插入图片描述
第四次运行:
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_47678894/article/details/119736142