十一、File 和 Io 流
File是java.io.包下的类,File类的对象, 用于代表当前操作系统的文件(可以是文件、或文件夹)
注意: File类 只能对文件本身进行操作,不能读写文件里面存储的数据。
IO流 用于读写文件里的数据(可以读写文件,或网络中的数据...)
1、File类
1.1 File对象的创建
创建File类的对象(构造器) | 说明 |
---|---|
public File( String pathname ) | 根据文件路径创建文件对象 |
public File( String parent , String child ) | 根据父路径和子路径名创建文件对象 |
publuc File (File parent , String child ) | 根据父路径对应文件对象和子路径名字创建文件对象 |
注意:
File对象既可以代表文件、也可以代表文件夹。
文件路径根据不同的操作系统可以使用(正斜杠/、双反斜杠\\)。
File封装的对象仅仅是一个路径名, 这个路径可以是存在的,也允许是不存在的。
File.separator
:会根据不同操作系统来获取对应的分割符获取分割符
java
public class FileTest {
public static void main(String args[]){
//双反斜杠,代表一个反斜杠(是一个转义字符)
File f1=new File("C:\\Users\\永恒之月\\Desktop\\ab.xlsx");
//正斜杠
File f2=new File("C:/Users/永恒之月/Desktop/ab.xlsx");
//File.separator(会根据不同操作系统来获取对应的分割符获取分割符)
File f3=new File("C:"+File.separator+"Users"+File.separator+"永恒之月/Desktop"+File.separator+"ab.xlsx");
//通常我们在项目中使用的都是这种不带盘符的相对路径
File f4=new File("object-app\\src\\akc4\\abc.txt");
//也可以是一个文件夹(如果获取大小,只能获取文件夹本身的大小,而不包含里面的文件)
File f5=new File("object-app\\src\\akc4");
//获取文件大小(如果文件不存在,获取的大小为0)
System.out.println(f1.length());
//获取文件的名称
System.out.println(f2.getName());
System.out.println(f3.getName());
System.out.println(f4.length());
System.out.println(f5.length());
}
}
1.2、File类常用方法
File提供的判断文件类型、获取文件信息功能。
方法名称 | 说明 |
---|---|
public boolean exists( ) | 判断当前文件对象(文件或目录),对应的文件路径是否存在,存在返回true |
public boolean isFile() | 判断当前文件对象指代的是否是文件,是文件返回true,反之。 |
public boolean isDirectory() | 判断当前文件对象指代的是否是文件夹,是文件夹返回true,反之。 |
public String getName() | 获取文件的名称(包含后缀名) |
public long lenght() | 获取文件大小,返回字节个数 |
public long lastModified() | 获取文件的最后修改时间 |
public String getPath() | 获取创建文件对象时,使用的路径 |
public String getAbsolutePath() | 获取绝对路径 |
public String getParent() | 获取父目录对应的路径 |
java
public class FileTest {
public static void main(String args[]){
File f1=new File("object-app\\src\\akc4\\abc.txt");
File f2=new File("object-app\\src\\akc4");
//判断文件或目录是否存在
System.out.println(f1.exists()); //true
//获取文件大小(字节)
System.out.println(f1.length()); //5
//获取文件名(包含后缀名)
System.out.println(f1.getName()); //abc.txt
//判断是不是一个文件
System.out.println(f1.isFile()); //true
//判断是不是一个目录
System.out.println(f2.isDirectory()); //true
//获取创建文件对象时的路径(绝对路径或者相对路径,要看创建时使用的路径)
System.out.println(f1.getPath()); //object-app\src\akc4\abc.txt
//获取文件最后修改的时间(时间毫秒值)
long time=f1.lastModified();
System.out.println(time); //1705418848044
SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println(sdf.format(time)); //2024-01-16 23:27:28
//获取文件的绝对路径
System.out.println(f1.getAbsolutePath());
}
}
File提供的创建文件或删除文件文件常用方法
方法名 | 说明 |
---|---|
public boolean createNewFile() | 创建一个新文件(文件内容为空),创建成功返回true,反之。 |
public boolean mkdir() | 用户创建文件夹,注意:只能创建一级文件夹 |
public boolean mkdirs() | 用于创建文件夹,注意:可以创建多级文件夹 |
public boolean delete() | 删除文件,或者空文件夹,注意:不能删除非空文件夹 |
java
public class FileTest {
public static void main(String args[]) throws IOException{
//创建3个,文件和目录不存在的文件对象
File f1=new File("object-app\\src\\akc4\\cc.txt");
File f2=new File("object-app\\src\\akc4\\cc");
File f3=new File("object-app\\src\\akc4\\aa\\bb\\cc");
//判断文件是否存在
System.out.println(f1.exists()); //false
//创建cc.txt文件(注意要抛异常),如果文件已经存在会创建失败
System.out.println(f1.createNewFile()); //true
//创建目录cc
System.out.println(f2.mkdir()); //true
//创建多级目录aa/bb/cc
System.out.println(f3.mkdirs()); //true
//删除文件cc.txt
System.out.println(f1.delete()); //true
//因为是多级目录所以只能删除最后一个空目录cc
System.out.println(f3.delete()); //true
}
}
File提供的遍历文件夹的方法。
方法名 | 说明 |
---|---|
public String[ ] list( ) | 获取当前目录下所有的"一级文件名称"到一个字符串数组中去返回 |
public File[ ] listFiles( ) | (重点)获取当前目录下所有的"一级文件对象"到一个文件对象数组中去返回 |
使用 listFiles 方法时的注意事项
当主调是文件,或者路径不存在时,返回null。
当主调是空文件夹时,返回一个长度为0的数组。
当主调是一个有内容的文件夹时,将里面所有一级文件和文件夹的路径放在File数组中返回
当主调是一个文件夹,且里面有隐藏文件时,将里面所有文件和文件夹的路径放在File数组中返回,包含隐藏文件。
当主调是一个文件夹,但是没有权限访问该文件夹时,返回null。
java
public class FileTest {
public static void main(String args[]) throws IOException{
//创建1个不存在的目录文件对象
File f1=new File("object-app\\src\\akc4\\aa");
System.out.println(f1.mkdir());
//在创建的aa的目录创建3个文件
File f2 =new File(f1.getPath()+File.separator+"小明.txt");
File f3 =new File(f1.getPath()+File.separator+"小花.txt");
File f4 =new File(f1.getPath()+File.separator+"小强.txt");
f2.createNewFile();
f3.createNewFile();
f4.createNewFile();
//获取指定目录下的所以文件名(返回值为数值)
String[] fileNames=f1.list();
System.out.println(Arrays.toString(fileNames)); //[小强.txt, 小明.txt, 小花.txt]
//获取指定目录下的所以文件对象(返回值为数值),可以对文件对象进行操作
File[] fileObjs=f1.listFiles();
System.out.println(Arrays.toString(fileObjs)); //[object-app\src\akc4\aa\小强.txt, object-app\src\akc4\aa\小明.txt, object-app\src\akc4\aa\小花.txt]
System.out.println(fileObjs[0].length()); //0
System.out.println(fileObjs[1].getName()); //小明.txt
}
}
2、前置知识:递归
定义:递归是一种算法,在程序设计语言中广泛应用。从形式_上说,方法调用自身的形式称为方法递归( recursion) 。
递归的形式:
- 直接递归:方法自己调用自己。
- 间接递归:方法调用其他方法,其他方法又回调方法自己。
注意:递归如果没有控制好终止,会出现递归死循环,导致栈内存溢出错误。
java
public class Recursion {
public static void main(String[] args) {
test1();
}
//直接递归:自己调用自己,会报栈内存溢出错误( java.lang.StackOverflowError)
public static void test1(){
System.out.println("test1");
test1();
}
//间接递归:两个方法相互调用也会报栈内存溢出错误
public static void test2(){
System.out.println("test2");
test3();
}
public static void test3(){
test2();
}
}
2.1 示例:阶乘
通过递归的方式求某个数的阶乘
java
public class Recursion {
public static void main(String[] args) {
System.out.println(res(5)); //120
}
//通过递归的方式求阶乘
public static int res(int n){
if(n==1){
return 1;
}else {
System.out.println(n);
return res(n-1)*n;
}
}
}
2.3 示例:猴子吃桃
猴子第一天摘下若干桃子,当即吃了一半,觉得好不过瘾,于是又多吃了一个
第二天又吃了前天剩余桃子数量的一半,觉得好不过瘾,于是又多吃了一个
以后每天都是吃前天剩余桃子数量的一半,觉得好不过瘾,又多吃了一个
等到第10天的时候发现桃子只有1个了。请问猴子第一天摘了多少个桃子?
java
public class Recursion {
public static void main(String[] args) {
//目标:猴子吃桃问题、
// f(10) = 1
//公式: f(x) - f(x)/2-1=f(x+1) 前一天的桃子总数减前一天的桃子总数的一半再减一个=后一天的桃子数量
//变形: 2f(x) - f(x) - 2 = 2f(x+1)
//变形2:f(x)=2f(x+1)+2
System.out.println(EatPeach(1)); //第一天的桃子数: 1534
System.out.println(EatPeach(2)); //第二天的桃子数: 766
System.out.println(EatPeach(10)); //第10天的桃子数:1
}
public static int eatPeach(int n){
if(n==10){
return 1;
}else {
return 2*EatPeach(n+1)+2;
}
}
}
2.4 示例:文件搜索
需求:查找到某个对应名字的文件,找到后并输出其绝对路径,可以打开。
分析:
① 先找出D:盘下的所有一级文件对象。
② 遍历全部一级文件对象,判断是否是文件。
③ 如果是文件,判断是否是自己想要的。
④ 如果是文件夹,需要继续进入到该文件夹,重复.上述过程。
java
public class SearchFile {
public static void main(String[] args) throws IOException {
//调用查询方法,传入文件夹文件对象,和要查询的文件名
serchFile(new File("D:\\Socials"),"QQ.exe");
}
//传入文件夹(路径)和要查找的文件名
public static void serchFile(File dir,String fileName) throws IOException {
if(!dir.exists() || dir.isFile() || dir==null){
return;
}
//获取文件夹里面的所有一级文件夹和文件,存入数组中中
File[] fileArray=dir.listFiles();
//判断是不是空文件夹或者值为空
if(fileArray.length>0 && fileArray!=null){
for (File file : fileArray) {
//判断找到的是文件还是文件夹。如果是文件。就判断文件名
if(file.isFile() && file.getName().equals("QQ.exe")){
//输出绝对路径
System.out.println(file.getAbsolutePath());
//创建一个Runtime对象
Runtime runtime=Runtime.getRuntime();
//运行该文件,传入绝对路径
runtime.exec(file.getAbsolutePath());
}else if(file.isDirectory()){
//如果是文件夹,就通过递归调用,来继续查找
serchFile(file,fileName);
}
}
}
}
}
2.5 示例:非空目录删除
给定一个非空目录,将其删除(注意:delete()方法只能删除空目录和文件)
java
public class SearchFile {
public static void main(String[] args) {
//删除对应的目录(非空或空)
delDirFile(new File("C:\\Users\\永恒之月\\Desktop\\数据恢复软件大合集"));
}
//删除一个非空目录
public static void delDirFile(File dir){
//判断是不是有该路径
if(!dir.exists() || dir==null){
System.out.println("路径错误");
return;
}
//获取目录里面的文件放到数组里
File[] fileArray=dir.listFiles();
//判断该数组是不是有文件
if(fileArray!=null && fileArray.length>0){
//把数组里的文件对象遍历出来
for (File file : fileArray) {
//判断是不是文件(是:删除文件,不是且是目录:递归并传入对应的目录路径)
if(file.isFile()){
System.out.println(file.getName()+" 文件已删除");
//删除文件
file.delete();
//如果是目录,递归并传入对应的目录路径
}else if(file.isDirectory()){
//递归调用
delDirFile(file);
}
}
System.out.println(dir.getName()+" 非空目录已删除 ");
//已经循环删除完毕该目录下的所有文件,删除该目录
dir.delete();
//如果本来就是空目录,那么直接删除该目录
}else if(fileArray.length==0){
System.out.println(dir.getName()+" 空目录已删除");
//删除空目录
dir.delete();
}
}
}
3、前置知识:字符集
- ASCCII字符集:
标准ASCII字符集:美国信息交换标准代码,包括了英文、符号等。
标准ASCII使用1个字节(8位)存储一个字符,首位是0,总共 可表示128个字符,对美国佬来说完全够用。
- GBK字符集:
汉字编码字符集,包含了2万多个汉字等字符, GBK中一个中文字符编码成两个字节的形式存储。
注意: GBK兼容了ASCII字符集。
GBK规定:汉字的第一个字节的第一位必须是1
- Unicode(字符集(统一码,也叫万国码):
Unicode是国际组织制定的, 可以容纳世界.上所有文字、符号的字符集。
UTF-32 我4个字节表示一个字符,有容乃大,但是占用内存比较大,很少用。
- UTF-8:
UTF-8是Unicode字符集的一种编码方案,采取可变长编码方案,共分四个长度区: 1个字节,2个字节,3个字节,4个字节。
英文字符、数字等只占1个字节(兼容标准ASCII编码),汉字字符占用3个字节。
注意:技术人员在开发时都应该使用UTF-8编码!
- 要点:
ASCII字符集:只有英文、数字、符号等,占1个字节。
GBK字符集:汉字占2个字节,英文、数字占1个字节。
UTF-8字符集:汉字占3个字节,111开头,英文、数字占1个字节。
3.1 字符集的编码和解码
编码:把字符按照指定字符集编码成字节。
解码:把字节按照指定字符集解码成字符。
Java代码完成对字符的编码
String提供了如下方法 | 说明 |
---|---|
byte[ ] getBytes() | 使用平台的默认字符集将该String编码为一系列字节,将结果存储到新的字节数组中 |
byte[ ] getBytes ( String charsetName ) | 使用指定的字符集将该String编码为 一系列字节,将结果存储到新的字节数组中 |
Java代码完成对字符的解码
String提供了如下方法 | 说明 |
---|---|
String ( byte bytes) | 通过使用平台的默认字符集解码指定的字节数组来构造新的String |
String( byte[ ] bytes , String charsetName ) | 通过指定的字符集解码指定的字节数组来构造新的String |
java
public class CharacterSet {
public static void main(String[] args) throws UnsupportedEncodingException {
String str="a我b";
//1、编码(UTF-8:英文字母占1字节,汉字占3字节)
byte[] charSet01=str.getBytes(); //采用开发平台上的编码,我现在使用的是(UTF-8)
System.out.println(Arrays.toString(charSet01)); //[97, -26, -120, -111, 98]
//2、编码(要抛异常)(GBK:英文字母占1位,汉字占2位)
byte[] charSet02=str.getBytes("GBK"); //采用指定编码进行编码
System.out.println(Arrays.toString(charSet02)); //[97, -50, -46, 98]
//3、解码
String str02=new String(charSet01); //采用开发平台所使用的编码,我用的是(UTF-8)
System.out.println(str02); //a我b
//4、解码
String str03=new String(charSet01,"GBK"); //采用指定编码进行解码(会乱码因为GBK和UTF-8汉字的占用字节数都不同)
System.out.println(str03); //a鎴慴
//5、解码
String str04=new String(charSet02,"GBK"); //正常解码
System.out.println(str04); //a我b
}
}
4、IO流
I指Input:称为输入流:负责把数据读到内存中去。
0指Output:称为输出流:负责写数据出去。
作用:将磁盘或网络的数据读入到程序中去,将程序里面的数据写出到磁盘或者网络上去。
学习IO流的方法:
1、先搞清楚I0流的分类、体系。
2、再挨个学习每个I0流的作用、用法。
4.1 I0流的分类、体系
IO流总体来看就有四大流:字节输入流、字节输出流、字符输入流、字符输出流。
字节输出流:将磁盘上的数据以字节的方式一个一个的输入的程序(内存)中去。
字节输出流:将程序(内存)中的数据按字节的方式一个一个的写到磁盘上去。
字符输入流:将磁盘上的数据以字符的方式一个一个的输入的程序(内存)中去。
字符输出流:将程序(内存)中的数据按字符的方式一个一个的写到磁盘上去。
4.2 IO流-字节流
字节流适合文件内容的转移(文件复制等)。
字符流适合文件内容的读取。
4.2.1 FileInputStream (文件字节输入流)
作用:以内存为基准,可以把磁盘文件中的数据以字节的形式读入到内存中去。
使用FilelnputStream每次读取一个字节,读取性能较差,并且读取汉字输出会乱码。
构造器 | 说明 |
---|---|
public FileInputStream(File file ) | 创建字节输入流管道与源文件接通(传入文件对象) |
public FileInputStream ( String pathname ) | 创建字节输入流管道与源文件接通(传入路径,常用) |
方法名称 | 说明 |
---|---|
public int read() | 每次读取一个字节返回,如果发现没有数据可读会返回-1. |
public int read( byte[ ] buffer) | 每次用一个字节数组去读取数据,返回字节数组读取了多少个字节,如果发现没有数据可读会返回-1. |
public byte[] readAllBytes( ) | 直接将当前字节输入流对应的文件对象的字节数据装到一个字节数组返回 |
一个字节一个字节的读取
java
//当个字符的读取read()
public class FileinputStreamTest {
public static void main(String[] args) throws Exception {
//abc.txt文件内容:abcjhdddcsdfvcdjnvhjdvbjhdvd
//将文件接入到IO流中(注意要抛异常)
InputStream inputStream= new FileInputStream("object-app\\src\\akc4\\abc.txt");
//每调用一次read都会按照顺序读取文件中的一个字符(不能读取汉字),直到读取完毕,返回-1
int charNum=inputStream.read(); //返回字符对应的编码(如果第一个字符是a,则返回:97)
System.out.println((char)charNum); //将字符编码转为对应字符输出a
//一个一个读效率太低,可通过循环实现读取文件里所有的字符
int charNumber;
//注意:由于前面读取过第一个字符,所有是从第二个字符开始读取的
while ((charNumber=inputStream.read())!=-1){
System.out.print((char)charNumber); //bcjhdddcsdfvcdjnvhjdvbjhdvd
}
//关闭流,每次使用完都要关闭,为了减少系统开销
inputStream.close();
}
}
批量读取(无中文):
注意空白字符(空格、换行等)也算作字符。
java
public class FileinputStreamTest {
public static void main(String[] args) throws Exception {
//abc.txt文件内容:abcdef
//将文件接入到IO流中(注意要抛异常)
InputStream inputStream= new FileInputStream("object-app\\src\\akc4\\abc.txt");
byte[] buffer=new byte[5]; //准备装字符的容器(数组)长度自定
int len=inputStream.read(buffer); //返回值为装字符的长度
String str=new String(buffer);
System.out.println("字节数量:"+ len+" 字节为:"+str); //长度为:5 字符为:abcde
int len2=inputStream.read(buffer);
//注意:读多少,倒多少,(如果不写范围,只会替换a这个字符,结果会是:fbcde)
String str2=new String(buffer,0,len2);
System.out.println("字节数量:"+ len2+" 字节为:"+str2); //长度为:1 字符为:f
//如果没有数据可读,就返回-1
int len3=inputStream.read(buffer);
System.out.println(len3); //-1
//通过循环将里面的内容遍历出来
// 123.txt内容:zxcvbnmasdfg
InputStream inputStream2=new FileInputStream("object-app\\src\\akc4\\123.txt");
byte[] buffer2=new byte[3];
int len4;
while ((len4=inputStream2.read(buffer2))!=-1){
注意:读多少,倒多少
String str3=new String(buffer2,0,len4);
System.out.print(str3); //zxcvbnmasdfg
}
//关闭流,每次使用完都要关闭,为了减少系统开销
inputStream.close();
inputStream2.close();
}
}
批量读取(有中文):
读取中文可能乱码,但是可以定义一个与文件字节大小相同的数组,直接一次性读取完就能解决乱码的问题.
注意:如果文件过大,创建的字节数组也会过大,可能引起内存溢出。
java
public class FileinputStreamTest {
public static void main(String[] args) throws Exception {
/*abc.txt文件内容:
abcde经济的接触的123
对法律的
*/
//方法1:通过创建文件的方式来获取文件的字节大小,来定义数组长度
File file=new File("object-app\\src\\akc4\\abc.txt");
//创建流
InputStream inputStream= new FileInputStream(file);
//返回值类型为long
long size=file.length();
//数组长度为int类型的所有要强转(这个方法适用于小型的文件)
byte[] buffer=new byte[(int)size];
int len=inputStream.read(buffer);
String str=new String(buffer);
System.out.println("字节数为:"+len+"字节为:"+str);
//方法2:通过提供的readAllBytes方法
InputStream inputStream2=new FileInputStream(file);
//返回值就是文件所有的字节数组
byte[] bytes=inputStream2.readAllBytes();
String str2=new String(bytes);
System.out.println(str2);
//关闭所有的流
inputStream.close();
inputStream2.close();
}
}
4.2.2 FileOutputStream ( 文件字节输出流 )
作用:以内存为基准,把内存中的数据以字节的形式写出到文件中去。
构造器 | 说明 |
---|---|
public FileOutputStream ( File file ) | 创建字节输出流管道与源文件对象接通 |
public FileOutputStream( String filepath) | 创建字节输出流管道与源文件路径接通 |
public FileOutputStream( File file , boolean append ) | 创建字节输出流管道与源文件对象接通,可追加数据 |
public FileOutputStream ( String filepath , boolean append ) | 创建字节输出流管道与源文件路径接通,可追加数据 |
方法名 | 说明 |
---|---|
public void write( int a) | 写一个字节出去 |
public void write ( byte[] buffer ) | 写一个字节数组出去 |
public void write ( byte[] buffer , int pos , int len ) | 写一个字节数组的一部分出去 |
public void close() throws IOException | 关闭流 |
java
public class FileOutputStreamTest {
public static void main(String[] args) throws Exception {
//创建一个输出流(如果没有该文件,文件可以自动创建),会覆盖原有的内容,除非在参数中加入true,如下注释
//OutputStream outputStream=new FileOutputStream("object-app\\src\\akc4\\fileandio\\abc.txt",true);
OutputStream outputStream=new FileOutputStream("object-app\\src\\akc4\\fileandio\\abc.txt");
//向文件中写入一个字节(可以是字节的ASCII码,也可以是对应的字节)
outputStream.write(97); //写入:a
outputStream.write('b'); //写入:b
//写入数组里的所有字节
byte[] buffer={'a','b','c',65,66,'d'};
outputStream.write(buffer); //再原来的基础上追加:abcABd
//可通过getBytes方法将字符串转为字节数组
byte[] buffer2="小妹妹笑眯眯".getBytes();
//写入数组里的某部分字节(从数组的第9个下标开始,截取9个字节写入到文件中)
//因为采用的UTF-8,所有一个汉字占3个字节
outputStream.write(buffer2,9,9); //追加写入:笑眯眯
//关闭流
outputStream.close();
}
}
文件复制:
可以复制任意文件:任何文件的底层都是字节,字节流做复制,是一字不漏的转移完全部字节,只要复制后的文件格式一致就没问题!
java
//将文件abc.txt里面的内容拷贝到文件bcd.txt中(bcd.txt不存在)
public class CopyFile {
public static void main(String[] args) throws Exception {
//创建输入流
InputStream inputStream=new FileInputStream("object-app\\src\\akc4\\fileandio\\abc.txt");
//将文件里的所有内容读取到字节数组中
byte[] buffer=inputStream.readAllBytes();
//创建输出流(自动创建文件bcd.txt)
OutputStream outputStream=new FileOutputStream("object-app\\src\\akc4\\fileandio\\bcd.txt");
//通过字节数组将所有内容写入到bcd.txt文件中
outputStream.write(buffer);
//关闭所有流
inputStream.close();
outputStream.close();
}
}
java
//拷贝图片文件(将图片123.jpg,拷贝到当前目录下并重命名为321.jpg)
public class CopyFile {
public static void main(String[] args) throws Exception {
//创建输入流,导入图片
InputStream inputStream=new FileInputStream("object-app\\src\\akc4\\fileandio\\123.jpg");
//创建输出流,拷贝到的路径
OutputStream outputStream=new FileOutputStream("object-app\\src\\akc4\\fileandio\\321.jpg");
//将图片字节全部存放到字节数组中(也可以通过自定义数组长度,使用while来拷贝)
byte[] buffer=inputStream.readAllBytes();
//写出图片
outputStream.write(buffer);
//关闭流
outputStream.close();
inputStream.close();
}
}
4.3 释放资源两种方法
在程序执行过程中,字节流创建了,可能程序中间出现了某个错误,导致没法执行关闭流。导致资源一直不能释放,所以要学释放资源。
作用: 一般用于在程序执行完成后进行资源的释放操作(专业级做法)。
4.3.1 try-catch-finally
finally代码区的特点:无论try中的程序是正常执行了,还是出现了异常,最后都一定会执行finally区,除非JVM终止。
之前的拷贝图片的程序就可以将关闭流的代码放到finally中了
java
//拷贝图片文件(将图片123.jpg,拷贝到当前目录下并重命名为321.jpg)
public class CopyFile {
public static void main(String[] args) {
//将变量定义到try外边,为了让finally里面也能访问到
InputStream inputStream=null;
OutputStream outputStream=null;
try {
//创建输入流,导入图片
inputStream=new FileInputStream("object-app\\src\\akc4\\fileandio\\123.jpg");
//创建输出流,拷贝到的路径
outputStream=new FileOutputStream("object-app\\src\\akc4\\fileandio\\321.jpg");
//将图片字节全部存放到字节数组中(也可以通过自定义数组长度,使用while来拷贝)
byte[] buffer=inputStream.readAllBytes();
//写出图片
outputStream.write(buffer);
}catch (Exception e){
}finally {
//关闭流
try {
//防止空指针异常
if (outputStream!=null){
outputStream.close();
}
//防止空指针异常
if (inputStream!=null){
inputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
4.3.1 try-with-resource
常用,直接在try后面的小括号里写入要释放释放的资源,该资源使用完毕后,会自动调用其close()方法,完成对资源的释放!
注意:括号里面只能写资源对象
什么是资源呢? 资源都是会实现AutoCloseable接口。资源都会有一 个close方法,并且资源放到这里后
用完之后,会被自动调用其close方法完成资源的释放操作。
java
//拷贝图片文件(将图片123.jpg,拷贝到当前目录下并重命名为321.jpg)
public class CopyFile {
public static void main(String[] args) {
try (
//创建输入流,导入图片
InputStream inputStream=new FileInputStream("object-app\\src\\akc4\\fileandio\\123.jpg");
//创建输出流,拷贝到的路径
OutputStream outputStream=new FileOutputStream("object-app\\src\\akc4\\fileandio\\321.jpg");
){
//将图片字节全部存放到字节数组中(也可以通过自定义数组长度,使用while来拷贝)
byte[] buffer=inputStream.readAllBytes();
//写出图片
outputStream.write(buffer);
}catch (Exception e){
e.printStackTrace();
}
}
}
4.4 IO流-字符流
字符流:适合读写文本文件内容
4.4.1 FileReader(文件字符输入流)
作用:以内存为基准,可以把文件中的数据以字符的形式读入到内存中去。
构造器 | 说明 |
---|---|
public FileReader ( File file ) | 创建字符输入流管道与源文件接通 |
public FileReader ( String pathname ) | 创建字符输入流管道与源文件对接通 |
方法名称 | 说明 |
---|---|
public int read( ) | 每次读取一个字符返回,如果发现没有数据可读会返回-1. |
public int read( char[] buffer) | 每次用一个字符数组去读取数据,返回字符数组读取了多少个字符,如果发现没有数据可读会返回-1. |
java
public class FileReadTest {
public static void main(String[] args) {
try(
//创建一个字符流
Reader reader=new FileReader("object-app\\src\\akc4\\fileandio\\abc.txt");
Reader reader2=new FileReader("object-app\\src\\akc4\\fileandio\\abc.txt");
) {
//通过当个字符进行读取
int c;
while ((c=reader.read())!=-1){
System.out.print((char) c);
}
//通过数组发方式读取全部内容
char[] buffer=new char[3];
int len; //记录读取的字符数
while ((len=reader2.read(buffer))!=-1){
System.out.print(new String(buffer,0,len));
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
4.4.2 FileWrider(文件字符输出流)
作用:以内存为基准,把内存中的数据以字符的形式写出到文件中去。
注意:字符输出流写出数据后,必须刷新流,或者关闭流,写出去的数据才能生效
构造器 | 说明 |
---|---|
public FileWriter ( File file ) | 创建字节输出流管道与源文件对象接通 |
public FileWriter ( String filepath ) | 创建字节输出流管道与源文件路径接通 |
public FileWriter ( File file , boolean append ) | 创建字节输出流管道与源文件对象接通,可追加数据 |
public FileWriter ( String filepath , boolean append ) | 创建字节输出流管道与源文件路径接通,可追加数据 |
方法名称 | 说明 |
---|---|
void write( int c ) | 写一个字符 |
void write ( String str ) | 写一个字符串 |
void write ( String str , int off , int len ) | 写一个字符串的一部分 |
void write (char[] cbuf ) | 写入一个字符数组 |
void write( char[] cbuf , int off , int len ) | 写入字符数组的一部分 |
void flush() | 刷新流,只有刷新或关闭流才能真正的写入数据,刷新后可以继续操作流 |
void close() | 关闭流,关闭流包含刷新 |
java
public class FileWriderTest {
public static void main(String[] args) {
try( //该文件可以不存在(会自动创建),追加的方式
Writer writer=new FileWriter("object-app\\src\\akc4\\fileandio\\123.txt",true)
){
//写入一个字符
writer.write('t');
//可以通过写入\t\n来换行
writer.write("\t\n");
//写入字符串
writer.write("好好好");
//写入一部分字符串
String str="今天真美好呀!";
writer.write(str,2,2); //(从0开始算)从第二个字符开始,截取2个写入(真美)
//写入一个字符数组
char[] chars={'和','平','共','处'};
writer.write(chars);
//写入数组的部分数据
writer.write(chars,0,2); //写入:和平
}catch (Exception e){
e.printStackTrace();
}
}
}
4.5 IO-缓存流
包含4个:字节缓冲输入流字、节缓冲输出流字、符缓冲输入流字、符缓冲输出流。
作用:对原始流进行包装,以提高原始流读写数据的性能。
4.5.1 字节缓冲流
作用:提高字节流读写数据的性能。
原理:字节缓冲输入流自带了8KB缓冲池;字节缓冲输出流也自带了8KB缓冲池。
构造器 | 说明 |
---|---|
public BufferedInputStream ( InputStream is ) | 把低级的字节输入流包装成一个高级的缓冲字节输入流,从而提高读数据的性能(默认8kb缓冲池,也就是1024*8) |
public BufferedInputStream ( InputStream is , Int size ) | 把低级的字节输入流包装成一个高级的缓冲字节输入流,从而提高读数据的性能(可自定义缓冲池大小,如:1024*32) |
public BufferedOutputStream( OutputStream os ) | 把低级的字节输出流包装成一个高级的缓冲字节输出流,从而提高写数据的性能(默认8kb缓冲池,也就是1024*8)) |
public BufferedOutputStream( OutputStream os , Int size ) | 把低级的字节输出流包装成一个高级的缓冲字节输出流,从而提高写数据的性能(可自定义缓冲池大小,如:1024*32) |
java
public class BufferInOutStreamTest {
public static void main(String[] args) {
try(
//原始字节输入流(多态写法)
InputStream inputStream=new FileInputStream("object-app\\src\\akc4\\fileandio\\123.txt");
//缓冲字节输入流(多态写法)
InputStream bis=new BufferedInputStream(inputStream);
//原始字节输出流(多态写法)
OutputStream outputStream=new FileOutputStream("object-app\\src\\akc4\\fileandio\\a.txt");
//缓冲字节输出流(多态写法)
OutputStream bos=new BufferedOutputStream(outputStream);
) {
byte[] buffer=new byte[3];
int len;
while ((len=bis.read(buffer))!=-1){
//拷贝文件(写入多少,倒出多少)
bos.write(buffer,0,len);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
4.5.2 字符缓冲流
作用:自带8K (8192)的字符缓冲池,可以提高字符输入输出流读取和写入字符数据的性能。
构造器 | 说明 |
---|---|
public BufferReader(Reader r) | 把低级的字符输入流包装成字符缓冲输入流管道, 从而提高字符输入流读字符数据的性能(默认8kb缓冲池) |
public BufferReader(Reader r , Int Size) | 把低级的字符输入流包装成字符缓冲输入流管道, 从而提高字符输入流读字符数据的性能(自定义缓冲池大小) |
public BufferWrider(Wrider w) | 把低级的字符输出流包装成一 个高级的缓冲字符输出流管道,从而提高字符输出流写数据的性能(默认8kb缓冲池) |
public BufferWrider(Wrider w , Int Size) | 把低级的字符输出流包装成一 个高级的缓冲字符输出流管道,从而提高字符输出流写数据的性能(自定义缓冲池大小) |
新增方法 | 说明 |
---|---|
public String readLine() | 按照行读取字符,读取一行数据返回,如果没有数据可读了,会返回nu11 |
public void newLine() | 写入换行字符,要和readLine配合使用(每写一行,换一行) |
4.5.3 原始流和缓冲流的性能说明
- 我们一般使用字节数组来写入写出,字节输入输出流使用字节数组来写入写出时,数组越大,性能越好,不过越大性能提升越不明显。
- 缓存流的缓存池,默认8kb,可以进行指定大小,一般是通过1024字节(1kb)来乘以一个整数来扩大(32kb提升比较明显),在在扩大缓冲池的同时,也可以扩大字节数组的大小,因为它们都和性能成正比。
4.6 IO流-转换流
4.6.1 不同编码读取时会乱码
1、如果代码编码和被读取的文本文件的编码是-致的,使用字符流读取文本文件时不会出现乱码!
2、如果代码编码和被读取的文本文件的编码是不- -致的,使用字符流读取文本文件时就会出现乱码!
4.6.2 InputStreamReader(字符输入转换流)
解决思路:先获取文件的原始字节流,再将其按真实的字符集编码转成字符输入流,这样字符输入流中的字符就不乱码了。
构造器 | 说明 |
---|---|
public InputStreamReader( InputStream is ) | 把原始的字节输入流,按照代码默认编码转成字符输入流(与直接用FileReader的效果一样) |
public InputStreamReader( InputSream is , String charset ) | 把原始的字节输入流,按照指定字符集编码转成字符输入流(重点) |
java
public class InputStreamReaderTest {
public static void main(String[] args) {
try(
//将文件转换为字节输入流(UTF-8)
InputStream is=new FileInputStream("object-app\\src\\akc4\\fileandio\\123.txt");
//将字节流转换为对应编码的字符输入流
Reader reader=new InputStreamReader(is,"UTF-8");
//将转换好的字符流,转换为字符缓冲流。(提高性能)
BufferedReader br=new BufferedReader(reader);
){
String str;
while ((str=br.readLine())!=null){
System.out.println(str);
}
}catch (Exception e){
e.printStackTrace();
}
}
}
4.6.3 OutputStreamWrite(字符输出转换流)
作用:需要控制写出去的字符使用什么字符集编码。
解决思路:获取字节输出流,再按照指定的字符集编码将其转换成字符输出流,以后写出去的字符就会用该字符集编码了。
也可以使用:String提供的getBytes() 方法解决,如下:将字符转换对应编码的数组,将输出输出即可。
java
String data = "我爱你中国abc";
byte[] bytes = data.getBytes("GBK");
构造器 | 说明 |
---|---|
public OutputStreamWrite(OutputStream os ) | 可以把原始的字节输出流,按照代码默认编码转换成字符输出流。 |
public OutputStreamWrite(OutputStream os ,String charset) | 可以把原始的字节输出流,按照指定编码转换成字符输出流(重点) |
java
public class OutputStreamWriteTest {
public static void main(String[] args) {
try(
//创建一个字节输出流
OutputStream os=new FileOutputStream("object-app\\src\\akc4\\fileandio\\abc.txt");
//将字节输出流转换为对应编码的字符输出流
Writer writer=new OutputStreamWriter(os,"GBK");
//将字符输出流,转换为输出缓冲流
BufferedWriter bw=new BufferedWriter(writer);
) {
//写出数据
bw.write("我是谁,我在哪?123abc");
} catch (Exception e) {
e.printStackTrace();
}
}
}
4.7 IO流-打印流
作用:打印流可以实现更方便、更高效的打印数据出去,能实现打印啥出去就是啥出去。(基本可以替代前面的输出流)
性能也是很好的,因为它内部包装了bufferedWrite(缓冲流)
4.7.1 PrintStream 提供的打印方案
构造器 | 说明 |
---|---|
public PrintStream(OutputStream/File/String) | 打印流直接通向字节输出流/文件/文件路径 |
public PrintStream(String fileName,String csn) | 可以指定写出去的字符编码 |
public PrintStream(OutputStream out , boolean autoFlush) | 可以指定实现自动刷新 |
public PrintStream(OutputStream out ,boolean autoFlush,String encoding) | 可以指定实现自动刷新,并指定字符的编码 |
方法 | 说明 |
---|---|
public void println(XXXXX) | 打印任意类型的数据出去 |
public void write(int/byte[] / byte[] 一部分) | 可以支持写字节数据出去 |
java
public class PrintStreamTest {
public static void main(String[] args) {
try(
//创建一个打印流对象(如果指定的文件不存在,就会自动创建)
PrintStream ps=new PrintStream("object-app\\src\\akc4\\fileandio\\a.txt");
//创建一个指定文件编码的打印流对象
PrintStream ps2=new PrintStream("object-app\\src\\akc4\\fileandio\\b.txt","GBK");
) {
//向文件中打印acd,并换行
ps.println("acd");
//向文件中打印dca,打印完不换行
ps.print("dca");
//写入字节数据a
ps.write(97);
//打印出GBK类型的字符串
ps2.println("好呀好呀好呀");
} catch (Exception e) {
e.printStackTrace();
}
}
}
构造器 | 说明 |
---|---|
public PrintWriter(OutputStream / Writer / File /String ) | 打印流直接通向字节输出流/文件/文件路径 |
public PrintWriter (String fileName , String csn) | 可以指定写出去的字符编码 |
public PrintWriter(OutputStream out / Writer,boolean autoFlush) | 可以指定实现自动刷新 |
public PrintWriter (OutputStream out , boolean autoFlush ,String encoding ) | 可以指定实现自动刷新,并可指定字符的编码 |
方法 | 说明 |
---|---|
public void println(XXXXX) | 打印任意类型的数据出去 |
public void write(int/byte[] / byte[] 一部分) | 可以支持写字节数据出去 |
java
public class PrintWriterTest {
public static void main(String[] args) {
try(
//创建一个打印流对象(如果指定的文件不存在,就会自动创建)
PrintWriter pw=new PrintWriter("object-app\\src\\akc4\\fileandio\\a.txt");
//创建一个指定文件编码的打印流对象
PrintWriter pw2=new PrintWriter("object-app\\src\\akc4\\fileandio\\b.txt","GBK");
) {
//向文件中打印acd,并换行
pw.println("acd");
//向文件中打印dca,打印完不换行
pw.print("dca");
//写入字节数据a
pw.write(97);
//打印出GBK类型的字符串
pw2.println("好呀好呀好呀");
} catch (Exception e) {
e.printStackTrace();
}
}
}
注意:它们本身是不能直接追加数据的需要进行包装低级流,如下:只有是低级流就行。
java
PrintWriter pw=new PrintWriter(new FileWriter("fileandio\\a.txt",true));
PrintStream ps=new PrintStream(new FileOutputStream("fileandio\\a.txt",true));
PrintStream和PrintWriter的区别:
- 打印数据的功能上是一模一 样的,都是使用方便,性能高效(核心优势)
- PrintStream继承自字 节输出流OutputStream,因此支持写字节数据的方法。
- PrintWriter继承自字符输出流Writer, 因此支持写字符数据出去。
4.7.3 输出重定向
当我们需要写日志时可以使用,输出重定向。将信息输出到文件中保存起来。
也可以直接写日志,根据个人喜好来就行!
java
public class SetOutTest {
public static void main(String[] args) {
try (
PrintStream ps=new PrintStream("object-app\\src\\akc4\\fileandio\\b.txt");
){
//将系统默认的输出内容,写入到对应文件中
System.setOut(ps);
//不会在控制台输出,会写入到b.txt这个文件中
System.out.println("呵呵呵呵");
} catch (Exception e) {
e.printStackTrace();
}
}
}
4.8 IO流-数据流
作用:可以将不同类型的数据保存到文件中,方便下次读取使用。
4.8.1 DateOutputStream(数据输出流)
作用:数据输出流允许把数据和其类型一并写出去。
注意:写出数据是为了下次好读取使用的,所以写出的数据是乱码的,只能通过数据输入流来读取。
构造器 | 说明 |
---|---|
public DateOutputStream(OutputStream out) | 创建新数据输出流包装基础的字节输出流 |
方法 | 说明 |
---|---|
public final void writeByte(int v)throws IOException | 将byte类型的数据写入基础的字节输出流 |
public final void writeInt(int v)throws IOException | 将int类型的数据写入基础的字节输出 |
public final void writeDouble(Double v) throws IOException | 将double类型的数据写入基础的字节输出流 |
public final void writeUTF(String str)throws IOException | 将字符串数据以UTF-8编码成字节写入基础的字节输出流 |
public final void write(int / byte[] / byte[] 一部分) | 支持写字节数据出去 |
java
public class DateOutputStreamTest {
public static void main(String[] args) {
try( //创建一个数据输出流,传入一个低级流(字节输出流,并且是追加的方式)
DataOutputStream dos=new DataOutputStream(new FileOutputStream("object-app\\src\\akc4\\fileandio\\c.txt",true));
) {
//写出一个布尔值true
dos.writeBoolean(true);
//写出一个整型数据20
dos.writeInt(20);
//写出一Double类型2.5
dos.writeDouble(2.5);
//写出一个字符串
dos.writeUTF("好好好");
} catch (Exception e) {
e.printStackTrace();
}
}
}
4.8.2 DataInputStream(数据输入流)
作用:读取通过数据输出流保存的数据。
注意:必须按照写出的顺序进行写入,要不然数据会出错。
构造器 | 说明 |
---|---|
public DataInputStream(InputStream is) | 创建新数据输入流包装基础的字节输入流 |
方法 | 说明 |
---|---|
public final byte readByte()throws IOException | 读取字节数据返回 |
public final int readInt()throws IOException | 读取int类型的数据返回 |
public final double readDouble()throws IOException | 读取double类型的数据返回 |
public final String readUTF()throws IOException | 读取字符串数(UTF-8) 据返回 |
public int readInt() / read(byte[] ) | 支持读字节数据进来 |
java
public class DateInputStreamTest {
public static void main(String[] args) {
try( //创建一个数据输出流,传入一个低级流(字节输出流,并且是追加的方式)
DataInputStream dis=new DataInputStream(new FileInputStream("object-app\\src\\akc4\\fileandio\\c.txt"));
) {
//读取一个布尔值true
boolean bo=dis.readBoolean();
System.out.println(bo); //true
//读取一个整型数据20
int in= dis.readInt();
System.out.println(in);//20
//读取一Double类型2.5
double dou=dis.readDouble();
System.out.println(dou); //2.5
//读取一个字符串
String str=dis.readUTF();
System.out.println(str); //好好好
} catch (Exception e) {
e.printStackTrace();
}
}
}
4.9 IO流-序列化流
作用:将java对象从文件中写入或写出。
属于是字节流下面发流
4.9.1 ObjectOutputStream (对象字节输出流)
作用:可以把Java对象进行序列化,把Java对象存入到文件中去。
注意:对象如果要参与序列化,必须实现序列化接口(java.io.Serializable )
注意:如果想让某个字段不参与序列化,就在该类的字段前加入 transient 来修饰字段(如:private transient String password)
如果要同时序列号多个对象:用一个ArrayList集合存储多个对象,然后直接对集合进行序列化即可。注意:ArrayList集合已经实现了序列化接口!
构造器 | 说明 |
---|---|
public ObjectOutputStream(OutputStream out) | 创建对象字节输出流,包装基础的字节输出流 |
方法 | 说明 |
---|---|
public final void writeObject(Object o)throws IOException | 把对象写出去 |
java
//对象输出流,将对象保存到文本文件中
public class ObjectOutStreamTest {
public static void main(String[] args) {
try(
//2.创建一个对象输出流,参数为原始文件输出流
ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("object-app\\src\\akc4\\fileandio\\c.txt"));
) {
//1.创建一个User对象
UserTest userTest=new UserTest("张三","男",19,"123456");
oos.writeObject(userTest);
} catch (IOException e) {
e.printStackTrace();
}
}
}
java
//用户对象类
//注意:被写出的对象必须实现 Serializable 接口,它只是一个标识而已(表示该对象可以被写出),里面没有任何内容。
public class UserTest implements Serializable {
private String name;
private String sex;
private int age;
private transient String password;
public UserTest(String name, String sex, int age, String password) {
this.name = name;
this.sex = sex;
this.age = age;
this.password = password;
}
public UserTest() {
}
@Override
public String toString() {
return "UserTest{" +
"name='" + name + '\'' +
", sex='" + sex + '\'' +
", age=" + age +
", password='" + password + '\'' +
'}';
}
}
4.9.2 ObjectInputStream(对象字节输入流)
作用:可以把Java对象进行反序列化:把存储在文件中的Java对象读入到内存中来。
构造器 | 说明 |
---|---|
public ObjectInputStream(InputStream is) | 创建对象字节输入流,包装基础的字节输入流 |
方法 | 说明 |
---|---|
public final Object readObject() | 把存储在文件中的Java对象读出来 |
java
public class ObjectInputStreamTest {
public static void main(String[] args) {
try(
//创建一个对象输入流(将文件读进来)
ObjectInputStream ois=new ObjectInputStream(new FileInputStream("object-app\\src\\akc4\\fileandio\\c.txt"))
) {
//将文件内的对象读取出来,返回值为Object类型(强制转换为UserTest)
UserTest userTest=(UserTest) ois.readObject();
//查看一下该对象对应的信息(重写过toString方法)
System.out.println(userTest);
//UserTest{name='张三', sex='男', age=19, password='null'}
}catch (Exception e){
e.printStackTrace();
}
}
}
5、IO框架
什么是框架?
解决某类问题,编写的一套类、接口等,可以理解成-一个半成品,大多框架都是第三方研发的。
好处:在框架的基础上开发,可以得到优秀的软件架构,并能提高开发效率。
框架的形式:一般是把类 、接口等编译成class形式,再压缩成一个.jar结尾的文件发行出去。
什么是IO框架?
封装了Java提供的对文件、数据进行操作的代码,对外提供了更简单的方式来对文件进行操作,对数据进行读写等。
idea怎么导入jar包?
例如导入,Commons-io的jar包(commons-io-2.15.1.jar),
1、下载对应的jar包,Commons-io下载链接如:https://commons.apache.org/proper/commons-io/download_io.cgi
2、在idea对应项目的模块中创建一个lib的文件夹,将对应的jar包拷贝到里面去
3、右击lib文件夹,选中 Add as library... 加载到项目库去,就行了,加载进去的包是可以打开的。
5.1 Commons-io 框架
Commons-io是apache开源基金组织提供的一组有关I0操作的小框架,目的是提高I0流的开发效率。
对应jar包的下载链接:https://commons.apache.org/proper/commons-io/download_io.cgi
FileUtils类提供的部分方法展示 | 说明 |
---|---|
public static void copyFile(File srcFile , File destFile ) | 复制文件 |
public static void copeDirectory(File srcDir , File destDir ) | 复制文件夹 |
public static void deleteDirectory( File directory ) | 删除文件夹 |
public static String readFileToString(File file , String encoding) | 读数据 |
public static void writeStringToFile(File file , String data , String charname , boolean append) | 写数据(文件路径,写的内容,编码,是否是追加方式)和 write方法相同的用法 |
java
public class FileUtilsTest {
public static void main(String[] args) throws Exception {
//拷贝指定文件到指定目录下
FileUtils.copyFile(new File("object-app\\src\\akc4\\123.txt"),new File("object-app\\src\\akc4\\abc2.txt"));
//拷贝指定目录及其里面所以的文件或文件夹到指定目录下
FileUtils.copyDirectory(new File("object-app\\src\\111"),new File("object-app\\src\\222"));
//删除指定目录,及其里面所以的文件和目录
FileUtils.deleteDirectory(new File("object-app\\src\\222"));
//读取文件中的字符数据,可指定编码读取,也可以默认不写。
String str= FileUtils.readFileToString(new File("object-app\\src\\akc4\\123.txt"),"UTF-8");
System.out.println(str);
//向指定文件追加数据
FileUtils.writeStringToFile(new File("object-app\\src\\akc4\\123.txt"),"我要成为java高手","UTF-8",true);
FileUtils.write(new File("object-app\\src\\akc4\\123.txt"),"我要成为java精英","UTF-8",true);
}
}
l0Utils类提供的部分方法展示 | 说明 |
---|---|
public static int copy (InpuStream inputStream ,OutputStream outputStream ) | 复制文件 |
public static int copy(Reader reader ,Writer writer ) | 复制文件 |
public static void write(String data , OutputStream output , String charsetName) | 写数据 |