黑马程序员---IO流二

IO流二  

 ----------- android培训java培训、java学习型技术博客、期待与您交流! ------------

4.字符流

4.1 概念

计算机不区分二进制文件与文本文件。所有的文件都是以二进制形式存储的,因此,从本质上说,所以的文件都是二进制文件。所以字符流是建立在字节流之上,它能够提供字符层次的编码和解码。

在写入一个字符时,Java虚拟机会将字符转为文件指定的编码,在读取字符时,再将文件指定的编码转化为字符。

字符流能解决汉字乱码问题:因为一个汉字占2个字节(编码关系),这显然不能用字节流一个字节一个字节那样读了,所以便产生了字符流。

字符流是以字符为单位的。

总结:字符流就是字节流加上编码表,为了更便于操作文字数据。字符流的抽象基类:Reader,Writer ,由这些基类派生出来的子类是:FileReader、FileWriter。

4.2 Reader

常用方法

由于Reader是抽象类,所以使用字符输入流就必须使用Reader的实现类 -- FileReader。

4.2.1 FileReader

FileReader的作用

1)用于读取文本文件的流对象

2)用于关联文本文件

构造函数:在读取流对象初始化的时候,必须要指定一个被读取的文件。

代码实例

比较一下字节流与字符流的分别

package com.ping.IO;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
public class ReaderAndInput {
    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        String path = "c:" + File.separatorChar + "b.txt"; //新建文件
        readFileByInput(path);
        System.out.println();
        readFileByReader(path);
    }
    /*
     * 使用字节流读取文件内容
     * */
    public static void readFileByInput(String path){  
        InputStream  is = null;
        try {
            is = new FileInputStream(path);
            int len ;
            while((len = is.read())!= -1){
                System.out.print((char)len);
            }
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        try {
            is.close();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
    /*
     * 使用字符流读取文件内容
     * */
    public static void readFileByReader(String path){
        Reader re = null;
        try {
            re = new FileReader(path);
            int len;
            while((len = re.read()) != -1){
                System.out.print((char)len);
            }
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}
输出:
使用字节流读取文件内容
??CSS??Cascading Style Sheets?¡§?????¨´??¡À¨ª???????¨°?????¨¹?¨¤????¡ã??¨¹??¡Á¡Â?¨´??¡À¨ª?????????????¨¹???????¨¨???????¨´?????¡è?????¨¨?¨²CSS?????¨®???????????????¨²¡¤¨¢???????¨®?????¡ì¡À??¨°????
使用字符流读取文件内容
 CSS是Cascading Style Sheets(层叠样式表单)的简称。更多的人把它称作样式表。顾名思义,它是一种设计网页样式的工具。借助CSS的强大功能,网页将在您丰富的想象力下千变万化。

由结构上看,字节流与字节流的创建方式是一样的,只是字符流更方便于读取文件。可以解决汉字乱码的问题。

4.3 Writer

常见方法

常用的构造方法有Writer是抽象类,所以实现功能还是它的实现类FileWriter.

如输出流:默认的FileWriter方法新值都会覆盖旧值,想实现追加功能就需要在构造方法后,加上true即可。

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
public class WriterDemo {
    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        String path = "C:" + File.separatorChar + "a.txt";
        writeFile(path);
    }
    /*
     * 将数据写入到指定文件
     * */
    public static void writeFile(String path){
        Writer writer = null;
        try {
            writer = new FileWriter(path,true);
            writer.write("我是胡志平");
            writer.write("黑马程序员");
            writer.write("云七");
           
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        try {
            writer.flush();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}
发现用FileWriter非常方便,可以直接用字符串也没有关系。 

flush方法

如果使用字符输出流,没有使用close方法,会发生什么。

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
public class WriterDemo {
    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        String path = "C:" + File.separatorChar + "a.txt";
        writeFile(path);
    }
    /*
     * 将数据写入到指定文件
     * */
    public static void writeFile(String path){
        Writer writer = null;
        try {
            writer = new FileWriter(path,true);
            writer.write("我是胡志平");
             writer.flush();
            writer.write("黑马程序员");
            writer.write("云七");
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}
发现只有”我是胡志平”这一段录入到文档,这是因为使用了flush方法,刷新了流的缓冲,如之前的代码,没有使用flush()也可以将文件录入,这是因为close()本身有调用flush()
练习:字符流的文件拷贝
package com.ping.IO;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Reader;
import java.io.Writer;
public class ReaderAndWriter {
    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        String path = "C:" + File.separatorChar + "a.txt";
        String target = "c:" + File.separatorChar + "b.txt";
        ReadAndWriter(path,target);
        ReadFile(target);
    }
//复制的方法
    public static void ReadAndWriter(String path , String target){
//声明输入输出流
        Reader reader = null;                                  
        Writer writer = null;
        try {
//创建输入输出流
            reader = new FileReader(path);                    
            writer = new FileWriter(target,true);
//缓冲区
            char [] ch = new char[1024];                   
            int len;
            while((len = reader.read(ch))!= -1){
                writer.write(ch , 0 , len);
            }
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        try {
//关闭流
            reader.close();                                   
            writer.close();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
    public static void ReadFile(String path){               //读取方法
        Reader reader = null;
        try {
            reader = new FileReader(path);
            int len;
            char [] ch = new char[1024];
            while((len = reader.read(ch)) != -1){
                System.out.println(new String(ch , 0 , len)); //读取文件内容
            }
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}
输出:
我是胡志平黑马程序员云七我是胡志平黑马程序员云七我是胡志平黑马程序员云七我是胡志平黑马程序员云七我是胡志平我是胡志平黑马程序员云七我是胡志平黑马程序员云七我是胡志平黑马程序员云七我是胡志平黑马程序员云七我是胡志平

4.4 字符流的缓冲区

字符流的缓冲区注:字符流不能拷贝图片文件,因为在读取文件的时候字符流会自动对这些二进制按照码表进行编码处理,但是图片本来就是二进制文件,不需要进行编码。在一些巧合在码表中有对应,就可以处理,并不是所有的二进制都可以找到对应的,所以信息就丢失了,所以字符流只能拷贝以字符为单位的文本文件。

Reader有一个子类BufferedReader。子类继承父类显然子类可以重写父类的方法,也可以增加自己的新方法。

BufferedReader的新增方法是readLine()方法,具备一次读取一个文本行的功能。

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.Reader;
public class BufferedDemo {
    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        String path = "C:" + File.separatorChar + "a.txt";
        readFile(path);
    }
    public static void readFile(String path){
        Reader reader = null;
        BufferedReader br = null;
        try {
            reader = new FileReader(path);
//将输入流赋给缓冲区
            br = new BufferedReader(reader);          
            String line;
//readLine到句末的是line = null;
            while((line = br.readLine()) != null){
//输出   
                System.out.println(line);         
            }
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}
输出:
我是胡志平

注:

在使用缓冲区时,要了解,缓冲的存在是为了增强流的功能而存在的,所以在建立缓冲区对象时,要先有对象存在。

缓冲区的出现提高了对流的操作效率。原理:其实就是将数组进行封装。

使用字符流拷贝文件

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.Reader;
import java.io.Writer;
public class BufferedCopyDemo {
    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        String srcFile = "D:"+ File.separatorChar + "IO.txt" ;
        String destFile = "E:" + File.separatorChar + "IO.txt";
        copyFile(srcFile,destFile);
    }
    public static void copyFile(String srcFile , String destFile){
        Reader reader = null;
        Writer writer = null;
        BufferedReader br = null;
        BufferedWriter bw = null;
        try {
//创建字符输入流
            reader = new FileReader(srcFile);
//创建字符输出流             
            writer = new FileWriter(destFile);
//创建缓冲输入流           
            br = new BufferedReader(reader);
//创建缓冲输出流           
            bw = new BufferedWriter(writer);           
            String len ; 
//一次读一行
            while((len = br.readLine()) != null){
                bw.write(len);
//刷新缓冲
                bw.flush();
//换行,readLine不能换行,需要手动换行                        
                bw.newLine();                       
            }
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

5.装饰器模式

需求:想要在读取文件的每一行加上行号。

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.Reader;
public class BufferLineNum {
    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        String path = "C:" + File.separatorChar + "b.txt";
        readFile(path);
    }
    public static void readFile(String path){
        Reader reader = null;
        BufferedReader br = null;
        try {
            reader = new FileReader(path);
            br = new BufferedReader(reader);
//行号
            int count = 0;                                
            String len = null;
            while((len = br.readLine()) != null){
                count ++;
/只需要在内容前面加上行号就可以了
                System.out.println(count + ":" + len);     /
            }
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

显然是很容易实现的,但如果每次都要加上行号的话,就显得很麻烦,可以通过继承BufferedReader重写该类的readLine方法,进行功能的增强。

代码练习:

1)

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
public class LineNumDemo {
    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        String path = "c:" + File.separatorChar + "c.txt";
        readFile(path);
    }
    public static void readFile(String path){
        Reader reader = null;
        BufferedReader br = null;
        try {
            reader = new FileReader(path);
            br = new MyBufferedReader(reader);
            String line = null;
            while((line = br.readLine()) != null){
                System.out.println(line);
            }
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}
class MyBufferedReader extends BufferedReader{  
//重写了readLine()方法
    public MyBufferedReader(Reader in) {
        super(in);
        // TODO Auto-generated constructor stub
    }
    int count ;
    public String readLine(){
        String line = null;
        try {
            line = super.readLine();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        if(line != null){
            count ++;
            return count + ":" + line;
        }
        else{
            return null;
        }
    }
}
效果:
1:睡前10个减肥好习惯 让你醒来就变瘦
2:
3:  导语:睡眠对减肥真的很重要,减肥方法中有不少都跟睡眠密切相关。而在睡前养成一些良好的习惯,可以让你在睡梦中一路瘦到天亮,醒来就会不一样哦!究竟有哪些懒人减肥的睡前好习惯,可以帮助你瘦下去呢?
4:
2)需要在输出的一行前加上引号
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
public class LineNumDemo {
    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        String path = "c:" + File.separatorChar + "c.txt";
        readFile(path);
    }
    public static void readFile(String path){
        Reader reader = null;
        BufferedReader br = null;
        try {
            reader = new FileReader(path);
            br = new MyBufferedReader(reader);
            String line = null;
            while((line = br.readLine()) != null){
                System.out.println(line);
            }
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}
class MyBufferedReader extends BufferedReader{
    public MyBufferedReader(Reader in) {
        super(in);
        // TODO Auto-generated constructor stub
    }   //重写了readLine()方法
    public String readLine(){
        String line = null;
        try {
            line = super.readLine();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        if(line != null){
//在一行前加上引号
            return "\"" + line + "\"";  
        }else{
            return null;
        }
    }
}
输出:
"睡前10个减肥好习惯 让你醒来就变瘦"
""
"  导语:睡眠对减肥真的很重要,减肥方法中有不少都跟睡眠密切相关。而在睡前养成一些良好的习惯,可以让你在睡梦中一路瘦到天亮,醒来就会不一样哦!究竟有哪些懒人减肥的睡前好习惯,可以帮助你瘦下去呢?"
""

上述就是装饰器模式

5.1 装饰器模式:

1)使用分层对象来动态透明的向单个对象中添加功能。

2)装饰器指定包装在最初的对象周围的所有的对象都具有相同的基本接口。

3)某些对象是可装饰的,可以通过将其他类包装在这个可装饰对象的四周,来将功能分层。

4)装饰器必须具有和他所装饰的对象相同的接口。

5.2 JavaIO中的应用

1)JavaI/O类库需要多种不同的功能组合,所以使用了装饰器模式。

2)FilterXxx类是JavaIO提供的装饰器基类,即我们要想实现一个新的装饰器,就要继承这些类。

5.3 装饰器与继承

继承实现的增强类和修饰模式实现的增强类的分别。

1)继承实现的增强类

     优点:代码结构清晰,而且实现简单

     缺点:对于每一个的需要增强的类都要创建具体的子类来帮助其增强,这样会导致继承体系过于庞大。

2)修饰模式实现的增强类

     优点:内部可以通过多态技术对多个需要增强的类进行增强

     缺点:需要内部通过多态技术维护需要增强的类的实例,使代码变得复杂。

6.序列流--合并流

6.1 概念

序列流(SequenceInputStream)对多个流进行合并。

表示其他输入流的逻辑串联。它从输入流的有序集合开始,并从第一个输入流开始读取,直到到达文件末尾,接着从第二个输入流读取,依次类推,直到包含的最后一个输入流的文件末尾为止。

构造函数

SequenceInputStream(InputStreams1 , InputStream s2)

实例:

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.SequenceInputStream;
public class SequenceDemo {
    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        String path = "c:" + File.separatorChar + "a.txt";
        System.out.println("输入流一的内容:");
        readFile(path);
        System.out.println("输入流二的内容");
        String path2 = "c:" + File.separatorChar + "b.txt";
        readFile(path2);
        String target = "c:" + File.separatorChar + "c.txt";
        testSequenceInputStream(path, path2 , target);
        System.out.println("合并后的内容:");
        readFile(target);
    }
    public static void testSequenceInputStream(String path , String path2 , String target){
        FileInputStream fis = null;
        FileInputStream fis2 = null;
        SequenceInputStream sis = null;
        FileOutputStream fos = null;
        try {
//创建两个输入流
            fis = new FileInputStream(path);                      
            fis2 = new FileInputStream(path2);
//一个合并流                   
            sis = new SequenceInputStream(fis , fis2); 
//一个输出流           
            fos = new FileOutputStream(target);                      
            int len ;
//缓冲区
            byte[] by = new byte[1024];                                 
            while((len = sis.read(by)) != -1){
//将合并流的东西写入至输出流                          
                fos.write(by , 0 , len);                                 
            }
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        try {
//关闭流
            sis.close();
            fos.close();                                       
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
//读内容
    public s tatic void readFile(String path){                              
        FileInputStream fis = null;
        try {
            fis = new FileInputStream(path);
            int len;
            byte [] by = new byte[1024];
            while((len = fis.read(by)) != -1){
                System.out.println(new String(by , 0 , len));
            }
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        try {
            fis.close();                                       
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }  
    }
}
输出:
输入流一的内容:
我是胡志平
输入流二的内容
黑马程序员
合并后的内容:
我是胡志平黑马程序员

6.2 合并多个流

如果合并多个流的话,就需要用到集合了

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.SequenceInputStream;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.LinkedHashSet;
public class MoreSequenceDemo {
    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
       String path1 = "c:" + File.separatorChar + "a.txt" ;
        System.out.println("文档1");
        readFile(path1);
        String path2 = "c:" + File.separatorChar + "b.txt";
        System.out.println("文档2");
        readFile(path2);
        String path3 = "c:" + File.separatorChar + "c.txt";
        System.out.println("文档3");
        readFile(path3);
        String target = "c:" + File.separatorChar + "d.txt";
        testSequenceInputStream(path1,path2,path3,target);
        System.out.println("合并后的文档:");       
readFile(target);
    }
    public static void testSequenceInputStream(String path , String path2 ,String path3 , String target){
        InputStream in1 = null;
        InputStream in2 = null;
        InputStream in3 = null;
        SequenceInputStream sis = null;
        LinkedHashSet<InputStream> set = new LinkedHashSet<InputStream>();    
 //创建一个InputStream的集合
        OutputStream out = null;
        try {
//创建输入流1
            in1 = new FileInputStream(path);
//创建输入流2                                  
            in2 = new FileInputStream(path2);
//创建输入流3                                
            in3 = new FileInputStream(path3);
//向集合里添加输入流                             
            set.add(in1);                                                  
            set.add(in2);
            set.add(in3);   
//迭代器                  
            final Iterator<InputStream>iter = set.iterator();
//将迭代器的内容添加到合并流中去            
            sis = new SequenceInputStream(                                  
                    new Enumeration<InputStream>(){
                        @Override
                        public boolean hasMoreElements() {
                            // TODO Auto-generated method stub
                            return iter.hasNext();
                        }
                        @Override
                        public InputStream nextElement() {
                            // TODO Auto-generated method stub
                            return iter.next();
                        }
                    });
            out = new FileOutputStream(target);
            for(int i = 0 ; (i = sis.read())!= -1; ){
                out.write(i);
            }
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        try {
            sis.close();
            out.close();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
    public static void readFile(String path){    
//读取文档内容                                  
        InputStream in = null;
        try {
            in = new FileInputStream(path);
            int len;
            byte [] by = new byte[1024];
            while((len = in.read(by)) != -1){
                System.out.println(new String(by , 0 , len));
            }
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        try {
            in.close();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}
输出:
文档1
我是胡志平
文档2
黑马程序员
文档3
我是胡志平黑马程序员
合并后的文档:
我是胡志平黑马程序员我是胡志平黑马程序员

6.3 对象的序列化

6.3.1 对象序列化的作用

当创建对象时,程序运行时它就会存在,但是程序停止时,对象也就消失了,但是如果希望对象在程序不运行的情况下仍然能存在并保存其信息,将会非常有用,对象被重建并且拥有与程序上次运行时拥有的信息相同。这就需要用到对象的序列化。

对象序列化:将内存中的对象直接写入到文件设备中。

对象反序列化:将文件设备中持久化的数据转换为内存对象。

基本的序列化的由两个方法产生:一个方法用于序列化对象并将它们写入到一个流,另一个方法用于读取流并反序列化对象。

ObjectOutput
         writeObject(Object obj)
         将对象写入底层存储或流
ObjectInput
         readObject();
         读取并返回对象
6.3.2 ObjectInputStream

如之前所说的字节流与字符流一般,ObjectOutput与ObjectInput是接口,所以需要具体实现类。

ObjectOutput
ObjectOutputStream 被写入的对象实现一个接口:Serializale
                  否则会抛出:NotSerializableException
ObjectInput
ObjectInputStream  该方法抛出异常:ClassNotFountException

ObjectOutputStream 和ObjectInputStream对象分别需要字节输出流和字节输入流对象来构建对象。也就是这两个流对象需要操作书有对象将对象进行本地持久化存储。

序列化与反序列化对象实例

package com.ping.Seria;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
public class SequDemo {
    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        String path = "c:" + File.separatorChar +"cat.txt";
        testSequ(path);
    }
    public static void testSequ(String path){
        Cat cat = new Cat("Tom",15);
        FileOutputStream fos = null;
        ObjectOutputStream oos = null;
        FileInputStream fis = null;
        ObjectInputStream  ois = null;
        try {
            fos = new FileOutputStream(path);
//将输出流对象放进序列化方法中
            oos = new ObjectOutputStream(fos);
//序列化  
            oos.writeObject(cat);             
            System.out.println(cat);
            oos.close();
//反序列化
            fis= new FileInputStream(path);
//将输入流对象放进反序列化方法中   
            ois = new ObjectInputStream(fis);     
            Object readObject = ois.readObject();
            Cat cat2 = (Cat)readObject;
            System.out.println(cat2);
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        try {
            ois.close();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}
@SuppressWarnings("serial")
class Cat implements Serializable{
    public String name;
    public int age;
    public Cat(){}
    public Cat(String name , int age){
        this.name = name;
        this.age = age;
    }
    @Override
    public String toString() {
        return "Cat [age=" + age + ", name=" + name + "]";
    }
}
输出:
Cat [age=15, name=Tom]
Cat [age=15, name=Tom]

实现序列化关键:

1)声明Cat类实现了Serializable接口,是一个标示器,没有要实现的方法。

2)新建Cat对象。

3)新建字节流对象(FileOutputStream)进序列化保存在本地文件中。

4)新建ObjectOutputStream对象,调用writeObject方法序列化Cat对象。

5)writeObject方法会执行两个工作:序列化对象,然后将序列化的对象写入文件中。

6)反序列化就是调用ObjectInputStream的readObject()方法。

7)异常处理和流的关闭动作要执行。

6.3.3 Serializable接口

类通过实现java.io.Serializable接口以启用其序列化功能。未实现此接口的类将无法使其任何状态序列化或反序列化。可序列化的所有子类类型本身都是可序列化的。序列化没有方法或字段,仅用于标识可序列化的语义。

所以需要被序列化的类必须实现Serializable接口,该接口中没有任何的属性和方法,称之为标记接口。

如果对象没有实现接口Serializable,在进行序列化时会抛出:NotSerializableException异常。

注意:

保存一个对象的真正意义是什么?

如果对象实例的实例变量都是基本数据类型,那么就非常简单。但是如果实例变量是包含对象的引用,很明显在Java中保存引用变量是没有意义,因为Java引用的值是通过JVM的单一实例的上下文中都有意义。通过序列化后,尝试在JVM的另一个实例中恢复对象,是没有用处。

如一个Person对象,也建立一个Computer对象。Person包含一个Computer(电脑)。如果想保存Person对象,就必须保存Computer对象,但是如果Computer中也包含了其他对象的引用,这就意味着保存一个Person对象需要清楚地知道Person对象的内部结构。

Java的序列化机制可以解决该类问题,当序列化一个对象时,Java的序列化机制会负责保存的所有关联的对象(就是对象图),反序列化时,也会恢复所有的相关内容。

如序列化Person会自动序列化Computer。但是如果只是Person实现了该接口,而Computer没有实现该接口,那会怎么样。

Person与Computer

import java.io.File;
import java.io.FileOutputStream;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
import java.io.Serializable;
public class SeqDemo {
    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        Computer computer = new Computer(4401);
        Person person = new Person(computer,"小平");
        FileOutputStream fos = null;
        ObjectOutput oos = null;
        try {
            fos = new FileOutputStream("c:" + File.separatorChar + "dog.txt");
            oos = new ObjectOutputStream(fos);
            oos.writeObject(person);
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}
class Person implements Serializable{
    private Computer computer;
    private String name;
    public Person(Computer computer, String name) {
        super();
        this.computer = computer;
        this.name = name;
    }
    public Computer getComputer() {
        return computer;
    }
}
class Computer{
    private double price;
    public double getPrice() {
        return price;
    }
    public void setPrice(double price) {
        this.price = price;
    }
    public Computer(double price) {
        super();
        this.price = price;
    }
}
执行程序时,出现了异常
输出:
java.io.NotSerializableException: com.ping.Sequence.Computer

所以我们知道必须在使用Person时将Computer序列化。但如果我们没有办法访问Computer的源码,或者无法使Computer序列化。

解决办法有:

1.继承Computer类,使子类可序列化

如果Computer是final类,就无法继承。并且如果Computer引用了其他非序列化对象,也无法解决问题。

Transient

此时就可以使用transient修饰符,可以将Person类中的成员变量标识为transient,那么在序列化Person对象时,序列化就会路过Computer。

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInput;
import java.io.ObjectInputStream;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
import java.io.Serializable;
public class SeqDemo {
    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        Computer computer = new Computer(4401);
        Person person = new Person(computer,"小平");
        System.out.println(person.getComputer().getPrice());
        FileOutputStream fos = null;
        FileInputStream fis = null;
        ObjectInput ois = null;
        ObjectOutput oos = null;
        try {
//序列化
            fos = new FileOutputStream("c:" + File.separatorChar + "dog.txt");
            oos = new ObjectOutputStream(fos);
            oos.writeObject(person);
//反序列化
             fis = new FileInputStream("c:" + File.separatorChar + "dog.txt");
             ois = new ObjectInputStream(fis);
             Object readObject = ois.readObject();
             computer = (Computer)readObject;
             System.out.println(computer);
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}
class  Person implements Serializable{
    private transient Computer computer;
    private String name;
    public Person(Computer computer, String name) {
        super();
        this.computer = computer;
        this.name = name;
    }
    public Computer getComputer() {
        return computer;
    }
}
class Computer{
    private double price;
    public double getPrice() {
        return price;
    }
    public void setPrice(double price) {
        this.price = price;
    }
    public Computer(double price) {
        super();
        this.price = price;
    }
}
输出:
4401.0
java.lang.ClassCastException: com.ping.Sequence.Person cannot be cast to com.ping.Sequence.Computer
    at com.ping.Sequence.SeqDemo.main(SeqDemo.java:33)
我们得到一个序列化的Person和一个非序列化的Computer
而反序列化Person,访问Computer时,就会出现运行时异常
注:静态变量不适合于序列化,因为静态变量并不属于对象的实例变量的一部分。静态变量随着类的加载,是类变量。由于序列化只适用于对象。

2.基本数据数据可以被序列化

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
public class BasicSeqDemo {
    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        String path = "c:" + File.separatorChar + "basic.txt";
        basicSeq(path);
    }
    public static void basicSeq(String path) {
        FileOutputStream fos = null;
        ObjectOutputStream oos = null;
        FileInputStream fis = null;
        ObjectInputStream ois = null;
        try {
            fos = new FileOutputStream(path); // 创建字节输出流
            oos = new ObjectOutputStream(fos); // 创建序列化流对象
            oos.writeDouble(5.21); // 序列化基本数据类型
            oos.writeBoolean(false);
            oos.writeInt(1000);
            oos.close(); // 关闭流
            fis = new FileInputStream(path); // 创建字节输入流
            ois = new ObjectInputStream(fis); // 反序列化
            System.out.println(ois.readDouble());
            System.out.println(ois.readBoolean());
            System.out.println(ois.readInt());
            ois.close(); // 关闭流
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}
输出:
5.21
false
1000

注:输出的顺序不能变,一旦变化,将会出现错误数据。

   System.out.println(ois.readBoolean());  //应先输出Double类型
            System.out.println(ois.readInt());
            System.out.println(ois.readDouble());
结果:
true
349637181
3.942619695398356E234
输出的数据与原来的完全不同。
6.3.4 SerialVersionUID

定义:用于给类指定一个UID。该UID是通过类中的可序列化成员的数字签名运算出来的一个Long型的值。

只要是这些成员没有变化,那么该值每次运算都一样。

该值用于判断被序列化的对象和类文件是否兼容。

如果被序列化的对象需要被不同的类版本所兼容。可以在类中自定义UID.

定义方式:static final long serialVersionUID = 451658L;

7.Properties类

可以和流相关联的集合对象Properties。

Map  
   |----Hashtable
     |----Properties

7.1 Properties的特点

Properties:该集合不需要泛型,因为该集合中的键值对都是String 类型。

1.存入键值对:setPropertyNames();

2.获取指定键中对应的值:value getProperty(key);

3.获取集合中所有键元素。 Enumeration propertyNames();

4.列出该集合中的所有键值对可以通过参数打印流指定列出到的目的地。

list(PrintStream)

list(PrintWriter)

如:list(System.out):将集合的键值对打印到控制台

   list(new PrintStream(“info.txt”)):将集合中的键值对存储到info.txt文件中

5.可以将流中的规则数据加载进行集合,并称为键值对。

Load(InputStream)

注:流中的数据要符合“键值对的要求”

6.可以将集合中的数据进行指定目的的存储。

Store(OutputStream,String,comment)方法。

注:Properties只加载key == value 这样的键值对,与文件名无关,注释用#

练习:记录一个程序运行的次数,当满足指定次数时,程序就不可以再运行了。(可用于控制软件使用次数)

 public static void sysPropList(){
        Properties prop = System.getProperties();
        try {
//将JvM的属性信息存储到一个文件中
            prop.list(new PrintStream("java.txt")); 
        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
    public static void sysProp(){
        Properties prop = System.getProperties();
        Set<String> keys = prop.stringPropertyNames();
        for(String key : keys){
            System.out.println(key + ":" + prop.getProperty(key));
        }
    }

7.2 Properties类与配置文件

Map  
   |----Hashtable
     |----Properties

注意:这是一个Map集合,该集合中的键值对都是字符串。该集合通常用于对键值对形式的配置文件进行操作。

配置文件:将软件中可变的部分数据可以定义到一个文件中,方便以后更改,该文件称之为配置文件。

优势:提高代码的维护性。

Properties:该类是一个Map的子类,提供了可以快速操作配置文件的方法。

Load():将文件设置数据装载为Map集合数据。

get(key):获取Map中的数据。

getProperty():获取Map中的数据特有方法。

案例:

/*
     * 将配置文件中的数据通过流加载到集合中。
     */
    public static void loadFile() throws IOException {
        // 1,创建Properties(Map)对象
        Properties prop = new Properties();
        // 2.使用流加载配置文件。
        FileInputStream fis = new FileInputStream("c:\\qq.txt");
        // 3.使用Properties 对象的load方法将流中数据加载到集合中。
        prop.load(fis);
        // 遍历该集合
        Set<Entry<Object, Object>> entrySet = prop.entrySet();
        Iterator<Entry<Object, Object>> it = entrySet.iterator();
        while (it.hasNext()) {
            Entry<Object, Object> next = it.next();
            Object key = next.getKey();
            Object value = next.getValue();
        }
        // 通过键获取指定的值
        Object object = prop.get("jack");
        System.out.println(object);
        // 通过键修改值
        prop.setProperty("jack", "888888");
        // 将集合中的数据写入到配置文件中。
        FileOutputStream fos = new FileOutputStream("c:\\qq.txt");
        // 注释:
        prop.store(fos, "yes,qq");
        fos.close();
        fis.close();
    }

获取记录程序运行次数:

public class Demo6 {
    public static void main(String[] args) throws IOException {
        int count = 0;
        Properties pro = new Properties();
        File file = new File("c:\\count.ini");
        FileInputStream fis = null;
        if (!file.exists()) {
            file.createNewFile();
        }
        fis = new FileInputStream(file);
        pro.load(fis);
        String str = pro.getProperty("count");
        if (str != null) {
            count = Integer.parseInt(str);
        }
        if (count == 3) {
            System.out.println("使用次数已到,请付费");
            System.exit(0);
        }
        count++;
        System.out.println("欢迎使用本软件" + "你已经使用了:" + count + " 次");
        pro.setProperty("count", count + "");
        FileOutputStream fos = new FileOutputStream(new File("c:\\count.ini"));
        pro.store(fos, "请保护知识产权");
        fis.close();
        fos.close();
    }
}

8.打印流

PrintStream可以接受文件和其他字节输出流,所以打印流是对普通字节输出流的增强,其中定义了很多重载的print()和println(),方便各种类型的数据。

8.1 PrintStream

说明:这是一个字符打印流,System.out对应的类型就是PrintStream。

它的构造函数可以接收有一种数据类型。

1.字符串路径。

2. File对象

3. OutputStream类型

import java.io.File;
import java.io.PrintStream;
public class PrintDemo {
    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        PrintStream ps = System.out;
        //普通write方法需要调用flush或者close才会在控制台在显示
        ps.println(100);
        ps.println(2514.);
        ps.println(new Object());
        try {
            //定义新的打印流
            PrintStream ps2 = new PrintStream(new File("c:\\new.txt"));
            System.setOut(ps2);
            //不换行打印
            ps2.print(1000);
            //换行打印
            ps2.println("a");
            ps2.println(false);
            ps2.println(new Object());
            //格式化
            ps2.printf("%d,%f,%c,%s", 100, 3.14, 'a', "中国");
            ps2.printf("%4s和%8s打价格战", "京东", "苏宁");
           
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        ps.close();
    }
}
打印流的三种方法
  1. print(数据类型 变量)
  2. println(数据类型 变量)
  3. printf(String format , Object … args) -- 可以自定义格式

注意:

1.print 和println方法的区别在于,一个不换行一个换行。

2.Print与Write都是能写入文件,他们的区别在于print 方法 和write 方法的却在于,print提供自动刷新。

3.格式化输出(printf()):格式化输出,但是在格式化输出的时候需要指定输出的数据类型。

数据类型化的各种标记

8.2 PrintWriter

说明:PrintWriter是一个字符打印流。构造函数可以接收四种类型的值。

1.字符串路径

2.File对象

3.OutputStream

4.Writer

注:1,2类型的数据,还可以指定编码表。也就是字符集。

3,4类型的数据,可以指定自动刷新。如果该自动刷新值为true时,只有有三种方法可用:println,printf,format。

能自动刷新又可自动执行编码的流对象包装。

PrintWriter pw = newPrintWriter(new OutputStreamWriter(new FileOutputStream(“a.txt”),”utf-8”),true);

如果想要提高效率。还要使用打印方法。

PrintWriter pw = newPrintWriter(new BufferdWriter(new OutputStreamWriter(new FileOutputStream(“a.txt”),”utf-8”)),true);

8.3 操作基本数据类型的流对象

8.3.1 DataInputStream

说明:DataInputStream从数据流读取字节,并将它们转换为正确的基本数据类型值或字符串。该类继承FileterInputStream类,并实现了DataInput接口。

8.3.2 DataOutputStream

说明:DataOutputStream将基本类型的值或字符串转换为字节,并且将字节输出到数据流。该类继承FilterOutputStream并实现DataOutput接口

DataInputStream操作数据类型的方法

DataOutputStream操作数据类型的方法

代码实例:使用DataOutputStream写数据文件

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
public class DataInputDmeo {
    /**
     * @param args
     * @throws Exception
     */
    public static void main(String[] args) throws Exception {
        // TODO Auto-generated method stub
    //  String path = "c:" + File.separatorChar + "a.txt";
        testDataInputStream();
    }
    public static void testDataInputStream() throws Exception {
        DataOutputStream out = new DataOutputStream(new FileOutputStream(
                "c:/a.txt"));
        out.writeBoolean(true);
// 0x05 1 个字节
        out.writeByte(15);
// 0x 0041 2个字节
        out.writeBytes("abc");
// ??
        out.writeChar('X');
        out.writeChars("xyz");
        out.writeLong(111);
        out.writeUTF("中国");
        out.close();
        DataInputStream in = new DataInputStream(
                new FileInputStream("c:/a.txt"));
        System.out.println(in.readBoolean());
        System.out.println(in.readByte());
        System.out.println(in.readByte());
        System.out.println(in.readByte());
        System.out.println(in.readByte());
        System.out.println(in.readChar());
        System.out.println(in.readChar());
        System.out.println(in.readChar());
        System.out.println(in.readChar());
        System.out.println(in.readLong());
        System.out.println(in.readUTF());
        in.close();
    }
}
输出:
true
15
97
98
99
X
x
y
z
111
中国

       ----------- android培训java培训、java学习型技术博客期待与您交流! ------------

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值