- 下面哪个流类属于面向字符的输入流()选择一项)
A. BufferedWriter
B. FileInputStream
C. ObjectInputStream
D. InputStreamReader
答案:D
分析:A:字符输出的缓冲流
B:字节输入流
C:对象输入流 - 要从文件”file.dat”文件中读出第 10 个字节到变量 c 中,
下列哪个正确()(选择一项)
A. FileInputStream in=new FileInputStream("file.dat");
in.skip(9);
int c=in.read();
B. FileInputStream in=new FileInputStream("file.dat");
in.skip(10);
int c=in.read();
C. FileInputStream in=new FileInputStream("file.dat");
int c=in.read();
D. RandomAccessFile in=new RandomAccessFile("file.dat");
in.skip(7);
int c=in.readByte();
答案:A
分析: skip(long n)该方法中的 n 指的是要跳过的字节数 - 新建一个流对象,下面那个选项的代码是错误的?()
A. new BufferedWriter(new FileWriter(“a.txt”));
B. new BufferedReader (new FileInputStream(“a.dat”));
C. new GZIPOutputStream(new FileOutputStream(“a.zip”));
D. new ObjectInputStream(new FileInputStream(“a.dat”));
答案:B
分析:
BufferedReader 类的参数只能是 Reader 类型的,不能是 InputStream 类型。 - 下面哪个流是面向字符的输入流()
A BufferedWriter
B. FileInputStream
C. ObjectInputStream
D. InputStreamReader
答案:D
分析:
以 InputStream(输入流)/OutputStream(输出流)为后缀的是字节流;
以 Reader(输入流)/Writer(输出流)为后缀的是字符流。 - Java 类库中,将信息写入内存的类是()
A Java.io.FileOutputStream
B. java.ByteArrayOutputStream
C. java.io.BufferedOutputStream
D. java,.io.DataOutputStream
答案:B
分析: ACD都是io到文件 - 请写出一段代码,能够完成将字符串写入文件
public class test {
public static void main(String[] args) {
String str = "bjsxt";
writeFile(str);
}
public static void writeFile(String str) {
File file = new File("c:/test.txt");
PrintStream ps = null;
try {
OutputStream fos = new FileOutputStream(file);
ps = new PrintStream(fos);
ps.print(str);
} catch (FileNotFoundException e) {
e.printStackTrace();
} finally {
ps.close();
} } } - 下面哪个流类属于面向字符的输入流()
A. BufferedWriter
B. FileInputStream
C. ObjectInputStream
D. InputStreamReader
答案:D - Java 中如何实现序列化,有什么意义?
答:序列化就是一种用来处理对象流的机制,所谓对象流也就是将对象的内
容进行流化。可以对流化后的对象进行读写操作,也可将流化后的对象传输
于网络之间。序列化是为了解决对象流读写操作时可能引发的问题(如果不
进行序列化可能会存在数据乱序的问题)。
要实现序列化,需要让一个类实现 Serializable 接口,该接口是一个标识性
接口,标注该类对象是可被序列化的,然后使用一个输出流来构造一个对象
输出流并通过 writeObject(Object obj)方法就可以将实现对象写出(即保存
其状态);如果需要反序列化则可以用一个输入流建立对象输入流,然后通过
readObject 方法从流中读取对象。序列化除了能够实现对象的持久化之外,
还能够用于对象的深度克隆(参见 Java 面试题集 1-29 题) - Java 中有几种类型的流?
答:两种流分别是字节流,字符流。
字节流继承于 InputStream、OutputStream,字符流继承于 Reader、
Writer。在 java.io 包中还有许多其他的流,主要是为了提高性能和使用方
便。
补充:关于 Java 的 IO 需要注意的有两点:一是两种对称性(输入和输出的
对称性,字节和字符的对称性);二是两种设计模式(适配器模式和装潢模
式)。另外 Java 中的流不同于 C#的是它只有一个维度一个方向。
补充:下面用 IO 和 NIO 两种方式实现文件拷贝,这个题目在面试的时候是
经常被问到的。
package com.bjsxt;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
public class MyUtil {
private MyUtil() {
throw new AssertionError();
}
public static void fileCopy(String source, String
target) throws IOException {
try (InputStream in = new FileInputStream(source))
{
try (OutputStream out = new
FileOutputStream(target)) {
byte[] buffer = new byte[4096];
int bytesToRead;
while((bytesToRead = in.read(buffer)) != -1)
{
out.write(buffer, 0, bytesToRead);
}
}
}
}
public static void fileCopyNIO(String source, String
target) throws IOException {
try (FileInputStream in = new
FileInputStream(source)) {
try (FileOutputStream out = new
FileOutputStream(target)) {
FileChannel inChannel = in.getChannel();
FileChannel outChannel = out.getChannel();
ByteBuffer buffer =
ByteBuffer.allocate(4096);
while(inChannel.read(buffer) != -1) {
buffer.flip();
outChannel.write(buffer);
buffer.clear();
}
}
}
}
}
注意:上面用到 Java 7 的 TWR,使用 TWR 后可以不用在 finally 中释放外
部资源 ,从而让代码更加优雅。
266. 写一个方法,输入一个文件名和一个字符串,统计这个字符串
在这个文件中出现的次数。
答:代码如下:
package com.bjsxt;
import java.io.BufferedReader;
import java.io.FileReader;
public class Account {
// 工具类中的方法都是静态方式访问的因此将构造器私有不允许创建对象
(绝对好习惯)
private Account() {
throw new AssertionError();
}
/**
- 统计给定文件中给定字符串的出现次数
- @param filename 文件名
- @param word 字符串
- @return 字符串在文件中出现的次数
*/
public static int countWordInFile(String filename, String
word) {
int counter = 0;
try (FileReader fr = new FileReader(filename)) {
try (BufferedReader br = new BufferedReader(fr)) {
String line = null;
while ((line = br.readLine()) != null) {
int index = -1;
while (line.length() >= word.length() &&
(index = line.indexOf(word)) >= 0) {
counter++;
line = line.substring(index +
word.length());
}
}
}
} catch (Exception ex) {
ex.printStackTrace();
}
return counter;
}
}
- 输入流和输出流联系和区别,节点流和处理流联系和区别
首先,你要明白什么是“流”。直观地讲,流就像管道一样,在程序和
文件之间,输入输出的方向是针对程序而言,向程序中读入东西,就是输入
流,从程序中向外读东西,就是输出流。
输入流是得到数据,输出流是输出数据,而节点流,处理流是流的另一
种划分,按照功能不同进行的划分。节点流,可以从或向一个特定的地方(节 点)读写数据。处理流是对一个已存在的流的连接和封装,通过所封装的流的
功能调用实现数据读写。如 BufferedReader。处理流的构造方法总是要带
一个其他的流对象做参数。一个流对象经过其他流的多次包装,称为流的链
接。 - 字符流字节流联系区别;什么时候使用字节流和字符流?
字符流和字节流是流的一种划分,按处理照流的数据单位进行的划分。
两类都分为输入和输出操作。在字节流中输出数据主要是使用
OutputStream 完成,输入使的是 InputStream,在字符流中输出主要是使
用 Writer 类完成,输入流主要使用 Reader 类完成。这四个都是抽象类。
字符流处理的单元为 2 个字节的 Unicode 字符,分别操作字符、字符
数组或字符串,而字节流处理单元为 1 个字节,操作字节和字节数组。字节
流是最基本的,所有的 InputStrem 和 OutputStream 的子类都是,主要用在
处理二进制数据,它是按字节来处理的 但实际中很多的数据是文本,又提
出了字符流的概念,它是按虚拟机的编码来处理,也就是要进行字符集的转
化 这两个之间通过 InputStreamReader,OutputStreamWriter 来关联,
实际上是通过 byte[]和 String 来关联的。 - 列举常用字节输入流和输出流并说明其特点,至少 5 对。
答:
FileInputStream 从文件系统中的某个文件中获得输入字节。
FileOutputStream 从程序当中的数据,写入到指定文件。
ObjectInputStream 对以前使用 ObjectOutputStream 写入的基本
数据和对象进行反序列化。ObjectOutputStream 和 ObjectInputStream
分别与 FileOutputStream 和 FileInputStream 一起使用时,可以为应用
程序提供对对象图形的持久存储。ObjectInputStream 用于恢复那些以前
序列化的对象。其他用途包括使用套接字流在主机之间传递对象,或者用于
编组和解组远程通信系统中的实参和形参。
ByteArrayInputStream 包含一个内部缓冲区,该缓冲区包含从流中读
取的字节。内部计数器跟踪 read 方法要提供的下一个字节。
FilterInputStream 包含其他一些输入流,它将这些流用作其基本数据
源,它可以直接传输数据或提供一些额外的功能。FilterInputStream 类本
身只是简单地重写那些将所有请求传递给所包含输入流的 InputStream
的所有方法。FilterInputStream 的子类可进一步重写这些方法中的一些方
法,并且还可以提供一些额外的方法和字段。
StringBufferInputStream 此类允许应用程序创建输入流,在该流中读
取的字节由字符串内容提供。应用程序还可以使用 ByteArrayInputStream
从 byte 数组中读取字节。 只有字符串中每个字符的低八位可以由此类使
用。
ByteArrayOutputStream 此类实现了一个输出流,其中的数据被写入
一个 byte 数组。缓冲区会随着数据的不断写入而自动增长。可使用
toByteArray() 和 toString() 获取数据。
FileOutputStream 文件输出流是用于将数据写入 File 或
FileDescriptor 的输出流。文件是否可用或能否可以被创建取决于基础平台。
特别是某些平台一次只允许一个 FileOutputStream(或其他文件写入对象)
打开文件进行写入。在这种情况下,如果所涉及的文件已经打开,则此类中
的构造方法将失败。
FilterOutputStream 类是过滤输出流的所有类的超类。这些流位于已
存在的输出流(基础 输出流)之上,它们将已存在的输出流作为其基本数
据接收器,但可能直接传输数据或提供一些额外的功能。
FilterOutputStream 类本身只是简单地重写那些将所有请求传递给所包含
输出流的 OutputStream 的所有方法。FilterOutputStream 的子类可进
一步地重写这些方法中的一些方法,并且还可以提供一些额外的方法和字段。
ObjectOutputStream 将 Java 对象的基本数据类型和图形写入
OutputStream。可以使用 ObjectInputStream 读取(重构)对象。通过
在流中使用文件可以实现对象的持久存储。如果流是网络套接字流,则可以
在另一台主机上或另一个进程中重构对象。
PipedOutputStream 可以将管道输出流连接到管道输入流来创建通信
管道。管道输出流是管道的发送端。通常,数据由某个线程写入
PipedOutputStream 对象,并由其他线程从连接的 PipedInputStream
读取。不建议对这两个对象尝试使用单个线程,因为这样可能会造成该线程
死锁。如果某个线程正从连接的管道输入流中读取数据字节,但该线程不再
处于活动状态,则该管道被视为处于毁坏状态。 - 说明缓冲流的优点和原理
不带缓冲的流的工作原理:
它读取到一个字节/字符,就向用户指定的路径写出去,读一个写一个,
所以就慢了。
带缓冲的流的工作原理:
读取到一个字节/字符,先不输出,等凑足了缓冲的最大容量后一次性
写出去,从而提高了工作效率
优点:减少对硬盘的读取次数,降低对硬盘的损耗。 - 序列化的定义、实现和注意事项
想把一个对象写在硬盘上或者网络上,对其进行序列化,把他序列化成为一
个字节流。
实现和注意事项:
1)实现接口 Serializable Serializable 接口中没有任何的方法,实现该
接口的类不需要实现额外的方法。
2)如果对象中的某个属性是对象类型,必须也实现 Serializable 接口才
可以,序列化对静态变量无效
3)如果不希望某个属性参与序列化,不是将其 static,而是 transient
串行化保存的只是变量的值,对于变量的任何修饰符,都不能保存
序列化版本不兼容 - 使用 IO 流完成文件夹复制
(结合递归)
package com.bjsxt;
import java.io.*;
/**
- CopyDocJob定义了实际执行的任务,即
- 从源目录拷贝文件到目标目录
/
public class CopyDir2 {
public static void main(String[] args) {
try {
copyDirectiory("d:/301sxt","d:/301sxt2");
} catch (IOException e) {
e.printStackTrace();
} }
/* - 复制单个文件
- @param sourceFile 源文件
- @param targetFile 目标文件
- @throws IOException
/
private static void copyFile(File sourceFile, File targetFile)
throws IOException {
BufferedInputStream inBuff = null;
BufferedOutputStream outBuff = null;
try {
// 新建文件输入流
inBuff = new BufferedInputStream(new
FileInputStream(sourceFile));
// 新建文件输出流
outBuff = new BufferedOutputStream(new
FileOutputStream(targetFile));
// 缓冲数组
byte[] b = new byte[1024 * 5];
int len;
while ((len = inBuff.read(b)) != -1) {
outBuff.write(b, 0, len);
}
// 刷新此缓冲的输出流
outBuff.flush();
} finally {
// 关闭流
if (inBuff != null)
inBuff.close();
if (outBuff != null)
outBuff.close();
}
}
/* - 复制目录
- @param sourceDir 源目录
- @param targetDir 目标目录
- @throws IOException
*/
private static void copyDirectiory(String sourceDir, String
targetDir) throws IOException {
// 检查源目录
File fSourceDir = new File(sourceDir);
if(!fSourceDir.exists() || !fSourceDir.isDirectory()){
return;
}
//检查目标目录,如不存在则创建
File fTargetDir = new File(targetDir);
if(!fTargetDir.exists()){
fTargetDir.mkdirs();
}
// 遍历源目录下的文件或目录
File[] file = fSourceDir.listFiles();
for (int i = 0; i < file.length; i++) {
if (file[i].isFile()) {
// 源文件
File sourceFile = file[i];
// 目标文件
File targetFile = new File(fTargetDir,
file[i].getName());
copyFile(sourceFile, targetFile);
}
//递归复制子目录
if (file[i].isDirectory()) {
// 准备复制的源文件夹
String subSourceDir = sourceDir + File.separator +
file[i].getName();
// 准备复制的目标文件夹
String subTargetDir = targetDir + File.separator +
file[i].getName();
// 复制子目录
copyDirectiory(subSourceDir, subTargetDir); }
}
} }
- 说说 BIO、NIO 和 AIO 的区别
Java BIO: 同步并阻塞,服务器实现模式为一个连接一个线程,即客户端有连
接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会
造成不必要的线程开销,当然可以通过线程池机制改善。
Java NIO: 同步非阻塞,服务器实现模式为一个请求一个线程,即客户端发送
的连接请求都会注册到多路复用器上,多路复用器轮询到连接有 I/O 请求时才启
动一个线程进行处理。
Java AIO: 异步非阻塞,服务器实现模式为一个有效请求一个线程,客户端的
I/O 请求都是由 OS 先完成了再通知服务器应用去启动线程进行处理。
NIO 比 BIO 的改善之处是把一些无效的连接挡在了启动线程之前,减少了这部
分资源的浪费(因为我们都知道每创建一个线程,就要为这个线程分配一定的内
存空间)
AIO 比 NIO 的进一步改善之处是将一些暂时可能无效的请求挡在了启动线程之
前,比如在 NIO 的处理方式中,当一个请求来的话,开启线程进行处理,但这
个请求所需要的资源还没有就绪,此时必须等待后端的应用资源,这时线程就被
阻塞了。
适用场景分析:
BIO 方式适用于连接数目比较小且固定的架构,这种方式对服务器资源要求比较
高,并发局限于应用中,JDK1.4 以前的唯一选择,但程序直观简单易理解,如
之前在 Apache 中使用。
NIO 方式适用于连接数目多且连接比较短(轻操作)的架构,比如聊天服务器,
并发局限于应用中,编程比较复杂,JDK1.4 开始支持,如在 Nginx,Netty 中
使用。
AIO 方式使用于连接数目多且连接比较长(重操作)的架构,比如相册服务器,
充分调用 OS 参与并发操作,编程比较复杂,JDK7 开始支持,在成长中,Netty
曾经使用过,后来放弃