【Java基础】IO 流

时间:2022-11-01 02:18:46

IO 流

File 类

java.io.File 类是文件和文件目录路径的抽象表示形式,与平台无关。

File 能新建、删除、重命名文件和目录,但 File 不能访问文件内容本身。

如果需要访问文件内容本身,则需要使用输入输出流。File 对象可以作为参数传递给流的构造器

Java 程序支持跨平台运行,因此路径分隔符要慎用,为了解决这个隐患,File 类提供了一个常量,即 public static final String separator,它能根据操作系统,动态的提供分隔符。

常用构造器:

  • public File(String pathname) 以 pathname 为路径创建 File 对象,可以是绝对路径或者相对路径,如果 pathname 是相对路径,则相对于当前 module;
  • public File(String parent,String child) 以 parent 为父路径,child 为子路径创建 File 对象;
  • public File(File parent,String child) 根据一个父 File 对象和子文件路径创建 File 对象;

常用方法:

  • 获取功能:
    • public String getAbsolutePath():获取绝对路径
    • public String getPath():获取路径
    • public String getName():获取名称
    • public String getParent():获取上层文件目录路径。若无,返回 null
    • public long length():获取文件长度,即字节数,不能获取目录的长度
    • public long lastModified():获取最后一次的修改时间,毫秒值
  • 重命名功能:
    • public boolean renameTo(File dest):把文件重命名为指定的文件路径,要求 srcFile 存在且 destFile 不能存在
  • 判断功能
    • public boolean isDirectory():判断是否是文件目录
    • public boolean isFile():判断是否是文件
    • public boolean exists():判断文件是否存在
    • public boolean canRead():判断文件是否可读
    • public boolean canWrite():判断文件是否可写
    • public boolean isHidden():判断文件是否隐藏
  • 创建功能
    • public boolean createNewFile():创建文件。若文件存在,则不创建,返回 false。
    • public boolean mkdir():创建文件目录。如果此文件目录存在,则不创建。如果此文件目录的上层目录不存在,也不创建。
    • public boolean mkdirs():创建文件目录。如果上层文件目录不存在,一并创建。
  • 删除功能
    • public boolean delete():删除文件或者文件夹,要删除一个文件目录,要求该文件目录内不能包含文件或者文件目录。
package parzulpan.com.java;

import org.junit.Test;

import java.io.File;
import java.io.IOException;
import java.util.Date; /**
* @Author : parzulpan
* @Time : 2020-11-26
* @Desc : File 类的使用
*/ public class FileTest { @Test
public void test1(){ File file1 = new File("hello.txt"); // 相对于当前 module
File file2 = new File("/Users/parzulpan/Study/LearnJava/Basis/JavaBasis/code/ch11" + File.separator + "hi.txt"); System.out.println(file1.getAbsolutePath());
System.out.println(file1.getPath());
System.out.println(file1.getName());
System.out.println(file1.length());
System.out.println(new Date(file1.lastModified())); System.out.println(); System.out.println(file2.getAbsolutePath());
System.out.println(file2.getPath());
System.out.println(file2.getName());
System.out.println(file2.length());
System.out.println(file2.lastModified()); System.out.println(); File file3 = new File("/Users/parzulpan/Study/LearnJava/Basis/JavaBasis/code");
String[] list = file3.list();
for (String s : list) {
System.out.println(s);
} System.out.println(); File[] files = file3.listFiles();
for (File f: files) {
System.out.println(f);
}
} @Test
public void test2() {
File file1 = new File("hello.txt"); // 相对于当前 module
File file2 = new File("/Users/parzulpan/Study/LearnJava/Basis/JavaBasis/code" + File.separator + "hi.txt");
File file3 = new File("/Users/parzulpan/Study/LearnJava/Basis/JavaBasis/code"); boolean b = file1.renameTo(file2);
System.out.println(b); System.out.println();
System.out.println(file3.isDirectory());
System.out.println(file1.isFile());
System.out.println(file1.exists());
System.out.println(file3.exists());
System.out.println(file1.canRead());
System.out.println(file1.canWrite());
System.out.println(file1.canExecute());
System.out.println(file1.isHidden());
} @Test
public void test3() {
File file1 = new File("helloW.txt"); // 相对于当前 module
if (!file1.exists()) {
try {
boolean newFile = file1.createNewFile();
System.out.println("创建文件 " + newFile);
} catch (IOException e) {
e.printStackTrace();
} } else {
boolean delete = file1.delete();
System.out.println("删除文件 " + delete);
} File file2 = new File("/Users/parzulpan/Study/LearnJava/Basis/JavaBasis/code/newDir");
if (!file2.exists()) {
boolean mkdirs = file2.mkdirs();
System.out.println("创建文件夹 " + mkdirs);
} else {
boolean delete = file2.delete();
System.out.println("删除文件夹 " + delete);
}
}
}

IO 流原理及流的分类

I/O 是 Input/Output 的缩写, I/O 技术是非常实用的技术,用于

处理设备之间的数据传输。例如读/写文件,网络通讯等。

Java 程序中,对于数据的输入/输出操作以“流(stream)” 的方式进行。

流的分类

  • 操作数据单位不同分为:字节流(8 bit),字符流(16 bit);
  • 数据流的流向不同分为:输入流,输出流;
  • 流的角色的不同分为:节点流,处理流。

抽象基类

  • InputStream 字节输入流;
  • OutputStream 字节输出流;
  • Reader 字符输入流;
  • Writer 字符输出流;
  • Java 的 IO 流共涉及 40 多个类,实际上非常规则,都是从如下 4 个抽象基类派生的;
  • 由这四个类派生出来的子类名称都是以其父类名作为子类名后缀。

编码流程

  • 创建 File 类对象,指明读入或写入的文件;
  • 创建输入流或输出流的对象;
  • 数据的读入或输出操作;
  • 关闭流资源;

文件流(节点流)

注意:

  • 写入一个文件时,如果使用构造器 FileOutputStream(file) ,则目录下有同名文件将被覆盖。
  • 如果使用构造器 FileOutputStream(file, true) ,则目录下的同名文件不会被覆盖,在文件内容末尾追加内容。
  • 读取文件时,必须保证该文件已存在,否则报异常。
  • 字节流操作字节,比如:.mp3,.avi,.rmvb,mp4,.jpg,.doc,.ppt
  • 字符流操作字符,只能操作普通文本文件。最常见的文本文件:.txt,.java,.c,.cpp 等语言的源代码。尤其注意 .doc,.excel,.ppt 这些不是文本文件。
package parzulpan.com.java;

import org.junit.Test;

import java.io.*;

/**
* @Author : parzulpan
* @Time : 2020-11-27
* @Desc : 文件字符流操作
*/ public class FileReaderWriter {
public static void main(String[] args) {
File file = new File("hello.txt"); // 相较于当前工程
} // 将 hello.txt 读入到内存程序中,并输出
@Test
public void testFileReader(){
// 1.
File file = new File("hello.txt"); // 相较于当前Module
FileReader fileReader = null; try {
// 2.
fileReader = new FileReader(file); char[] chars = new char[5];
int data; // 3.
while (true) {
// // int read()
// if ((data = fileReader.read()) == -1) break;
// System.out.print((char) data); // int read(char[] buf)
if ((data = fileReader.read(chars)) == -1) break;
for (int i = 0; i < data; ++i) { // 注意这里是 data
System.out.print(chars[i]);
}
}
} catch (IOException e) {
e.printStackTrace();
} // 4.
try {
if (fileReader != null) {
fileReader.close();
}
} catch (IOException e) {
e.printStackTrace();
}
} // 将内存程序数据 写入 hello1.txt
@Test
public void testFileWrite() {
// 1.
File file = new File("hello1.txt");
FileWriter fileWriter = null; try {
// 2.
fileWriter = new FileWriter(file, true); // append 为 true 则追加,否则覆盖 // 3.
fileWriter.write("hello world!!!!!");
fileWriter.write("yes!!".toCharArray());
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (fileWriter != null) {
// 4.
fileWriter.close();
}
} catch (IOException e) {
e.printStackTrace();
}
} }
}
package parzulpan.com.java;

import org.junit.Test;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException; /**
* @Author : parzulpan
* @Time : 2020-11-27
* @Desc : 文件字节流操作
*/ public class FileInputOutputStreamTest { // 实现对 excel 文件的复制
@Test
public void testFileFileInputOutputStream() {
File srcFile = new File("test.xlsx");
File destFile = new File("test1.xlsx");
FileInputStream fis = null;
FileOutputStream fos = null; try {
fis = new FileInputStream(srcFile);
fos = new FileOutputStream(destFile); int data; // // 读取单个字符
// while ((data = fis.read()) != -1) {
// fos.write(data);
// } // 将字符读入数组
byte[] buffer = new byte[1024];
while ((data = fis.read(buffer)) != -1) {
fos.write(buffer, 0, data);
} } catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (fis != null) {
fis.close();
}
if (fos != null) {
fos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
} }
}

缓冲流

为了提高数据读写的速度,Java API 提供了带缓冲功能的流类,在使用这些流类时,会创建一个内部缓冲区数组,缺省使用 8192 个字节(8 Kb)的缓冲区。

创建缓冲流对象,它是处理流,是对节点流的包装。

当读取数据时,数据按块读入缓冲区,其后的读操作则直接访问缓冲区,直到缓冲区装满了,才重新从文件中读取下一个 8Kb 数组。

向流中写入字节时,不会直接写到文件,先写到缓冲区中直到缓冲区写满,

BufferedOutputStream 才会把缓冲区中的数据一次性写到文件里。使用 flush() 可以强制将缓冲区的内容全部写入输出流。

/**
* 缓冲流
* @param srcFilePath 源文件路径
* @param destFilePath 目标文件路径
* @return 复制是否成功
*/
public static boolean copyFileWithBufferInputOutputStream(String srcFilePath, String destFilePath) {
File srcFile = new File(srcFilePath);
File destFile = new File(destFilePath); BufferedInputStream bis = null;
BufferedOutputStream bos = null;
try {
FileInputStream fis = new FileInputStream(srcFile);
FileOutputStream fos = new FileOutputStream(destFile);
bis = new BufferedInputStream(fis);
bos = new BufferedOutputStream(fos); int data;
byte[] buffer = new byte[1024];
while ((data = bis.read(buffer)) != -1) {
bos.write(buffer, 0, data);
}
} catch (IOException e) {
e.printStackTrace();
return false;
} // 关闭资源时,要求先关闭外层的流,再关闭外层的流。但是可以直接关闭外层的流,内层的流会自动关闭。
try {
if (bos != null) {
bos.close();
}
if (bis != null) {
bis.close();
}
} catch (IOException e) {
e.printStackTrace();
} return true;
}

转换流

转换流提供了在字节流和字符流之间的转换,因为字节流中的数据都是字符时,转成字符流操作更高效。

很多时候使用转换流来处理文件乱码问题,实现编码解码的功能。

Java API 提供了两个转换流:

  • InputStreamReader:将 InputStream 转换为 Reader
    • 实现将字节的输入流按指定字符集转换为字符的输入流,需要和InputStream “套接”。
    • public InputStreamReader(InputStream in)
    • public InputSreamReader(InputStream in,String charsetName)
  • OutputStreamWriter:将 Writer 转换为 OutputStream
    • 实现将字符的输出流按指定字符集转换为字节的输出流,需要和OutputStream “套接”。
    • public OutputStreamWriter(OutputStream out)
    • public OutputSreamWriter(OutputStream out,String charsetName)
package parzulpan.com.java;

import org.junit.Test;

import java.io.*;
import java.nio.charset.StandardCharsets; /**
* @Author : parzulpan
* @Time : 2020-11-27
* @Desc : 转换流
*/ public class InputStreamReaderOutputStreamWriterTest { // 实现将字节的输入流按指定字符集转换为字符的输入流,等效于解码
// 然后将字符的输出流按指定字符集转换为字节的输出流,等效于编码
@Test
public void test1() {
InputStreamReader isr = null;
OutputStreamWriter osw = null;
try {
FileInputStream fis = new FileInputStream("word.txt");
isr = new InputStreamReader(fis, StandardCharsets.UTF_8); FileOutputStream fos = new FileOutputStream("wordGBk.txt");
osw = new OutputStreamWriter(fos, "GBK"); char[] buffer = new char[1024];
int data;
while ((data = isr.read(buffer)) != -1) {
String str = new String(buffer, 0, data);
System.out.println(str);
osw.write(str);
}
} catch (IOException e) {
e.printStackTrace();
} try {
if (isr != null) {
isr.close();
}
if (osw != null) {
osw.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}

标准输入输出流、打印流、数据流

package parzulpan.com.java;

import java.io.*;

/**
* @Author : parzulpan
* @Time : 2020-11-27
* @Desc : 标准输入输出流、打印流、数据流
*/ public class OtherStreamTest {
public static void main(String[] args) {
test2();
} /**
* 1. 标准输入输出流
* 1.1 System.in 标准的输入流,默认从键盘输入,System.out 标准的输出流,默认从控制台输出
* 1.2 System.setIn(InputStream is) 和 System.setOut(OutputStream ps) 方式重新指定输入和输出的流
*
* 练习:从键盘输入字符串,要求将读取到的整行字符串转成大写输出。然后继续进行输入操作,直至当输入 “e” 或者 “exit” 时,退出程序。
* 方法一:使用 Scanner;
* 方法二:使用 System.in; -> System.in -> 转换流 -> BufferedReader.readLine
*/
public static void test() {
System.out.print("请输入信息(e 或 exit 退出):"); BufferedReader br = null;
try {
InputStreamReader isr = new InputStreamReader(System.in);
br = new BufferedReader(isr); String s;
while ((s = br.readLine()) != null) {
if ("e".equalsIgnoreCase(s) || "exit".equalsIgnoreCase(s)) {
System.out.print("已安全退出!");
break;
}
System.out.println(s.toUpperCase());
System.out.print("请输入信息(e 或 exit 退出):");
}
} catch (IOException e) {
e.printStackTrace();
} try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
} /**
* 2. 打印流
* 实现将基本数据类型的数据格式转化为字符串输出,提供了一系列重载的 print() 和 println() 方法,用于多种数据类型的输出
* 练习:将打印输出流不通过控制台,而是直接写入到文件
*/
public static void test1() {
PrintStream ps = null;
try {
FileOutputStream fos = new FileOutputStream(new File("ASCII.txt"));
// 创建打印输出流,设置为自动刷新模式(写入换行符或字节 '\n' 时都会刷新输出缓冲区)
ps = new PrintStream(fos, true);
if (ps != null) {
System.setOut(ps); // 把标准输出流(控制台输出)改成文件
} for (int i = 0; i < 256; i++) {
System.out.print((char) i);
if (i % 50 == 0) {
System.out.println();
}
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} if (ps != null) {
ps.close();
}
} /**
* 3. 数据流
* 为了方便地操作 Java 语言的 基本数据类型 和 String 的数据,可以使用数据流。
* 数据流有两个类:(用于读取和写出基本数据类型、String类的数据)
* DataInputStream 和 DataOutputStream 分别“套接”在 InputStream 和 OutputStream 子类的流上
*/
public static void test2() {
DataOutputStream dos = null;
DataInputStream dis = null;
try {
dos = new DataOutputStream(new FileOutputStream("destData.dat"));
dis = new DataInputStream(new FileInputStream("destData.dat")); dos.writeUTF("Beijing");
dos.writeBoolean(true);
dos.writeLong(1008611); String info = dis.readUTF();
boolean b = dis.readBoolean();
long l = dis.readLong();
System.out.println(info);
System.out.println(b);
System.out.println(l);
} catch (IOException e) {
e.printStackTrace();
} try {
if (dos != null) {
dos.close();
}
if (dis != null) {
dis.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}

对象流

ObjectInputStream 和 OjbectOutputSteam:用于存储读取 基本数据类型数据对象的处理流。它的强大之处就是可

以把 Java 中的对象写入到数据源中,也能把对象从数据源中还原回来。

注意:ObjectOutputStream 和 ObjectInputStream 不能序列化 static 和 transient 修饰的成员变量。

*对象的序列化

  • 序列化:用 ObjectOutputStream 类保存基本类型数据或对象的机制;
  • 反序列化:用 ObjectInputStream 类读取基本类型数据或对象的机制;
  • 对象序列化机制允许把内存中的 Java 对象转换成平台无关的二进制流,从而允许把这种二进制流持久地保存在磁盘上,或通过网络将这种二进制流传输到另一个网络节点;
  • 序列化的好处在于可将任何实现了 Serializable 接口的对象转化为字节数据,使其在保存和传输时可被还原;
    • 如果需要让某个对象支持序列化机制,则必须让对象所属的类及其属性是可序列化的,为了让某个类是可序列化的,该类必须实现如下两个接口之一:
      • Serializable
      • Externalizable

*理解对象的序列化

  • 凡是实现 Serializable 接口的类都有一个表示序列化版本标识符的静态变量:private static final long serialVersionUID;
  • serialVersionUID 用来表明类的不同版本间的兼容性;
  • 如果类没有显示定义这个静态常量,它的值是 Java 运行时环境根据类的内部细节自动生成的。若类的实例变量做了修改,serialVersionUID 可能发生变化。故建议显式声明

简单来说Java 的序列化机制是通过在运行时判断类的 serialVersionUID 来验证版本一致性的。在进行反序列化时,JVM 会把传来的字节流中的 serialVersionUID 与本地相应实体类的 serialVersionUID 进行比较,如果相同就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异常。

package parzulpan.com.java;

import org.junit.Test;

import java.io.*;

/**
* @Author : parzulpan
* @Time : 2020-11-27
* @Desc : 对象流的使用
*/ public class ObjectInputOutputStreamTest { // 序列化,将对象写入到磁盘或者进行网络传输。
@Test
public void testObjectOutputStream() {
ObjectOutputStream oos = null;
try {
oos = new ObjectOutputStream(new FileOutputStream("object.data"));
Person par = new Person("Par", 99);
oos.writeObject(par);
oos.flush();
} catch (IOException e) {
e.printStackTrace();
} try {
if (oos != null) {
oos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
} // 反序列化,将磁盘或者网络中的对象数据源读出
@Test
public void testObjectInputStream() {
ObjectInputStream ois = null;
try {
ois = new ObjectInputStream(new FileInputStream("object.data"));
Object o = ois.readObject();
System.out.println(o.toString());
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
} try {
if (ois != null) {
ois.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
} // 要求序列化,
// 1. 需要实现 Serializable 接口
// 2. 并且显式声明 serialVersionUID
// 3. 其内部属性要求都是可序列化的,但是 static 和 transient 修饰的成员变量不能序列化
class Person implements Serializable{
private String name;
private int age;
private static final long serialVersionUID = 15683901680463743L; public Person(String name, int age) {
this.name = name;
this.age = age;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public int getAge() {
return age;
} public void setAge(int age) {
this.age = age;
} @Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}

随机存取文件流

RandomAccessFile 声明在 java.io 包下,但直接继承于 java.lang.Object 类。并且它实现了 DataInput、DataOutput 这两个接口,也就意味着这个类既可以读也可以写。

注意:作为输出流时,写入的文件如果不存在,则在执行过程中自动创建,如果存在,则会对原有文件内容进行覆盖,默认情况下,从头开始覆盖。

RandomAccessFile 对象包含一个记录指针,用以标示当前读写处的位置。它可以*移动记录指针:

  • long getFilePointer():获取文件记录指针的当前位置
  • void seek(long pos):将文件记录指针定位到 pos 位置
package parzulpan.com.java;

import org.junit.Test;

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile; /**
* @Author : parzulpan
* @Time : 2020-11-27
* @Desc : 随机存取文件流的使用
*/ public class RandomAccessFileTest { @Test
public void test1() {
RandomAccessFile r = null;
RandomAccessFile rw = null;
try {
// 以只读方式打开,则不会创建文件,而是会去读取一个已经存在的文件,如果读取的文件不存在则会出现异常
r = new RandomAccessFile(new File("hello.txt"), "r");
// 以读取和写入方式打开,如果文件不存在则会去创建文件,如果存在则不会创建。
rw = new RandomAccessFile(new File("hello2.txt"), "rw"); byte[] bytes = new byte[1024];
int data;
while ((data = r.read(bytes)) != -1) {
rw.write(bytes, 0, data);
}
} catch (IOException e) {
e.printStackTrace();
} try {
if (rw != null) {
rw.close();
}
if (r != null) {
r.close();
}
} catch (IOException e) {
e.printStackTrace();
} }
}

NIO.2

Java NIO (New IO,Non-Blocking IO) 是从 Java4 版本开始引入的一套新的 IO API,可以替代标准的 Java IO API。NIO 与原来的 IO 有同样的作用和目的,但是使用的方式完全不同,NIO 支持面向缓冲区的(IO是面向流的)、基于通道的 IO 操作。NIO 将以更加高效的方式进行文件的读写操作。但是 NIO 写的一般。

随着 Java7 的发布,Java 对 NIO 进行了极大的扩展,增强了对文件处理和文件系统特性的支持,称为 NIO.2

Java API 中提供了两套 NIO,一套是针对标准输入输出 NIO,另一套就是网

络编程 NIO。

  • java.nio.channels.Channel
    • FileChannel 处理本地文件
    • SocketChannel TCP 网络编程的客户端的 Channel
    • ServerSocketChannel TCP 网络编程的服务器端的 Channel
    • DatagramChannel UDP 网络编程中发送端和接收端的 Channel

练习和总结


判断指定目录下是否有后缀名为 .jpg 的文件,如果有,就输出该文件名称?

package parzulpan.com.exer;

import java.io.File;
import java.util.Scanner; /**
* @Author : parzulpan
* @Time : 2020-11-27
* @Desc : 判断指定目录下是否有后缀名为 .jpg 的文件,如果有,就输出该文件名称?
*/ public class GetJPGName {
public static void main(String[] args) {
System.out.print("请输入目录:");
Scanner scanner = new Scanner(System.in);
String dir = scanner.next();
printJPGtName(dir);
} public static void printJPGtName(String dir) {
File file = new File(dir);
if (file.isDirectory()) {
String[] list = file.list();
if (list != null) {
for (String l : list) {
boolean b = l.toLowerCase().endsWith(".jpg");
if (b) {
System.out.println(l);
}
}
}
}
}
}

遍历指定目录所有文件名称,包括子文件目录中的文件,并计算指定目录占用空间的大小,并删除指定文件目录及其下的所有文件?

package parzulpan.com.exer;

import java.io.File;
import java.util.Scanner; /**
* @Author : parzulpan
* @Time : 2020-11-27
* @Desc : 遍历指定目录所有文件名称,包括子文件目录中的文件,并计算指定目录占用空间的大小,并删除指定文件目录及其下的所有文件?
*/ public class GetAllFile {
public static void main(String[] args) {
System.out.print("请输入目录:");
Scanner scanner = new Scanner(System.in);
String dir = scanner.next();
File file = new File(dir);
System.out.println("目录占用空间的大小(字节数):" + getDir(file)); System.out.print("请输入目录:");
Scanner scanner1 = new Scanner(System.in);
String dir1 = scanner1.next();
File file1 = new File(dir1);
deleteDir(file1); } public static long getDir(File dir) {
File[] subFiles = dir.listFiles();
long len = 0;
for (File file : subFiles) {
if (file.isDirectory()) {
len += getDir(file);
} else {
String absolutePath = file.getAbsolutePath();
System.out.println(absolutePath);
len += file.length();
}
}
return len;
} public static void deleteDir(File file) {
if (file.isDirectory()) {
File[] files = file.listFiles();
for (File f : files) {
deleteDir(f);
}
}
file.delete();
}
}

获取文本上每个字符出现的次数,并将结果写入文件?

package parzulpan.com.exer;

import java.io.*;
import java.util.HashMap;
import java.util.Map;
import java.util.Set; /**
* @Author : parzulpan
* @Time : 2020-11-27
* @Desc : 获取文本上每个字符出现的次数,并将结果写入文件
*/ public class WordCount {
public static void main(String[] args) { worldCount("ch11/word.txt", "ch11/wordCount.txt"); } public static void worldCount(String srcFilePath, String destFilePath) {
HashMap<Character, Integer> word = new HashMap<Character, Integer>(); BufferedReader br = null;
BufferedWriter bw = null;
try {
br = new BufferedReader(new FileReader(new File(srcFilePath)));
bw = new BufferedWriter(new FileWriter(new File(destFilePath))); int data;
while ((data = br.read()) != -1) {
if (word.containsKey((char)data)) {
word.put((char)data, word.get((char)data) + 1);
} else {
word.put((char)data, 1);
}
} Set<Map.Entry<Character, Integer>> entries = word.entrySet();
for(Map.Entry<Character, Integer> e : entries) {
bw.write(e.getKey() + " : " + e.getValue());
bw.newLine();
}
} catch (IOException e) {
e.printStackTrace();
} try {
if (bw != null) {
bw.close();
}
if (br != null) {
br.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}

谈谈你对java.io.Serializable接口的理解,我们知道它用于序列化,

是空方法接口,还有其它认识吗?

  • 实现 Serializable 接口的对象,可以进行序列化,即将对象写入到磁盘或者进行网络传输,也可以进行反序列化,即将磁盘或者网络中的对象数据源读出。这意味着序列化机制能自动补偿操作系统间的差异
  • 由于大部分作为参数的类如 String、Integer 等都实现了 java.io.Serializable 的接口,也可以利用多态的性质,作为参数使接口更灵活。