# IO编程

Java提供了java.io工具包来处理,其核心类有File,InputStream,OutputStream,Reader,Writer,Serializable。它们的关联如下:

oldJavaIO

# File类基本操作

import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;

class MyFileOper {
    File file;

    public boolean createFile(String filePath, boolean isDeleteCurrent) throws IOException {
        file = new File(filePath);
        if (!file.getParentFile().exists()) {
            file.getParentFile().mkdirs();
        }
        if (isDeleteCurrent && file.exists()) {
            file.delete();
            System.out.println("删除已有文件!");
        }
        return file.createNewFile();
    }

    public static void GetCurrentWorkFolder() {
        // 获取当前目录
        File currentDirectory = new File("");// 设定为当前文件夹
        try {
            System.out.println(currentDirectory.getCanonicalPath());
            System.out.println(currentDirectory.getAbsolutePath());// 获取绝对路径
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    public void GetAttribute() {
        System.out.println("文件是否可写:" + file.canWrite());
        System.out.println("文件是否可读:" + file.canRead());
        System.out
                .println("最后修改时间:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date(file.lastModified())));
        System.out.println("文件是否为目录:" + file.isDirectory());
    }

    public static void GetFolderFileList(String filePath) {
        File fileOper = new File(filePath);
        if (fileOper != null && fileOper.isDirectory()) {
            File result[] = fileOper.listFiles();
            if (result != null)
                for (int i = 0; i < result.length; i++) {
                    System.out.println(result[i].getAbsolutePath());
                    GetFolderFileList(result[i].getAbsolutePath());
                }
        }
    }
    public void ChangeFileName() {
        File newFileName = new File(file.getParentFile(),"NewFileName.md");
        file.renameTo(newFileName);
    }
}

public class Main {
    public static void main(String[] args) throws IOException {
        MyFileOper myFileOper = new MyFileOper();
        //创建某个文件,File.separator代表了不同系统中的文件分隔符,Windows中路径分隔符为"\",Linux系统中为"/"。
        myFileOper.createFile("test" + File.separator + "work" + File.separator + "hello.txt", false);
        //获取文件属性
        myFileOper.GetAttribute();
        //获取文件目录列表
        myFileOper.GetFolderFileList("D:\\");
        //改变文件名
        myFileOper.ChangeFileName();
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70

# 字节流和字符流

在程序中,数据以流的形式传输和保存,想象下家里的自来水管,从水厂中引流到家里,然后使用后进入下水道,可以形象地展示流的概念。在Java中读取数据时利用输入流来完成,输出数据时用输出流,基本上有两类支持:

  • 字节操作流,在JDK 1.0定义,有OutputStream和InputStream。
  • 字符操作流,在JDK 1.1定义,有Writer和Reader。

操作步骤有:

  1. 使用文件对象来新建一个File类对象作为文件描述符。
  2. 通过字节流或字符流子类来实例化相应流。
  3. 执行读写操作。
  4. 通过文件描述符关闭资源。

# 字节输入输出流

字节是I/O操作的基本数据单位

  • 输出流由抽象类OutputStream来完成,它实现了Closeable和Flushable接口,在JDK1.5之前close()和flush()都是直接定义在OutputStream类中。如果要向文件输出,可使用FileOutputStream子类。
  • 输入流由抽象类InputStream来完成,它实现了Closeable接口。如果要从文件读入,可使用FileInputStream子类。

FileOutputStream类继承结构如下:

FileOutputStream

oldOutputStream

FileInputStream类继承结构如下:

FileInputStream

oldInputStream

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

public class Main {
    public static void main(String[] args) {
        File file = new File("ByteStream.txt");
        try {
            OutputStream outputFile = new FileOutputStream(file);
            outputFile.write("这是一个测试文件!".getBytes());
            outputFile.close();

        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        
        InputStream inputFile;
        try {
            inputFile = new FileInputStream(file);
            byte data[] = new byte[1024];
            int len=inputFile.read(data);
            System.out.println("结果为:"+new String(data,0,len));
            inputFile.close();
        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38

它们可使用AutoCloseable自动关闭接口:

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

public class Main {
    public static void main(String[] args) {
        File file = new File("ByteStream.txt");
        try (OutputStream outputFile = new FileOutputStream(file)) {
            outputFile.write("这是一个测试文件!".getBytes());
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        try (InputStream inputFile = new FileInputStream(file)) {
            byte data[] = new byte[1024];
            int len = inputFile.read(data);
            System.out.println("结果为:" + new String(data, 0, len));
        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

# 字符输入输出流

Java中底层通信处理都是依靠字节实现数据交互,在程序为了方便进行中文处理,可使用字符数据类型:

  • Writer类实现了Appendable,Closeable,Flushable接口。
  • Reader类实现了Readable和Closeable接口。

Writer类的继承结构如下:

Writer

oldWriterStream

Reader类的继承结构如下:

Reader

oldReaderStream

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 Main {
    public static void main(String[] args) {
        File file = new File("CharStream.txt");
        try (Writer outputFile = new FileWriter(file)) {
            outputFile.write("这是一个测试文件!");
            outputFile.append("hello!");
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        try (Reader inputFile = new FileReader(file)) {
            char data[] = new char[1024];
            int len = inputFile.read(data);
            System.out.println("结果为:" + new String(data, 0, len));
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

# 字节流和字符流区别

它们底层传输的都是字节数据,它们最大的区别是字符流使用了缓冲区,字节流则是直接进行数据处理操作,当使用字符输出流时必须使用Flushable接口提供了flush()方法强制性刷新缓冲区的内容。

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 Main {
    public static void main(String[] args) {
        File file = new File("CharStream.txt");
        try (Writer outputFile = new FileWriter(file)) {
            outputFile.write("这是一个测试文件!");
            outputFile.append("hello!");
            outputFile.flush();
            try (Reader inputFile = new FileReader(file)) {
                char data[] = new char[1024];
                int len = inputFile.read(data);
                System.out.println("结果为:" + new String(data, 0, len));
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

注意进行close操作时也会刷新缓冲区。

# 转换流

转换流的目的是解决字节流和字符流之间操作类型的转换,它有两个类:OutputStreamWriter和InputStreamReader。它们分别是Writer和Reader的子类,可传入相应的字节流即可完成将字节流转换为字符流的操作。

OutputStreamWriter类的继承结构如下:

OutputStreamWriter

InputStreamReader类的继承结构如下:

InputStreamReader

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;

public class Main {
    public static void main(String[] args) throws IOException {
        File file = new File("CharStream.txt");
        OutputStream outputStream = new FileOutputStream(file);
        Writer outWriter = new OutputStreamWriter(outputStream);
        outWriter.append("呵呵呵呵呵");
        outWriter.close();
        outputStream.close();
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# 文件夹复制实例

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

class CopyFolder {
    private String targetBaseFolderString;
    
    public boolean CreateFolder=false;

    private boolean SetTarget(String targetPath) {
        File tempFile = new File(targetPath);
        if (tempFile.isDirectory()) {
            this.targetBaseFolderString = targetPath;
            return true;
        } else {
            return false;
        }
    }

    public void CopyFolderFileToTarget(String filePath,String targetFolder) {
        if(!SetTarget(targetFolder)) {
            System.out.println("目标目录 不是目录!");
            return;
        }
        GetFolderFileListAndCopy(new File(filePath), new File(targetFolder));
    }
    private void GetFolderFileListAndCopy(File sourcePathDes,File targetPathDes) {

        if(sourcePathDes.isDirectory() && !targetPathDes.exists() && CreateFolder) {
            targetPathDes.mkdir();
            System.out.println("创建了目录:"+targetPathDes);
        }

        if (sourcePathDes != null && sourcePathDes.isDirectory()) {
            File result[] = sourcePathDes.listFiles();
            if (result != null)
                for (int i = 0; i < result.length; i++) {
                    //System.out.println(result[i].getAbsolutePath());
                    GetFolderFileListAndCopy(result[i],new File(targetPathDes+File.separator+result[i].getName()));
                }
        }
        if (sourcePathDes.isFile()) {
            CopyFileToTarget(sourcePathDes,targetPathDes);
            System.out.println(sourcePathDes.getName()+" copied to "+targetPathDes);
        }
    }

    private void CopyFileToTarget(File source, File target) {
        if (!target.getParentFile().exists()) {
            target.getParentFile().mkdirs();
            System.out.println("创建了目标文件目录"+target.getParentFile());
        }
        InputStream inputStream = null;
        OutputStream outputStream = null;
        try {
            inputStream = new FileInputStream(source);
            outputStream = new FileOutputStream(target);
            try {
                inputStream.transferTo(outputStream);
//				int len=0;
//				byte data[]=new byte[1000];
//				while((len=inputStream.read(data))!=-1) {
//					outputStream.write(data,0,len);
//				}
                inputStream.close();
                outputStream.close();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

public class Main {
    public static void main(String[] args) {
        CopyFolder cp1 = new CopyFolder();
        cp1.CreateFolder=true;
        cp1.CopyFolderFileToTarget("D:\\temp\\cn","D:\\temp\\target");
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88

如果上面的CreateFolder设置为True,会创建原目录中的空目录,否则只创建原目录中有文件的目录。JDK 1.9之后才提供transferTo()方法,在JDK 1.9之前否则需要上面注释部分。

# 字符编码

计算机中所有语言显示的文字都是有编码的,一个编码对应相应图形,在显示器上是图形,在内存里是编码数字。下面是常用的编码:

  • ISO8859-1:国际通用单字节编码,0-255的范围,主要用于英文,向下兼容ASCII码。
  • GBK/GB2312:中文的国际编码,双字节编码,GBK可表示简体和繁体中文,GB2312只能表示简体中文,GBK兼容GB2312。
  • UNICODE:可以准确表示任何语言文字,不兼容ISO8859-1。
  • UTF-8编码:是UNICODE的编码方式,兼容ISO8859-1,不定长编码。

从JDK 1.9开始,Java的默认编码是UTF-8。

# 乱码问题

有些文件在系统中打开会是乱码,可能是因为文件自身的编码不是系统的默认编码,比如系统默认是GBK,但在写文件时用的是UTF-8,这就好比一个英国人用英语跟日本人说话,一个不熟悉英语的日本人根本就听不懂。

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;

public class Main {
    public static void main(String[] args) {
        System.out.println("系统编码:"+System.getProperty("file.encoding"));
        
        //写文件时指定编码
        try {
            OutputStream outputStream = new FileOutputStream("test.txt");
            try {
                outputStream.write("世界和平".getBytes("GB2312"));
                outputStream.close();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

# 内存操作流

它以内存作为操作对象实现的IO数据处理,前面的操作对象都是文件,内存操作流不会进行磁盘数据操作。Java中提供以下两类操作流:

  • 字节内存操作流:ByteArrayOutputStream,ByteArrayInputStream。
  • 字符内存操作流:CharArrayWriter,CharArrayReader。

字节的类继承结构如下:

ByteArrayInputStream

ByteArrayOutputStream

字符的类继承结构如下:

CharArrayReader

CharArrayWriter

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

public class Main {
    public static void main(String[] args) {
        String strOrg = "hello world!";
        InputStream inputStream = new ByteArrayInputStream(strOrg.getBytes());
        OutputStream outputStream = new ByteArrayOutputStream();
        int data = 0;
        byte result[] = null;
        try {
            while ((data = inputStream.read()) != -1) {
                outputStream.write(Character.toUpperCase(data));
            }
            System.out.println(outputStream);

            //将全部数据转为字节取出。
            result = ((ByteArrayOutputStream) outputStream).toByteArray();
            inputStream.close();
            outputStream.close();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        System.out.println(new String(result));
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

# 管道流

管道流主要用在两个线程间通信,分为管道输出流(PipedOutputStream,PipedWriter)和管道输入流(PipedInputStream,PipedReader)。

字节管道流的继承结构如下:

BytePipeStream

字符管道流的继承结构如下:

CharPipeStream

要想进行管道输出,则必须把输出流连在输入流上。有下面两个方法:

  • PipedOutputStream类中的connect(PipedInputStream snk)方法。
  • PipedWriter类中的connect(PipedReader snk)方法。
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;

class SendDataThread implements Runnable{
    private PipedOutputStream output;
    
    public SendDataThread() {
        // TODO Auto-generated constructor stub
        this.output = new PipedOutputStream();
    }

    @Override
    public void run() {
        // TODO Auto-generated method stub
        try {
            this.output.write("hello world,this is sended by send data thread".getBytes());
            this.output.close();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
    
    public PipedOutputStream getOutput() {
        return output;
    }
}

class ReceiveDataThread implements Runnable{
    private PipedInputStream input;
    
    public ReceiveDataThread() {
        this.input=new PipedInputStream();
    }

    @Override
    public void run() {
        // TODO Auto-generated method stub
        byte data[]=new byte[1024];
        int len=0;
        ByteArrayOutputStream bOutputStream = new ByteArrayOutputStream();
        try {
            while((len=this.input.read(data))!=-1) {
                bOutputStream.write(data,0,len);
            }
            System.out.println(new String(bOutputStream.toByteArray()));
            bOutputStream.close();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        
        try {
            this.input.close();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
    
    public PipedInputStream getInput() {
        return input;
    }
}
public class Main {
    public static void main(String[] args) {
        SendDataThread send = new SendDataThread();
        ReceiveDataThread receive = new ReceiveDataThread();

        try {
            send.getOutput().connect(receive.getInput());
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        new Thread(send,"消息发送线程").start();
        new Thread(receive,"消息接收线程").start();
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82

# RandomAccessFile

可实现文件数据的随机读取,通过对文件指针位置的设置,实现部分数据的读取。

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

public class Main {
    static RandomAccessFile randomFile;
    public static void main(String[] args) throws IOException {
        File file = new File("test.txt");
        randomFile = new RandomAccessFile(file, "rw");
        String name[] = new String[] {"zhang","wang","li"};
        int score[] = new int[] {18,20,22};
        for(int i=0;i<name.length;i++) {
            randomFile.write(name[i].getBytes());
            randomFile.writeInt(score[i]);
        }
        randomFile.seek(0);
        GetInfo(5);
        randomFile.seek(9);
        GetInfo(4);
        randomFile.seek(17);
        GetInfo(2);
        randomFile.close();
    }
    
    public static byte[] GetInfo(int nameLength) {
        byte[] data = new byte[nameLength];
        try {
            int len = randomFile.read(data);
            System.out.println("名字:"+new String(data,0,len).trim()+" 年龄:"+randomFile.readInt());
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return data;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36

# 打印流

java.io包中对于数据的输出可通过OutputStream或Writer类完成,但是OutputStream只允许输出字节数据,Writer只允许输出字符数据和字符串数据,但是在实际打印中需要多种数据类型的数据,比如整数,浮点数,字符,引用参数,为了简化输出操作,提供了两个打印流操作类:

  • 字节打印流(PrintStream)
  • 字符打印流(PrintWriter)

它的类继承结构如下:

printStream

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintWriter;

public class Main {
    public static void main(String[] args) {
        File file = new File("test.txt");
        PrintWriter printWriter;
        try {
            printWriter = new PrintWriter(new FileOutputStream(file));
            printWriter.println("this is title:");
            printWriter.println("info:hello");
            String nameString = "xie";
            int age = 22;
            double score = 89.887;
            printWriter.printf("name:%s,age:%d,money:%5.2f", nameString, age, score);
            printWriter.close();
        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

# System的IO

System中有三个和IO操作的常量,它们的声明如下:

public static final PrintStream err;
public static final PrintStream out;
public static final InputStream in;
1
2
3

实例如下:

import java.io.IOException;
import java.io.InputStream;

public class Main {
    public static void main(String[] args) {
        System.out.println("测试err和in:");
        System.err.println("这是一个错误!");
        
        InputStream inputStream = System.in;
        byte[] data = new byte[5];
        try {
            int len = inputStream.read(data);
            System.out.println("数据为:"+new String(data,0,len));
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

注意上面的System.in对中文的处理支持不够好。

# BufferedReader缓冲输入流

它提供一种字符流的缓冲区数据读取,它读取数据时会把数据暂时放到缓冲区,利用其它方法将读取的内容一次性取出。它的类继承结构如下:

bufferedReader

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class Main {
    public static void main(String[] args) {
        BufferedReader inputBufferedReader = new BufferedReader(new InputStreamReader(System.in));
        System.out.println("输入年龄:");
        try {
            String msg = inputBufferedReader.readLine();
            if(msg.matches("\\d{1,3}")) {
                System.out.print("年龄为:"+Integer.parseInt(msg));
            }
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        
        
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# Scanner输入流

Scanner支持各种输入数据类型的转换,开发者可以利用其提供的方法判断输入的数据类型,也可以获取指定类型的数据。它的类继承结构如下:

ScannerClasspng

判断是否为整形:

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        System.out.println("输入年龄:");
        if(scanner.hasNextInt()) {
            System.out.println("年龄为:"+scanner.nextInt());
        }else {
            System.out.println("输入的内容不是数字!");
        }
        scanner.close();
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

判断输入是否为要求的日期格式:

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        System.out.println("输入日期:");
        if(scanner.hasNext("\\d{4}-\\d{2}-\\d{2}")) {
            String str = scanner.next("\\d{4}-\\d{2}-\\d{2}");
            try {
                System.out.println("输入信息为:"+new SimpleDateFormat("yyy-MM-dd").parse(str));
            } catch (ParseException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }else {
            System.out.println("输入的内容不符合要求!");
        }
        scanner.close();
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

读取文件内容:

import java.io.File;
import java.io.FileNotFoundException;
import java.util.Scanner;

public class Main {
    public static void main(String[] args) throws FileNotFoundException {
        Scanner scanner = new Scanner(new File("test.txt"));
        scanner.useDelimiter("\n");
        while(scanner.hasNext()) {
            System.out.print(scanner.next());
        }
        scanner.close();
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

Scanner默认把空字符作为分隔符。

# 对象序列化

对象序列化是把一个对象变为二进制的数据流的一种方法,这样可以方便地实现对象的传输和存储。一个类如果想被序列化,所在的类必须实现java.io.Serializable接口,此接口并没提供任何抽象方法,只是一个标识接口,标识对象可被序列化。

Serializable接口只是标识了一个类对象能否被允许序列化,对于对象序列化和反序列化的具体实现需要依靠ObjectOutputStream与ObjectInputStream两类完成。ObjectInputStream可以读取ObjectOutputStream类输出的二进制对象数据,并转为具体类型的对象并返回。相关类的关系如下:

SerialObject

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

class MyInfo implements Serializable{
    String keyString;
    transient String infoString;
    
    public MyInfo(String key,String info){
        keyString = key;
        infoString = info;
    }
    
    @Override
    public String toString() {
        // TODO Auto-generated method stub
        return "key:"+keyString+";"+"info:"+infoString;
    }
}
public class Main {
    public static void main(String[] args) throws ClassNotFoundException {
        File saveFile = new File("test.txt");
        MyInfo myInfo = new MyInfo("name", "xie");

        try {
            ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(saveFile));
            oos.writeObject(myInfo);
            oos.close();

            ObjectInputStream ois = new ObjectInputStream(new FileInputStream(saveFile));
            Object obj = ois.readObject();
            ois.close();

            System.out.println(obj);
        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49

注意在对象进行序列化与反序列化的时候要考虑JDK版本的问题,如果序列化的JDK和反序列化的JDK版本不统一则很有可能造成异常,序列化的时候引入了一个serialVersionUID常量,通过它来判断版本的一致性,反序列化时会判断该值与本地的serialVersionUID是否相同,如果相同就一致。

在实现java.io.Serializable接口的实体类并没有定义一个serialVersionUID,Java虚拟机在编译时自动生成一个该变量,如果希望自己控制可以自己来定义一个这样的变量。

默认情况下,执行对象序列化的时候会将类中的全部属性的内容进行序列化操作,当有一些属性并不需要序列化的时候,可以在属性定义上使用transient关键字来定义。