Java IO-(第六天)
一、IO简介
IO是指Input/Output,即输入与输出
Input从外部读入数据到内存,读文件,从网络读取等
Output从内存输出到外部,写文件,输出到网络等
IO流是一种顺序读写数据的模式,单向流动,以byte为最小单位(字节流)
如果字符不是单个自己表示的ASCII码,字符流传输的最小数据单位是char
字符流输出的byte取决于编码方式。(ASCII码,UTF-8,Unicode)
Bytes和String字符串之间相互转换
//Original String
String string = "hello world";
//Convert to byte[]
byte[] bytes = string.getBytes();
//Convert back to String
String s = new String(bytes);
//Check converted string against original String
System.out.println("Decoded String : " + s);
二、同步和异步IO
同步IO:读写IO时代码等待数据返回后才继续执行后续代码,代码编写简单,CPU执行效率低。
异步IO:读写IO时仅发出请求,然后立刻执行后续代码,代码编写复杂,CPU执行效率高。
JDK提供的JAVA.IO是同步IO
JDK提供的JAVA.NIO是异步IO
JAVA.IO
抽象类 | InputStream | OutputStream | Reader | Writer |
---|---|---|---|---|
实现类 | FileInputStream | FileOutputStream | FileReader | FileWriter |
三、File类
java.io.File表示文件系统的一个文件或目录
File f = new File(String pathName)//可以是相对路径和绝对路径
//windows
String path1 = "c:\\windows\\test.txt";
//linux
String path2 = "/usr/bin/java.txt";
获取路径的方法
f.getPath();
f.getAbsolutePath();//绝对路径
f.getCanonicalPath();
判断文件对象是文件还是目录
f.isFile();//是否为文件
f.isDirectory();//是否为目录
用File对象获取到一个文件时,还可以进一步判断文件的权限和大小:
boolean canRead():是否可读;
boolean canWrite():是否可写;
boolean canExecute():是否可执行;
long length():文件字节大小。
通过createNewFile()创建一个新文件,用delete()删除该文件,File对象提供了createTempFile()来创建一个临时文件,以及deleteOnExit()在JVM退出时自动删除该文件。
使用list()和listFiles()列出目录下的文件和子目录名。当File对象表示一个目录时,可以使用list()和listFiles()列出目录下的文件和子目录名。listFiles()提供了一系列重载方法,可以过滤不想要的文件和目录。
File[] fs2 = f.listFiles(new FilenameFilter() { // 仅列出.exe文件
public boolean accept(File dir, String name) {
return name.endsWith(".exe"); // 返回true表示接受该文件
}
});
和文件操作类似,File对象如果表示一个目录,可以通过以下方法创建和删除目录:
boolean mkdir():创建当前File对象表示的目录;
boolean mkdirs():创建当前File对象表示的目录,并在必要时将不存在的父目录也创建出来;
boolean delete():删除当前File对象表示的目录,当前目录必须为空才能删除成功。
四、InputStream类
java.io.InputStream是所有输入流的超类。
abstract int read() 读取下一个字节,并返回字节数(0-255);如果已读到末尾,返回-1。
int read(byte[] b)读取若干字节,并填充到byte[]数组,返回读取的字节数。
int read(byte[] b,int off,int len)指定byte[]数组的偏移量和最大填充数。
void close()关闭输入流
try{}catch(Exception e){}保证输入流的正常关闭
public void readFile() throws IOException {
try (InputStream input = new FileInputStream("src/readme.txt")) {
// 定义1000个字节大小的缓冲区:
byte[] buffer = new byte[1000];
int n;
while ((n = input.read(buffer)) != -1) { // 读取到缓冲区
System.out.println("read " + n + " bytes.");
}
}
}
read()是一个阻塞blocking的方法,需要等待读取结束
五、OutputStram类
java.io.OutputStream是所有输出流的超类。
abstract write(int b)写入一个字节
void write(byte[] b)写入byte[]数组的所有字节
void write(byte[] b,int off,int len)写入byte[]数组指定范围的字节
void close()关闭输出流
void flush()将缓冲区的内容输出
try{}finally{}保证输入流的正常关闭
public void writeFile() throws IOException {
OutputStream output = new FileOutputStream("out/readme.txt");
output.write("Hello".getBytes("UTF-8")); // Hello
output.close();
}
OutputStream的write()方法也是阻塞的
六、Filter模式
JDK提供InputStream包括:
FileInputStream从文件读取数据
ServletInputStream从HTTP请求读取数据
Socket.getInputStream()从TCP连接读取数据
如果我们要给FileInputStream添加缓冲功能,则可以从FileInputStream派生一个类:
BufferedFileInputStream extends FileInputStream
如果要给FileInputStream添加计算签名的功能,类似的,也可以从FileInputStream派生一个类:
DigestFileInputStream extends FileInputStream
如果要给FileInputStream添加加密/解密功能,还是可以从FileInputStream派生一个类:
CipherFileInputStream extends FileInputStream
如果要给FileInputStream添加缓冲和签名的功能,那么我们还需要派生BufferedDigestFileInputStream。如果要给FileInputStream添加缓冲和加解密的功能,则需要派生BufferedCipherFileInputStream。
InputStream input = new GZIPInputStream(
new BufferInputStream(
new FileInputStream("String pathName")
));
通过组合功能而非继承的设计模式称为Filter模式(或Decorator模式)
七、Zip
ZipInputStream是一种FilterInputStream,可以直接读取Zip内容。
读取zip包
try (ZipInputStream zip = new ZipInputStream(new FileInputStream(...))) {
ZipEntry entry = null;
while ((entry = zip.getNextEntry()) != null) {
String name = entry.getName();
if (!entry.isDirectory()) {
int n;
while ((n = zip.read()) != -1) {
...
}
}
}
}
写入zip包
try (ZipOutputStream zip = new ZipOutputStream(new FileOutputStream(...))) {
File[] files = ...
for (File file : files) {
zip.putNextEntry(new ZipEntry(file.getName()));
zip.write(getFileDataAsBytes(file));
zip.closeEntry();
}
}
八、读取classpath文件
从classpath读取文件可以避免不同环境下文件路径不一致的问题。
如果我们把default.properties文件放到classpath中,就不用关心它的实际存放路径。
在classpath中的资源文件,路径总是以/开头,我们先获取当前的Class对象,然后调用getResourceAsStream()就可以直接从classpath读取任意的资源文件。
try (InputStream input = getClass().getResourceAsStream("/default.properties")) {
// TODO:
}
九、序列化和反序列化
序列化是指把一个java对象变成二进制内容(byte[])
- 序列化后可以把byte[]保存到文件中
- 序列化后可以把byte[]通过网络传输
必须实现Serializable接口。此接口没有定义任何方法,空接口被称为标记接口(Marker Interface)
反序列化是指把一个二进制内容(也就是byte[]数组)变回Java对象。
- 到文件中的byte[]数组又可以“变回”Java对象
- 从网络上读取byte[]并把它“变回”Java对象。
ObjectInputStream 从二进制流读取一个java对象
ObjectOutputStream 把一个java对象写入二进制流
反序列化由JVM直接构造出Java对象,不调用构造方法
java的序列化机制仅仅适用于java,如果需要与其他语言交换数据,必须使用通用的序列化方法,例如:JSON
十、Reader
InputStream | Reader |
---|---|
字节流,以byte为单位 | 字符流,以char为单位 |
读取字节(-1,0~255):int read() | 读取字符(-1,0~65535):int read() |
读到字节数组:int read(byte[] b) | 读到字符数组:int read(char[] c) |
java.io.Reader是所有字符输入流的超类,它最主要的方法是:
public int read() throws IOException;
int read(char[] c) 读取若干字符并填充到char[]数组,返回读取的字符数。
int read(char[] c,int off,int len) 指定char[]数组的偏移量和最大填充数。
void close() 关闭reader
try{}finally{}保证输入流的正常关闭
public void readFile() throws IOException {
try (Reader reader = new FileReader("src/readme.txt", StandardCharsets.UTF_8)) {
char[] buffer = new char[1000];
int n;
while ((n = reader.read(buffer)) != -1) {
System.out.println("read " + n + " chars.");
}
}
}
try (Reader reader = new InputStreamReader(new FileInputStream("src/readme.txt"), "UTF-8")) {
// TODO:
}
十一、Writer
Reader是带编码转换器的InputStream,它把byte转换为char,而Writer就是带编码转换器的OutputStream,它把char转换为byte并输出。
Writer和OutputStream的区别如下:
OutputStream | Writer |
---|---|
字节流,以byte为单位 | 字符流,以char为单位 |
写入字节(0~255):void write(int b) | 写入字符(0~65535):void write(int c) |
写入字节数组:void write(byte[] b) | 写入字符数组:void write(char[] c) |
无对应方法 | 写入String:void write(String s) |
Writer是所有字符输出流的超类,它提供的方法主要有:
写入一个字符(0~65535):void write(int c);
写入字符数组的所有字符:void write(char[] c);
写入String表示的所有字符:void write(String s)。
try (Writer writer = new FileWriter("readme.txt", StandardCharsets.UTF_8)) {
writer.write('H'); // 写入单个字符
writer.write("Hello".toCharArray()); // 写入char[]
writer.write("Hello"); // 写入String
}
try (Writer writer = new OutputStreamWriter(new FileOutputStream("readme.txt"), "UTF-8")) {
// TODO:
}
十二、printStream和printWriter
printStream
System.out.print(12345); // 输出12345
System.out.print(new Object()); // 输出类似java.lang.Object@3c7a835a
System.out.println("Hello"); // 输出Hello并换行
printWriter
StringWriter buffer = new StringWriter();
try (PrintWriter pw = new PrintWriter(buffer)) {
pw.println("Hello");
pw.println(12345);
pw.println(true);
}
System.out.println(buffer.toString());