文件传输基础——Java IO流

1 文件的编码

1.1 文件的编码

这里写图片描述

public static void main(String[] args) {
    // TODO Auto-generated method stub
    String s = "慕课ABC";
    /**
     * 转换成字节序列利用的是项目默认的编码,
     * eclipse查看项目默认编码的方式:点击项目->右击Properties->Resource->Text file encoding
     * 项目编码也可以显示的指定:byte[] bytes = s.getBytes("gbk"); 
     */
    byte[] bytes1 = s.getBytes(); 
    for(byte b:bytes1) {
        // 把字节转换为int,以16进制的方式显示
        System.out.print(Integer.toHexString(b & 0xff) + " ");
    }
    System.out.println();

    try {
        byte[] bytes2 = s.getBytes("gbk");
        /**
         * gbk编码中文占2个字节,英文占1个字节
         */
        for(byte b:bytes2) {
            System.out.print(Integer.toHexString(b & 0xff) + " ");
        }
    } catch (UnsupportedEncodingException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    System.out.println();

    try {
        byte[] bytes3 = s.getBytes("utf-8");
        /**
         * 在utf-8编码中,中文占用3个字节,英文占用1个字节
         */
        for(byte b:bytes3) {
            System.out.print(Integer.toHexString(b & 0xff)+" ");
        }
    } catch (UnsupportedEncodingException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    System.out.println();

    byte[] bytes4 = null;
    try {
        /**
         * 在utf-16be中,中文和英文都占用2个字节
         */
        bytes4 = s.getBytes("utf-16be");
        for(byte b:bytes4) {
            System.out.print(Integer.toHexString(b & 0xff)+" ");
        }
    } catch (UnsupportedEncodingException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    System.out.println();

    /**
     * 字节序列转化为字符串时,当你的字节序列的编码方式与项目默认编码方式不同时,
     * 这个时候需要手动添加编码方式进行转换,
     * 否则会出现乱码
     * 
     */
    String str1 = new String(bytes4);
    System.out.println(str1);
    try {
        String str2 = new String(bytes4,"utf-16be");
        System.out.println(str2);
    } catch (UnsupportedEncodingException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

    /**
     * eclipse中什么编码的项目只认识什么编码的文件,
     * 将一个项目的文件拷贝到另一个编码方式不同的项目中,就会出现乱码,
     * 这就是直接拷贝别人项目文件出现乱码的原因
     */ 

    /**
     * 文本文件 就是字节序列
     * 可以是任意编码的字节序列
     * 如果我们在中文机器上直接创建文本文件,那么该文本文件只认识ansi编码
     */
}

2 File 类的使用

2.1 File 类常用 API 介绍

java.io.File 类用于表示文件(目录)
File类只用于表示文件(目录)的信息(名称、大小等),不能用于文件内容的访问

public static void main(String[] args) {
    // TODO Auto-generated method stub
    /**
     * eclipse中用 Alt+/ 来查看File的构造函数,
     * 文件分割符可以用 \\ 或者 / 或者  File.separator,
     * File.separator不论win系统还是linux系统,都表示分割符
     */
    File file = new File("D:\\javaio"); 
    System.out.println(file.exists()); // 判断文件是否存在
    if(!file.exists()) { 
        file.mkdir(); // 创建一级目录
        file.mkdirs(); // 如果文件不存在,创建多级目录
    }else {
        file.delete(); // 如果文件存在,删除目录
    }

    /**
     * 判断是否是一个目录 ,
     * 如果是目录返回true,如果不是目录或者不存在返回false
     */
    System.out.println(file.isDirectory());
    // 判断是否是一个文件
    System.out.println(file.isFile());

    /**
     * 创建文件
     */
    File file2 = new File("d:\\javaio\\日记1.txt");
    File file3 = new File("d:\\javaio","日记2.txt");
    if(!file2.exists()) {
        try {
            file2.createNewFile(); // 文件不存在,创建这个文件
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }else {
        file2.delete(); // 文件存在删除文件
    }

    // 常用的File对象的API
    System.out.println(file); // 打印file.toString()的内容
    System.out.println(file.getAbsolutePath()); // 打印路径
    System.out.println(file.getName()); // 打印目录名
    System.out.println(file2.getName()); // 打印文件名
    System.out.println(file.getParent()); // 返回父目录路径
    System.out.println(file2.getParent()); // 返回父目录路径
    System.out.println(file.getParentFile().getAbsolutePath()); // 返回父目录路径
}

2.2 遍历目录

package com.amoscxy;

import java.io.File;
import java.io.IOException;

/**
 * 列出File的一些常用操作比如过滤、遍历等操作
 * @author BG235144
 *
 */
public class FileUtils {
    /**
     * 列出指定目录下(包括其子目录)的所有文件
     * @param dir
     * @throws IOException
     */
    public static void listDirectory(File dir) throws IOException{
        if(!dir.exists()) {
            throw new IllegalArgumentException("目录:"+dir+"不存在");
        }
        if(!dir.isDirectory()) {
            throw new IllegalArgumentException(dir+"不是目录");
        }
        /**
         * 返回的是字符串数组,
         * 不包含子目录下的内容
         */
//      String[] filenames = dir.list(); 
//      for(String filename:filenames) {
//          System.out.println(dir+"\\"+filename);
//      }

        /**
         * 如果要遍历子目录下的内容就要构造成File对象做递归操作,
         * File提供了直接返回File对象的api,
         * 返回的是直接子目录(文件)的对象
         */
        File[] files = dir.listFiles();
//      for(File file:files) {
//          System.out.println(file);
//      }
        if(files!=null && files.length>0) {
            for(File file:files) {
                if(file.isDirectory()) {
                    /**
                     * 递归
                     */
                    listDirectory(file);
                }else {
                    System.out.println(file);
                }
            }
        }
    }
}

3 RandomAccessFile类的使用

3.1 RandomAccessFile基本操作

RandomAccessFile java提供的对文件内容的访问,既可以读文件,也可以写文件。
RandomAccessFile支持随机访问文件,可以访问文件的任意位置

RandomAccessFile 读写过程:
1. Java文件模型:
在硬盘上的文件是byte byte byte存储,是数据的集合
2. 打开文件:
有两种模式”rw”(读写)”r”(只读)
RandomAccessFile raf = new RandomAccessFile(file,”rw”);
文件指针,pointer = raf.getFilePointer(),
打开文件时指针在开头 pointer = 0;
3. 写方法:
raf.write(int)—>只写一个字节(后8位),同时指针指向下一个位置,准备再次写入
4. 读方法:
文件读写完成以后一定要关闭(oracle官方说明)

public static void main(String[] args) throws IOException{
    File demo = new File("D:\\javaio");
    if(!demo.exists()) {
        demo.mkdir();
    }
    File file = new File(demo,"raf.dat");
    if(!file.exists()) {
        file.createNewFile();
    }
    RandomAccessFile raf = new RandomAccessFile(file,"rw");
    // 指针位置
    System.out.println(raf.getFilePointer());

    /**
     * java中char是2个字节,
     * 只写最后边一个字节进去
     */
    raf.write('A'); 
    System.out.println(raf.getFilePointer());
    raf.write('B');

    /**
     * 用write方法每次只能写一个字节,
     * 如果要把i写进去就得写4次
     */
    int i = 0x7fffffff;
    raf.write(i>>>24); // 高8位
    raf.write(i>>>16);
    raf.write(i>>>8);
    raf.write(i);
    System.out.println(raf.getFilePointer());

    // 可以直接写一个int
    raf.writeInt(i);
    /**
     * 可以直接写一个中文汉子,
     */
    String s = "中";
    byte[] gbk = s.getBytes("gbk");
    /**
     * 可以写入字节数组
     */
    raf.write(gbk);
    System.out.println(raf.length());

    /**
     * 读文件,必须把指针移到头部
     */
    raf.seek(0);
    /**
     * 一次性读取,把文件中的内容都读到字节数组中
     */
    byte[] buf = new byte[(int)raf.length()];
    raf.read(buf);
    System.out.println(Arrays.toString(buf));
    String s1 = new String(buf,"gbk");
    System.out.println(s1);
    /**
     * 以16进制方式输出
     */
    for(byte b:buf) {
        System.out.print(Integer.toHexString(b & 0xff) + " ");
    }

    /**
     * 关闭
     */
    raf.close();
}

输出

0
1
6
12
[65, 66, 127, -1, -1, -1, 127, -1, -1, -1, -42, -48]
AB������中
41 42 7f ff ff ff 7f ff ff ff d6 d0 

4 字节流

4.1 字节流之文件输入流FileInputStream-1

这里写图片描述
这里写图片描述
这里写图片描述

public class IOUtil {
    /**
     * 读取指定文件内容,按照16进制输出到控制台,
     * 并且每输出10个byte换行
     * @param fileName
     */
    public static void printHex(String fileName) throws IOException{
        /**
         * 把文件作为字节流进行读操作
         */
        FileInputStream in = new FileInputStream(fileName);
        int b;
        int i = 1;
        /**
         * 文件读,
         * 判断文件读结束
         */
        while((b = in.read())!=-1) {
            if(b<=0xf) {
                // 单位数前面补0
                System.out.println("0");
            }
            // 将整数b转换为16进制表示的字符串
            System.out.print(Integer.toHexString(b) + " ");
            if(i++%10 == 0) {
                System.out.println();
            }
        }
        in.close();
    }
}

4.2 字节流之文件输入流FileInputStream-2

/**
 * 从int中批量读取字节,放入到buf这个字节数组中,
 * 从第0个位置开始放,最多放buf.length个,
 * 返回的是读到的字节个数
 */
public static void printHexByByteArray(String fileName) throws IOException{
    FileInputStream in = new FileInputStream(fileName);
    /**
     * 适宜大小,直到读取全部文件为止
     */
    byte[] buf = new byte[8*1024];
    int bytes = 0;
    int j = 1;
    while((bytes = in.read(buf,0,buf.length))!=-1) {
        for(int i = 0; i < bytes; i ++) {
            if((buf[i] & 0xff) <= 0xf) {
                System.out.print("0");
            }
            /**
             * byte类型8位,int类型32位,
             * 为避免数据转换错误,通过&0xff将高位24位请零
             */
            System.out.print(Integer.toHexString(buf[i] & 0xff)+" ");
            if(j++%10 == 0) {
                System.out.println();
            }
        }
    }
}

4.3 字节流之文件输出流FileOutputStream

这里写图片描述

public static void main(String[] args) throws IOException{
    // TODO Auto-generated method stub
    /**
     * 参数一:文件
     * 参数二:是否在文本后面追加
     */
    FileOutputStream out = new FileOutputStream("D:\\javaio\\out.dat",true);
    out.write('A'); // 写出了'A'的低8位
    out.write('B'); // 写出了'B'的低8位
    int a = 10; // write 只能写低8位,那么写一个int需要写4次
    out.write(a>>>24); // 写入高8位
    out.write(a>>>16);
    out.write(a>>>16);
    out.write(a);
    byte[] gbk = "中国".getBytes("gbk");
    out.write(gbk); // 可以写出字符数组
    out.close();
}
/**
 * 拷贝文件
 * @param srcFile
 * @param destFile
 * @throws IOException
 */
public static void copyFile(File srcFile,File destFile) throws IOException {
    if(!srcFile.exists()) {
        throw new IllegalArgumentException("文件" + srcFile + "不存在");
    }
    if(!srcFile.isFile()) {
        throw new IllegalArgumentException(srcFile + "不是文件");
    }
    if(!destFile.exists()) {
        destFile.createNewFile();
    }
    FileInputStream in = new FileInputStream(srcFile);
    FileOutputStream out = new FileOutputStream(destFile);
    /**
     * 适当的大小,
     * 一次一次中转文件,
     * 知道文件全部拷贝为止
     */
    byte[] buf = new byte[8*1024];
    int b;
    while((b = in.read(buf, 0, buf.length))!=-1) {
        out.write(buf, 0, b);
        out.flush(); // 最好加上
    }
    in.close();
    out.close();
}

4.4 字节流之数据输入输出流

DataOutputStream/DataInputStream
对“流”功能的扩展,可以更加方便的读取int,long,字符等类型数据 DataOutputStream
writeInt()/writeDouble()/writeUTF()

public static void main(String[] args) throws IOException{
    // TODO Auto-generated method stub
    String file = "D:\\javaio\\dos.dat";
    /**
     * FileOutputStream 包装成  DataOutputStream 目的就是为了用它的 write 方法,
     * 写出基本数据类型的时候更加方便
     */
    DataOutputStream dos = new DataOutputStream(new FileOutputStream(file));
    dos.writeInt(10);
    dos.writeInt(-10);
    dos.writeLong(10l);
    dos.writeDouble(10.5);
    // 采用 utf-8 编码写出
    dos.writeUTF("中国");
    // 采用 utf-16be 编码写出
    dos.writeChars("中国");
    dos.close();
    /**
     * FileInputStream 包装成 DataInputStream 目的就是为了用 read 方法,
     * 读取基本数据类型的时候更加方便
     */
    DataInputStream dis = new DataInputStream(new FileInputStream(file));
    int i = dis.readInt();
    System.out.println(i);
    i = dis.readInt();
    System.out.println(i);
    long j = dis.readLong();
    System.out.println(j);
    double h = dis.readDouble();
    System.out.println(h);
    String k = dis.readUTF();
    System.out.println(k);
    dis.close();
}

4.5 字节缓冲流

8) BufferedInputStream & BufferedOutputStream 这两个流类为 IO 提供了带缓冲区的操作,一般打开文件进行写入或读取操作时,都会加上缓冲,这种流模式提高了 IO 的性能,从应用程序中把输入放入文件,相当于将一缸水倒入到另一个缸中:
FileOutputStream —> write() 方法相当于一滴一滴地把水“转移”过去,

DataOutputStream—>writeXxx() 方法会方便一些,相当于一瓢一瓢把水“转移”过去,
BufferedOutputStream—>write()方法更方便,相当于一瓢一瓢先放入桶中,再从桶中倒入缸中

FileInputStream/FileOutputStream(需要时间:20多万ms)
单字节,不带缓冲,进行文件拷贝

/**
 * 单字节,不带缓冲,进行文件拷贝
 * @param srcFile
 * @param destFile
 * @throws IOException
 */
public static void copyFileByByte(File srcFile, File destFile) throws IOException{
    if(!srcFile.exists()) {
        throw new IllegalArgumentException("文件" + srcFile + "不存在");
    }
    if(!srcFile.isFile()) {
        throw new IllegalArgumentException(srcFile + "不是文件");
    }
    FileInputStream in = new FileInputStream(srcFile);
    FileOutputStream out = new FileOutputStream(destFile);
    int c;
    while((c = in.read())!=-1) {
        out.write(c);
        out.flush();
    }
    in.close();
    out.close();
}

文件拷贝,字节批量读取(需要时间:7ms,拷贝文件速度最快)

/**
 * 文件拷贝,字节批量读取
 * @param srcFile
 * @param destFile
 * @throws IOException
 */
public static void copyFileBybuf(File srcFile,File destFile) throws IOException {
    if(!srcFile.exists()) {
        throw new IllegalArgumentException("文件" + srcFile + "不存在");
    }
    if(!srcFile.isFile()) {
        throw new IllegalArgumentException(srcFile + "不是文件");
    }
    FileInputStream in = new FileInputStream(srcFile);
    FileOutputStream out = new FileOutputStream(destFile);
    /**
     * 适当的大小,
     * 开辟内存,批量读取
     * 直到文件全部拷贝为止
     */
    byte[] buf = new byte[8*1024];
    int b;
    while((b = in.read(buf, 0, buf.length))!=-1) {
        out.write(buf, 0, b);
        out.flush(); // 最好加上
    }
    in.close();
    out.close();
}   

DataInputStream/DataOutputStream

public static void main(String[] args) throws IOException{
    // TODO Auto-generated method stub
    String file = "D:\\javaio\\dos.dat";
    /**
     * FileOutputStream 包装成  DataOutputStream 目的就是为了用它的 write 方法,
     * 写出基本数据类型的时候更加方便
     */
    DataOutputStream dos = new DataOutputStream(new FileOutputStream(file));
    dos.writeInt(10);
    dos.writeInt(-10);
    dos.writeLong(10l);
    dos.writeDouble(10.5);
    // 采用 utf-8 编码写出
    dos.writeUTF("中国");
    // 采用 utf-16be 编码写出
    dos.writeChars("中国");
    dos.close();
    /**
     * FileInputStream 包装成 DataInputStream 目的就是为了用 read 方法,
     * 读取基本数据类型的时候更加方便
     */
    DataInputStream dis = new DataInputStream(new FileInputStream(file));
    int i = dis.readInt();
    System.out.println(i);
    i = dis.readInt();
    System.out.println(i);
    long j = dis.readLong();
    System.out.println(j);
    double h = dis.readDouble();
    System.out.println(h);
    String k = dis.readUTF();
    System.out.println(k);
    dis.close();
} 

BufferedInputStream/BufferedOutputStream(需要时间10万多ms)
进行文件的拷贝,利用带缓冲的字节流

/**
 * 进行文件的拷贝,利用带缓冲的字节流
 * @param srcFile
 * @param destFile
 * @throws IOException
 */
public static void copyFileByBuffer(File srcFile, File destFile) throws IOException{
    if(!srcFile.exists()) {
        throw new IllegalArgumentException("文件" + srcFile + "不存在");
    }
    if(!srcFile.isFile()) {
        throw new IllegalArgumentException(srcFile + "不是文件");
    }
    BufferedInputStream bis = new BufferedInputStream(new FileInputStream(srcFile));
    BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(destFile));
    int c;
    while((c = bis.read())!=-1) {
        bos.write(c);
        /**
         * 带缓冲,必须刷新缓冲区
         */
        bos.flush();
    }
    bis.close();
    bos.close();
}

5 字符流

5.1 字节字符转换流

1)编码问题
2)认识文本和文本文件
java的文本(char)是16位无符号整数,是字符的unicode编码(双字节编码),文本是byte byte byte…的数据序列
文本文件是文本(char)按照某种编码方案(utf-8,utf-16be,gbk)序列化为byte的存储结果
3)字符流(输入流的抽象类:Reader、输出流的抽象类:Writer)—>操作的是文本文件
字符流的处理方式:读/写一次处理一个字符(可能是英文字符、中文字符、可能是不同编码的… 所占大小不同)
字符的底层,任然是基本的字节序列
字符流的基本实现
InputStreamReader 完成byte流解析为char流,按照编码解析
OutputStreamWriter 提供char流到byte流,按照编码处理

public static void main(String[] args) throws IOException{
    // TODO Auto-generated method stub
    /**
     * 义字符为单位,批量拷贝,
     * 有编码的问题,不写为项目默认编码,
     * 编码要与文件本身的编码相同,否则会出现乱码问题
     * 
     */
    InputStreamReader isr = new InputStreamReader(new FileInputStream("D:\\javaio\\imooc.txt"),"gbk");
    OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("D:\\javaio\\imoocgbk"),"gbk");
//      int c;
//      while((c = isr.read())!=-1) {
//          System.out.print((char)c);
//      }
    char[] buf = new char[8*1024];
    int c;
    /**
     * 批量读取,放入buffer这个字符数组,从第0个位置开始,最多放buf。length个,
     * 返回的是读到的字符个数
     */
    while((c = isr.read(buf,0,buf.length))!=-1) {
        String s = new String(buf,0,c);
        System.out.println(s);
        osw.write(buf,0,c);
        osw.flush();
    }
    isr.close();
    osw.close();
}

5.2 字符流之文件读写流

FileReader/FileWriter

public static void main(String[] args) throws IOException {
    // TODO Auto-generated method stub
    /**
     * 不能进行编码设置,此时:
     * 1. 项目编码与文件编码相同
     * 2. 回去用InputStreamReader/OutputStreamWriter,这只编码
     */
    FileReader fr = new FileReader("D:\\javaio\\imooc.txt");
    /**
     * 参数二:true,在文件末尾追加
     */
    FileWriter fw = new FileWriter("D:\\javaio\\imooc2.txt",true);
    char[] buf = new char[8*1024];
    int c;
    while((c = fr.read(buf,0,buf.length))!=-1) {
        fw.write(buf,0,c);
        fw.flush();
    }
    fr.close();
    fw.close();
}

5.3 字符流的过滤器

BufferedReader —> readLine 一次读一行
BufferedWriter/PrintWriter —> 写一行

使用BufferedReader/BufferedWriter

public static void main(String[] args) throws IOException {
    // 对文件进行读操作
    BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("D:\\javaio\\imooc.txt"),"gbk"));
    BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("D:\\javaio\\imooc3.txt"),"gbk"));
    String line;
    while((line = br.readLine())!=null) {
        /**
         * 一次读一行,并不能识别换行符
         */
        System.out.println(line);
        /**
         * 一次写入一行,不识别换行
         */
        bw.write(line);
        /**
         * BufferedWriter的换行
         */
        bw.newLine();
        bw.flush();
    }
    br.close();
    bw.close();
}

使用BufferedReader/PrinterWriter

public static void main(String[] args) throws IOException {
    // 对文件进行读操作
    BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("D:\\javaio\\imooc.txt"),"gbk"));
    PrintWriter pw = new PrintWriter("D:\\javaio\\imooc4.txt","gbk");
    /**
     * 参数二:自动刷新
     */
//      PrintWriter pw2 = new PrintWriter(new FileOutputStream("D:\\javaio\\imooc5.txt"),true);
    String line;
    while((line = br.readLine())!=null) {
        /**
         * 一次读一行,并不能识别换行符
         */
        System.out.println(line);
        /**
         * 识别换行符用println
         */
        pw.println(line);
        pw.flush();
    }
    br.close();
    pw.close();
}

6 对象的序列化和反序列化

6.1 序列化基本操作

对象的序列化、反序列化
1)对象的序列化:将Object转换成byte序列,反之叫对象的反序列化
2)序列化流(ObjectOutputStream),是过滤流,
反序列化流(ObjectInputStream)—> readObject
3)序列化接口(Serializable)
对象必须实现序列化接口,才能进行序列化,否则将出现异常
这个接口,没有任何方法,只是一个标准

序列化、反序列化

public static void main(String[] args) throws IOException, ClassNotFoundException {
    // TODO Auto-generated method stub
    String file = "D:\\javaio\\obj.dat";
    /**
     * 序列化
     */
    ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(file));
    /**
     * 反序列化
     */
    ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));
    Student stu = new Student("10001","张三",20);
    Student stu2;
    /**
     * 可以写出任意序列化数据
     * 文件保存和在网络中传输都要变成字节,都需要序列化,
     * Student如果不序列化,就会出现bug,
     * Exception in thread "main" java.io.NotSerializableException,没有实现序列化接口
     */
    oos.writeObject(stu);
    oos.flush();
    /**
     * 读入任意序列化数据
     */
    stu2 = (Student) ois.readObject();
    System.out.println(stu2);
    oos.close();
}

Student

package com.amoscxy;

import java.io.Serializable;
/**
 * 实现了序列化接口
 */
public class Student implements Serializable {
    private String stuno;
    private String stuname;
    private int stuage;


    public Student(String stuno, String stuname, int stuage) {
        super();
        this.stuno = stuno;
        this.stuname = stuname;
        this.stuage = stuage;
    }
    public String getStuno() {
        return stuno;
    }
    public void setStuno(String stuno) {
        this.stuno = stuno;
    }
    public String getStuname() {
        return stuname;
    }
    public void setStuname(String stuname) {
        this.stuname = stuname;
    }
    public int getStuage() {
        return stuage;
    }
    public void setStuage(int stuage) {
        this.stuage = stuage;
    }
}

6.2 transient及ArrayList源码分析

transient修饰的元素,不会进行jvm默认的序列化

/**
 * transient修饰元素,
 * 该元素不会进行jvm默认的序列化,
 * 可以自己完成这个元素的序列化
 */
private transient int stuage;

自己完成transient修饰元素的序列化

/**
 * 自己完成序列化写出
 */
private void writeObject(ObjectOutputStream s) throws IOException{
}
/**
 * 自己完成序列化读入
 */
private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException {
}
public class Student implements Serializable {
    private String stuno;
    private String stuname;
    /**
     * transient修饰元素,
     * 该元素不会进行jvm默认的序列化,
     * 但可以自己完成这个元素的序列化
     */
    private transient int stuage;

    public Student() {

    }
    public Student(String stuno, String stuname, int stuage) {
        super();
        this.stuno = stuno;
        this.stuname = stuname;
        this.stuage = stuage;
    }
    public String getStuno() {
        return stuno;
    }
    public void setStuno(String stuno) {
        this.stuno = stuno;
    }
    public String getStuname() {
        return stuname;
    }
    public void setStuname(String stuname) {
        this.stuname = stuname;
    }
    public int getStuage() {
        return stuage;
    }
    public void setStuage(int stuage) {
        this.stuage = stuage;
    }
    @Override
    public String toString() {
        return "Student [stuno=" + stuno + ", stuname=" + stuname + ", stuage=" + stuage + "]";
    }

    private void writeObject(ObjectOutputStream s) throws IOException{
        /**
         * 把jvm虚拟机能默认序列化的元素进行序列化
         */
        s.defaultWriteObject();
        /**
         * 自己完成stuage的序列化
         */
        s.writeInt(stuage);
    }
    private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException {
        /**
         * 把jvm虚拟机能默认反序列化的元素进行反序列化操作
         */
        s.defaultReadObject();
        /**
         * 自己完成stuage的反序列化操作
         */
        this.stuage = s.readInt();
    }
}

分析 ArrayList 源码中序列化和反序列化的问题

/**
 * transient修饰元素,
 * 不能用jvm虚拟机自动进行序列化,
 * 可以自己进行序列化
 */
transient Object[] elementData;

序列化写出

private void writeObject(java.io.ObjectOutputStream s)
    throws java.io.IOException{
    // Write out element count, and any hidden stuff
    int expectedModCount = modCount;
    s.defaultWriteObject();

    // Write out size as capacity for behavioural compatibility with clone()
    s.writeInt(size);

    // Write out all elements in the proper order.
    /**
     * 对数组的有效元素进行序列化,
     * 无效元素不管,
     * 提高性能
     */
    for (int i=0; i<size; i++) {
        s.writeObject(elementData[i]);
    }

    if (modCount != expectedModCount) {
        throw new ConcurrentModificationException();
    }
}

序列化读入

private void readObject(java.io.ObjectInputStream s)
    throws java.io.IOException, ClassNotFoundException {
    elementData = EMPTY_ELEMENTDATA;

    // Read in size, and any hidden stuff
    s.defaultReadObject();

    // Read in capacity
    s.readInt(); // ignored

    if (size > 0) {
        // be like clone(), allocate array based upon size not capacity
        ensureCapacityInternal(size);

        Object[] a = elementData;
        // Read in all elements in the proper order.
        /**
         * 对数组有效部分反序列化,
         * 无效部分不管
         */
        for (int i=0; i<size; i++) {
            a[i] = s.readObject();
        }
    }
}

可以用自己序列化做一些序列化的优化工作

6.3 序列化中子父类构造函数问题

5)序列化中子类和父类构造函数的调用问题

public class objectSeriaDemo2 {

    public static void main(String[] args) throws Exception{
        // TODO Auto-generated method stub
        /**
         * 序列化,
         * 递归调用父类的构造函数
         */
        System.out.println("Foo2序列化:");
        ObjectOutputStream oos = new ObjectOutputStream(
                new FileOutputStream("/Volumes/D/javaio/obj1.dat"));
        Foo2 foo2 = new Foo2();
        oos.writeObject(foo2);
        oos.flush();
        oos.close();
        System.out.println();

        /**
         * 对子类对象进行反序列化时,
         * 如果其父类的构造函数实现了序列化接口,
         * 那么其父类的构造函数不会被调用
         */
        System.out.println("Foo2反序列化:");
        ObjectInputStream ois = new ObjectInputStream(
                new FileInputStream("/Volumes/D/javaio/obj1.dat"));
        Foo2 foo22 = (Foo2)ois.readObject();
        System.out.println(foo22);
        ois.close();
        System.out.println();

        /**
         * 序列化,
         * 递归调用父类的构造函数
         */
        System.out.println("Bar2序列化:");
        ObjectOutputStream oos2 = new ObjectOutputStream(
                new FileOutputStream("/Volumes/D/javaio/obj1.dat"));
        Bar2 bar2 = new Bar2();
        oos2.writeObject(bar2);
        oos2.flush();
        oos2.close();
        System.out.println();

        /**
         * 对子类对象进行反序列化时,
         * 如果其父类的构造函数没有实现序列化接口,
         * 那么其父类的构造函数会被调用
         */
        System.out.println("Bar2反序列化:");
        ObjectInputStream ois2 = new ObjectInputStream(
                new FileInputStream("/Volumes/D/javaio/obj1.dat"));
        Bar2 bar22 = (Bar2)ois2.readObject();
        System.out.println(bar22);
        ois2.close();
    }

}

/**
 * 一个类实现了序列化接口,那么其子类都可以进行序列化
 * @author amoscxy
 *
 */
class Foo implements Serializable{
    public Foo() {
        System.out.println("foo...");

    }
}

class Foo1 extends Foo{
    public Foo1() {
        System.out.println("foo1...");
    }
}

class Foo2 extends Foo1{
    public Foo2() {
        System.out.println("foo2...");
    }
}

class Bar{
    public Bar() {
        System.out.println("bar...");
    }
}

class Bar1 extends Bar implements Serializable{
    public Bar1() {
        System.out.println("bar1...");
    }
}

class Bar2 extends Bar1{
    public Bar2() {
        System.out.println("bar2...");
    }
}

猜你喜欢

转载自blog.csdn.net/amoscxy/article/details/78881869