目录
一:IO流小结
1.1 案例:复制多级文件(使用递归)
package package01;
import java.io.*;
class Main1{
public static void copyFile(File srcFile,File destFile) throws IOException {
BufferedInputStream bis=new BufferedInputStream(new FileInputStream(srcFile));
BufferedOutputStream bos=new BufferedOutputStream(new FileOutputStream(destFile));
int len;
byte[] bys=new byte[1024];
while((len=bis.read(bys))!=-1){
bos.write(bys,0,len);
bos.flush();
}
bis.close();
bos.close();
}
public static void main(String[] args) throws IOException {
//创建数据源目录File对象
File srcFolder=new File("D:\\IT");
//创建目的地File对象
File destFile =new File("D:\\");
//写方法实现文件夹的复制,参数为数据源File对象和目的地File对象
copyFolder(srcFolder,destFile);
}
private static void copyFolder(File srcFolder,File destFolder) throws IOException {
//判断数据File是否是目录
if(srcFolder.isDirectory()){
//在目的地下创建和数据源File名称一样的目录
String srcFolderName=srcFolder.getName();
File newFolder=new File(destFolder, srcFolderName);
if(!newFolder.exists()){
newFolder.mkdir();
}
//获取数据源File文件下所有文件或者目录的File数组
File[] fileArray=srcFolder.listFiles();
//遍历该数组,得到每一个File对象
for(File file:fileArray){
//把该File作为数据File对象,递归调用复制文件夹的方法
copyFolder(file,newFolder);
}
}else{
//是文件,直接赋值,用字节流
copyFile(srcFolder, destFolder);
}
}
}
1.2 复制文件的异常处理
import java.io.*;
class Main1 {
//1.抛出异常
public static void mehtod1() throws IOException{
FileReader fr=new FileReader("fr.txt");
FileWriter fw=new FileWriter("fw.txt");
char[] chs=new char[1024];
int len;
while((len=fr.read(chs))!=-1){
fw.write(chs,0,len);
}
fw.close();
fr.close();
}
//2.try...catch...finally
public static void method2(){
FileReader fr=null;
FileWriter fw=null;
try{
fr=new FileReader("fr.txt");
fw=new FileWriter("fw.txt");
char[] chs=new char[1024];
int len;
while((len=fr.read(chs))!=-1){
fw.write(chs,0,len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if(fr!=null){
try{
fr.close();
}catch (IOException e){
e.printStackTrace();
}
}
if(fw!=null){
try{
fw.close();
}catch (IOException e){
e.printStackTrace();
}
}
}
}
//3.JDK7的改进方案
public static void method3() {
try (FileReader fr = new FileReader("fr.txt");
FileWriter fw = new FileWriter("fw.txt");) {
char[] chs = new char[1024];
int len;
while ((len = fr.read(chs)) != -1) {
fw.write(chs, 0, len);
}
} catch (IOException e) {
e.printStackTrace();
}
}
//JDK9的改进方案
public static void mehtod4() throws IOException{
FileReader fr=new FileReader("fr.txt");
FileWriter fw=new FileWriter("fw.txt");
try(fr;fw){
char[] chs = new char[1024];
int len;
while ((len = fr.read(chs)) != -1) {
fw.write(chs, 0, len);
}
}catch (IOException e){
e.printStackTrace();
}
}
public static void main(String[] args) {
}
}
二:特殊操作流
2.1 标准输入输出流
System类中有两个静态的成员变量:
- public static final InputStream in:标准输入流。通常该流对应于键盘输入或由主机环境或用户指定的另一个输入源。
- public satic final PrintStream out:标准输出流。通常该流对应于显示输出或由主机环境或用户指定的另一个输出目标。
自己实现键盘录入数据:
BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
写起来太麻烦,Java就提供了一个类实现键盘录入:
Scanner sc=new Scanner(System.in);
输出语句的本质:是一个标准的输出流
PrintStream ps=System.out;
PrintStream类有的方法,System.out都可以使用。
2.2 打印流
打印流分类:
- 字节打印流:PrintStream
- 字符打印流:PrintWriter
打印流的特点:
- 只负责输出数据,不负责读取数据
- 有自己的特有方法
字节打印流:
PrintStream(String fileName):使用指定的文件名创建新的打印流。
使用继承父类的方法写数据,查看的时候会转码;使用字节的特有方法写数据,查看的数据原样输出。
字符打印流的构造方法:
2.2 案例:复制Java文件(打印流改进版)
import java.io.*;
class Main1{
public static void main(String[] args) throws IOException{
//根据数据源创建字符输入流对象
BufferedReader br=new BufferedReader(new FileReader("mokuai-06\\Farmers.java"));
//根据目的地创建字符输出流对象
PrintWriter pw=new PrintWriter(new PrintWriter("mokuai-06\\copy.java"));
//读写数据,复制文件
String line;
while((line=br.readLine())!=null){
pw.println(line);
}
//释放资源
br.close();
pw.close();
}
}
三: 对象序列化流
3.1 对象序列化流:ObjectOutputStream
将java对象的原始数据类型和图形写入OutputStream。可以使用ObjectInputStream读取(重构)对象。可以通过使用流的文件来实现对象的持久存储。如果流是网络套接字流 ,则可以在另一个主机上或另一个进程中重构对象。
构造方法:ObjectOutputStream(OutputStream out):创建一个写入指定的OutputStream的ObjectOutputStream
序列化对象的方法:
void writeObject(Object obj):将指定的对象写入ObjectOutputStream
注意:
- 一个对象要想被序列化,该对象所属的类型必须实现Serializable接口
- Serializable是一个标记接口,实现该接口,不需要重写任何方法
3.2 对象反序列化流:ObjectInputStream
ObjectInputStream反序列化先前使用ObjectOutputStream编写的原始数据和对象
构造方法:
ObjectInputStream(InputStream in):创建从指定的InputStream读取的ObjectInputStream
反序列化对象的方法:
Object readObject():从ObjectInputStream读取一个对象
import java.io.*;
class Main1 {
public static void main(String[] args) throws IOException, ClassNotFoundException {
//ObjectInputStream(InputStream in):创建从指定的InputStream读取的ObjectInputStream
ObjectInputStream ois=new ObjectInputStream(new FileInputStream("mokuai-06\\oos.txt"));
//Object readObject():从ObjectInputStream读取一个对象
Object obj=ois.readObject();
student s=(student) obj;
System.out.println(s.getName()+","+s.getAge());
ois.close();
}
}
3.3 serialVersionUID&transient
1. 用对象序列化流序列了一个对象后,加入我们修改了对象所属的类文件,读取数据会不会出现问题呢?
答:会出问题,抛出InvalidClassException异常
2. 如果出问题了,然后解决呢?
答:给对象所属的类加一个serialVersionUID
private static final long serialVersionUID=42L;
3. 如果一个对象中的某个成员变量的值不想被序列化,又改如何实现呢?
答:给改成员变量加transient关键字修饰,该关键字标记的成员变量不参与序列化过程。
package package01;
import java.io.*;
// InvalidClassException
/*.
InvalidClassException:
package01.student; local class incompatible:
stream classdesc serialVersionUID = -1516615626842904233,
local class serialVersionUID = -5576440835325464672
InvalidClassException:当序列化运行时检测到类中的以下问题之一时抛出。
1.类的串行版本与从流中读取的类描述符的类型不匹配
2.该类包含未知的数据类型
3.该类没有可访问的无参数构造函数
*/
class Main1 {
//序列化
public static void write() throws IOException{
ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("mokuai-06\\oos.txt"));
student s=new student("小花",18);
oos.writeObject(s);
oos.close();
}
//反序列化
public static void read() throws IOException, ClassNotFoundException {
ObjectInputStream ois=new ObjectInputStream(new FileInputStream("mokuai-06\\oos.txt"));
Object obj=ois.readObject();
student s=(student) obj;
System.out.println(s.getName()+","+s.getAge());
ois.close();
}
public static void main(String[] args) throws IOException, ClassNotFoundException {
write();
read();
}
}
/*序列化运行时与每个可序列化的类关联一个版本号,称为serialVersionUID,
它在反序列化过程中使用,以验证序列化对象的发送者和接收者是否加载了与序列化兼容的对象的类。如果接收者已经为具有与对应发件人类别不同的serialVersionUID的对象加载了一个类,则反序列化将导致一个InvalidClassException 。 一个可序列化的类可以通过声明一个名为"serialVersionUID"的字段来显式地声明它自己的serialVersionUID,
该字段必须是static,final和long类型:*/
class student implements Serializable{
private String name;
private transient int age;
public static final long serialVersionUID = 42L;//声明一个显式的serialVersionUID值
/*如果可序列化类没有显式声明serialVersionUID,则序列化运行时将根据Java(TM)对象序列化规范中所述的类的各个方面计算该类的默认serialVersionUID值。 然而, 强烈建议所有可序列化的类显式声明serialVersionUID值,因为默认的serialVersionUID计算对类细节非常敏感,这些细节可能因编译器实现而异,因此可能会在反序列化期间导致意外的InvalidClassException 。 因此,为了保证不同Java编译器实现之间的一致的serialVersionUID值,一个可序列化的类必须声明一个显式的serialVersionUID值。 还强烈建议,显式的serialVersionUID声明在可能的情况下使用private修饰符,因为这种声明仅适用于立即声明的类 - serialVersionUID字段作为继承成员无效。 数组类不能声明一个显式的serialVersionUID,所以它们总是具有默认的计算值,但是对于数组类,放弃了匹配serialVersionUID值的要求。 */
public student(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;
}
@Override
public String toString() {
return "student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
四:properties
4.1 Properties的概述:
- 是一个Map体系的集合类
- Properties可以保存到流中或从流中加载
import java.util.Properties;
import java.util.Set;
class Main1{
public static void main(String[] args) {
/*
* Properties作为Map集合的使用
* */
//创建集合对象
Properties prop=new Properties();
//存储元素
prop.put("ithieima001","小白");
prop.put("ithieima002","小黑");
//遍历集合
Set<Object> keySet = prop.keySet();
for(Object key:keySet){
Object value = prop.get(key);
System.out.println(key+","+value);
}
}
}
4.2 Properties作为集合的特有方法:
import java.util.Properties;
import java.util.Set;
class Main1{
public static void main(String[] args) {
/*
* Properties作为Map集合的使用
* */
//创建集合对象
Properties prop=new Properties();
//存储对象
prop.setProperty("itheima001","小花");
prop.setProperty("itheima002","小白");
prop.setProperty("itheima003","小黑");
// System.out.println(prop.getProperty("itheima001"));---根据键获取值
Set<String> names=prop.stringPropertyNames();//返回一个不可修改的键集
for(String key:names){
String value=prop.getProperty(key);
System.out.println(key+","+value);
}
// System.out.println(prop);
}
}
4.3 Properties和IO流结合的方法:
import java.io.*;
import java.util.Iterator;
import java.util.Properties;
import java.util.Set;
class Main1{
public static void myStore() throws IOException{
Properties prop=new Properties();
prop.setProperty("itheima001","小花");
prop.setProperty("itheima002","小白");
prop.setProperty("itheima003","小黑");
FileWriter fw=new FileWriter("mokuai-06\\fws.txt");
prop.store(fw,null);
fw.close();
}
public static void myLoad() throws IOException{
Properties prop=new Properties();
FileReader fr=new FileReader("mokuai-06\\fws.txt");
prop.load(fr);
Set<String> keySet = prop.stringPropertyNames();
for(String key:keySet){
String value=prop.getProperty(key);
System.out.println(key+","+value);
}
fr.close();
}
public static void main(String[] args) throws IOException {
myStore();
myLoad();
}
}
4.4 案例:游戏次数
package package01;
import java.io.*;
import java.util.*;
class Main1{
public static void main(String[] args) throws IOException {
Properties prop=new Properties();
FileReader fr=new FileReader("mokuai-06\\Game.txt");
prop.load(fr);
fr.close();
//通过Properties集合获取到玩游戏的次数
String count=prop.getProperty("count");
int number=Integer.parseInt(count);
//判断游戏次数是否到达三次
if(number>=3){
System.out.println("游戏试玩已结束,想玩请充值(www.itCast.cn)");
}else{
Game.start();
number++;
prop.setProperty("count",String.valueOf(number));
FileWriter fw=new FileWriter("mokuai-06\\Game.txt");
prop.store(fw,null);
fw.close();
}
}
}
class Game{
private static int GuessNumber;
public static void start(){
Random r=new Random();
GuessNumber=r.nextInt(100)+1;
Scanner sc=new Scanner(System.in);
while (true){
System.out.println("请输入你要猜的数字:");
int guessNumber=sc.nextInt();
if(GuessNumber>guessNumber){
System.out.println("你猜的数字"+guessNumber+"小了");
}else if(GuessNumber<guessNumber){
System.out.println("你猜的数字"+guessNumber+"大了");
}else if(GuessNumber==guessNumber){
System.out.println("恭喜你,猜对了!");
break;
}
}
}
}
(PS:文章截图来自黑马程序员java全套视频)