字节流、字符流、异常处理、Properties
一、IO流
1.1 IO
- I:input:输入(读取) 把硬盘中的数据,读取到内存中使用,输入流
- O:output:输出(写入) 把内存中的东西,写入到硬盘中,输出流
- 流:数据(字符字节) 1个字符 = 2个字节 = 8个二进制位
1.2 IO的分类
- 根据流向:输入流和输出流
- 输入流:把数据从其他设备上读取到内存中的流
- 输出流:把数据从内存中写出到其他设备上的流
- 数据类型:字节流和字符流
- 字节流:以字节为单位。
- 字符流:以字符为单位
1.3 IO流的超类
输入流 | 输出流 | |
---|---|---|
字节流 | 字节输入流:InputStram | 字节输出流:OutputStream |
字符流 | 字节输出流:Read | 字符输出流:Writer |
二、字节流
2.1 一切皆为字节
- 一切文本(文本、图片、视频)等在存储的时候都是以二进制的形式保存的,都是一个字节,所以字节流可以传输任意文件数据。
- 底层传输都是二进制数据
2.2 字节输出流 【 OutputStream 】
java.io.OutputStream 抽象类是表示字节输出流的所有类的超类,将指定字节信息写出到目的地,它定义了字节输出流的基本共性功能方法
- public void close():关闭此输出流并释放此流相关的任何系统资源
- public void flush():刷新此输出流并强制任何缓冲的输出字节被写出
- public void write(byte[]b):将b.length个字节从指定的字节数组写出到目的地
- public void write(byte[]b,int off,int len):从指定的字节数组写出len字节,从偏移量off开始。
- public abstract void write(int b):将指定的字节输出流
【close 方法,当完成流的操作时,必须调用此方法,释放系统资源】
2.3 数据写出到文件 FileOutputStram 类
2.3.1 构造方法
- public FileOutputStream(File file):创建文件输出流以写入指定的File对象
- public FileOutputStream(File file, boolean append) 创建文件输出流以写入指定的File对象,true表示采用追加模式,false采用覆盖模式
- public FileOutputStream(String name):创建文件输出流以指定的名称写入文件。
- 创建一个 FileOutputStream 对象
- 会根据构造方法中的传递的文件/文件路径,创建一个空的文件
- 会把 FileOutputStream 对象指向创建好的文件
【步骤:
1、 准备一个字节输出流对象,并指定文件
2、 调用write方法,写数据
3、 调用flush方法,刷新
4、 在finally代码块中,关闭IO流对象,以释放所占的内存资源
】
2.4 字节输入流 【InputStream】
- java.io.InputStream 抽象类是表示输入流的所有类的超类,可以读取字节信息到内存中,它定义了字节输入流的基本共性的方法。
- public void close():关闭此输入流对象释放资源
- public abstract int read():从输入流中读取一个字节
- public int read(byte[]b):从输入流中读取一些字节,并将它们存储到字节数组b中
2.5 FileInputStream 类
java.io.FileInputStream类是文件输入流,从文件中读取字节
构造方法
- public FileInputStream(File file): 通过打开与实际文件的连接创建一个 FileInputStream ,该文件由文件系统中的 File对象 file命名。
- public FileInputStream(String name):通过打开与实际文件的连接来创建一个 FileInputStream ,该文件由文件系统中的路径名 name命名。
package com.bdit.test;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class Test01 {
public static void main(String[] args) {
FileOutputStream fios = null;
try {
//创建字节输出流
fios = new FileOutputStream(new File("F:\\bdit\\os\\osa.txt"));
// fios = new FileOutputStream("F:\\bdit\\os\\osb.txt");//以字符串模式写入地址
// fios = new FileOutputStream(new File("F:\\bdit\\os\\osa.txt"),true); 可以连续写入(默认是覆盖模式,写入rtue是追加)
//写入 write只能int和char类型,并且会转成AScii
fios.write(100);
fios.write(new byte[]{101,102,103,104});
fios.write("\r\n".getBytes());//换行
fios.write(new byte[]{101,102,103,104,105},2,3);//从第三个开始(包括第三个)往后截取3个
fios.write("\r\n".getBytes());
String str01 = "山东省";
byte[] ch01 =str01.getBytes();
fios.write(ch01);
fios.write('a');
fios.write("甲二".getBytes());
fios.write("jiaer".getBytes());
//刷新
fios.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
}catch (IOException e) {
e.printStackTrace();
}finally {
//关闭输入流
try {
fios.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
package com.bdit.test;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
public class Test02 {
public static void main(String[] args) {
//字节输入流
FileInputStream flos = null;
try {
//创建字节输入流
flos=new FileInputStream(new File("F:\\bdit\\os\\osa.txt"));
//读取数据
int a = flos.read();
while(a!=-1){
System.out.print((char)a+" ");
a=flos.read();
}
/* flos.read(new byte[]{101,102,103,104});
flos.read(new byte[]{101,102,103,104},1,3);*/
} catch (FileNotFoundException e) {
e.printStackTrace();
}catch (IOException e) {
e.printStackTrace();
}finally {
try {
flos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
2.6 练习
实现图片的复制
package com.bdit.vifition;
import java.io.*;
//通过字节输入输出流实现图片的复制
public class Vifi02 {
public static void main(String[] args) {
FileInputStream filios = null;
FileOutputStream fos =null;
try {
filios = new FileInputStream(new File("F:\\bdit\\c\\guolvqi.png"));
fos = new FileOutputStream(new File("F:\\bdit\\b\\aa.png"));
int a = filios.read();
while (a!=-1){
// System.out.println((char)a);
//读写
fos.write(a);
a=filios.read();
}
fos.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
finally {
try {
if (filios!=null){
filios.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if (fos!=null){
fos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
【字节流只适合用于操作,图片、视频、音频等以字节为单位存储数据的文件,不适合操作文本文件】
三、字符流
当字节流读取的时候,遇到中文字符的时,可能不显示完整的字符,因为一个中文字符占用两个字符存储,所以java提供了字符流,以字符为单位读写数据,专门用于处理文本文件的。
3.1 字符输入流 【Reader】
java.io.Reader 抽象类是表示用于读取字符的所有类的超类,可以读取字符信息到内存中。
- public void close():关闭对象释放所占的系统资源
- public int read():读取一个字符
- public int read(char[] buf):从输入流中读取一些字符,存储到字符数组中。
3.2 FileReader类
java.io.FileReader类是读取字符文件的类,构造方法使用系统默认的字符编码和默认字节缓冲区。
【1. 字符编码:字节与字符的对应规则,Windows系统的中文编码默认是GBK编码表,IDEA中UTF-8
【2. 字节缓冲区:一个字节数组,用来临时存储字节数据的】
构造方法
- FileReader(File file):创建一个新的 FileReader ,给出 File读取。
- FileReader(String fileName):创建一个新的 FileReader ,给定要读取的文件的名称。
package com.bdit;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
/*
FileReader 字符输入流
*/
public class Demo5 {
public static void main(String[] args)throws IOException {
FileReader fr=new FileReader(new File("e:\\a.txt"));
int len;
char[]cbuf=new char[2];
while((len=fr.read(cbuf))!=-1){
//System.out.println((char)len);
System.out.println(new String(cbuf));
}
fr.close();
}
}
3.3 字符输出流 【Writer】
java.io.FileWriter 类是写出字符到文件的类,构造时使用系统默认的字符编码和默认字节缓冲区
- void write(int c):写入单个字符
- void write(char[]cbuf):写入字符数组
- abstract void write(char[]cbuf,int off,int len):写入字符数组的某一部分,off数组的开始位置,len写的字符个数
- void write(String str):写入字符串
- void flush():刷新该流的缓冲
- void close():关闭对象,释放资源
3.4 FileWriter 类
java.io.FileWriter类是写出字符到文件的类,构造时使用系统默认的字符编码和默认字节缓冲区
构造方法
- FileWriter(File file) 给一个File对象构造一个FileWriter对象。
- FileWriter(File file, boolean append) 给一个File对象构造一个FileWriter对象。
- FileWriter(String fileName) 构造一个给定文件名的FileWriter对象。
- FileWriter(String fileName, boolean append) 构造一个FileWriter对象,给出一个带有布尔值的文件名,表示是否附加写入的数据。
package com.bdit;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
/*
FileWriter字符输出流
*/
public class Demo6 {
public static void main(String[] args)throws IOException {
FileWriter fw=new FileWriter(new File("e:\\a.txt"));
fw.write("张高三李四");
fw.write(48);
fw.write(new char[]{'a','b','c'});
fw.close();
}
}
3.5 关闭和刷新
因为内置缓冲区的原因:不关闭输出流,就无法写出字符到文件里,但是关闭的流对象,是无法继续写出数据的,如果我们要同时写数据和使用流,就需要flush 方法了。
- flush:刷新缓冲区,流对象可以继续使用
- close:先刷新缓冲区,然后通知系统释放资源,流对象不可再使用
- 使用完流一定要关闭
四、异常处理
4.1 jdk7之前
public class Demo1 {
public static void main(String[] args) {
FileOutputStream fos=null;
try {
//创建对象
fos = new FileOutputStream(new File("E:\\a.txt"),true);
fos.write(97);
fos.write(98);
fos.write(99);
fos.write('w');
fos.write(49);
//一个汉字占两个字节,所以如果写中文就会乱码
fos.write('博');
//
fos.write("zhangsan".getBytes());
fos.write("博道IT实训".getBytes());
byte[]b="abcde".getBytes();
fos.write(b,2,2);
//windows系统里,换行符是\r\n
fos.write("\r\n".getBytes());
fos.write("javase".getBytes());
}catch (FileNotFoundException e){
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
//关闭
fos.close();
}catch (IOException ce){
ce.printStackTrace();
}
}
}
}
4.2 jdk7的处理
jdk7支持 try-with-resource 语句,该语句确保了每个资源在语句结束时关闭,所谓的资源(resource)指在程序完成后,必须关闭的对象
try(需要关闭的资源对象,如果多个使用;隔开){ }catch(Exception ce){ }
package com.bdit;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
/*
try-with-resource
*/
public class Demo7 {
public static void main(String[] args) {
try(FileReader fr=new FileReader("e:\\a.txt");
FileWriter fw=new FileWriter("e:\\b.txt")){
int len;
while((len=fr.read())!=-1){
System.out.println((char)len);
}
}catch (IOException ce){
ce.printStackTrace();
}
}
}
4.3 jdk9的改进
JDK9中try-with-resource的改进,对于引入对象的方式,支持的更加简洁,被引入的对象,同样可以启动关闭无需手动close.
package com.bdit;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
/*
try-with-resource
*/
public class Demo8 {
public static void main(String[] args)throws IOException {
FileReader fr=new FileReader("e:\\a.txt");
FileWriter fw=new FileWriter("e:\\b.txt");
try(fr;fw){
int len;
while((len=fr.read())!=-1){
System.out.println((char)len);
}
}catch (IOException ce){
ce.printStackTrace();
}
}
}
五、属性集 Properties
5.1 概述
【HashMap 和Hashtable 都是通过 key=value 来存储数据,他的区别在于,Hashtable不能把null当作key和value】
java.util.Properties 继承自 Hastable ,来表示一个持久的属性及集,它使用键值对的结构存储数据,每个键及其对应的值都是一个字符串。
5.2 创建属性文件
- 属性文件一般放置在src根目录下,属性文件的后缀名.properties
- 属性文件中,每一行只能写一个key=value,而且不需要任何的符合来修饰
5.3 构造方法
- Properties() 创建一个没有默认的空属性列表
基本的存储方法
- public Object setProperty(String key, String value):保存一对键值对
- public String getProperty(String key):根据键获取值
package com.bdit;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
/*
Properties属性集
*/
public class Demo9 {
public static void main(String[] args) throws IOException {
//读取config.properties属性文件
InputStream inputStream=Thread.currentThread().getContextClassLoader().getResourceAsStream("config.properties");
//创建Properties对象
Properties properties=new Properties();
//把InputStream对象加载到properties对象中
properties.load(inputStream);
//根据key获取值
String name=properties.getProperty("name");
String age=properties.getProperty("age");
String address=properties.getProperty("address");
//给属性集对象中的key设置值
properties.setProperty("id","1006");
String id=properties.getProperty("id");
System.out.println(id+" "+name+" "+age+"
"+address);
}
}
六、序列化流
6.1 概述
- Java提供了一种对象序列化的机制用一个字节序列可以表示一个对象,该字节序列包含对象的数据、对象的类型、对象中存储的属性等信息。字节序列写出文件之后,相当于文件中持久存储了一个对象的信息 :
- 反之,该字节序列还可以从文件中读取回来,重构对象,对它进行反序列化。对象的数据,对象的类型和对象中存储的数据信息,都可以用来在内存中创建对象 。
【为什么要序列化?序列化后的数据,都会以二进制的形式在网络中进行传输,通过反序列化可以在任何一个终端进行数据的还原,简单的来说就是为了数据能够更方便地在网络中进行传输】
6.2 ObjectOutputStream 类
- java.io.ObjectOutputStream类,将Java对象的原始数据类型写出到文件,实现对象的持久存储。
构造方法
- ObjectOutputStream(OutputStream out):创建一个写入指定的OutputStream的ObjectOutputStream
6.3 序列化操作
- 一个对象想要序列化
- 必须实现 java.io. Serializable接口 ,该接口是一个标记接口,凡是有该接口的类的对象都可以序列化,如果没有实现该接口,则该类无法序列化或者反序列化同时还会爆出 NotSerializableException 问题
- 想要序列化,其类的属性一定是可序列化的,不能是被transient、static 关键字修饰
6.4 反序列化
ObjectInputStream 但序列化流
构造方法
- ObjectInputStream(InputStream in): 创建从指定的InputStream读取的ObjectInputStream。
- public final Object readObject():读取一个对象
package com.bdit.xulie;
import java.io.Serializable;
public class Student implements Serializable {
private static final long serialVersionUID = 4759662288187860941L;
private String id;
private String name;
private int age;
public Student(){}
public Student(String id,String name,int age){
this.id=id;
this.name=name;
this.age=age;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
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{" +
"id='" + id + '\'' +
", name='" + name + '\'' +
", age=" + age +
'}';
}
}
package com.bdit.xulie;
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import java.util.Scanner;
import java.util.zip.InflaterOutputStream;
//序列化
public class Xulie01 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
try(FileOutputStream fos =new FileOutputStream("F:\\bdit\\aas.txt");
ObjectOutputStream obj = new ObjectOutputStream(fos)){
Student stu01 = new Student("1001","张三",20);
obj.writeObject(stu01);
Student stu02 = new Student("1002","李四",25);
obj.writeObject(stu02);
Student stu03 = new Student("1002","李四",25);
obj.writeObject(stu03);
}catch (Exception ec){
ec.printStackTrace();
}
}
}
package com.bdit.xulie;
import java.io.FileInputStream;
import java.io.ObjectInputStream;
//反序列化
public class Fanxuli {
public static void main(String[] args) {
try(FileInputStream fin = new FileInputStream("F:\\bdit\\aas.txt");
ObjectInputStream oins = new ObjectInputStream(fin);){
/*一个
Object obj =oins.readObject();
Student student = (Student)obj;
System.out.println(student);*/
while (oins!= null){
Student student = (Student)oins.readObject();
System.out.println(student);
// oins.readObject();
}
}catch (Exception ec){
// ec.printStackTrace();
}
}
}
当JVM反序列化对象时,抛出了一个InvalidClassException异常,发生这个异常的原因如下:
1、 该类的序列化版本号与从流中读取的类描述的版本不匹配
2、 该类包含未知数据类型
Serializable
接口给需要序列化的类,提供了一个序列化版本号,
该版本号的目的在于验证序列化的对象和对应类是否版本匹配