本文的首要内容有以下几点
- File类介绍
- 传统IO流介绍
- 传统IO流常见的文件操作
- 规范流
- NIO
Paths
和Files
东西类的介绍和文件操作
Java中的File类
在Java中,File
类是对文件和文件夹的笼统,因而能够用一个File
实例来表示一个文件或许文件夹。如在E:\java-io-file\cat.txt
文件夹中存在cat.txt
文件。,
如下代码就能看见Java对文件和文件夹的笼统,便能够运用封装好的API得到一些文件信息。
public static void main(String[] args) {
File diction = new File("E:\java-io-file");
System.out.println(diction.isDirectory()); // true
File file = new File("E:\java-io-file\cat.txt");
System.out.println(file.isDirectory()); // false
System.out.println(file.isFile()); // true
}
File
类常见的api
//获取类功用
- public String getAbsolutePath(); //获取绝对途径
- public String getPath(); //获取相对途径
- public String getName(); //获取文件名
- public String getParent(); //获取上级目录
- public int length(); //长度 字节
- public long lastModeified(); //最后一次修正的时刻 //以下适用于文件目录
- public String [] list(); //获取指定途径下的文件或许文件目录的目录称号数组
- public File[] listFiles();//获取指定目录下的一切文件或文件目录的File数组
//file1.renameTo(file2) file1有必要存在,file2不能存在 才能回来true- public boolean renameTo(); //File 类 判别
- public boolean isDirectory();
- public boolean isFile();
- public boolean exists();
- public boolean canRead();
- public boolean canWrite();
- public boolean isHidden(); //File 创立
- public boolean createNewFile(); //创立文件
- public boolean mkdir(); //创立文件目录 假如上级目录不存在 则不创立
- public boolean mkdirs(); //创立文件目录 假如上级目录不存在,则一同创立。
- public boolean delete();//删去 不走回收站
递归拜访文件和文件夹的完成
在有了上述根本的认识之后,就能够运用这些知识做到递归拜访文件
// recursiveVisitedFiles("E:\springboot-websocket");
public void recursiveVisitedFiles(String path){
File root = new File(path);
if (root.isDirectory()){
System.out.println("directoryName = "+root.getName());
File[] subFiles = root.listFiles();
for (File file : subFiles){
recursiveVisitedFiles(file.getAbsolutePath());
}
}else {
System.out.println("fileName = " + root.getName());
}
}
得到如下输出
File
类中值得注意的是
delete()
: 由于Java是把文件和文件夹都笼统为一个File实例,因而在调用该办法删去文件夹时,需求确保文件夹为为空时,才能正确删去,否则回来false
.
传统的流式IO
在Java1.4
曾经,Java中规划的IO比较复杂。Java的IO流规划屏蔽了实际的i/o设备中处理的细节,其原则如下
- 字节省对应原生的二进制数据。
- 字符流对应字符数据,主动处理与本地字符集之间的转化。
- 缓冲流能够提高读写性能,经过减少底层
API
的次数来优化IO。
规划原则
在JDK1.0时,一切与输入有关系的类都承继于InputStream
, 一切与输出相关的类都承继自OutputStream
。Java1.1 对根本的IO流类库做了许多的修正,添加了许多以Reader/writer
为基类的衍生类,Reader和Writer
的承继体系首要是为了国际化。旧的IO承继体系仅支撑8bit的字节省,不能够很好的处理16bit的Unicode字符。
一切InputStream/reader
派生而来的类都含有read()
办法,用来读取单个字节或许字节数组。
一切OutputStream/Writer
派生而来的类都含有write()
办法,用来写单个字节或许字节数组。
字节省能够操作一切类型的文件。
- read()办法能够读取单个字节(字符)也能够读取多个字节(字符),读取单个字节(字符)时,回来的是当时字节,
- read(array)时,回来的是读取的字节(字符)数
- -1 都表示读到文件结尾, 读完了整个文件。
输入流和输出流
InputStream/OutputStream
基于字节省的。
InputStream
作为输入流,是从外部
获取数据到内存中,因而输入流类型有
类 | 功用 |
---|---|
ByteArrayInputStream |
允许将内存的缓冲区作为InputStream
|
StringBufferInputStream |
将String 转化成InputStream
|
FileInputStream |
从文件中读取信息 |
PipedInputStream |
产生用于写入相关 PipedOutputStream 的数据。 |
SequenceInputStream |
将两个或多个 InputStream 目标转化成一个 InputStream
|
OutputStream
做为输出流,该类别的类首要决定输出到哪里:文件,字节数组等
类 | 功用 |
---|---|
ByteArrayOutputStream |
在内存中创立缓冲区,一切送往流 的数据都要放置在此缓冲区。 |
FileOutputStream |
用于将信息写入文件 |
PipedOutputStream |
任何写入其中的信息都会主动作为相关 PipedInputStream 的输出 |
以字节省演示:仿制文件
// String source = "E:\java-io-file\cat.png";
// String dest1 = "E:\java-io-file\cat1.png";;
// copyFileWithStream(source,dest1);
public void copyFileWithStream(String source,String dest){
File sourceFile = new File(source);
File destFile = new File(dest);
FileInputStream fis = null;
FileOutputStream fos = null;
try {
fis = new FileInputStream(sourceFile);
fos = new FileOutputStream(destFile);
int len = 0;
byte[] bytes = new byte[1024];
while ((len = fis.read(bytes)) != -1){
fos.write(bytes,0,len);
}
fos.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
fos.close();
fis.close();
}
仿制成果如下:
字符流Writer和Reader
字符流首要针对的是文本文件,假如运用字符流进行图片的仿制,则会导致仿制生成的文件打不开!
字符流的引进首要是首要是为了国际化Unicode
, 旧的IO流不能够很好的处理16bit的字符。新的类库的IO操作比旧类库要快。
除此之外,有时候也能够将面向字节省的操作转化为面向字符流的操作如以下
字节省 | 字符流适配 |
---|---|
InputStream | InputStreamReader |
OutputStream | OutputStreamWriter |
FileInputStream | FileReader |
FileOutputStream | FileWriter |
ByteArrayInputStream | CharArrayReader |
ByteArrayOutputStream | CharArrayWriter |
还有许多其他的列别,未罗列全,请参阅JDK相关文档
以字符流为例:演示仿制文本文件
// copyFileWithChar("E:\java-io-file\cat.txt","E:\java-io-file\cat_.txt");
public void copyFileWithChar(String source,String dest){
try {
FileReader reader = new FileReader(source);
FileWriter fileWriter = new FileWriter(dest);
int len = 0;
char[] chars = new char[1024];
while ((len = reader.read(chars)) != -1){
fileWriter.write(chars,0,len);
}
fileWriter.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
演示成果如下:
缓冲流
为了IO流的操作速度,Java供给了缓冲流。缓冲流作用于已有流的基础上如FileInputStream/FileReader
缓冲流 | 被缓冲的流 |
---|---|
BufferedReader | FileReader |
BufferefWriter | BufferedWriter |
BufferedInputStream | FileInputStream |
BufferedOutputStream | FileOutputStream |
// String dest3 = "E:\java-io-file\cat3.png";
// String source = "E:\java-io-file\cat.png";
// copyFileWithBufferStream(source,dest3);
public void copyFileWithBufferStream(String source, String dest) {
try (FileInputStream fis = new FileInputStream(source);
FileOutputStream fos = new FileOutputStream(dest);
BufferedInputStream bis = new BufferedInputStream(fis);
BufferedOutputStream bos = new BufferedOutputStream(fos)) {
int len = 0;
byte[] chars = new byte[1024];
while ((len = bis.read(chars)) != -1) {
bos.write(chars, 0, len);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
其他流
RandomAccessFile类:
RandomAccessFile
适用于由巨细已知的记载组成的文件,所以咱们能够运用seek()
将文件指针从一条记载移动到另一条记载,然后对记载进行读取和修正。用得不多。
- 直接承继java.lang.Object类 完成了DataInput, DataOutput接口
- 即可输入也可输出
ObjectOutputStream / ObjectInputStream
前者将Java目标序列化到磁盘中或许经过网络传出去,后者将前者转化为Java目标
需求阐明的是:
- 有必要完成Serializable接口
- 类的一切属性属性有必要可序列化,默许情况下根本数据类型是可序列化的。
- 供给静态常量 serialVersionUID;
规范流
程序的一切输入都能够来自于规范输入,程序的一切输出都能够流向规范输出,程序的一切过错都能够发送到规范过错。
Java供给以下规范流
System.in
: 规范输入流System.out
: 规范输出流System.err
: 规范过错
规范输出流和规范过错流现已被预先包装为PrintStream
目标,因而能够直接运用,而规范输入流是原生的InputStream
,所以在读取规范输入流的内容时,需求将其转化包装为其他流。
// System源码
public static final InputStream in = null;
public static final PrintStream out = null;
public static final PrintStream err = null;
规范输入流结合Scanner
读取用户输入:
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("准备输入");
do {
String next = scanner.next();
System.out.println("读取用户输入:"+next);
} while (scanner.hasNext());
scanner.close();
}
注:Scanner.next()
默许以空格分隔.
NIO
NIO是Java1.4引进的,以同步非堵塞的办法重写了老的IO。
在此之前,咱们处理IO的办法根本上都是以字节或许字符。如InputStream/OutputStream
或许FileReader/FileWriter
体系,在NIO
中不需求这么底层的操作。通常是和各种Buffer目标,如
- ByteBuffer: 字节缓冲区
- DoubleBuffer
- ShortBuffer
- LongBuffer
- IntBuffer
- FloatBuffer
- CharBuffer
xxxBuffer
目标都是都相应类型的一个封装,在上面所罗列的目标中,首要运用的是ByteBuffer
目标。
nio中运用了更挨近操作体系IO履行办法的结构:Channel和Buffer
只有上述的这一类型可直接与Channel
通道交互。
而旧IO中的FileInputStream、FileOutputStream、RandomAccessFile
被更新成FileChannel。而字符形式的Writer/Reader
不能生成Channel,但Channel相关的类具有生成Reader和Writer的办法
public void bufferTest() {
try {
FileChannel out = new FileOutputStream("data.txt").getChannel();
FileChannel in = new FileInputStream("data.txt").getChannel();
out.write(ByteBuffer.wrap("Yierisacat".getBytes())); // 生成data.txt 文件内容是Yierisacat
ByteBuffer buffer = ByteBuffer.allocate(1024);
in.read(buffer);
buffer.flip();
while (buffer.hasRemaining())
System.out.write(buffer.get()); // output: Yierisacat
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
注:Java NIO 的ByteBuffer API内容较多且规划得比较复杂,这儿仅仅简略提及一下。
Files和Paths类的运用
经过前文的简略描绘,也能够发现在进行文件IO操作时,需求运用到许多的类,创立许多的目标。Java的规划者为了便利相关操作,引进了Files东西类,对常见的文件操作供给了封装办法,如创立、删去、判别文件是否存在,可读可写、遍历等
- Files.createFile()
- Files.createDirectory()
- FIles.createDirectories()
- Files.delete()
- Files.deleteIfExist()
- Files.copy()
- Files.exists()
- Files.walkFileTree()
Files的大多数办法都需求传递一个Path
目标作为参数,因而在运用之前,需求先把Path目标弄理解。
一个Path目标表示一个文件或许目录的途径,是一个跨操作体系和文件体系的笼统。Java的规划者供给了Paths东西类对相关操作进行了封装。其中供给了一个静态办法用于获取Path
实例
public static Path get(URI uri) {
return Path.of(uri);
}
public static Path get(String first, String... more) {
return Path.of(first, more);
}
get
办法能够承受一系列String
字符串或一个统一资源标识符作为参数。回来一个Path
目标。
Path
目标封装了对途径的相关操作,如增添,删去部分途径,判别途径以什么最初或许结尾
- getNameCount(),回来途径的个数
- getName(int index): 回来index处的称号
- getRoot()
- getParent()
- startsWith()
- endsWith()
public void testPaths(){
String p = "E:\java-io-file\cat.png";
Path path = Paths.get(p);
System.out.println("getNameCount = "+path.getNameCount());
System.out.println("getName(1) = "+path.getName(1));
System.out.println("getRoot = "+ path.getRoot() );
System.out.println("getParent = "+path.getParent());
System.out.println("start with [E] ="+ path.startsWith("E"));
System.out.println("start with [E:] ="+ path.startsWith("E:"));
System.out.println("start with [E:\] ="+ path.startsWith("E:\"));
System.out.println("endsWith cat ="+path.endsWith("cat"));
System.out.println("endsWith .png ="+path.endsWith(".png"));
System.out.println("endsWith cat.png ="+path.endsWith("cat.png"));
System.out.println("parent end with [java-o-file] = "+ path.getParent().endsWith("java-o-file"));
System.out.println("parent end with [java-o-file\] = "+ path.getParent().endsWith("java-io-file\"));
}
需求阐明的是startWith、endsWith
这两个办法比较的是当时部分途径的全部途径,请注意赤色框出的部分。
Path
接口还供给了resolve()
办法增添尾途径和relativize()
将绝对途径转化为相对途径
Path path1 = Paths.get("E:\java-io-file\");
System.out.println(path1.resolve("gus"));
System.out.println("上一级:"+path1.resolve("gus").relativize(Paths.get("E:\java-io-file\")));
System.out.println("同等级:"+path1.resolve("gus").relativize(Paths.get("E:\java-io-file\gus\")));
System.out.println("下一等级:"+path1.resolve("gus").relativize(Paths.get("E:\java-io-file\gus\next")));
注:gus文件夹并不存在,Path
办法许多,其他办法参请看源码。
演示了Path
的根本API,接下来看一下啊Files循环遍历。
try {
Files.walkFileTree(Paths.get("E:\BrowserDownLoad\GoogleChromeDownLoad"), new SimpleFileVisitor<>() {
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
System.out.println("进入文件夹 dirName:" + dir.getFileName() + " 之前");
return super.preVisitDirectory(dir, attrs);
}
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
System.out.println("拜访当时文件 fileName = " + file.getFileName());
return super.visitFile(file, attrs);
}
@Override
public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException {
System.out.println("拜访失利");
return super.visitFileFailed(file, exc);
}
@Override
public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
System.out.println("拜访文件夹:" + dir.getFileName() + " 结束");
return super.postVisitDirectory(dir, exc);
}
});
} catch (IOException e) {
e.printStackTrace();
}
部分成果截图如下:
java.nio.file.SimpleFileVisitor
供给了一切办法的默许完成。这样,在咱们的匿名内部类中,咱们只需求重写非规范行为的办法:visitFile() 和 postVisitDirectory() 完成删去文件和删去目录
总结
前文中的传统流式IO也好或许规范IO也罢。在实际工作中都用得不多,毕竟没有那个公司的事务需求你从控制台输入指令或许操作。Java中的文件操作如读取或许删去等操作优先考虑Files、Paths
相关类的运用。
别的无论是输入流仍是输出流,只需理解了输入是从其他地方读入内存,输出是从内存写
到其他地方。挑选什么样的流,或许什么样的API去完成这个过程就简略明了了。
参阅资料
- on Java 8 中文版