IO技术概述

  • Output 把内存中的数据存储到持久化设备上这个动作称为输出(写)Output操作
  • Input 把持久设备上的数据读取到内存中的这个动作称为输入(读)Input操作
  • 把上面的这种输入和输出动作称为IO操作

IO中的异常处理

如果不使用try catch 可以添加抛出声明

public static void main(String[] args) throws IOException{

}
  • 保证流对象变量,作用域足够
  • catch里面,怎么处理异常
    • 输出异常的信息,目的看到哪里出现了问题
    • 停下程序,从新尝试
  • 如果流对象建立失败了,需要关闭资源吗
    • new 对象的时候,失败了,没有占用系统资源
    • 释放资源的时候,对流对象判断null
    • 变量不是null,对象建立成功,需要关闭资源
FileOutputStream fos = null;
// 注意变量的作用域问题
// try 外面声明变量,try 里面建立对象
try{
    fos = new FileOutputStream("s:\\a.txt");
    fos.write(100);
}catch(IOException ex){
    System.out.println(ex);
    throw new RuntimeException("文件写入失败,重试");
}finally{
    try{
        if(fos!=null)
          fos.close();
    }catch(IOException ex){
        throw new RuntimeException("关闭资源失败");
    }
}

Properties类

  • Properties 类表示了一个持久的属性集。Properties 可保存在流中或从流中加载。属性列表中每个键及其对应值都是一个字符串
  • Hashtable的子类,map集合中的方法都可以用。
  • 该集合没有泛型。键值都是字符串。
  • 它是一个可以持久化的属性集。键值可以存储到集合中,也可以存储到持久化的设备(硬盘、U盘、光盘)上。键值的来源也可以是持久化的设备。
  • 有和流技术相结合的方法。
Modifier and Type Method and Description
String getProperty(String key)
使用此属性列表中指定的键搜索属性。
String getProperty(String key, String defaultValue)
使用此属性列表中指定的键搜索属性。
void list(PrintStream out)
将此属性列表打印到指定的输出流。
void list(PrintWriter out)
将此属性列表打印到指定的输出流。
void load(InputStream inStream)
从输入字节流读取属性列表(键和元素对)。
void load(Reader reader)
以简单的线性格式从输入字符流读取属性列表(关键字和元素对)。
void loadFromXML(InputStream in)
将指定输入流中的XML文档表示的所有属性加载到此属性表中。
Enumeration<?> propertyNames()
返回此属性列表中所有键的枚举,包括默认属性列表中的不同键,如果尚未从主属性列表中找到相同名称的键。
void save(OutputStream out, String comments)
已弃用
如果在保存属性列表时发生I / O错误,此方法不会抛出IOException。 保存属性列表的store(OutputStream out, String comments)方法是通过store(OutputStream out, String comments)方法或storeToXML(OutputStream os, String comment)方法。
Object setProperty(String key, String value)
致电 Hashtable方法 put
void store(OutputStream out, String comments)
将此属性列表(键和元素对)写入此 Properties表中,以适合于使用 load(InputStream)方法加载到 Properties表中的格式输出流。
void store(Writer writer, String comments)
将此属性列表(键和元素对)写入此 Properties表中,以适合使用 load(Reader)方法的格式输出到输出字符流。
void storeToXML(OutputStream os, String comment)
发出表示此表中包含的所有属性的XML文档。
void storeToXML(OutputStream os, String comment, String encoding)
使用指定的编码发出表示此表中包含的所有属性的XML文档。
Set<String> stringPropertyNames()
返回此属性列表中的一组键,其中键及其对应的值为字符串,包括默认属性列表中的不同键,如果尚未从主属性列表中找到相同名称的键。

setProperty()方法Properties类

/*
 * 使用Properties集合,存储键值对
 * setProperty等同与Map接口中的put
 * setProperty(String key, String value)
 * 通过键获取值, getProperty(String key)
 */

Properties pro = new Properties();
pro.setProperty("a", "1");
pro.setProperty("b", "2");
pro.setProperty("c", "3");
System.out.println(pro);

String value = pro.getProperty("c");
System.out.println(value);

//方法stringPropertyNames,将集合中的键存储到Set集合,类似于Map接口的方法keySet
Set<String> set = pro.stringPropertyNames();
for(String key : set){
    System.out.println(key+"..."+pro.getProperty(key));
}

load()方法Properties类

/*
 * Properties集合特有方法 load
 * load(InputStream in)
 * load(Reader r)
 * 传递任意的字节或者字符输入流
 * 流对象读取文件中的键值对,保存到集合
 */

Properties pro = new Properties();
FileReader fr = new FileReader("c:\\pro.properties");
//调用集合的方法load,传递字符输入流
pro.load(fr);
fr.close();
System.out.println(pro);

store()方法Properties类

/*
 * Properties集合的特有方法store
 * store(OutputStream out)
 * store(Writer w)
 * 接收所有的字节或者字符的输出流,将集合中的键值对,写回文件中保存
 */

Properties pro = new Properties();
pro.setProperty("name", "zhangsan");
pro.setProperty("age", "31");
pro.setProperty("email", "123456789@163.com");
FileWriter fw = new FileWriter("c:\\pro.properties");
//键值对,存回文件,使用集合的方法store传递字符输出流
pro.store(fw, "");
fw.close();

对象的序列化与反序列化

  • 对象的序列化
    • 对象中的数据,以流的形式,写入到文件中保存过程称为写出对象,对象的序列化
    • ObjectOutputStream将对象写道文件中,实现序列化
Modifier and Type Method and Description
protected void annotateClass(类<?> cl)
子类可以实现此方法,以允许类数据存储在流中。
protected void annotateProxyClass(类<?> cl)
子类可以实现这种方法来存储流中的自定义数据以及动态代理类的描述符。
void close()
关闭流。
void defaultWriteObject()
将当前类的非静态和非瞬态字段写入此流。
protected void drain()
排除ObjectOutputStream中的缓冲数据。
protected boolean enableReplaceObject(boolean enable)
启用流来替换流中的对象。
void flush()
刷新流。
ObjectOutputStream.PutField putFields()
检索用于缓冲要写入流的持久性字段的对象。
protected Object replaceObject(Object obj)
该方法将允许ObjectOutputStream的可信子类在序列化期间将一个对象替换为另一个对象。
void reset()
复位将忽略已写入流的任何对象的状态。
void useProtocolVersion(int version)
指定在编写流时使用的流协议版本。
void write(byte[] buf)
写入一个字节数组。
void write(byte[] buf, int off, int len)
写入一个子字节数组。
void write(int val)
写一个字节。
void writeBoolean(boolean val)
写一个布尔值。
void writeByte(int val)
写入一个8位字节。
void writeBytes(String str)
写一个字符串作为字节序列。
void writeChar(int val)
写一个16位的字符。
void writeChars(String str)
写一个字符串作为一系列的字符。
protected void writeClassDescriptor(ObjectStreamClass desc)
将指定的类描述符写入ObjectOutputStream。
void writeDouble(double val)
写一个64位的双倍。
void writeFields()
将缓冲的字段写入流。
void writeFloat(float val)
写一个32位浮点数。
void writeInt(int val)
写一个32位int。
void writeLong(long val)
写一个64位长
void writeObject(Object obj)
将指定的对象写入ObjectOutputStream。
protected void writeObjectOverride(Object obj)
子类使用的方法来覆盖默认的writeObject方法。
void writeShort(int val)
写一个16位短。
protected void writeStreamHeader()
提供了writeStreamHeader方法,因此子类可以在流中附加或预先添加自己的头。
void writeUnshared(Object obj)
将“非共享”对象写入ObjectOutputStream。
void writeUTF(String str)
此字符串的原始数据写入格式为 modified UTF-8 。
  • 对象的反序列化
    • 在文件中,以流的形式,将对象读出来,读取对象,对象的反序列化
    • ObjectInputStream 将文件对象读取出来
Modifier and Type Method and Description
int available()
返回可以读取而不阻塞的字节数。
void close()
关闭输入流。
void defaultReadObject()
从此流读取当前类的非静态和非瞬态字段。
protected boolean enableResolveObject(boolean enable)
启用流以允许从流中读取的对象被替换。
int read()
读取一个字节的数据。
int read(byte[] buf, int off, int len)
读入一个字节数组。
boolean readBoolean()
读取布尔值。
byte readByte()
读取一个8位字节。
char readChar()
读一个16位字符。
protected ObjectStreamClass readClassDescriptor()
从序列化流读取类描述符。
double readDouble()
读64位双倍。
ObjectInputStream.GetField readFields()
从流中读取持久性字段,并通过名称获取它们。
float readFloat()
读32位浮点数。
void readFully(byte[] buf)
读取字节,阻塞直到读取所有字节。
void readFully(byte[] buf, int off, int len)
读取字节,阻塞直到读取所有字节。
int readInt()
读取一个32位int。
String readLine()
已弃用
此方法无法将字节正确转换为字符。 有关详细信息和替代方案,请参阅DataInputStream。
long readLong()
读64位长。
Object readObject()
从ObjectInputStream读取一个对象。
protected Object readObjectOverride()
此方法由ObjectOutputStream的受信任子类调用,该子类使用受保护的无参构造函数构造ObjectOutputStream。
short readShort()
读取16位短。
protected void readStreamHeader()
提供了readStreamHeader方法来允许子类读取和验证自己的流标题。
Object readUnshared()
从ObjectInputStream读取一个“非共享”对象。
int readUnsignedByte()
读取一个无符号的8位字节。
int readUnsignedShort()
读取无符号16位短。
String readUTF()
以 modified UTF-8格式读取字符串。
void registerValidation(ObjectInputValidation obj, int prio)
在返回图之前注册要验证的对象。
protected 类<?> resolveClass(ObjectStreamClass desc)
加载本地类等效的指定流类描述。
protected Object resolveObject(Object obj)
此方法将允许ObjectInputStream的受信任子类在反序列化期间将一个对象替换为另一个对象。
protected 类<?> resolveProxyClass(String[] interfaces)
返回一个代理类,它实现代理类描述符中命名的接口; 子类可以实现此方法从流中读取自定义数据以及动态代理类的描述符,从而允许它们为接口和代理类使用备用加载机制。
int skipBytes(int len)
跳过字节。

注意事项

  • 静态不能序列化

    • 序列化是把对象数据进行持久化存储
    • 静态的东西不属于对象,而属于类
  • transient关键字

    • 被transient修饰的属性不会被序列化
    • transient关键字只能修饰成员变量
  • Serializable接口的含义

    • 给需要序列化的类上加标记。该标记中没有任何抽象方法
    • 只有实现了 Serializable接口的类的对象才能被序列化
  • 序列化中的序列号冲突问题

    • 当一个类实现Serializable接口后,创建对象并将对象写入文件,之后更改了源代码,再次从文件中读取对象时会报异常

writeObject()方法ObjectOutputStream类

定义Person类实现Serializable接口

public class Person implements Serializable{
    public String name;
    public int age;
    public Person(String name, int age) {
        super();
        this.name = name;
        this.age = age;
    }
    public Person(){}
    public String toString() {
        return "Person [name=" + name + ", age=" + age + "]";
    }               
}

实例化Person类并进行存储

/*
 * ObjectOutputStream
 * 构造方法: ObjectOutputStream(OutputSteam out)
 * 传递任意的字节输出流
 * void writeObject(Object obj)写出对象的方法
 */

//创建字节输出流,封装文件
FileOutputStream fos = new FileOutputStream("c:\\person.txt");
//创建写出对象的序列化流的对象,构造方法传递字节输出流
ObjectOutputStream oos = new ObjectOutputStream(fos);
Person p = new Person("lisi",25);
//调用序列化流的方法writeObject,写出对象
oos.writeObject(p);
oos.close();

readObject()方法ObjectInputStream类

/*
 * ObjectInputStream
 * 构造方法:ObjectInputStream(InputStream in)
 * 传递任意的字节输入流,输入流封装文件,必须是序列化的文件
 * Object readObject() 读取对象
 */

FileInputStream fis = new FileInputStream("c:\\person.txt");
//创建反序列化流,构造方法中,传递字节输入流
ObjectInputStream ois = new ObjectInputStream(fis);
//调用反序列化流的方法 readObject()读取对象
Object obj =ois.readObject();
System.out.println(obj);
ois.close();

序列化中自定义的序列号

public class Person implements Serializable{
    public String name;
    public int age;
        //类,自定义了序列号,编译器不会计算序列号
        private static final long serialVersionUID = 1478652478456L;
    public Person(String name, int age) {
        super();
        this.name = name;
        this.age = age;
    }
    public Person(){}
    public String toString() {
        return "Person [name=" + name + ", age=" + age + "]";
    }               
}

字节打印流PrintStream类和字符打印流PrintWriter类

  • 此流不负责数据源,只负责数据目的
  • 为其他输出流,添加功能
  • 永远不会抛出IOException,但是可能抛出别的异常
  • 两个打印流的方法,完全一致
  • 构造方法,就是打印流的输出目的端
  • PrintStream构造方法
    • 接收File类型,接收字符串文件名,接收字节输出流OutputStream
  • PrintWriter构造方法
    • 接收File类型,接收字符串文件名,接收字节输出流OutputStream, 接收字符输出流Writer

输出目的是File对象

/*
 * 打印流,向File对象的数据目的写入数据
 * 方法print println  原样输出
 * write方法走码表
 */

File file = new File("c:\\1.txt");
PrintWriter pw = new PrintWriter(file);
pw.println(true);
pw.write(100);
pw.close();

输出目的是String

/*
 * 打印流,输出目的,String文件名
 */

PrintWriter pw = new PrintWriter("c:\\2.txt");
pw.println(3.5);
pw.close();

输出目的是流对象

/*
 * 打印流,输出目的,是流对象
 * 可以是字节输出流,可以是字符的输出流
 * OutputStream  Writer
 */

FileWriter fw = new FileWriter("c:\\4.txt");
PrintWriter pw = new PrintWriter(fw);
pw.println("打印流");
pw.close();

输出语句是char数组

/*      
 * println数组,只有打印字符数组时只有容,其余均打印数组的地址
 * 因为api中定义了打印字符数组的方法,其底层是在遍历数组中的元素
 * 而其他打印数组的方法,都是将数组对象编程Object,其底层再将对象编程String,调用了String s = String.valueOf(x);方法
 */

int[] arr = {1};
System.out.println(arr);

char[] ch = {'a','b'};
System.out.println(ch);

byte[] b = {};
System.out.println(b);

打印流开启自动刷新

/* 
 * 打印流,可以开启自动刷新功能
 * 满足2个条件:
 *   1. 输出的数据目的必须是流对象
 *       OutputStream  Writer
 *   2. 必须调用println,printf,format三个方法中的一个,启用自动刷新
 */

FileOutputStream fos = new FileOutputStream("c:\\5.txt");
PrintWriter pw = new PrintWriter(fos,true);
pw.println("i");
pw.println("love");
pw.println("java");
pw.close();

打印流复制文本文件

/*
 * 打印流实现文本复制
 *   读取数据源  BufferedReader+File 读取文本行
 *   写入数据目的 PrintWriter+println 自动刷新
 */

BufferedReader bfr = new BufferedReader(new FileReader("c:\\a.txt"));
PrintWriter pw = new PrintWriter(new FileWriter("d:\\a.txt"),true);
String line = null;
while((line = bfr.readLine())!=null){
    pw.println(line);
}
pw.close();
bfr.close();