Java笔记(十二) 文件基础技术

文件基础技术

一、文件概述

一)基本概念

1.文件的分类:

1)文本文件:文件中每个二进制字节都是某个可打印字符的一部分。如.java文件

2)二进制文件:文件中每个二进制字节不一定用来表示字符,也可表示颜色、字体、声音。

2.文件的元数据信息:如文件名、创建时间、修改时间、文件大小等。

3.文件名的大小写敏感问题:Windows系统文件名不大小写敏感,Linux系统中是大小写敏感的

4.文件的读写:

1)文件是放在硬盘中的,程序处理文件需要将文件读入内存,修改后,写回硬盘。

2)基本常识1:

硬盘的访问延时,相比内存是很慢的。操作系统一般是按块批量传输而不是按字节,

以摊销延时开销,块大小一般至少为512字节,即使应用程序只需要文件的一个字节,也是按块读取的。

一般而言,应该尽量减少硬盘的接触,接触一次尽量多做些事情。对于网络请求和其他输入输出设备,原则都是类似的。

3)基本常识2:

一般读写文件需要两次数据复制,比如读文件,需要先从硬盘复制到操作系统内核,

再从内核复制到应用程序分配的内存中。操作系统运行所在的环境和应用程序是不一

样的,操作系统所在的环境是内核态,应用程序是用户态,应用程序调用操作系统的

功能需要两次环境的切换,先从用户态到内核态,再从内核态到用户态。这种切换是

有开销的,应该尽量减少这种开销。

为了提升文件操作的效率,应用程序经常使用的一种策略----使用缓冲区。注意,操作

系统和应用程序都可以缓冲区。

4)文件的打开关闭:

操作系统操作文件一般都有打开和关闭的概念。打开文件会在操作系统内核建立一个有关

该文件的内存结构,这个结构一般通过一个整数索引来引用,这个索引被称为文件描述符。

这个结构是消耗内存的,操作系统能同时打开的文件也是有限的,所以在文件不用时应该

关闭文件。关闭文件会同步缓冲区内容到硬盘,并释放占据的内存结构。

二)Java中的文件概述

1.流

在Java中,文件一般不是单独处理的,而是视为输入\输出(Input\Output,IO)设备的一种。Java使用

统一的概念处理所有的IO,包括键盘、显示终端、网络等。

这个统一的概念就是流,流被分为:

1)输入流:可以从输入流中获取数据,其实际提供者可以是文件、键盘、网络等。

2)输出流:可以向输出流中写入数据,实际目的地可以是显示终端、文件、网络等。

2.序列化和反序列化

序列化:将内存中的Java对象持久保存到一个流中。

反序列化:从流中恢复Java对象到内存中。

序列化和反序列化有两个应用:一是对象的持久化,二是网络远程调用,用于传递和返回对象。

Java主要通过接口Serializable 和类ObjectInputStream/ObjectOutputStream提供对序列化的支持。

二、二进制文件和字节流 

在Java中以二进制方式读写的主要流有:

一)InputStream/OutputStream

字节流基类。

1.InputStream 

An object from which we can read a sequence of bytes

InputStream是一个抽象类,主要方法是:

public abstract int read() throws IOException

reads a byte of data and returns the byte read; returns -1 at the end of the input stream .
如果流中没有数据,read方法会阻塞直到数据到来,流关闭或者异常出现。

还有一个方法:

public int read(byte b[]) throws IOException

reads into an array of bytes and returns the actual number of bytes read, or -1 at the
end of the input stream; this method reads at most b.length bytes.

该方法不是抽象方法,有一个默认实现。

public int read(byte b[], int off, int len) throws IOException

跳过输入流中的n个字节方法:

public long skip(long n) throws IOException

返回值为实际略过的字节数。

返回下次不需要阻塞就能读取到的大概字节数:

public int available() throws IOException

默认实现返回0.

关闭流:

public void close();
//Just close the inputStream, without flushing.

InputStream还定义了三个方法:mark、reset、markSupported,用于支持从读过的流中重复读取。具体使用请查看API。

2.OutputStream 

An object to which we can write a sequence of bytes

基本方法:

public abstract void write(int b) throws IOException;

向流中写入一个字节,参数类型虽然是int,但实际上只会用到最低的8位。

批量写入的方法:

public void write(byte b[]) throws IOException
public void write(byte b[], int off, int len) throws IOException

writes from byte[] b into outputstream

其他方法:

public void flush() throws IOException
public void close() throws IOException

基类中flush()方法为空代码,其子类不一定重写了flush方法。

close()方法一般会先调用flush方法,然后在释放流占用的系统资源。

二)FileInputStream/FileOutputStream

FileInputStream和FileOutputStream输入源和输出目标都是文件。

1.FileOutputStream 

构造方法:

public FileOutputStream(File file, boolean append)throws FileNotFoundException
public FileOutputStream(String name) throws FileNotFoundException

file和name实际表示的都是文件路径,可以是相对路径,也可以是绝对路径。

append参数指定追加或者覆盖,true表示追加,false表示覆盖。第二个构造函数默认覆盖。

new一个FileOutputStream会打开文件,操作系统会分配相关的资源。

方法sync:

public native void sync() throws SyncFailedException;

该方法将操作系统缓存的数据写到硬盘。与flush方法区别:

flush方法只能将应用程序缓冲的数据写到操作系统。不过

一般情况下,我们不需要手工调用该方法,只要操作系统和

硬件设备没问题,数据迟早会写入硬盘。

2.FileInputStream 

主要构造方法:

public FileInputStream(String name) throws FileNotFoundException
public FileInputStream(File file) throws FileNotFoundException

new一个FileInputStream对象也会实际打开文件,操作系统会分配相关资源。

三)ByteArrayInputStream\ByteArrayOutputStream

 1.ByteArrayOutputStream 

ByteArrayOutputStream的输出目标是一个byte数组(内部数组实现),这个数组的长度是根据数据内容动态扩展的。

public ByteArrayOutputStream() //动态数组默认大小为32,如果使用过程中不够用会进行指数级扩展
public ByteArrayOutputStream(int size) //指定数组大小

转换数据的方法:

public synchronized byte[] toByteArray()
public synchronized String toString()
public synchronized String toString(String charsetName)

可以写到另一个OutputStream:

public synchronized void writeTo(OutputStream out) throws IOException
public synchronized int size() //返回当前写入的字节数
public synchronized void reset() //重置字节个数为0

2.ByteArrayInputStream 

ByteArrayInputStream将byte数组包装为一个输入流,是一种适配器模式。

public ByteArrayInputStream(byte buf[])
public ByteArrayInputStream(byte buf[], int offset, int length)

ByteArrayInputStream的所有数据都在内存,支持mark/reset读取。

四)DataInputStream/DataOutputStream

上面介绍的stream只能以字节为单位进行读写,如果要其他基本数据类型进行读写

可以使用这两个类。这两个类都是装饰类。

1.DataOutputStream  

该类是装饰基类FilterOutputStream的子类,它接受一个已有的OutputStream,

基本上将所有的操作代理给它。构造函数:

public DataOutputStream(OutputStream out)

DataOutputStream实现了DataOutput接口,该接口部分方法如下:

void writeBoolean(boolean v) throws IOException;
void writeInt(int v) throws IOException;
void writeUTF(String s) throws IOException;

在写入时会将这些类型的数据转换为二进制字节:

1)writeBoolean:写入一个字节,如果值为true,则写入1,否则写入0

2)writeUTF:将字符串的UTF-8编码写入

2.DataInputStream  

按字节读入,然后转换为相应的类型。

boolean readBoolean() throws IOException;
int readInt() throws IOException;
String readUTF() throws IOException;

五)BufferedInputStream/BufferedOutputStream

FileInputStream/FileOutputStream是没有缓冲的,按单个字节读取的效率比较低,

虽然可以按字节数组读取提高性能,但有时必须要按字节读写。怎么解决这个问题呢?

方法是将文件流包装到缓冲流中。缓冲流内部有字节数组作为缓冲区,读取时,先从

缓冲区读,读完后再调用包装的流读。构造方法:

public BufferedInputStream(InputStream in)
public BufferedInputStream(InputStream in, int size)

其中size表示缓冲区的大小,默认值为8192.例子:

DataOutputStream output = new DataOutputStream(
new BufferedOutputStream(new FileOutputStream("students.dat")));
DataInputStream input = new DataInputStream(
new BufferedInputStream(new FileInputStream("students.dat")));

三、文本文件和字符流

使用字节流处理文本文件的缺点:没有编码的概念,不能按行处理。

一)Writer/Reader

字符流的基类,是抽象类。Reader主要方法有:

public int read() throws IOException
public int read(char cbuf[]) throws IOException
abstract public void close() throws IOException
public long skip(long n) throws IOException
public boolean ready() throws IOException //与InoutStream的available方法对应

Writer的方法有:

public void write(int c)
public void write(char cbuf[])
public void write(String str) throws IOException
abstract public void close() throws IOException;
abstract public void flush() throws IOException;

二)InputStreamReader/OutputStreamWriter

这两个类是适配器类,能够将InputStream/OutputStream转换为Reader/Writer。

public OutputStreamWriter(OutputStream out, String charsetName)
public InputStreamReader(InputStream in)
public InputStreamReader(InputStream in, String charsetName)

三)FileWriter/FileReader

这两个类分别是OutputStreamWriter和InputStreamReader的子类。

public FileReader(File file) throws FileNotFoundException
public FileReader(String fileName) throws FileNotFoundException

public FileWriter(File file) throws IOException
public FileWriter(String fileName, boolean append) throws IOException

注意这两个类不能指定编码类型,只能使用默认编码。

四)BufferedReader/BufferedWriter

这两个类是装饰类,提供缓冲,及按行读写功能。

BufferedWriter构造方法:

public BufferedWriter(Writer out)
public BufferedWriter(Writer out, int sz)//sz为缓冲大小
public void newLine() throws IOException

BufferedReader构造方法:

public BufferedReader(Reader in)
public BufferedReader(Reader in, int sz)
public String readLine() throws IOException

readLine返回一行,但不会包含换行符。

五)PrintWriter

构造方法:

public PrintWriter(File file) throws FileNotFoundException
public PrintWriter(String fileName, String csn)
public PrintWriter(OutputStream out, boolean autoFlush)
public PrintWriter(Writer out)
public PrintWriter(String fileName) throws FileNotFoundException {
    this(new BufferedWriter(new OutputStreamWriter(
    new FileOutputStream(fileName))), false);
}
public PrintWriter(OutputStream out, boolean autoFlush) {
    this(new BufferedWriter(new OutputStreamWriter(out)), autoFlush);
    …
}

对于一Writer为参数的构造函数,就不会包装BufferedWriter了。

//会对参数调用String.valueOf()
public void print(int i)
public void print(Object obj)
public PrintWriter printf(String format, Object ... args)
PrintWriter writer = …
writer.format("%.2f", 123.456f);

四、文件和目录操作

一)构造方法

File既可以表示文件,也可以表示目录,他的构造方法有:

//pathname表示完整路径,可以是相对路径,也可以是绝对路径
public File(String pathname)
//parent表示父目录,child表示子目录
public File(String parent, String child)
public File(File parent, String child)

File中的路径可以是已经存在的,也可以是不存在的。通过new新建一个File对象,不会实际创建一个

文件,只是创建一个文件或者目录对象,new之后,File对象中的路径是不可变的。

二)文件元数据

相关的主要方法有:

public String getName() //返回文件或目录名,不含绝对路径
public boolean isAbsolute() //判断File中的路径是否是绝对路径
public String getPath() //返回完整路径名
public String getAbsolutePath() //返回完整的绝对路径名
//返回标准的完整路径名
public String getCanonicalPath() throws IOException
public String getParent() //返回父路径
public File getParentFile() //返回父目录的File对象
public File getAbsoluteFile()
public File getCanonicalFile() throws IOException

File类中有4个表示路径分隔符的静态变量:

public static final String separator
public static final char separatorChar
public static final String pathSeparator
public static final char pathSeparatorChar

其中,separator和separatorChar表示文件路径分隔符,在Windows系统中,一般为'\',Linux中为'/'.

pathSeparator在windows中为';',Linux中为':'。

获取文件或目录信息的方法:

public boolean exists() 
public boolean isDirectory() 
public boolean isFile()
public long length() 
public long lastModified() 
public boolean setLastModified(long time) 

文件或权限相关方法:

public boolean isHidden() 
public boolean canExecute() 
public boolean canRead() 
public boolean canWrite() 
public boolean setReadOnly() 
public boolean setReadable(boolean readable, boolean ownerOnly)
public boolean setReadable(boolean readable)
public boolean setWritable(boolean writable, boolean ownerOnly)
public boolean setWritable(boolean writable)
public boolean setExecutable(boolean executable, boolean ownerOnly)
public boolean setExecutable(boolean executable)

三)文件操作

new一个File对象不会实际创建文件,但如下方法可以:

public boolean createNewFile() throws IOException \\创建成功返回true

创建临时文件:

public static File createTempFile(String prefix, String suffix)
throws IOException
public static File createTempFile(String prefix, String suffix,
File directory) throws IOException

File类的删除方法:

public boolean delete() //如果File是目录且目录不为空,则不会删除成功
public void deleteOnExit() //将File对象添加到待删列表

重命名方法:

public boolean renameTo(File dest)

四)目录操作

创建目录:

public boolean mkdir()
public boolean mkdirs()

创建成功返回true,失败返回false。如果目录已经存在

返回值是false。这两个方法的区别在于:如果某一个中间父目录不存在,则mkdir会失败,

返回false,mkdirs则会创建该父目录。

访问目录和文件:

public String[] list()
public String[] list(FilenameFilter filter)
public File[] listFiles()

public File[] listFiles(FileFilter filter)
public File[] listFiles(FilenameFilter filter)
public interface FileFilter {
    boolean accept(File pathname);
}

public interface FilenameFilter {
    boolean accept(File dir, String name);
}

只有accept方法返回true时,才会将子目录或者文件包含到返回结果中。

posted @ 2018-12-11 10:55  Shadowplay  阅读(210)  评论(0编辑  收藏  举报