我是南城余!阿里云开发者平台专家博士证书获得者!
欢迎关注我的博客!一同成长!
一名从事运维开发的worker,记录分享学习。
专注于AI,运维开发,windows Linux 系统领域的分享!
本章节对应知识库
https://www.yuque.com/nanchengcyu/java
本内容来自尚硅谷课程,此处在知识库做了个人理解
————————————————
1. java.io.File类的使用
1.1 概述
- File类及本章下的各种流,都定义在java.io包下。
- 一个File对象代表硬盘或网络中可能存在的一个文件或者文件目录(俗称文件夹),与平台无关。(体会万事万物皆对象)
- File 能新建、删除、重命名文件和目录,但 File 不能访问文件内容本身。如果需要访问文件内容本身,则需要使用输入/输出流。
- File对象可以作为参数传递给流的构造器。
- 想要在Java程序中表示一个真实存在的文件或目录,那么必须有一个File对象,但是Java程序中的一个File对象,可能没有一个真实存在的文件或目录。
1.2 构造器
public File(String pathname)
:以pathname为路径创建File对象,可以是绝对路径或者相对路径,如果pathname是相对路径,则默认的当前路径在系统属性user.dir中存储。public File(String parent, String child)
:以parent为父路径,child为子路径创建File对象。public File(File parent, String child)
:根据一个父File对象和子文件路径创建File对象
关于路径:
- **绝对路径:**从盘符开始的路径,这是一个完整的路径。
- **相对路径:**相对于
项目目录
的路径,这是一个便捷的路径,开发中经常使用。- IDEA中,main中的文件的相对路径,是相对于"
当前工程
" - IDEA中,单元测试方法中的文件的相对路径,是相对于"
当前module
"
- IDEA中,main中的文件的相对路径,是相对于"
举例:
package com.atguigu.file;
import java.io.File;
public class FileObjectTest {
public static void main(String[] args) {
// 文件路径名
String pathname = "D:\\aaa.txt";
File file1 = new File(pathname);
// 文件路径名
String pathname2 = "D:\\aaa\\bbb.txt";
File file2 = new File(pathname2);
// 通过父路径和子路径字符串
String parent = "d:\\aaa";
String child = "bbb.txt";
File file3 = new File(parent, child);
// 通过父级File对象和子路径字符串
File parentDir = new File("d:\\aaa");
String childFile = "bbb.txt";
File file4 = new File(parentDir, childFile);
}
@Test
public void test01() throws IOException{
File f1 = new File("d:\\atguigu\\javase\\HelloIO.java"); //绝对路径
System.out.println("文件/目录的名称:" + f1.getName());
System.out.println("文件/目录的构造路径名:" + f1.getPath());
System.out.println("文件/目录的绝对路径名:" + f1.getAbsolutePath());
System.out.println("文件/目录的父目录名:" + f1.getParent());
}
@Test
public void test02()throws IOException{
File f2 = new File("/HelloIO.java");//绝对路径,从根路径开始
System.out.println("文件/目录的名称:" + f2.getName());
System.out.println("文件/目录的构造路径名:" + f2.getPath());
System.out.println("文件/目录的绝对路径名:" + f2.getAbsolutePath());
System.out.println("文件/目录的父目录名:" + f2.getParent());
}
@Test
public void test03() throws IOException {
File f3 = new File("HelloIO.java");//相对路径
System.out.println("user.dir =" + System.getProperty("user.dir"));
System.out.println("文件/目录的名称:" + f3.getName());
System.out.println("文件/目录的构造路径名:" + f3.getPath());
System.out.println("文件/目录的绝对路径名:" + f3.getAbsolutePath());
System.out.println("文件/目录的父目录名:" + f3.getParent());
}
@Test
public void test04() throws IOException{
File f5 = new File("HelloIO.java");//相对路径
System.out.println("user.dir =" + System.getProperty("user.dir"));
System.out.println("文件/目录的名称:" + f5.getName());
System.out.println("文件/目录的构造路径名:" + f5.getPath());
System.out.println("文件/目录的绝对路径名:" + f5.getAbsolutePath());
System.out.println("文件/目录的父目录名:" + f5.getParent());
}
}
注意:
无论该路径下是否存在文件或者目录,都不影响File对象的创建。
window的路径分隔符使用“\”,而Java程序中的“\”表示转义字符,所以在Windows中表示路径,需要用“\”。或者直接使用“/”也可以,Java程序支持将“/”当成平台无关的
路径分隔符
。或者直接使用File.separator常量值表示。比如:File file2 = new File(“d:” + File.separator + “atguigu” + File.separator + “info.txt”);
当构造路径是绝对路径时,那么getPath和getAbsolutePath结果一样
当构造路径是相对路径时,那么getAbsolutePath的路径 = user.dir的路径 + 构造路径
1.3 常用方法
1、获取文件和目录基本信息
- public String getName() :获取名称
- public String getPath() :获取路径
public String getAbsolutePath()
:获取绝对路径- public File getAbsoluteFile():获取绝对路径表示的文件
public String getParent()
:获取上层文件目录路径。若无,返回null- public long length() :获取文件长度(即:字节数)。不能获取目录的长度。
- public long lastModified() :获取最后一次的修改时间,毫秒值
如果File对象代表的文件或目录存在,则File对象实例初始化时,就会用硬盘中对应文件或目录的属性信息(例如,时间、类型等)为File对象的属性赋值,否则除了路径和名称,File对象的其他属性将会保留默认值。
举例:
package com.atguigu.file;
import java.io.File;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
public class FileInfoMethod {
public static void main(String[] args) {
File f = new File("d:/aaa/bbb.txt");
System.out.println("文件构造路径:"+f.getPath());
System.out.println("文件名称:"+f.getName());
System.out.println("文件长度:"+f.length()+"字节");
System.out.println("文件最后修改时间:" + LocalDateTime.ofInstant(Instant.ofEpochMilli(f.lastModified()),ZoneId.of("Asia/Shanghai")));
File f2 = new File("d:/aaa");
System.out.println("目录构造路径:"+f2.getPath());
System.out.println("目录名称:"+f2.getName());
System.out.println("目录长度:"+f2.length()+"字节");
System.out.println("文件最后修改时间:" + LocalDateTime.ofInstant(Instant.ofEpochMilli(f.lastModified()),ZoneId.of("Asia/Shanghai")));
}
}
输出结果:
文件构造路径:d:\aaa\bbb.java
文件名称:bbb.java
文件长度:636字节
文件最后修改时间:2022-07-23T22:01:32.065
目录构造路径:d:\aaa
目录名称:aaa
目录长度:4096字节
文件最后修改时间:2022-07-23T22:01:32.065
2、列出目录的下一级
- public String[] list() :返回一个String数组,表示该File目录中的所有子文件或目录。
- public File[] listFiles() :返回一个File数组,表示该File目录中的所有的子文件或目录。
package com.atguigu.file;
import org.junit.Test;
import java.io.File;
import java.io.FileFilter;
import java.io.FilenameFilter;
public class DirListFiles {
@Test
public void test01() {
File dir = new File("d:/atguigu");
String[] subs = dir.list();
for (String sub : subs) {
System.out.println(sub);
}
}
}
3、File类的重命名功能
- public boolean renameTo(File dest):把文件重命名为指定的文件路径。
4、判断功能的方法
public boolean exists()
:此File表示的文件或目录是否实际存在。public boolean isDirectory()
:此File表示的是否为目录。public boolean isFile()
:此File表示的是否为文件。- public boolean canRead() :判断是否可读
- public boolean canWrite() :判断是否可写
- public boolean isHidden() :判断是否隐藏
举例:
package com.atguigu.file;
import java.io.File;
public class FileIs {
public static void main(String[] args) {
File f = new File("d:\\aaa\\bbb.java");
File f2 = new File("d:\\aaa");
// 判断是否存在
System.out.println("d:\\aaa\\bbb.java 是否存在:"+f.exists());
System.out.println("d:\\aaa 是否存在:"+f2.exists());
// 判断是文件还是目录
System.out.println("d:\\aaa 文件?:"+f2.isFile());
System.out.println("d:\\aaa 目录?:"+f2.isDirectory());
}
}
输出结果:
d:\aaa\bbb.java 是否存在:true
d:\aaa 是否存在:true
d:\aaa 文件?:false
d:\aaa 目录?:true
如果文件或目录不存在,那么exists()、isFile()和isDirectory()都是返回true
5、创建、删除功能
public boolean createNewFile()
:创建文件。若文件存在,则不创建,返回false。public boolean mkdir()
:创建文件目录。如果此文件目录存在,就不创建了。如果此文件目录的上层目录不存在,也不创建。public boolean mkdirs()
:创建文件目录。如果上层文件目录不存在,一并创建。public boolean delete()
:删除文件或者文件夹
删除注意事项:① Java中的删除不走回收站。② 要删除一个文件目录,请注意该文件目录内不能包含文件或者文件目录。
举例:
package com.atguigu.file;
import java.io.File;
import java.io.IOException;
public class FileCreateDelete {
public static void main(String[] args) throws IOException {
// 文件的创建
File f = new File("aaa.txt");
System.out.println("aaa.txt是否存在:"+f.exists());
System.out.println("aaa.txt是否创建:"+f.createNewFile());
System.out.println("aaa.txt是否存在:"+f.exists());
// 目录的创建
File f2= new File("newDir");
System.out.println("newDir是否存在:"+f2.exists());
System.out.println("newDir是否创建:"+f2.mkdir());
System.out.println("newDir是否存在:"+f2.exists());
// 创建一级目录
File f3= new File("newDira\\newDirb");
System.out.println("newDira\\newDirb创建:" + f3.mkdir());
File f4= new File("newDir\\newDirb");
System.out.println("newDir\\newDirb创建:" + f4.mkdir());
// 创建多级目录
File f5= new File("newDira\\newDirb");
System.out.println("newDira\\newDirb创建:" + f5.mkdirs());
// 文件的删除
System.out.println("aaa.txt删除:" + f.delete());
// 目录的删除
System.out.println("newDir删除:" + f2.delete());
System.out.println("newDir\\newDirb删除:" + f4.delete());
}
}
运行结果:
aaa.txt是否存在:false
aaa.txt是否创建:true
aaa.txt是否存在:true
newDir是否存在:false
newDir是否创建:true
newDir是否存在:true
newDira\newDirb创建:false
newDir\newDirb创建:true
newDira\newDirb创建:true
aaa.txt删除:true
newDir删除:false
newDir\newDirb删除:true
API中说明:delete方法,如果此File表示目录,则目录必须为空才能删除。
1.4 练习
练习1:利用File构造器,new 一个文件目录file
1) 在其中创建多个文件和目录
2) 编写方法,实现删除file中指定文件的操作
练习2:判断指定目录下是否有后缀名为.jpg的文件。如果有,就输出该文件名称
public class FindJPGFileTest {
//方法1:
@Test
public void test1(){
File srcFile = new File("d:\\code");
String[] fileNames = srcFile.list();
for(String fileName : fileNames){
if(fileName.endsWith(".jpg")){
System.out.println(fileName);
}
}
}
//方法2:
@Test
public void test2(){
File srcFile = new File("d:\\code");
File[] listFiles = srcFile.listFiles();
for(File file : listFiles){
if(file.getName().endsWith(".jpg")){
System.out.println(file.getAbsolutePath());
}
}
}
//方法3:
/*
* File类提供了两个文件过滤器方法
* public String[] list(FilenameFilter filter)
* public File[] listFiles(FileFilter filter)
*/
@Test
public void test3(){
File srcFile = new File("d:\\code");
File[] subFiles = srcFile.listFiles(new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
return name.endsWith(".jpg");
}
});
for(File file : subFiles){
System.out.println(file.getAbsolutePath());
}
}
}
练习3:遍历指定目录所有文件名称,包括子文件目录中的文件。
拓展1:并计算指定目录占用空间的大小
拓展2:删除指定文件目录及其下的所有文件
public class ListFilesTest {
//练习3:(方式1)
public static void printSubFile(File dir) {
// 打印目录的子文件
File[] subfiles = dir.listFiles();
for (File f : subfiles) {
if (f.isDirectory()) {// 文件目录
printSubFile(f);
} else {// 文件
System.out.println(f.getAbsolutePath());
}
}
}
// //练习3:(方式2)
public void listAllSubFiles(File file) {
if (file.isFile()) {
System.out.println(file);
} else {
File[] all = file.listFiles();
// 如果all[i]是文件,直接打印
// 如果all[i]是目录,接着再获取它的下一级
for (File f : all) {
listAllSubFiles(f);// 递归调用:自己调用自己就叫递归
}
}
}
@Test
public void testListAllFiles(){
// 1.创建目录对象
File dir = new File("E:\\teach\\01_javaSE\\_尚硅谷Java编程语言\\3_软件");
// 2.打印目录的子文件
printSubFile(dir);
}
// 拓展1:求指定目录所在空间的大小
public long getDirectorySize(File file) {
// file是文件,那么直接返回file.length()
// file是目录,把它的下一级的所有file大小加起来就是它的总大小
long size = 0;
if (file.isFile()) {
size = file.length();
} else {
File[] all = file.listFiles();// 获取file的下一级
// 累加all[i]的大小
for (File f : all) {
size += getDirectorySize(f);// f的大小;
}
}
return size;
}
// 拓展2:删除指定的目录
public void deleteDirectory(File file) {
// 如果file是文件,直接delete
// 如果file是目录,先把它的下一级干掉,然后删除自己
if (file.isDirectory()) {
File[] all = file.listFiles();
// 循环删除的是file的下一级
for (File f : all) {// f代表file的每一个下级
deleteDirectory(f);
}
}
// 删除自己
file.delete();
}
}
2. IO流原理及流的分类
2.1 Java IO原理
-
Java程序中,对于数据的输入/输出操作以“
流(stream)
” 的方式进行,可以看做是一种数据的流动。 -
I/O流中的I/O是
Input/Output
的缩写, I/O技术是非常实用的技术,用于处理设备之间的数据传输。如读/写文件,网络通讯等。输入input
:读取外部数据(磁盘、光盘等存储设备的数据)到程序(内存)中。输出output
:将程序(内存)数据输出到磁盘、光盘等存储设备中。
2.2 流的分类
java.io
包下提供了各种“流”类和接口,用以获取不同种类的数据,并通过标准的方法
输入或输出数据。
-
按数据的流向不同分为:输入流和输出流。
- 输入流 :把数据从
其他设备
上读取到内存
中的流。- 以InputStream、Reader结尾
- 输出流 :把数据从
内存
中写出到其他设备
上的流。- 以OutputStream、Writer结尾
- 输入流 :把数据从
-
按操作数据单位的不同分为:字节流(8bit)和字符流(16bit)。
- 字节流 :以字节为单位,读写数据的流。
- 以InputStream、OutputStream结尾
- 字符流 :以字符为单位,读写数据的流。
- 以Reader、Writer结尾
- 字节流 :以字节为单位,读写数据的流。
-
根据IO流的角色不同分为:节点流和处理流。
-
节点流:直接从数据源或目的地读写数据
-
处理流:不直接连接到数据源或目的地,而是“连接”在已存在的流(节点流或处理流)之上,通过对数据的处理为程序提供更为强大的读写功能。
-
小结:图解
-
2.3 流的API
- Java的IO流共涉及40多个类,实际上非常规则,都是从如下4个抽象基类派生的。
(抽象基类) | 输入流 | 输出流 |
---|---|---|
字节流 | InputStream | OutputStream |
字符流 | Reader | Writer |
- 由这四个类派生出来的子类名称都是以其父类名作为子类名后缀。
常用的节点流:
- 文件流: FileInputStream、FileOutputStrean、FileReader、FileWriter
- 字节/字符数组流: ByteArrayInputStream、ByteArrayOutputStream、CharArrayReader、CharArrayWriter
- 对数组进行处理的节点流(对应的不再是文件,而是内存中的一个数组)。
常用处理流:
- 缓冲流:BufferedInputStream、BufferedOutputStream、BufferedReader、BufferedWriter
- 作用:增加缓冲功能,避免频繁读写硬盘,进而提升读写效率。
- 转换流:InputStreamReader、OutputStreamReader
- 作用:实现字节流和字符流之间的转换。
- 对象流:ObjectInputStream、ObjectOutputStream
- 作用:提供直接读写Java对象功能
3. 节点流之一:FileReader\FileWriter
3.1 Reader与Writer
Java提供一些字符流类,以字符为单位读写数据,专门用于处理文本文件。不能操作图片,视频等非文本文件。
常见的文本文件有如下的格式:.txt、.java、.c、.cpp、.py等
注意:.doc、.xls、.ppt这些都不是文本文件。
3.1.1 字符输入流:Reader
java.io.Reader
抽象类是表示用于读取字符流的所有类的父类,可以读取字符信息到内存中。它定义了字符输入流的基本共性功能方法。
public int read()
: 从输入流读取一个字符。 虽然读取了一个字符,但是会自动提升为int类型。返回该字符的Unicode编码值。如果已经到达流末尾了,则返回-1。public int read(char[] cbuf)
: 从输入流中读取一些字符,并将它们存储到字符数组 cbuf中 。每次最多读取cbuf.length个字符。返回实际读取的字符个数。如果已经到达流末尾,没有数据可读,则返回-1。public int read(char[] cbuf,int off,int len)
:从输入流中读取一些字符,并将它们存储到字符数组 cbuf中,从cbuf[off]开始的位置存储。每次最多读取len个字符。返回实际读取的字符个数。如果已经到达流末尾,没有数据可读,则返回-1。public void close()
:关闭此流并释放与此流相关联的任何系统资源。
注意:当完成流的操作时,必须调用close()方法,释放系统资源,否则会造成内存泄漏。
3.1.2 字符输出流:Writer
java.io.Writer
抽象类是表示用于写出字符流的所有类的超类,将指定的字符信息写出到目的地。它定义了字节输出流的基本共性功能方法。
public void write(int c)
:写出单个字符。public void write(char[] cbuf)
:写出字符数组。public void write(char[] cbuf, int off, int len)
:写出字符数组的某一部分。off:数组的开始索引;len:写出的字符个数。public void write(String str)
:写出字符串。public void write(String str, int off, int len)
:写出字符串的某一部分。off:字符串的开始索引;len:写出的字符个数。public void flush()
:刷新该流的缓冲。public void close()
:关闭此流。
注意:当完成流的操作时,必须调用close()方法,释放系统资源,否则会造成内存泄漏。
3.2 FileReader 与 FileWriter
3.2.1 FileReader
java.io.FileReader
类用于读取字符文件,构造时使用系统默认的字符编码和默认字节缓冲区。
FileReader(File file)
: 创建一个新的 FileReader ,给定要读取的File对象。FileReader(String fileName)
: 创建一个新的 FileReader ,给定要读取的文件的名称。
**举例:**读取hello.txt文件中的字符数据,并显示在控制台上
/**
* @author 尚硅谷-宋红康
* @create 14:09
*/
public class FileReaderWriterTest {
//实现方式1
@Test
public void test1() throws IOException {
//1. 创建File类的对象,对应着物理磁盘上的某个文件
File file = new File("hello.txt");
//2. 创建FileReader流对象,将File类的对象作为参数传递到FileReader的构造器中
FileReader fr = new FileReader(file);
//3. 通过相关流的方法,读取文件中的数据
// int data = fr.read(); //每调用一次读取一个字符
// while (data != -1) {
// System.out.print((char) data);
// data = fr.read();
// }
int data;
while ((data = fr.read()) != -1) {
System.out.print((char) data);
}
//4. 关闭相关的流资源,避免出现内存泄漏
fr.close();
}
//实现方式2:在方式1的基础上改进,使用try-catch-finally处理异常。保证流是可以关闭的
@Test
public void test2() {
FileReader fr = null;
try {
//1. 创建File类的对象,对应着物理磁盘上的某个文件
File file = new File("hello.txt");
//2. 创建FileReader流对象,将File类的对象作为参数传递到FileReader的构造器中
fr = new FileReader(file);
//3. 通过相关流的方法,读取文件中的数据
/*
* read():每次从对接的文件中读取一个字符。并将此字符返回。
* 如果返回值为-1,则表示文件到了末尾,可以不再读取。
* */
// int data = fr.read();
// while(data != -1){
// System.out.print((char)data);
// data = fr.read();
// }
int data;
while ((data = fr.read()) != -1) {
System.out.println((char) data);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//4. 关闭相关的流资源,避免出现内存泄漏
try {
if (fr != null)
fr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
//实现方式3:调用read(char[] cbuf),每次从文件中读取多个字符
@Test
public void test3() {
FileReader fr = null;
try {
//1. 创建File类的对象,对应着物理磁盘上的某个文件
File file = new File("hello.txt");
//2. 创建FileReader流对象,将File类的对象作为参数传递到FileReader的构造器中
fr = new FileReader(file);
//3. 通过相关流的方法,读取文件中的数据
char[] cbuf = new char[5];
/*
* read(char[] cbuf) : 每次将文件中的数据读入到cbuf数组中,并返回读入到数组中的
* 字符的个数。
* */
int len; //记录每次读入的字符的个数
while ((len = fr.read(cbuf)) != -1) {
//处理char[]数组即可
//错误:
// for(int i = 0;i < cbuf.length;i++){
// System.out.print(cbuf[i]);
// }
//错误:
// String str = new String(cbuf);
// System.out.print(str);
//正确:
// for(int i = 0;i < len;i++){
// System.out.print(cbuf[i]);
// }
//正确:
String str = new String(cbuf, 0, len);
System.out.print(str);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//4. 关闭相关的流资源,避免出现内存泄漏
try {
if (fr != null)
fr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
不同实现方式的类比:
3.2.2 FileWriter
java.io.FileWriter
类用于写出字符到文件,构造时使用系统默认的字符编码和默认字节缓冲区。
FileWriter(File file)
: 创建一个新的 FileWriter,给定要读取的File对象。FileWriter(String fileName)
: 创建一个新的 FileWriter,给定要读取的文件的名称。FileWriter(File file,boolean append)
: 创建一个新的 FileWriter,指明是否在现有文件末尾追加内容。
举例:
public class FWWrite {
//注意:应该使用try-catch-finally处理异常。这里出于方便阅读代码,使用了throws的方式
@Test
public void test01()throws IOException {
// 使用文件名称创建流对象
FileWriter fw = new FileWriter(new File("fw.txt"));
// 写出数据
fw.write(97); // 写出第1个字符
fw.write('b'); // 写出第2个字符
fw.write('C'); // 写出第3个字符
fw.write(30000); // 写出第4个字符,中文编码表中30000对应一个汉字。
//关闭资源
fw.close();
}
//注意:应该使用try-catch-finally处理异常。这里出于方便阅读代码,使用了throws的方式
@Test
public void test02()throws IOException {
// 使用文件名称创建流对象
FileWriter fw = new FileWriter(new File("fw.txt"));
// 字符串转换为字节数组
char[] chars = "尚硅谷".toCharArray();
// 写出字符数组
fw.write(chars); // 尚硅谷
// 写出从索引1开始,2个字符。
fw.write(chars,1,2); // 硅谷
// 关闭资源
fw.close();
}
//注意:应该使用try-catch-finally处理异常。这里出于方便阅读代码,使用了throws的方式
@Test
public void test03()throws IOException {
// 使用文件名称创建流对象
FileWriter fw = new FileWriter("fw.txt");
// 字符串
String msg = "尚硅谷";
// 写出字符数组
fw.write(msg); //尚硅谷
// 写出从索引1开始,2个字符。
fw.write(msg,1,2); // 硅谷
// 关闭资源
fw.close();
}
@Test
public void test04(){
FileWriter fw = null;
try {
//1. 创建File的对象
File file = new File("personinfo.txt");
//2. 创建FileWriter的对象,将File对象作为参数传递到FileWriter的构造器中
//如果输出的文件已存在,则会对现有的文件进行覆盖
fw = new FileWriter(file);
// fw = new FileWriter(file,false);
//如果输出的文件已存在,则会在现有的文件末尾写入数据
// fw = new FileWriter(file,true);
//3. 调用相关的方法,实现数据的写出操作
//write(String str) / write(char[] cbuf)
fw.write("I love you,");
fw.write("you love him.");
fw.write("so sad".toCharArray());
} catch (IOException e) {
e.printStackTrace();
} finally {
//4. 关闭资源,避免内存泄漏
try {
if (fw != null)
fw.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
3.2.3 小结
①
因为出现流资源的调用,为了避免内存泄漏,需要使用try-catch-finally处理异常
②
对于输入流来说,File类的对象必须在物理磁盘上存在,否则执行就会报FileNotFoundException。如果传入的是一个目录,则会报IOException异常。
对于输出流来说,File类的对象是可以不存在的。
> 如果File类的对象不存在,则可以在输出的过程中,自动创建File类的对象
> 如果File类的对象存在,
> 如果调用FileWriter(File file)或FileWriter(File file,false),输出时会新建File文件覆盖已有的文件
> 如果调用FileWriter(File file,true)构造器,则在现有的文件末尾追加写出内容。
3.3 关于flush(刷新)
因为内置缓冲区的原因,如果FileWriter不关闭输出流,无法写出字符到文件中。但是关闭的流对象,是无法继续写出数据的。如果我们既想写出数据,又想继续使用流,就需要flush()
方法了。
flush()
:刷新缓冲区,流对象可以继续使用。close()
:先刷新缓冲区,然后通知系统释放资源。流对象不可以再被使用了。
注意:即便是flush()方法写出了数据,操作的最后还是要调用close方法,释放系统资源。
举例:
public class FWWriteFlush {
//注意:应该使用try-catch-finally处理异常。这里出于方便阅读代码,使用了throws的方式
@Test
public void test() throws IOException {
// 使用文件名称创建流对象
FileWriter fw = new FileWriter("fw.txt");
// 写出数据,通过flush
fw.write('刷'); // 写出第1个字符
fw.flush();
fw.write('新'); // 继续写出第2个字符,写出成功
fw.flush();
// 写出数据,通过close
fw.write('关'); // 写出第1个字符
fw.close();
fw.write('闭'); // 继续写出第2个字符,【报错】java.io.IOException: Stream closed
fw.close();
}
}
4. 节点流之二:FileInputStream\FileOutputStream
如果我们读取或写出的数据是非文本文件,则Reader、Writer就无能为力了,必须使用字节流。
4.1 InputStream和OutputStream
4.1.1 字节输入流:InputStream
java.io.InputStream
抽象类是表示字节输入流的所有类的超类,可以读取字节信息到内存中。它定义了字节输入流的基本共性功能方法。
public int read()
: 从输入流读取一个字节。返回读取的字节值。虽然读取了一个字节,但是会自动提升为int类型。如果已经到达流末尾,没有数据可读,则返回-1。public int read(byte[] b)
: 从输入流中读取一些字节数,并将它们存储到字节数组 b中 。每次最多读取b.length个字节。返回实际读取的字节个数。如果已经到达流末尾,没有数据可读,则返回-1。public int read(byte[] b,int off,int len)
:从输入流中读取一些字节数,并将它们存储到字节数组 b中,从b[off]开始存储,每次最多读取len个字节 。返回实际读取的字节个数。如果已经到达流末尾,没有数据可读,则返回-1。public void close()
:关闭此输入流并释放与此流相关联的任何系统资源。
说明:close()方法,当完成流的操作时,必须调用此方法,释放系统资源。
4.1.2 字节输出流:OutputStream
java.io.OutputStream
抽象类是表示字节输出流的所有类的超类,将指定的字节信息写出到目的地。它定义了字节输出流的基本共性功能方法。
public void write(int b)
:将指定的字节输出流。虽然参数为int类型四个字节,但是只会保留一个字节的信息写出。public void write(byte[] b)
:将 b.length字节从指定的字节数组写入此输出流。public void write(byte[] b, int off, int len)
:从指定的字节数组写入 len字节,从偏移量 off开始输出到此输出流。public void flush()
:刷新此输出流并强制任何缓冲的输出字节被写出。public void close()
:关闭此输出流并释放与此流相关联的任何系统资源。
说明:close()方法,当完成流的操作时,必须调用此方法,释放系统资源。
4.2 FileInputStream 与 FileOutputStream
4.2.1 FileInputStream
java.io.FileInputStream
类是文件输入流,从文件中读取字节。
FileInputStream(File file)
: 通过打开与实际文件的连接来创建一个 FileInputStream ,该文件由文件系统中的 File对象 file命名。FileInputStream(String name)
: 通过打开与实际文件的连接来创建一个 FileInputStream ,该文件由文件系统中的路径名 name命名。
举例:
//read.txt文件中的内容如下:
abcde
读取操作
public class FISRead {
//注意:应该使用try-catch-finally处理异常。这里出于方便阅读代码,使用了throws的方式
@Test
public void test() throws IOException {
// 使用文件名称创建流对象
FileInputStream fis = new FileInputStream("read.txt");
// 读取数据,返回一个字节
int read = fis.read();
System.out.println((char) read);
read = fis.read();
System.out.println((char) read);
read = fis.read();
System.out.println((char) read);
read = fis.read();
System.out.println((char) read);
read = fis.read();
System.out.println((char) read);
// 读取到末尾,返回-1
read = fis.read();
System.out.println(read);
// 关闭资源
fis.close();
/*
文件内容:abcde
输出结果:
a
b
c
d
e
-1
*/
}
@Test
public void test02()throws IOException{
// 使用文件名称创建流对象
FileInputStream fis = new FileInputStream("read.txt");
// 定义变量,保存数据
int b;
// 循环读取
while ((b = fis.read())!=-1) {
System.out.println((char)b);
}
// 关闭资源
fis.close();
}
@Test
public void test03()throws IOException{
// 使用文件名称创建流对象.
FileInputStream fis = new FileInputStream("read.txt"); // 文件中为abcde
// 定义变量,作为有效个数
int len;
// 定义字节数组,作为装字节数据的容器
byte[] b = new byte[2];
// 循环读取
while (( len= fis.read(b))!=-1) {
// 每次读取后,把数组变成字符串打印
System.out.println(new String(b));
}
// 关闭资源
fis.close();
/*
输出结果:
ab
cd
ed
最后错误数据`d`,是由于最后一次读取时,只读取一个字节`e`,数组中,
上次读取的数据没有被完全替换,所以要通过`len` ,获取有效的字节
*/
}
@Test
public void test04()throws IOException{
// 使用文件名称创建流对象.
FileInputStream fis = new FileInputStream("read.txt"); // 文件中为abcde
// 定义变量,作为有效个数
int len;
// 定义字节数组,作为装字节数据的容器
byte[] b = new byte[2];
// 循环读取
while (( len= fis.read(b))!=-1) {
// 每次读取后,把数组的有效字节部分,变成字符串打印
System.out.println(new String(b,0,len));// len 每次读取的有效字节个数
}
// 关闭资源
fis.close();
/*
输出结果:
ab
cd
e
*/
}
}
4.2.2 FileOutputStream
java.io.FileOutputStream
类是文件输出流,用于将数据写出到文件。
public FileOutputStream(File file)
:创建文件输出流,写出由指定的 File对象表示的文件。public FileOutputStream(String name)
: 创建文件输出流,指定的名称为写出文件。public FileOutputStream(File file, boolean append)
: 创建文件输出流,指明是否在现有文件末尾追加内容。
举例:
package com.atguigu.fileio;
import org.junit.Test;
import java.io.FileOutputStream;
import java.io.IOException;
public class FOSWrite {
//注意:应该使用try-catch-finally处理异常。这里出于方便阅读代码,使用了throws的方式
@Test
public void test01() throws IOException {
// 使用文件名称创建流对象
FileOutputStream fos = new FileOutputStream("fos.txt");
// 写出数据
fos.write(97); // 写出第1个字节
fos.write(98); // 写出第2个字节
fos.write(99); // 写出第3个字节
// 关闭资源
fos.close();
/* 输出结果:abc*/
}
@Test
public void test02()throws IOException {
// 使用文件名称创建流对象
FileOutputStream fos = new FileOutputStream("fos.txt");
// 字符串转换为字节数组
byte[] b = "abcde".getBytes();
// 写出从索引2开始,2个字节。索引2是c,两个字节,也就是cd。
fos.write(b,2,2);
// 关闭资源
fos.close();
}
//这段程序如果多运行几次,每次都会在原来文件末尾追加abcde
@Test
public void test03()throws IOException {
// 使用文件名称创建流对象
FileOutputStream fos = new FileOutputStream("fos.txt",true);
// 字符串转换为字节数组
byte[] b = "abcde".getBytes();
fos.write(b);
// 关闭资源
fos.close();
}
//使用FileInputStream\FileOutputStream,实现对文件的复制
@Test
public void test05() {
FileInputStream fis = null;
FileOutputStream fos = null;
try {
//1. 造文件-造流
//复制图片:成功
// fis = new FileInputStream(new File("pony.jpg"));
// fos = new FileOutputStream(new File("pony_copy1.jpg"));
//复制文本文件:成功
fis = new FileInputStream(new File("hello.txt"));
fos = new FileOutputStream(new File("hello1.txt"));
//2. 复制操作(读、写)
byte[] buffer = new byte[1024];
int len;//每次读入到buffer中字节的个数
while ((len = fis.read(buffer)) != -1) {
fos.write(buffer, 0, len);
// String str = new String(buffer,0,len);
// System.out.print(str);
}
System.out.println("复制成功");
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
//3. 关闭资源
try {
if (fos != null)
fos.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
try {
if (fis != null)
fis.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
4.3 练习
**练习:**实现图片加密操作。
提示:
/**
* @author 尚硅谷-宋红康
* @create 8:59
*/
public class FileSecretTest {
/*
* 图片的加密
* */
@Test
public void test1(){
FileInputStream fis = null;
FileOutputStream fos = null;
try {
File file1 = new File("pony.jpg");
File file2 = new File("pony_secret.jpg");
fis = new FileInputStream(file1);
fos = new FileOutputStream(file2);
//方式1:每次读入一个字节,效率低
// int data;
// while((data = fis.read()) != -1){
// fos.write(data ^ 5);
// }
//方式2:每次读入一个字节数组,效率高
int len;
byte[] buffer = new byte[1024];
while((len = fis.read(buffer)) != -1){
for(int i = 0;i < len;i++){
buffer[i] = (byte) (buffer[i] ^ 5);
}
fos.write(buffer,0,len);
}
System.out.println("加密成功");
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/*
* 图片的解密
* */
@Test
public void test2(){
FileInputStream fis = null;
FileOutputStream fos = null;
try {
File file1 = new File("pony_secret.jpg");
File file2 = new File("pony_unsecret.jpg");
fis = new FileInputStream(file1);
fos = new FileOutputStream(file2);
//方式1:每次读入一个字节,效率低
// int data;
// while((data = fis.read()) != -1){
// fos.write(data ^ 5);
// }
//方式2:每次读入一个字节数组,效率高
int len;
byte[] buffer = new byte[1024];
while((len = fis.read(buffer)) != -1){
for(int i = 0;i < len;i++){
buffer[i] = (byte) (buffer[i] ^ 5);
}
fos.write(buffer,0,len);
}
System.out.println("解密成功");
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}