JavaSE(六)IO编程

IO 基础

简介

IO:Input/Output
IO流是一种流式数据输入、输出模型:

1)二进制数据以byte为最小单位在InputStream/OutputStream中单向流动
2)字符数据以char为最小单位在Reader/Writer中单向流动
3)JDK的java.io包提供了同步IO功能
4)JDK的java.nio包提供了异步IO功能

Java的IO流的接口

字节流接口:InputStream、OutputStream
字符流接口:Reader、Writer

File对象

java.io.File 表示文件系统的一个文件或者目录

isFile():判断是否是文件
isDirectory():判断是否是目录

创建File对象本身不涉及IO操作

getPath():获取路径
getAbsolutePath():获取绝对路径
getCanonicalPath():获取规范路径

文件操作

canRead():是否允许读取该文件
canWrite():是否允许写入该文件
canExecute():是否允许运行该文件
length():获取文件大小
createNewFile():创建一个新文件
static createTempFile():创建一个临时文件
delete():删除该文件
deleteOnExit():在JVM退出时删除文件

目录操作

String[] list():列出目录下的文件和子目录名
File[] listFiles():列出目录下的文件和子目录名
File[] listFiles(FileFilter filter)
File[] listFiles(FilenameFilter filter)
mkdir():创建该目录
mkdirs():创建该目录,并在必要时将不存在的父目录也创建出来
delete():删除该目录

练习

编写程序,1)列出当前目录下的所有子目录和文件,并按层次打印;2)如果不指定参数,则使用当前目录,如果指定参数,则使用指定目录

public static void printFileName(File file){
    if(file.isDirectory() && 0 != file.listFiles().length){
        File[] files = file.listFiles();
        files = sortFile(files);
        for(File f : files){
        	StringBuilder sb = new StringBuilder();
            if(f.isFile()){
                sb.append(getTab(number));
                sb.append(f.getName());
            }else{
                sb.append(getTab(number));
                sb.append(f.getName());
                sb.append("\\");
            }
            System.out.println(sb);
            if(f.isDirectory()){
                number++;
                printFileName(f);
                number--;
            }
        }
    }
}
//得到子目录前需要加多少tab
private static String getTab(int number){
    StringBuilder sb = new StringBuilder();
    for(int i = 0; i < number; i++){
        sb.append("\t");
    }
    return sb.toString();
}
//对子目录进行排序,文件夹在前,文件在后
private static File[] sortFile(File[] files) {
    ArrayList<File> list = new ArrayList<File>();
    for(File f : files){
        if(f.isDirectory()){
            list.add(f);
        }
    }
    for(File f : files){
        if(f.isFile()){
            list.add(f);
        }
    }
    return list.toArray(new File[files.length]);
}

Input 和 Output

InputStream

InputStream是所有输入流的超类

1)int read():读取一个字节,并返回字节(0~255),已读到末尾返回-1
2)int read(byte[]):读取若干字节并填充到byte[]数组
3)int read(byte[] b, int off, int len)指定byte[]数组的偏移量和最大填充数
4)void close():关闭输入流
read()方法是阻塞的

使用try可以保证InputStream正确关闭

public static void readFile(){
	InputStream input = null;
	try {
		input = new FileInputStream("src/1.txt");
		int n;
		while((n = input.read()) != -1){
			System.out.println(n);
		}
	} catch(IOException e){
		e.printStackTrace();
	}finally {
		if(input != null){
			try {
				input.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
}
// JDK7 新特性
public static void readFile() throws FileNotFoundException, IOException{
	try(InputStream input = new FileInputStream("src/1.txt")){
		int n;
		while((n = input.read()) != -1){
			System.out.println(n);
		}
	} // 在此自动关闭InputStream(推荐写法)
}
// 利用缓存区一次读取多个字节
public static void readFile() throws FileNotFoundException, IOException{
	try(InputStream input = new FileInputStream("src/1.txt")){
		byte[] buffer = new byte[1024];
		int n;
		while((n = input.read(buffer)) != -1){
			System.out.println("read " + n + " bytes.");
		}
	}
}

常用的InputStream:
1)FileInputStream:可以从文件获取输入流
2)ByteArrayInputStream:可以在内存中模拟一个InputStream

byte[] data = {72, 101, 108, 111, -28, -67, -96, -27, -91, -67};
try(InputStream input = new ByteArrayInputStream(data)){// 可以用在测试中
	int n;
	while((n = input.read()) != -1){
		System.out.println(n)
	}
}

OutputStream

outputStream是所有输出流的超类

1)void write(int b):写入一个字节
2)void write(byte[]):写入byte[]数组的所有字节
3)void write(vyte[] b, int off, int len):写入byte[]数组指定范围的字节
4)void close():关闭输入流
5)void flush():将缓存区内容输出
write()方法也是阻塞的

使用try可以保证OutputStream正确关闭
常用OutputStream
1)FileOutputStream:可以输出到文件
2)ByteArrayOutputStream:可以在内存中模拟一个OutputStream

try(ByteArrayOutputStream output = new ByteArrayOutputStream()){
	output.write("Hello ".getBytes("UTF-8"));
	output.write("world!".getBytes("UTF-8"));
	byte[] data = output.toByteArray();
}

练习

编写程序,接收两个命令行参数,分别表示源文件和目标文件,用InputStream、OutputStream把源文件复制到目标文件。复制后,请检查源文件和目标文件是否相同(文件长度相同,内容相同),分别用文本文件、图片文件和zip文件测试

public class Main {
	public static void main(String[] args) throws FileNotFoundException, IOException {
		if (args.length < 2) {
			System.out.println("usage: java CopyFile sourcefile targetfile");
			System.exit(0);
		}
		byte[] bytes = new byte[1024];
		try(InputStream input = new FileInputStream(args[0]);
			OutputStream output = new FileOutputStream(args[1])){
			int n;
			while((n = input.read(bytes)) != -1){
				output.write(bytes, 0, n);
			}
		}
	}
}

Filter模式

Filter模式是为了解决子类数量爆炸的问题
直接提供数据的InputStream:

1)FileInputStream
2)ByteArrayInputStream
3)ServletInputStream

提供附加功能的InputStream从FilterInputStream派生

1)BufferedInputStream:缓存功能
2)DigestInputStream:计算签名功能
3)CipherInputStream:加密/解密功能
4)GZIPInputStream:压缩功能

在这里插入图片描述

InputStream input = new GZIPInputStram(
	new BufferedInputStream(
		new FileInputStream("test.gz");
	)
)

Filter模式又称Decorator模式,通过少量的类是实现了各种功能的组合
FilterInputStream 和 FilterOutputStream类似
在这里插入图片描述

// 实现一个字节数计数的InputStream
public class CounInputStream extends FilterInputStream{
	int count = 0;
	public CounInputStream(InputStream in) {
		super(in);
	}
	@Override
	public int read(byte[] b, int off, int len) throws IOException {
		int n = super.read(b, off, len);
		count++;
		return n;
	}
}

操作Zip

ZipInputStream可以读取Zip流
JarInputStream提供了额外读取jar包内容的功能
ZipOutputStream可以写入Zip流
配合FileInputStream和FileOutputStream就可以读写Zip文件

// ZipInputStream的基本使用
try(ZipInputStream zip = new ZipInputStream("test.zip")){
	ZipEntry entry = null;
	while((entry = zip.getNextEntry()) != null){
		String name = entry.getName();
		if(!entry.isDirectory()){
			int n;
			while((n = zip.read()) != -1){
				// ...
			}
		}
	}
}
// ZipOutputStream的基本使用
try(ZipOutputStream zip = new ZipOutputStream("test.zip")){
	File[] files = ...;
	for(File file : files){
		zip.putNextEntry(new ZipEntry(file.getName));
		zip.write(getFileDataAsBytes(file));
		zip.closeEntry();
	}
}

classpath资源

classpath中也可以包含任意类型的文件
从classpath读取文件可以避免不同环境下文件路径不一致的问题
读取classpath资源:

try(InputStream input = getClass().getResourceAsStream("/default.properties")){
	if(input != null){
		// Read from classpath
	}
}

序列化

序列化是指把一个Java对象变成二进制内容(byte[]),序列化后可以把byte[]保存到文件中,也可以通过网络传输
Java对象实现序列化必须实现Serializable接口

Serializable接口没有定义任何方法,空接口被称为“标记接口(Marker Interface)”

反序列化是指把一个二进制内容(bytep[])变成Java对象
使用ObjectOutputStream和ObjectInputStream实现序列化和反序列化

try(ObjectOutputStream output = new ObjectOutputStream(...)){
	output.writeObject(new Person("aa"));
	output.writeObject(new Person("bb"));
}
try(ObjectInputStream input = new ObjectInputStream(...)){
	Object obj = input.readObject();
	Person per = (Person)input.readObject();
}

readObject()可能抛出异常:

1)ClassNotFoundException:没有找到对应的Class
2)InvalidClassException:Class不匹配

反序列化的重要特点:反序列化由JVM直接构造出Java对象,不调用构造方法
可设置serialVersionUID作为版本号(非必须)

Reader 和 Writer

Reader

Reader以字符为最小单位实现了字符输入

1)int read():读取下一个字符,并返回字符(0~65535),读到末尾返回-1
2)int read(char[]):读取若干字符并填充到char[]数组
3)int read(char[] c, int off, int len):指定char[]数组的偏移量和最大填充数
4)void close():关闭Reader

常用的Reader类

1)FileReader:从文件读取
2)CharArrayReader:从char[]数组读取,可以再内存中模拟一个Reader

Reader是基于InputStream构造的,任何InputStream都可指定编码并通过InputStreamReader转换为Reader:

InputStream input = new FileInputStream(filename)
Reader reader = new InputStreamReader(input, "UTF-8")

Writer

Writer以字节为最小单位实现了字符流输出

1)write(int c):写入下一个字符
2)write(cahr[]):写入char[]数组的所有字符

常用Write类:

1)FileWriter:写入文件
2)CharArrayWriter:写入cahr[]数组

Writer是基于OutputStream构造的,任何Output都可指定编码并通过OutputStreamWriter转换为Writer:

Writer writer = new OutputStreamWriter(output, "UTF-8")

练习

编写一个程序,接收两个命令行参数,分别表示源文件和目标文件,然后用Reader、Writer把GBK编码的源文件转换为UTF-8编码的目标文件

public class Main {
	public static void main(String[] args) throws FileNotFoundException,
			IOException {
		if (args.length < 2) {
			System.out.println("usage: java CopyFile sourcefile targetfile");
			System.exit(0);
		}
		char[] chars = new char[1024];
		try (Reader reader = new InputStreamReader(
				new FileInputStream(args[0]), "GBK");
				Writer writer = new OutputStreamWriter(new FileOutputStream(
						args[1]), "UTF-8")) {
			int ch;
			while ((ch = reader.read(chars)) != -1) {
				writer.write(chars, 0, ch);
			}
		}
	}
}

猜你喜欢

转载自blog.csdn.net/yangwei234/article/details/84993007
今日推荐