1 简述节点流和处理流的区别,以及Java流式输入输出的架构特点

参考答案

1)按照流是否直接与特定的地方 (如磁盘、内存、设备等) 相连,分为节点流和处理流两类。节点流可以从或向一个特定的地方(节点)读写数据;处理流是对一个已存在的流的连接和封装,通过所封装的流的功能调用实现数据读写。

2)处理流的构造方法总是以一个其他的流对象做参数。一个流对象经过其他流的多次包装,称为流的链接。

2 简述RandomAccessFile和FileInputStream及FileOutputStream在使用中的区别

参考答案

RandomAccessFile使用随机访问的方式,而FileInputStream及FileOutputStream使用的是流式访问的方式。

3 用自定义缓存区的方式实现文件的移动

使用FileInputStream类的read(byte[])方法和FileOutputStream类的write(byte[])方法实现文件移动。

参考答案

实现此案例需要按照如下步骤进行。

步骤一:新建TestMoveFile类,添加测试方法testMove方法

首先新建类TestMoveFile,然后在该类中添加测试方法testMove,代码如下所示:

import org.junit.Test;

public class TestMoveFile {
	/**
	 * 测试使用字节数组形式移动文件
	 */
	@Test
	public void testMove()throws Exception{
	
	}
}

步骤二:实现文件移动

首先,创建文件字节输入流FileInputStream类的对象和文件字节输出流

FileOutputStream类的对象;然后,使用循环。在循环中,使用FileInputStream类的read(byte[])方法从文件fos.dat中读取数据;使用FileOutputStream类的write(byte[])方法将读取到的数据写入src下的fos_move.dat文件中,直到read(byte[])方法返回-1,则退出循环;最后,将流关闭,以释放资源,代码如下所示:

import java.io.FileInputStream;
import java.io.FileOutputStream;

import org.junit.Test;

public class TestMoveFile {
	/**
	 * 测试使用字节数组形式移动文件
	 */
	@Test
	public void testMove()throws Exception{
#cold_bold		FileInputStream fis
#cold_bold			= new FileInputStream("fos.dat");
#cold_bold		FileOutputStream fos
#cold_bold			= new FileOutputStream("src/fos_move.dat");
#cold_bold		
#cold_bold		int len = -1;
#cold_bold		byte[] buf = new byte[32];
#cold_bold		while((len = fis.read(buf)) != -1){
#cold_bold			fos.write(buf,0,len);
#cold_bold		}
#cold_bold		System.out.println("移动完毕");
#cold_bold		fis.close();
#cold_bold		fos.close();
	}
}

运行testMove方法,在当前工程的src目录下生成了文件fos_move.dat,该文件的内容与文件fos.dat的内容相同。

本案例的完整代码如下所示:

import java.io.FileInputStream;
import java.io.FileOutputStream;

import org.junit.Test;

public class TestMoveFile {
	/**
	 * 测试使用字节数组形式移动文件
	 */
	@Test
	public void testMove()throws Exception{
		FileInputStream fis
			= new FileInputStream("fos.dat");
		FileOutputStream fos
			= new FileOutputStream("src/fos_move.dat");
		
		int len = -1;
		byte[] buf = new byte[32];
		while((len = fis.read(buf)) != -1){
			fos.write(buf,0,len);
		}
		System.out.println("移动完毕");
		fis.close();
		fos.close();
	}
}

4 用缓冲流的方式实现文件的移动

使用BufferedInputStream类的read方法和BufferedOutputStream类的write方法实现文件移动。

参考答案

实现此案例需要按照如下步骤进行。

步骤一:添加测试方法testCopy方法

在类TestMoveFile 中添加测试方法testMove2,代码如下所示:

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;

import org.junit.Test;

public class TestMoveFile {
	/**
	 * 测试基于缓冲流的复制文件
	 */
#cold_bold	@Test
#cold_bold	public void testMove2()throws Exception{
#cold_bold	}
}

步骤二:实现文件复制

使用BufferedInputStream和BufferedOutputStream实现文件复制的详细过程如下:

1)首先,创建文件字节输入流FileInputStream类的对象,接着使用该文件字节输入流对象作为参数构造缓冲字节输入流BufferedInputStream类的对象;

2)首先,创建文件字节输出流FileOutputStream类的对象,接着使用该文件字节输出流对象作为参数构造缓冲字节输出流BufferedOutputStream类的对象;

3)构建循环。在循环中,使用BufferedInputStream类的read方法从文件fos.dat中读取数据;使用BufferedOutputStream类的write方法将读取到的数据写入src目录下的bfos_move.dat文件中,直到read方法返回-1,则退出循环。

4)将流关闭,以释放资源。

代码如下所示:

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;

import org.junit.Test;

public class TestMoveFile {
	/**
	 * 测试基于缓冲流的复制文件
	 */
	@Test
	public void testMove2()throws Exception{
#cold_bold		FileInputStream fis
#cold_bold			= new FileInputStream("fos.dat");
#cold_bold		//创建缓冲字节输入流
#cold_bold		BufferedInputStream bis
#cold_bold			= new BufferedInputStream(fis);
#cold_bold		
#cold_bold		FileOutputStream fos
#cold_bold			= new FileOutputStream("src/bfos_move.dat");
#cold_bold		BufferedOutputStream bos
#cold_bold			= new BufferedOutputStream(fos);
#cold_bold		
#cold_bold		int d = -1;
#cold_bold		while((d = bis.read()) != -1){
#cold_bold			bos.write(d);
#cold_bold		}
#cold_bold		System.out.println("移动完毕");
#cold_bold		bis.close();
#cold_bold		bos.close();
	}
}

运行testMove2方法,在当前工程的src目录下生成了文件bfos_move.dat,该文件的内容与文件fos.dat的内容相同。

本案例的完整代码如下所示:

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;

import org.junit.Test;

public class TestMoveFile {
	/**
	 * 测试使用字节数组形式移动文件
	 */
	@Test
	public void testMove()throws Exception{
		FileInputStream fis
			= new FileInputStream("fos.dat");
		FileOutputStream fos
			= new FileOutputStream("src/fos_move.dat");
		
		int len = -1;
		byte[] buf = new byte[32];
		while((len = fis.read(buf)) != -1){
			fos.write(buf,0,len);
		}
		System.out.println("移动完毕");
		fis.close();
		fos.close();
	}
	/**
	 * 测试基于缓冲流的复制文件
	 */
	@Test
	public void testMove2()throws Exception{
		FileInputStream fis
			= new FileInputStream("fos.dat");
		//创建缓冲字节输入流
		BufferedInputStream bis
			= new BufferedInputStream(fis);
		
		FileOutputStream fos
			= new FileOutputStream("src/bfos_move.dat");
		BufferedOutputStream bos
			= new BufferedOutputStream(fos);
		
		int d = -1;
		while((d = bis.read()) != -1){
			bos.write(d);
		}
		System.out.println("移动完毕");
		bis.close();
		bos.close();
	}
}

5 实现empList的序列化和反序列化

实现empList的序列化和反序列化,详细要求如下:

1)在List集合中存放Emp类型的对象;

2)将List集合序列化到文件emplist.obj中;

3) 从文件emplist.obj中将List集合反序列化取出并打印到控制台。

参考答案

实现此案例需要按照如下步骤进行。

步骤一:创建Emp类

创建Emp类,代码如下所示:

import java.io.Serializable;
/**
 * 实现序列化接口后该类可以被序列化
 */
public class Emp implements Serializable{
	/**
	 * 版本号
	 */
	private static final long serialVersionUID = 1L;
	private String name;
	private int age;
	private String gender;
	private double salary;

	public Emp(String name, int age, String gender, double salary) {
		this.name = name;
		this.age = age;
		this.gender = gender;
		this.salary = salary;
	}
	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;
	}
	public String getGender() {
		return gender;
	}
	public void setGender(String gender) {
		this.gender = gender;
	}
	public double getSalary() {
		return salary;
	}
	public void setSalary(double salary) {
		this.salary = salary;
	}
	@Override
	public String toString() {
		return "Emp [name=" + name + ", age=" + age + ", gender=" + gender
				+ ", salary=" + salary + "]";
	}
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((name == null) ? 0 : name.hashCode());
		return result;
	}
}

特别需要注意的是该类实现了Serializable接口。实现该接口不需要重写任何方法,其只是作为可序列化的标志。

步骤二:新建测试方法

首先,新建类TestSerialEmps类中新建测试方法testWiter,代码如下所示:

import org.junit.Test;

/**
 * 实现对象的序列化与反序列化
 */
public class TestSerialEmps {
	/**
	 * 使用OOS实现对象的序列化
	 */
	@Test
	public void testWiter()throws Exception{
	}
}

步骤三:序列化List对象到文件中

将List对象序列化到文件emplist.obj中的详细过程如下:

1)构造List对象;

2)创建文件字节输出流FileOutputStream类的对象,接着使用该文件字节输出流对象作为参数构造对象字节输出流ObjectOutputStream类的对象;

3) 使用ObjectOutputStream类的writeObject方法将List对象写入到文件emplist.obj中;

4)关闭oos对象流,以释放资源。

代码如下所示:

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.List;

import org.junit.Test;

/**
 * 实现对象的序列化与反序列化
 */
public class TestSerialEmps {
	/**
	 * 使用OOS实现对象的序列化
	 */
	@Test
	public void testWiter()throws Exception{
#cold_bold		FileOutputStream fos
#cold_bold			= new FileOutputStream("emplist.obj");
#cold_bold		ObjectOutputStream oos
#cold_bold			= new ObjectOutputStream(fos);
#cold_bold		
#cold_bold		List<Emp> emps = new ArrayList<Emp>();
#cold_bold		emps.add(new Emp("张三", 33, "男", 9000));
#cold_bold		emps.add(new Emp("李四", 26, "男", 5000));
#cold_bold		emps.add(new Emp("王五", 48, "男", 34000));
#cold_bold		oos.writeObject(emps);
#cold_bold		System.out.println("序列化完毕");
#cold_bold		oos.close();
	}
}

运行testWiter方法,在当前工程下生成了文件emplist.obj,该文件的内容是二进制文本,无法读懂其中的内容。

List对象可以直接进行序列化,说明List接口继承了Serializable接口。

步骤四:新建测试方法testRead

在TestSerialEmps类中,新建测试方法testRead,代码如下所示:

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.List;

import org.junit.Test;

/**
 * 实现对象的序列化与反序列化
 */
public class TestSerialEmps {
	/**
	 * 使用OOS实现对象的序列化
	 */
	@Test
	public void testWiter()throws Exception{
   ... ...
	}
	/**
	 * 使用OIS实现对象的反序列化
	 */
#cold_bold	@Test
#cold_bold	public void testRead()throws Exception{	
#cold_bold	}
}

步骤五:实现对Emp对象的反序列化

将List对象从文件emplist.obj反序列化读取出来的详细过程如下:

1)创建文件字节输入流FileInputStream类的对象,接着使用该文件字节输入流对象作为参数构造对象字节输入流ObjectInputStream类的对象;

2) 使用ObjectInputStream类的readObject方法将List对象从emplist.obj文件中读取出来;

3)关闭ois对象流,以释放资源。

代码如下所示:

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.List;

import org.junit.Test;

/**
 * 实现对象的序列化与反序列化
 */
public class TestSerialEmps {
	/**
	 * 使用OOS实现对象的序列化
	 */
	@Test
	public void testWiter()throws Exception{
		... ...
	}
	/**
	 * 使用OIS实现对象的反序列化
	 */
	@Test
	public void testRead()throws Exception{
#cold_bold		FileInputStream fis
#cold_bold			= new FileInputStream("emplist.obj");
#cold_bold		ObjectInputStream ois
#cold_bold			= new ObjectInputStream(fis);
#cold_bold		List emps = (List)ois.readObject();
#cold_bold		System.out.println("反序列化完毕");
#cold_bold		System.out.println(emps);
#cold_bold		ois.close();		
	}
}

运行testRead方法,控制台输出结果如下所示:

反序列化完毕
[Emp [name=张三, age=33, gender=男, salary=9000.0], Emp [name=李四, age=26, gender=男, salary=5000.0], Emp [name=王五, age=48, gender=男, salary=34000.0]]

从输出结果可以看出,已经将Emp对象反序列化出来了。

本案例中,类TestSerialEmps的完整代码如下所示:

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.List;

import org.junit.Test;

/**
 * 实现对象的序列化与反序列化
 */
public class TestSerialEmps {
	/**
	 * 使用OOS实现对象的序列化
	 */
	@Test
	public void testWiter()throws Exception{
		FileOutputStream fos
			= new FileOutputStream("emplist.obj");
		ObjectOutputStream oos
			= new ObjectOutputStream(fos);
		
		List<Emp> emps = new ArrayList<Emp>();
		emps.add(new Emp("张三", 33, "男", 9000));
		emps.add(new Emp("李四", 26, "男", 5000));
		emps.add(new Emp("王五", 48, "男", 34000));
		oos.writeObject(emps);
		System.out.println("序列化完毕");
		oos.close();
		
	}
	/**
	 * 使用OIS实现对象的反序列化
	 */
	@Test
	public void testRead()throws Exception{
		FileInputStream fis
			= new FileInputStream("emplist.obj");
		ObjectInputStream ois
			= new ObjectInputStream(fis);
		List emps = (List)ois.readObject();
		System.out.println("反序列化完毕");
		System.out.println(emps);
		ois.close();		
	}
}

类Emp的完整代码如下所示:

import java.io.Serializable;
/**
 * 实现序列化接口后该类可以被序列化
 */
public class Emp implements Serializable{
	/**
	 * 版本号
	 */
	private static final long serialVersionUID = 1L;
	private String name;
	private int age;
	private String gender;
	private double salary;

	public Emp(String name, int age, String gender, double salary) {
		this.name = name;
		this.age = age;
		this.gender = gender;
		this.salary = salary;
	}
	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;
	}
	public String getGender() {
		return gender;
	}
	public void setGender(String gender) {
		this.gender = gender;
	}
	public double getSalary() {
		return salary;
	}
	public void setSalary(double salary) {
		this.salary = salary;
	}
	@Override
	public String toString() {
		return "Emp [name=" + name + ", age=" + age + ", gender=" + gender
				+ ", salary=" + salary + "]";
	}
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((name == null) ? 0 : name.hashCode());
		return result;
	}
}

6 简述Serializable接口和transient关键字的意义

参考答案

1)ObjectOutputStream在对对象进行序列化时有一个要求,就是需要序列化的对象所属的类必须实现Serializable接口。实现该接口不需要重写任何方法。其只是作为可序列化的标志。

2)对象在序列化后得到的字节序列往往比较大,有时我们在对一个对象进行序列化时可以忽略某些不必要的属性,从而对序列化后得到的字节序列”瘦身”。关键字 transient表示被该关键字修饰的属性在序列化时其值将被忽略。

7 名称解释:ISO8859-1,GBK,UTF-8

参考答案

1) ISO8859-1: 西文编码就是ASCII,一个英文字母一个字节。

2)GBK:中文编码是GB2312的超集, 1-2变长编码, 英文与ASCII一致, 中文2个字节,可以对20000多个汉字进行编码。

3)UTF-8: 1到4字节的变长编码, 英文与ascII一致, 中文3个字节。

8 分别简述ISR和OSW的工作原理

参考答案

InputStreamReader字符输入流。使用该流可以设置字符集,并按照指定的字符集从字节流中按照指定编码将字节数据转换为字符数据并读取。

OutputStreamReader字符输出流。使用该流可以设置字符集,并按照指定的字符集将字符转换为对应的字节后通过该流写出。