Java核心类库(三)——IO

JavaEE 专栏收录该内容
40 篇文章 1 订阅

※ I/O流

  • 可以将数据的传输操作,看作一种数据流动,按流动的方向分为输入流输出流,按处理数据类型的单位可以分为字节流字符流

输入流: 程序从数据源中读取数据
输出流: 将数据从程序中写到指定的文件中

字节流: 每次读写一个字节

输入流:InputStream
输出流:OutputStream

字符流: 以字符为单位进行数据处理,基于字节流读取,去查找指定的码表

输入流:Reader
输出流:Writer

区别字节流字符流
读写单位以字节(8位2进制)为单位以字符为单位
处理对象所有类型的数据(如图片、avi等)只能处理字符类型的数据
一次读入或读出数据8位二进制16位二进制

Java中I/O操作主要是指Java.io包下一些常用类的使用,通过这些常用类对数据进行输入、输出操作
tips: 一切皆字节,计算机中任何数据流都以二进制形式存储,传输也是,后续传输的任何流,传输时底层都为二进制

※ java.io

Java.io包中最重要的5个类:FileOutputStreamInputStreamWriterReader
Java.io包中最重要的1个接口:Serializable

一、 File

  • 文件和目录路径名的抽象表示
  • 表示处理文件和文件系统的相关信息
    不具有从文件读取信息和向文件写入信息的功能,它仅描述文件本身的属性

1 常用字段

变量和类型字段描述
static charpathSeparatorChar与系统相关的路径分隔符" ; "
static charseparatorChar系统相关的默认名称分隔符" \ "

(基本不怎么用)

变量和类型字段描述
static StringpathSeparator与系统相关的路径分隔符
static Stringseparator系统相关的默认名称分隔符

2 常用构造方法

构造器描述
File​(String pathname)通过将给定的路径名字符串转换为抽象路径名来创建新的 File实例
File​(File parent, String child)父抽象路径名子路径名字符串创建新的 File实例(文件夹,文件名称)
File​(String parent, String child)父路径名字符串子路径名字符串创建新的 File实例(文件夹,文件名称)

2.1 创建文件夹

//File​(String pathname)

        File dir = new File("d:" + File.separator + "ha");
        dir.mkdir();//创建文件目录
        File dirs = new File("d:" + File.separator + "haha" + File.separator + "hahaha");
        dirs.mkdirs();//创建多层文件目录

		/*
		boolean flag = dir.mkdir();//tips:dir.mkdir()类型为Boolean型,如果创建成功值为true,失败为false
		system.out.println(flag);//文件创建成功,flag = true
		*/

2.2 创建文件

//File​(File parent, String child)
//File​(String parent, String child)

		//在d盘的"ha"文件夹中创建文件"a.txt"
        File a = new File(dir,"a.txt");
        a.createNewFile();
        
        //在d盘的"ha"文件夹中创建文件"b.java"
        File b = new File("d:" + File.separator + "ha","b.java");
        b.createNewFile();
        
        //在d盘的"haha"文件夹中的"hahaha"文件夹中创建文件"c.png"
        File c = new File(dirs,"c.png");
        c.createNewFile();
        
        //在d盘的"haha"文件夹中的"hahaha"文件夹中创建文件"d.png"
        File d = new File("d:" + File.separator + "haha" + File.separator + "hahaha","d.png");
        d.createNewFile();

3 常用方法

3.1 获取

变量和类型方法描述
StringgetName()返回此抽象路径名表示的文件或目录的名称
StringgetAbsolutePath()返回此抽象路径名的绝对路径名字符串
StringgetParent()返回此抽象路径名父项的路径名字符串,如果此路径名未指定父目录,则返回 null
FilegetParentFile()返回此抽象路径名父项的抽象路径名,如果此路径名未指定父目录,则返回 null
StringgetPath()将此抽象路径名转换为路径名字符串
longlength()返回此抽象路径名表示的文件的长度
String[]list()返回一个字符串数组,用于命名此抽象路径名表示的目录中的文件和目录
File[]listFiles()返回一个抽象路径名数组,表示此抽象路径名表示的目录中的文件
3.1.1 常用的获取方法

代码示例:

		/**
         * 常用的获取方法
         */
        System.out.println(c.getName());//获取文件c的名称
        System.out.println(c.getPath());//获取c的绝对路径(地址)
        System.out.println(c.getAbsolutePath());//获取c的绝对路径(地址)
        System.out.println(c.getParent());//获取文件c的父路径
        System.out.println(c.getParentFile());//获取文件c的父路径
        System.out.println(c.length());//获取文件c的大小,以字节为单位
        

输出结果:

c.png
d:\haha\hahaha\c.png
d:\haha\hahaha\c.png
d:\haha\hahaha
d:\haha\hahaha
0
3.1.2 获取文件

获取文件夹中的所有文件:见3.3重命名后 对文件夹中的全部文件进行输出
遍历文件:见3.5

3.2 判断

变量和类型方法描述
booleanisDirectory()测试此抽象路径名表示的文件是否为目录
booleanisFile()测试此抽象路径名表示的文件是否为普通文件
booleanexists()测试此抽象路径名表示的文件或目录是否存在

代码示例:

   	    /**
         * 常用的判断方法
         */
        System.out.println(c.exists());//判断文件c是否存在,true
        c.delete();
        System.out.println(c.exists());//删除文件c后判断文件c是否存在,false
        System.out.println(a.isFile());//判断文件a是否为文件,true
        System.out.println(a.isDirectory());//判断文件a是否为文件夹,false
        System.out.println(dir.isFile());//判断文件夹dir是否为文件,false
        System.out.println(dir.isDirectory());//判断文件夹dir是否为文件夹,true

输出结果:

true
false
true
false
false
true

3.3 重命名

变量和类型方法描述
booleanrenameTo​(File dest)重命名此抽象路径名表示的文件
String[]list()返回一个字符串数组,用于命名此抽象路径名表示的目录中的文件和目录

代码示例:

        /**
         *由上述操作可知已d盘的"haha"文件夹中的"hahaha"文件夹中创建文件"d.png"
         * 移动并重命名文件,删除d(d.png),将新的文件e添加到新路径(也可以改变文件的属性 png→txt)
         */
        File e = new File(dir,"eee.txt");
        d.renameTo(e);

        /**
         * 获取此文件夹的所有文件(包括文件夹),保存在数组中
         */
        String[] files1 = dir.list();
        for (String s:files1) {
            System.out.println(s);
        }

输出结果:

a.txt
b.java
eee.txt

3.4 删除

变量和类型方法描述
booleandelete()删除此抽象路径名表示的文件或目录

代码示例:

        /**
         * 删除文件
         * 文件存在 → 删除成功 → true
         * 文件不存在 → 删除失败 → false
         */
//        System.out.println(a.delete());
//        System.out.println(b.delete());
//        System.out.println(c.delete());//之前已经被删除,因此不存在
//        System.out.println(d.delete());
//        System.out.println(e.delete());

输出结果:

true
ture
flase
true
true

3.5 遍历文件

变量和类型方法描述
File[]listFiles()返回一个抽象路径名数组,表示此抽象路径名表示的目录中的文件

代码示例:

        /**
         * 遍历文件
         */
        File dd = new File("d:\\");//创建文件夹
        File[] files = dd.listFiles();//获取此文件夹(d盘)中的全部文件,保存到Files数组中
        listFiles(files);
	/**
     * 遍历文件
     * @param files
     */
    public static void listFiles(File[] files){
        if(files != null && files.length > 0){
            for(File file:files){
                if(file.isFile()){
                    //文件
                    if(file.getName().endsWith(".pdf")){
                        //找到了一个pdf文件
//                        System.out.println("pdf文件:" + file.getPath() + "  " +file.length()/1024 + "kB");
                        if(file.length() > 1000*1024){//>1000kb的文件
                            System.out.println("pdf文件:" + file.getPath() + "  " +file.length()/1024 + "kB");
                        }
                    }
                }else{
                    //文件夹
                    File[] file2 = file.listFiles();//得到该文件夹中的文件
                    listFiles(file2);//递归调用自己
                }
            }
        }
    }//end method

输出结果:

pdf文件:d:\hahatask\Java基础.pdf  3656kB
pdf文件:d:\hahatask\快递e栈.pdf  7275kB

4 文件路径

路径分为绝对路径和相对路径
绝对路径:从盘符开始,是一个完整的路径,例如:d:\ha\绝对路径.txt
相对路径:在Java代码中是相对于项目的目录路径,是一个不完整的便捷路径,在Java开发中经常使用,例如:相对路径.txt
代码示例:

        File f1 = new File(dir,"绝对路径.txt");
        File f2 = new File("相对路径.txt");
        System.out.println("f1的路径(绝对路径):" + f1.getAbsolutePath());
        System.out.println("f2的路径(相对路径):" + f2.getAbsolutePath());

输出结果:

f1的路径(绝对路径):d:\ha\绝对路径.txt
f2的路径(相对路径):D:\code_task\helloworld\相对路径.txt

5 文件过滤器

通过implements FileFilter ,自定义过滤规则、对文件进行遍历,输出符合条件的文件

变量和类型方法描述
String[]list​(FilenameFilter filter)返回一个字符串数组,用于命名由此抽象路径名表示的目录中的文件和目录,以满足指定的过滤器
    public static void main(String[] args) {
        File d = new File("d:" + File.separator);
        listFiles(d);//对d盘内容进行过滤
    }

过滤通常有两种写法

写法一

    /**
     * 文件过滤器
     */
    public static void listFiles(File file){
        //1.创建一个过滤器规则,并描述规则
        FileFilter filter = new PDFFileFilter();

        //2.通过文件获取子文件夹
        File[] files = file.listFiles(filter);
        if(files != null && files.length >0)
        {
            for (File f:files){
                if(f.isDirectory()){
                    //是文件夹
                    listFiles(f);//递归
                }else{
                    //是pdf
                    System.out.println("pdf文件:" + f.getPath() + "  " +f.length()/1024 + "kB");
                }
            }
        }
    }

    /**
     * 过滤的规则
     */
    static class PDFFileFilter implements FileFilter {
        @Override//过滤的方法
        public boolean accept(File pathname) {//pathname是过滤的文件
            if(pathname.getName().endsWith(".pdf") || pathname.isDirectory()){
                return true;//这个文件被保留
            }
            return false;
        }
    }

写法二

    /**
     * 文件过滤器
     */
    public static void listFiles(File file){
        /*
        //1.创建一个过滤器规则,并描述规则
        FileFilter filter = new FileFilter(){//通过匿名内部类来创建文件过滤器规则
            @Override
            public boolean accept(File pathname) {
                if(pathname.getName().endsWith(".pdf") || pathname.isDirectory()){
                    return true;//这个文件被保留
                }
                return false;
            }
        };
        //2.通过文件获取子文件夹
        File[] files = file.listFiles(filter);
        */
        
        //注释掉的部分可以这样写:直接将1右半部分作为2的参数传进去
        File[] files = file.listFiles(new FileFilter(){//通过匿名内部类来创建文件过滤器规则
            @Override
            public boolean accept(File pathname) {
                if(pathname.getName().endsWith(".pdf") || pathname.isDirectory()){
                    return true;//这个文件被保留
                }
                return false;
            }
        });
        if(files != null && files.length > 0)
        {
            for (File f:files){
                if(f.isDirectory()){
                    //是文件夹
                    listFiles(f);//递归
                }else{
                    //是pdf
                    System.out.println("pdf文件:" + f.getPath() + "  " +f.length()/1024 + "kB");
                }
            }
        }
    }

二、 字节输出流:OutputStream

  • 抽象类,基于字节的输出操作,是所有输出流的父类
  • 定义了所有输出流都具有的共同特征
变量和类型方法描述
voidclose()关闭此输出流并释放与此流关联的所有系统资源
voidflush()刷新此输出流并强制写出任何缓冲的输出字节
voidwrite​(byte[] b)将 b.length字节从指定的字节数组写入此输出流
voidwrite​(byte[] b, int off, int len)将从偏移量 off开始的指定字节数组中的 len字节写入此输出流
abstractvoid write​(int b)将指定的字节写入此输出流

FileOutputStream

是字节输出流的具体类

java.lang.Object 
java.io.OutputStream 
java.io.FileOutputStream 

构造方法

构造器描述
FileOutputStream​(File file)写入由指定的 File对象表示的文件
FileOutputStream​(File file, boolean append)写入由指定的 File对象表示的文件
FileOutputStream​(String name)写入具有指定名称的文件
FileOutputStream​(String name, boolean append)写入具有指定名称的文件
  • 创建一个对象
 //FileOutputStream,OutputStream的子类
        FileOutputStream fos = new FileOutputStream("d:" + File.separator + "test.txt");     

常用方法

变量和类型方法描述
voidclose()关闭此文件输出流并释放与此流关联的所有系统资源
voidwrite​(byte[] b)将指定字节数组中的 b.length字节写入此文件输出流
voidwrite​(byte[] b, int off, int len)将从偏移量 off开始的指定字节数组中的 len字节写入此文件输出流
voidwrite​(int b)将指定的字节写入此文件输出流

代码示例:

  • 写一个字节
//写一个字节

        fos.write(48);//字节48,表示字符'0',此时d盘中的a.txt中有内容'0'
        
  • 写一组字节
//写一组字节

        byte[] bytes1 = {49,50,51,52,53,54,55,56,57};//'1','2','3','4','5','6','7','8','9'
        fos.write(bytes1);
        byte[] byte2 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".getBytes();//输入字符串,再转换成bytes类型,但是这是字符,因此和后续使用字符流
        fos.write(byte2,0,26);//从下标1开始,写入两个长度
        fos.close();//写完之后再关闭,尽可能早地关闭
        

执行结果:
d盘中的“test.txt”文件中写入内容:

0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ

三、 字节输入流:InputStream

抽象类,基于字节的输入操作,是所有输入字节流的父类

变量和类型方法描述
voidclose()关闭此输入流并释放与该流关联的所有系统资源
abstract intread()从输入流中读取下一个数据字节
intread​(byte[] b)从输入流中读取一些字节数并将它们存储到缓冲区数组 b
intread​(byte[] b, int off, int len)从输入流 len最多 len字节的数据读入一个字节数组

FileInputStream

是字节输入流的具体类

java.lang.Object 
java.io.InputStream 
java.io.FileInputStream 

构造方法

构造器描述
FileInputStream​(File file)该文件由文件系统中的 File对象file命名
FileInputStream​(String name)该文件由文件系统中的路径名name命名
  • 创建一个对象
//FileInputStream,InputStream的子类
        FileInputStream fis = new FileInputStream("d:" + File.separator + "test.txt");

常用方法

变量和类型方法描述
voidclose()关闭此文件输入流并释放与该流关联的所有系统资源
intread()从此输入流中读取一个字节的数据
intread​(byte[] b)从此输入流 b.length最多 b.length字节的数据读 b.length字节数组
intread​(byte[] b, int off, int len)从此输入流 len最多 len字节的数据读入一个字节数组

代码示例:

  • 读一个字节
//读一个字节
        while(true){//读到超过总长度,一直返回-1
            byte b = (byte)fis.read();
            if(b == -1){
                break;//避免读到超过总长度了还一直读下去,因此加一个判断
            }
            System.out.println(b);
        }
  • 读一组字节,推荐使用
//读一组字节,推荐使用
        byte[] bytes = new byte[10];//设定每次都读取10个字节
        while(true){
            int len = fis.read(bytes);
            if(len == -1){
                break;
            }
            //输出的时候,以len为长度输出而不是10,原因在于:
            //由输出可知,最后为UVWXYZ,len只有6,如果此处不写len而默认为10的话,则输出为UVWXYZQRST(最后四位为上一次在数组中的内容)
            System.out.println(new String(bytes,0,len));
        }
        fis.close();

输出结果:

0123456789
ABCDEFGHIJ
KLMNOPQRST
UVWXYZ

四、 字符输出流:Writer

抽象类,基于字符的输出操作
tips:只能操作文字,而字节流能操作全部

变量和类型方法描述
Writerappend​(char c)将指定的字符追加到此writer
abstract voidclose()关闭流,先冲洗它
abstract voidflush()刷新流
voidwrite​(char[] cbuf)写一个字符数组。
abstract voidwrite​(char[] cbuf, int off, int len)写一个字符数组的一部分。
voidwrite​(int c)写一个字符。
voidwrite​(String str)写一个字符串。
voidwrite​(String str, int off, int len)写一个字符串的一部分。

FileWriter

字符输出流的具体类

java.lang.Object 
java.io.Writer 
java.io.OutputStreamWriter 
java.io.FileWriter 

构造方法

构造器描述
FileWriter​(File file)给 File写一个 FileWriter
FileWriter​(File file, boolean append)在给出要写入的 FileWriter下构造 File ,并使用平台的 default charset构造一个布尔值,指示是否附加写入的数据
FileWriter​(String fileName)构造一个 FileWriter给出文件名,使用平台的 default charset
FileWriter​(String fileName, boolean append)使用平台的 default charset构造一个 FileWriter给定一个文件名和一个布尔值,指示是否附加写入的数据。
        FileWriter fw = new FileWriter("d:" + File.separator + "testRW.txt");
        
//        FileWriter fw = new FileWriter("d:" + File.separator + "testRW.txt",true);
//        append方法:追加,即如果没有则默认false,每次write都会刷新、重新写入(每次都是新文件)
//                           如果有且值为true,则可以在原来的基础上不断追加(在原文件上不断追加)



        fw.write('a');//输入的应该是int,但是char的范围比int小,因此会自动转换
        fw.write("AAAAA,");

        /*
        FileWriter fw2 = (FileWriter) fw.append("BBBBB,");//追加,返回的是fw,因此fw2 = fw
        System.out.println(fw2 == fw);//验证,输出结果为true
//      由于fw2 为fw.append("BBBBB,")返回的对象,因此也可以在fw2的基础上进行追加
        fw2.append("CCCCC,");
        fw.append("DDDDD.");
         */       
         //上述这一段注释 执行完毕后,文件中输入内容:aAAAAA,BBBBB,CCCCC,DDDDD.

//      由于fw.append("BBBBB,")返回的是fw对象,因此可以在fw.append("BBBBB,")的基础上不断追加
        fw.append("BBBBB, ").append("CCCCC,").append("DDDDD.");
        fw.close();

执行结果:
程序执行完后,d盘的"testRW.txt"文件中写入内容

aAAAAA,BBBBB,CCCCC,DDDDD.

五、字符输入流:Reader

抽象类,基于字符的输入操作,读取

变量和类型方法描述
abstract voidclose()关闭流并释放与其关联的所有系统资源
intread()读一个字符
intread​(char[] cbuf)将字符读入数组
abstract intread​(char[] cbuf, int off, int len)将字符读入数组的一部分

FileWriter

字符输入流的具体类

java.lang.Object 
java.io.Reader 
java.io.InputStreamReader 
java.io.FileReader 

构造方法

构造器描述
FileReader​(File file)使用平台 FileReader ,在 File读取时创建一个新的 FileReader
FileReader​(String fileName)使用平台 default charset创建一个新的 FileReader ,给定要读取的文件的 名称

代码示例:

  • 创建对象
 FileReader fr = new FileReader("d:" + File.separator + "testRW.txt");
  • 一次读一个字符
//一次读一个字符
        /*
        while(true){
            int c = fr.read();
            if(c == -1){
                break;
            }
            System.out.println((char)c);
        }
         */
  • 一次读一组字符
   //一次读一组字符
        char[] chars = new char[100];
        int len = fr.read(chars);//这里获取了chars的长度,否则默认为100,空地方空格补齐,浪费
        String text = new String(chars,0,len);
        System.out.println(text);
        fr.close();

输出结果:

aAAAAA,BBBBB,CCCCC,DDDDD.

六、 转换流(字节流→字符流)

转换流,字节流转换为字符流,即字节流 ‘装饰’ 为字符流,使用了装饰者模式

常规转换流

输出流

将字节输出流转换为字符输出流
代码示例:

		FileOutputStream fos = new FileOutputStream("d:" + File.separator + "testRW.txt");
        OutputStreamWriter osw = new OutputStreamWriter(fos);
        osw.write("666666");
        osw.flush();
        osw.close();

执行结果:
d盘中的"testRW.txt"文件被写入内容:

666666

输入流

将字节输入流转换为字符输入流
代码示例:

        //参数1,要转换的字节流
        FileInputStream fis = new FileInputStream("d:" + File.separator + "testRW.txt");

        //参数2,指定编码名称  InputStreamReader isr = new InputStreamReader(fis,"gbk");
        
        InputStreamReader isr = new InputStreamReader(fis);
        while(true){
            int c = isr.read();
            if(c == -1){
                break;
            }
            System.out.println((char)c);
        }

执行结果:

6
6
6
6
6
6

Print

字符打印流(输出流)

使用PrintStream

代码示例1:

        PrintStream ps = new PrintStream("d:" + File.separator + "PrintStream.txt");
        ps.println("啦啦啦啦啦");
        ps.println("哈哈哈哈哈");
        ps.println("嘿嘿嘿嘿嘿");
      

执行结果:
d盘中的"PrintStream.txt"写入内容:

啦啦啦啦啦
哈哈哈哈哈
嘿嘿嘿嘿嘿


使用PrintWriter

代码示例2:

 		PrintWriter pw = new PrintWriter("d:" + File.separator + "PrintStream.txt");
        pw.println("啦啦啦啦啦2");
        pw.println("哈哈哈哈哈2");
        pw.println("嘿嘿嘿嘿嘿2");
        pw.flush();//字符与字节最大的不同就是输出时是否要刷新管道,字符需要刷新

执行结果:
d盘中的"PrintStream.txt"写入内容:

啦啦啦啦啦2
哈哈哈哈哈2
嘿嘿嘿嘿嘿2

字符输入流

代码示例:

      	//转换,PrintWriter转成字符流
        FileOutputStream fos = new FileOutputStream("d:" + File.separator + "PrintStream.txt");
        PrintWriter pw = new PrintWriter(fos);//转换成打印流
        pw.println("你好啦啦啦");
        pw.println("哈哈哈");
        pw.flush();

BufferedReader

缓存读取流,将字符输入流 转为 带有缓存、可以一次读取一行缓存字符读取流
代码示例:

        //缓存读取流,将字符输入流 转为 带有缓存、可以一次读取一行的缓存字符读取流
        FileReader fw = new FileReader("d:" + File.separator + "PrintStream.txt");
        BufferedReader br = new BufferedReader(fw);
        String text = br.readLine();
        System.out.println(text);

输出结果:

你好啦啦啦

七、 序列化技术Serializable

序列化,如果想要某个类能够被序列化,该类要实现序列化的接口,即implements Serializable,只要将类实现标识接口——Serializable接口,不需要重写任何方法

  • implements Serializable 只是一个标记
  • 如果没有允许序列化的接口,则会报错:NotSerializableException(判断是否实现了Serializable,没实现则报错)

序列化

序列化:将程序中的对象以文件形式存储下来(按对象在内存中的存储字符序列)
【Java对象转换为字节序列的过程】

代码示例:

  • 首先建一个Book类,implements Serializable,能够被序列化
 static class Book implements Serializable{
        private String name;
        private String info;

        public Book() {
        }

        public Book(String name, String info) {
            this.name = name;
            this.info = info;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public String getInfo() {
            return info;
        }

        public void setInfo(String info) {
            this.info = info;
        }

        @Override
        public String toString() {
            return "Book{" +
                    "name='" + name + '\'' +
                    ", info='" + info + '\'' +
                    '}';
        }
    }// end class book
  • 序列化

        ArrayList<序列化.Book> list = new ArrayList<序列化.Book>();
        list.add(new 序列化.Book("Java核心技术1","讲述了Java核心技术1的有关内容"));
        list.add(new 序列化.Book("Java核心技术2","讲述了Java核心技术2的有关内容"));
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("d:" + File.separator + "序列化技术.txt"));
        oos.writeObject(b);
        oos.close();

执行结果:
在d盘新建文档“序列化技术.txt”,并且将对象存储到该文档中

反序列化

反序列化:存储下来的数据读到程序中
【把字节序列恢复为Java对象的过程】

代码示例:

	 ObjectInputStream ois = new ObjectInputStream(new FileInputStream("d:" + File.separator + "序列化技术.txt"));
	 
        Object o = ois.readObject();
        System.out.println(o);

执行结果:

Book{name='Java核心技术1', info='讲述了Java核心技术1的有关内容'}
Book{name='Java核心技术2', info='讲述了Java核心技术2的有关内容'}

部分属性序列化

实现部分字段序列化的方式:

① 使用transient修饰符
② 使用static修饰符
③ 默认方法writeObject和readObject
④ Externalizable实现Java序列化

使用transient修饰符

给属性添加transient修饰词,对象被序列化的时候 忽略 这个属性

代码示例:

上述序列化的代码中,将Book的info属性添加transient修饰词

        private transient String info;

执行结果:

从输出结果可以看出,对象被序列化的时候忽略了info属性

Book{name='Java核心技术1', info='null'}, 
Book{name='Java核心技术2', info='null'}

使用static修饰符

static修饰符修饰的属性也不会参与序列化和反序列化

代码示例:

上述序列化的代码中,将Book的info属性添加static修饰词

        private static String info;

执行结果:

从输出结果可以看出,对象被序列化的时候忽略了info属性

Book{name='Java核心技术1', info='null'}, 
Book{name='Java核心技术2', info='null'}

默认方法writeObject和readObject

如果目标类中没有定义私有的writeObject或readObject方法,序列化和反序列化的时候将调用默认的方法来根据目标类中的属性来进行序列化和反序列化
如果目标类中定义了私有的writeObject或readObject方法,序列化和反序列化的时候将调用目标类指定的writeObject或readObject方法来实现,

注意,writeObject或readObject方法一定有修饰符private void,否则不生效

代码示例1:

  • 在类Book中定义私有的writeObject或readObject方法,此时name和info都能够被序列化
        private void writeObject(ObjectOutputStream objOut) throws IOException {
            objOut.writeObject(name);
            objOut.writeObject(info);
        }
        private void readObject(ObjectInputStream objIn) throws IOException,
                ClassNotFoundException {
            name= (String) objIn.readObject();
           info= (String) objIn.readObject();
        }

执行结果:

Book{name='Java核心技术1', info='讲述了Java核心技术1的有关内容'},
Book{name='Java核心技术2', info='讲述了Java核心技术2的有关内容'}

代码示例2:

  • 在类Book中定义私有的writeObject或readObject方法,此时仅name能够被序列化
        private void writeObject(ObjectOutputStream objOut) throws IOException {
            objOut.writeObject(name);
           // objOut.writeObject(info);
        }
        private void readObject(ObjectInputStream objIn) throws IOException,
                ClassNotFoundException {
            name= (String) objIn.readObject();
          // info= (String) objIn.readObject();
        }

执行结果:

Book{name='Java核心技术1', info='null'}, 
Book{name='Java核心技术2', info='null'}

Externalizable实现序列化

  • Externalizable继承自Serializable
  • 使用Externalizable接口定义了writeExternal和readExternal两个抽象方法,需要实现readExternal方法和writeExternal方法来实现序列化和反序列化
  • writeExternal和readExternal两个抽象方法对应Serializable接口的writeObject和readObject方法
区 别SerializableExternalizable
实现复杂度实现简单,Java对其有内建支持实现复杂,由开发人员自己完成
执行效率所有对象由Java统一保存,性能较低开发人员决定哪个对象保存,可能造成速度提升
保存信息保存时占用空间大部分存储,可能造成空间减少
使用频率偏低

代码实现:

tips:Book类一定要实现该接口,即implements Externalizable
在类Book中定义私有的readExternal方法和writeExternal方法,此时仅name能够被序列化

 public void writeExternal(ObjectOutput out) throws IOException {
        out.writeObject(name);
      //  out.writeObject(info);
    }
    public void readExternal(ObjectInput in) throws IOException,ClassNotFoundException {
        name = (String) in.readObject();
      //  info = (String) in.readObject();
    }

执行结果:

Book{name='Java核心技术1', info='null'},
Book{name='Java核心技术2', info='null'}

面试题中常问:
实现序列化有哪些方式?

  • Serializable 和 Externalizable 两种接口

※收集异常日志

创建文档bug.txt,来收集异常,即每次抛出异常时,将异常写入文档中

代码示例:

		try {
            String s = null;
            s.toString();
        }catch(Exception e){
            PrintWriter pw = new PrintWriter("d:" + File.separator + "bug.txt");
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm");
            pw.println(sdf.format(new Date()));
            e.printStackTrace(pw);
            pw.close();
        }

执行结果:
d盘创建"bug.txt"文档并写入内容:

2021-07-09 12:12
java.lang.NullPointerException
at com.company.online.t0103.t03_IO.ExceptionTxt.main(ExceptionTxt.java:14)

※ Properties

Properties可以保存到流中从流中加载
Properties既属于Map,又属于IO

构造方法

构造器描述
Properties()创建一个没有默认值的空属性列表
Properties​(int initialCapacity)创建一个没有默认值的空属性列表,并且初始大小容纳指定数量的元素,而无需动态调整大小
Properties​(Properties defaults)创建具有指定默认值的空属性列表

代码示例:

Properties ppt = new Properties();

常用方法

变量和类型方法描述
voidload​(Reader reader)以简单的面向行的格式从输入字符流中读取属性列表(键和元素对)
voidstore​(Writer writer, String comments)将此 Properties表中的此属性列表(键和元素对)以适合使用 load(Reader)方法的格式写入输出字符流

store

存数据

代码示例:

//ppt.store();  Map中的键值对存储变成.properties文件

        ppt.put("name","Java核心技术");
        ppt.put("info","讲述了Java核心技术的有关内容");
        FileWriter fw = new FileWriter("d:" + File.separator + "book.properties");
        ppt.store(fw,"存储的图书");//(文件,注释)
        fw.close();

执行结果:
在d盘创建“book.properties”文件,文件中写入内容

#\u5B58\u50A8\u7684\u56FE\u4E66(unicode码,翻译一下即可)
#Fri Jul 09 12:33:44 CST 2021(创建的日期)
name = Java核心技术
info = 讲述了Java核心技术的有关内容

load

读数据

代码示例:

//ppt.load(); 传入的文件(字符流字节流都可以)中的内容加载成程序中的Map集合
		Reader r = new FileReader("d:" + File.separator + "book.properties");
        ppt.load(r);
        
//      Map集合中的取值方法
//      System.out.println(ppt.get("name"));
//      System.out.println(ppt.get("info"));

//      properties中的取值方法
        System.out.println(ppt.getProperty("name"));
        System.out.println(ppt.getProperty("info"));
        

输出结果:

Java核心技术
讲述了Java核心技术的有关内容

※ try-with-resources

JDK1.7之前

		FileReader fr = null;
        try{
            fr = new FileReader("PrintStream.txt");
            int c = fr.read();
            System.out.println((char)c);
        } catch (IOException e) {
            e.printStackTrace();
        }finally{
            try {
                fr.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

JDK1.7

        try(FileReader fr = new FileReader("PrintStream.txt");){
            int c = fr.read();
            System.out.println((char)c);
        } catch (IOException e) {
            e.printStackTrace();
        }

JDK9进行了一些优化

	 static class CloseDemo implements Closeable{
        @Override
        public void close() throws IOException {
            System.out.println("close方法被调用了");
        }
    }
		//CloseDemo 实现接口(implements Closeable)之后不会报错
        try(CloseDemo d = new CloseDemo()){

        }catch(Exception e){

        }
  • 2
    点赞
  • 2
    评论
  • 1
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

简介 笔者当初为了学习JAVA,收集了很多经典源码,源码难易程度分为初级、中级、高级等,详情看源码列表,需要的可以直接下载! 这些源码反映了那时那景笔者对未来的盲目,对代码的热情、执着,对IT的憧憬、向往!此时此景,笔者只专注Android、Iphone等移动平台开发,看着这些源码心中有万分感慨,写此文章纪念那时那景! Java 源码包 Applet钢琴模拟程序java源码 2个目标文件,提供基本的音乐编辑功能。编辑音乐软件的朋友,这款实例会对你有所帮助。 Calendar万年历 1个目标文件 EJB 模拟银行ATM流程及操作源代码 6个目标文件,EJB来模拟银行ATM机的流程及操作:获取系统属性,初始化JNDI,取得Home对象的引用,创建EJB对象,并将当前的计数器初始化,调用每一个EJB对象的count()方法,保证Bean正常被激活和钝化,EJB对象是用完毕,从内存中清除,从账户中取出amt,如果amt>账户余额抛出异常,一个实体Bean可以表示不同的数据实例,我们应该通过主键来判断删除哪个数据实例…… ejbCreate函数用于初始化一个EJB实例 5个目标文件,演示Address EJB的实现 ,创建一个EJB测试客户端,得到名字上下文,查询jndi名,通过强制转型得到Home接口,getInitialContext()函数返回一个经过初始化的上下文,用client的getHome()函数调用Home接口函数得到远程接口的引用,用远程接口的引用访问EJB。 EJB中JNDI的使用源码例子 1个目标文件,JNDI的使用例子,有源代码,可以下载参考,JNDI的使用,初始化Context,它是连接JNDI树的起始点,查找你要的对象,打印找到的对象,关闭Context…… ftp文件传输 2个目标文件,FTP的目标是:(1)提高文件的共享性(计算机程序和/或数据),(2)鼓励间接地(通过程序)使用远程计算机,(3)保护用户因主机之间的文件存储系统导致的变化,(4)为了可靠和高效地传输,虽然用户可以在终端上直接地使用它,但是它的主要作用是供程序使用的。本规范尝试满足大型主机、微型主机、个人工作站、和TACs 的不同需求。例如,容易实现协议的设计。 Java EJB中有、无状态SessionBean的两个例子 两个例子,无状态SessionBean可会话Bean必须实现SessionBean,获取系统属性,初始化JNDI,取得Home对象的引用,创建EJB对象,计算利息等;在有状态SessionBean中,用累加器,以对话状态存储起来,创建EJB对象,并将当前的计数器初始化,调用每一个EJB对象的count()方法,保证Bean正常被激活和钝化,EJB对象是用完毕,从内存中清除…… Java Socket 聊天通信演示代码 2个目标文件,一个服务器,一个客户端。 Java Telnet客户端实例源码 一个目标文件,演示Socket的使用。 Java 组播组中发送和接受数据实例 3个目标文件。 Java读写文本文件的示例代码 1个目标文件。 java俄罗斯方块 一个目标文件。 Java非对称加密源码实例 1个目标文件 摘要:Java源码,算法相关,非对称加密   Java非对称加密源程序代码实例,本例中使用RSA加密技术,定义加密算法可用 DES,DESede,Blowfish等。   设定字符串为“张,你好,我是李四”   产生张的密钥对(keyPairZhang)   张生成公钥(publicKeyZhang)并发送给李四,这里发送的是公钥的数组字节   通过网络或磁盘等方式,把公钥编码传送给李四,李四接收到张编码后的公钥,将其解码,李四用张的公钥加密信息,并发送给李四,张用自己的私钥解密从李四处收到的信息…… Java利用DES私钥对称加密代码实例 同上 java聊天室 2个目标文件,简单。 java模拟掷骰子2个 1个目标文件,输出演示。 java凭图游戏 一个目标文件,简单。 java求一个整数的因子 如题。 Java生成密钥的实例 1个目标文件 摘要:Java源码,算法相关,密钥   Java生成密钥、保存密钥的实例源码,通过本源码可以了解到Java如何产生单钥加密的密钥(myKey)、产生双钥的密钥对(keyPair)、如何保存公钥的字节数组、保存私钥到文件privateKey.dat、如何用Java对象序列化保存私钥,通常应对私钥加密后再保存、如何从文件中得到公钥编码的字节数组、如何从字节数组解码公钥。 Java数据压缩与传输实例 1个目标文件 摘要:Java源码,文件操作,数据压缩,文件传输   Java数据压缩与传输实例,可以学习一下实例化套按字、得到文件输入流、压缩输入流、文件输出流、实例化缓冲
©️2021 CSDN 皮肤主题: 游动-白 设计师:白松林 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值