(8)IO流技术
1.IO是什么意思?data source是什么意思?
IO:Input Output
data source:数据流。
2.字节流和字符流有什么区别?输入流输出流有什么区别?
字节流和字符流是流的一种划分,按照处理流的数据单位进行的划分。两类都分为输入和输出操作。在字节流中输出数据主要是使用OutputStream完成,输入是使用InputStream;在字符流中输出主要是使用Writer类来完成,输入流主要使用Reader类来完成。这四个都是抽象类。
字符流处理的单元为2个字节的Unicode字符,分别是操作字符、字符数组或字符串,而字节流处理单元为1个字节,操作字节和字节数组。字节流是最基本的,所有的InputStream和OutputStream的子类都是字节流,主要用来处理二进制数据,它是按字节来处理的。但实际中很多数据都是文本,又提出了字符流的概念,它是按虚拟机的编码来处理,也就是说要进行字符集的转化,这两个之间通过InputStreamReader、OutputStreamWriter(转换流)来关联,实际上是通过byte()和String来关联的。
流就像管道一样,在程序和文件之间,输入和输出的方向是针对程序而言,向程序中读入读入东西,就像输入流,从程序中向外读东西,就是输出流。输入流是得到数据,输出流是输出数据。
3.节点流和处理流有什么区别?
节点流和处理流是把流按照功能的另一种划分。
节点流,可以从或向一个特定的地方(节点)读写数据。
处理流是对一个已存在的流的连接和封装,通过所封装的流的功能调用实现数据读写。如BufferReader。处理流的构造方法总是要带一个其他的流对象做参数。一个流对象经过其他流的多次包装,称为流的链接。
4.Word文档能使用字符流操作吗?
不能。因为Word文档不是纯文本文件,除了文字还包含很多格式信息。不能用字符流操作,可以用字节流操作。
5.BufferedInputStream和BufferedOutputStream的特点?
BufferedInputStream和BufferedOutputStream分别是【缓冲】字节输入、输出流,还有【缓冲】字符输入输出流(BufferedReader和BufferedWriter)。
缓冲流是处理流,他不是直接连接数据源/目的地,而是以一个节点流为参数,在节点流的基础上,提供一些简单参数。
先说不带缓冲的流的工作原理吧:它读取到一个字节/字符,就向用户指定的路径写进去,读一个写一个,所以就慢了。带缓冲的流的工作原理:读取到一个字节/字符,先不输出,等凑足了缓冲的最大容量后一次性写进去,从而提高了工作效率。
优点:减少对硬盘的读取次数,降低对硬盘的损耗。
6.使用BufferedReader和BufferedWriter实现文本的拷贝。
package IO;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.Reader;
import java.io.Writer;
public class CopyFile {
public static void main(String[] args) throws Exception {
//第一部分:准备从文件读数据到程序
//创建读取对象reader
Reader reader = new FileReader(new File("e:\\a.txt"));
//创建缓冲流包装reader
BufferedReader br = new BufferedReader(reader);
//第二部分:准备从程序写到文件
//创建写入对象writer
Writer writer = new FileWriter(new File("e:\\a2.txt"));
//创建缓冲包装对象
BufferedWriter bw = new BufferedWriter(writer);
String str = null;
//用循环边读边写
while((str=br.readLine()) != null) {
bw.write(str);
bw.newLine();
}
//清除缓冲区
bw.flush();
//copy完成后关闭资源
bw.close();
br.close();
}
}
7.InputStreamReader和OutputStreamWriter的作用是?
二者都是转换流,从字节流转换成字符流,是包装流,以一个节点流为参数,提供一些方便读写【字符】的方法。
package IO;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
public class InputStreamReaderDemo {
public static void main(String[] args) throws Exception {
//节点流
InputStream is = new FileInputStream("e:\\a.txt");
//处理流,把字节流转换成字符流
InputStreamReader isr = new InputStreamReader(is,"utf-8");
int temp = 0;
//用转换流对象isr进行读写操作,以字符(而不是字节)为单位读取文本文件,方便操作
while((temp = isr.read()) != -1) {
System.out.print((char) temp +" ");//打印出来的字符会以空格隔开
}
isr.close();
is.close();
}
}
8.PrintStream打印流经常用于什么情况?System.out是打印流吗。
PrintStream:字节打印流,是OutputStream的实现类。提供了多个重载的print,println等方法,可以方便地向文本文件写入数据。
System.out是字节打印流(PrintStream的对象),它被称作标准的输出流,输出的目的地是标准的输出设备,即显示器。所以,当我们使用System.out.print时会向屏幕(显示器)输出数据。
9.实现字节数组和任何基本类型、引用类型执行的相互转换。
提示:使用ByteArrayInputStream和ByteArrayOutputStream。
(1)字节数组和基本类型之间的转换,以boolean为例:
①输出一个boolean类型的数据:
boolean flag = true;//先定义一个boolean类型的变量
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(baos);
dos.writeBoolean(flag);
byte[] b = baos.toByteArray();
//接下来可以把byte数组建成一个数据包(DatagramPacket)发送出去;
②读取一个boolean类型的数据:
byte[] b = new byte[1024];
//我们已接收到包含一个Boolean类型数据的数据包;数据信息已经包含在byte数组b里,接下来就把boolean类型的数据读出来并保持原类型
ByteArrayInputStream bais = new ByteArrayInputStream(b);
DataInputStream dis = new DataInputStream(bais);
boolean flag = dis.readBoolean();//这样数据就读出来了并保持着原来的数据类型
(2)字节数组和引用类型之间的转换:
①输出一个引用类型信息:
//先建一个类,如User
User user = new User();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(user);
byte[] b = baos.toByteArray();
//接下来可以把byte数组构建成一个数据包发送出去
②读取一个引用类型信息
byte[] b = new byte[1024];
//我们已接收到包含一个User类型数据的数据包(代码略)
//数据信息已经包含在byte数组b里,接下来就把User类型的数据读出来并保持原类型
ByteArrayInputStream bais = new ByteArrayInputStream(b);
ObjectInputStream ois = new ObjectInputStream(bais);
User user = (User)ois.readObject();//这样数据就读出来了并保持着原来的数据类型
10.DataInputStream和DataOutputStream的特点是?
二者都是处理流,要以一个节点流为参数;二者被称为数据流,是用来操作基本数据类型的。用DataInputStream写入一个类型的数据,用DataOutputStream读出数据时可以保持类型不变。
如:用DataInputStream写入一个int类型数据,用DataOutputStream读出来的还是一个int数据,即可以直接当作int类型的数据来进行操作,不用作任何转换。
数据流特点:
①写入是什么类型的数据,读出的是相应类型的数据
②要先写后读,用DataOutputStream流写,用DataInputStream流读
③读写顺序要一致,否则会报EOF(End of file)异常
④数据流可以跨平台写入和读出,适合网路应用
实例:
package IO;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
public class DataStreamDemo {
public static void main(String[] args) throws IOException {
//1.写数据
OutputStream os = new FileOutputStream(new File("e:\\a1.txt"));//构建节点流
DataOutputStream dos = new DataOutputStream(os);//用数据流包装节点流
int n = 123456;
String str = "CSDN博客";
dos.writeInt(n);//用数据流写一个int类型数据
dos.writeUTF(str);//String
dos.writeDouble(3.14);//double
dos.writeChar('周');//char
dos.writeBoolean(true);//boolean
//2.读数据,(按上面写的顺序才行)
InputStream is = new FileInputStream(new File("e:\\a1.txt"));
DataInputStream dis = new DataInputStream(is);
int n1 = dis.readInt(); //用数据流读出刚才写入的int类型数据
String str1 = dis.readUTF();
double d = dis.readDouble();
char c = dis.readChar();
boolean flag = dis.readBoolean();
//打印输出用数据流读出的数据
System.out.println(n1);
System.out.println(str1);
System.out.println(d);
System.out.println(c);
System.out.println(flag);
//关闭资源
dis.close();
dos.close();
}
}
运行结果:
11.中文乱码是怎么造成的?Unicode字符集是几个字节表示一个字符,为什么需要utf-8?
字符流的读写根据需要,设置编码方式,若编码方式设置不当,就会出现中文乱码。
Unicode字符集2个字节表示一个字符。
12.序列化和反序列化指的是什么?
(1)序列化:
将对象以byte流的形式写入到文件中-》序列化;
将要被序列化对象的类要实现Serializable接口。
(2)反序列化:
将文件中的数据以byte流的形式读到程序中来,依然是一个对象-》反序列化
13.想序列化某个类的对象,该类必须实现Serializable借口吗?说说该接口的特点。
要序列化的对象必须实现Serializable接口,以启动序列化的功能。
Serializable接口特点:
①需要被序列化的对象的类必须实现Serializable接口;
②给类加个序列化编号,即给类定义一个标记,例:public static final long serialVersionUID = 1L;
新的修改后的类还可以操作曾经序列化的对象;
③静态类是不能被序列化的(序列化只能对堆中的对象进行序列化,不能对"方法区"中的对象进行序列化);
④不需要序列化的字段前加transient,如:private transient String password;
15.代码实现从源目录拷贝文件到目标文件(结合递归算法)。
package IO;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class CopyDir {
public static void main(String[] args) {
try {
copyDirectiory("E:\\learning_note","E:\\learning_note2");
} catch (IOException e) {
// TODO Auto-generated catch block
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();
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}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);
}
}
}
}
16.代码实现统计几个.java文件的行数的功能。
package IO;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
public class CountLines {
private int count;
/**
* 统计一个Java文件的行数
*
* @throws IOException
*/
private void countLine(File sourceFile) throws IOException {
BufferedReader br = null;
try {
// 新建文件输入流
br = new BufferedReader(new FileReader(sourceFile));
while (br.readLine() != null) {
count++;
}
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
br.close();
}
}
/**
* 统计一个目录下所有Java文件的行数
* @throws IOException
*/
private void countDir(String sourceDir) throws IOException {
// 检查源目录
File fSourceDir = new File(sourceDir);
if (!fSourceDir.exists() || !fSourceDir.isDirectory()) {
System.out.println("源目录不存在");
return;
}
//遍历目录下的文件或目录
File[] file = fSourceDir.listFiles();
for(int i=0; i<file.length; i++) {
if(file[i].isFile()) {
if(file[i].getName().toLowerCase().endsWith(".java")) {
countLine(file[i]);
}
}
//递归统计代码行数
if(file[i].isDirectory()) {
//准备统计的文件夹
String subSourceDir = sourceDir+File.separator+file[i].getName();
//统计子目录
countDir(subSourceDir);
}
}
}
public static void main(String[] args) throws IOException {
CountLines cl = new CountLines();
cl.countDir("e:\\javaFile");
System.out.println(cl.count);
}
}