一、File类概述
File类在包java.io.File下、代表操作系统的文件对象 (文件、文件夹)
File类提供了诸如: 定位文件,获取文件本身的信息、删除文件、创建文件(文件夹)等功能
File类创建对象
方法名称 |
说明 |
public File(String pathname) |
根据文件路径创建文件对象 |
public File(String parent,String child) |
从父路径名字符串和子路径名字符串创建文件对象 |
public File(File parent,String child) |
根据父路径对应文件对象和子路径名字符串创建文件对象 |
1、File类的作用?
创建对象定位文件,可以删除、获取文件信息等。但是不能读写文件内容
2、File类构建对象的方式?
Filefile =new File(“文件/文件/绝对路径/相对路径”);
3、绝对路径和相对路径是什么样的?
绝对路径是带盘符的,依赖当前系统
相对路径是不带盘符的,默认相对到工程下开始寻找文件
二、File类的常用API
(一)判断文件类型、获取文件信息
File类的判断文件类型、获取文件信息功能
方法名称 |
说明 |
public boolean isDirectory() |
测试此抽象路径名表示的File是否为文件夹 |
public boolean isFile() |
测试此抽象路径名表示的File是否为文件 |
public boolean exists() |
测试此抽象路径名表示的File是否存在 |
public String getAbsolutePath() |
返回此抽象路径名的绝对路径名字符串 |
public String getPath() |
将此抽象路径名转换为路径名字符串 |
public string getName() |
返回由此抽象路径名表示的文件或文件夹的名称 |
public long lastModified() |
返回文件最后修改的时间毫秒值 |
获取文件路径
System.out.println(f.getAbsoluteFile());
(二)创建文件、删除文件功能
File类创建文件的功能
方法名称 |
说明 |
public boolean createNewFile() |
创建一个新的空的文件 |
public boolean mkdir() |
只能创建一级文件夹 |
public boolean mkdirs() |
可以创建多级文件夹 |
File类删除文件的功能
方法名称 |
说明 |
public boolean delete() |
删除由此抽象路径名表示的文件或空文件夹 |
delete方法直接删除不走回收站;如果删除的是一个文件,且文件没有被占用则直接删除
delete方法默认只能删除空文件夹
(三)遍历文件夹
File类的遍历功能
方法名称 |
说明 |
public Stringl list() |
获取当前目录下所有的"一级文件名称”到一个字符串数组中去返回 |
public File[] listFiles()(常用) |
获取当前目录下所有的”一级文件对象”到一个文件对象数组中去返回(重点) |
//遍历一级对象名称
File f1=new File("D:/code");
String[] names = f1.list();
for (String name : names) {
System.out.println(name);
}
//获取文件对象(路径)
File[] files = f1.listFiles();
for (File file : files) {
System.out.println(file.getAbsolutePath());
}
listFiles方法注意事项:
当调用者不存在时,返回null
当调用者是一个文件时,返回null
当调用者是一个空文件夹时,返回一个长度为0的数组
当调用者是一个有内容的文件夹时,将里面所有文件和文件夹的路径放在File数组中返回
当调用者是一个有隐藏文件的文件夹时,将里面所有文件和文件夹的路径放在File数组中返回,包含隐藏内容
当调用者是一个需要权限才能进入的文件夹时,返回null
(四)总结
如何遍历文件夹下的文件对象,使用哪个API,有什么特点?
public File[T listFiles()(常用)
只能遍历当前文件夹对象下的一级文件对象
三、方法递归
(一)递归的形式和特点
1.什么是方法递归?
方法直接调用自己或者间接调用自己的形式称为方法递归 (recursion)
递归做为一种算法在程序设计语言中广泛应用。
2.递归的形式
直接递归: 方法自己调用自己
间接递归: 方法调用其他方法,其他方法又回调方法自己。
(二)递归的算法流程、核心要素
递归解决问题的思路
把一个复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解。
递归算法三要素大体可以总结为:
递归的公式: f(n)= f(n-1)* n
递归的终结点:f(1)
递归的方向必须走向终点
(三)递归常见案例
求f(5)的阶级 1×2×3×4×5
System.out.println(f(5));
public static int f(int n) {
if (n == 1) {
return 1;
} else {
return f(n-1)*n;
}
}
① 求计算1-n的和 f(n):1+2+3+...+(n-1)+n
② 公式:f(n)=f(n-1)+n
③ 递归的终结点:f(1)=1
System.out.println(f(5));
public static int f(int n) {
if (n == 1) {
return 1;
} else {
return f(n-1)+n;
}
}
(四)递归的经典问题
猴子吃桃问题
猴子第一天摘下若干桃子,当即吃了一半,觉得好不过瘾,于是又多吃了一个第二天又吃了前天剩余桃子数量的一半,觉得好不过瘾,于是又多吃了一个以后每天都是吃前天剩余桃子数量的一半,觉得好不过瘾,又多吃了一个等到第10天的时候发现桃子只有1个了需求: 请问猴子第一天摘了多少个桃子?
公式:f(x) - f(x)/2 - 1 = f(x+1)
2f(x) = 2f(x + 1) + 2
f(x) = 2f(x + 1) + 2
终结点:f(10) = 1
System.out.println(f(1));
public static int f(int n) {
if (n == 10) {
return 1;
} else {
return 2 * f(n + 1) + 2;
}
}
(五)非规律化递归案例-文件搜索
需求:文件搜索、从C:盘中,搜索出某个文件名称并输出绝对路径
分析:
① 先定位出的应该是一级文件对象
② 遍历全部一级文件对象,判断是否是文件
③ 如果是文件,判断是否是自己想要的
④ 如果是文件夹,需要继续递归进去重复上述过程
//2、传入目录和文件名称
searchFile(new File("D:/"), "elevator.exe");
/**
* 1、搜索某个目录下的全部文件,找到我们想要的文件。
*
* @param dir 被搜索的源目录
* @param fileName 被搜索的文件名称
*/
public static void searchFile(File dir, String fileName) {
//3、判断dir是否为目录(文件夹)
if (dir != null && dir.isDirectory()) {
//可以找了
//4、提取当前目录下的一级文件对象
File[] files = dir.listFiles();// null []
//5、判断是否存在一级文件,存在才可以遍历
if (files != null && files.length > 0) {
for (File file : files) {
//6、判断当前遍历的一级文件名称是文件 还是 目录(文件夹)
if (file.isFile()) {
//7、是不是咱们要找的,是吧其路径输出即可
if (file.getName().contains(fileName)) {//模糊查找
//打印文件路径
System.out.println("找到了:" + file.getAbsolutePath());
}
}else {
//8、是文件夹,继续递归寻找
searchFile(file,fileName);
}
}
}
} else {
System.out.println("您输入的搜索位置不是文件夹!");
}
}
//扩展知识
//启动exe软件
File file = new File("D:\\QQ\\Bin\\QQScLauncher.exe");
Runtime r = Runtime.getRuntime();
try {
r.exec(file.getAbsolutePath());
} catch (IOException e) {
e.printStackTrace();
}
(六)非规律化递归案例-啤酒问题
四、字符集
字符集基础知识
计算机底层不可以直接存储字符的。计算机中底层只能存储二进制(0、1
二进制是可以转换成十进制的
结论:计算机底层可以表示十进制编号。计算机可以给人类字符进行编号存储,这套编号规则就是字符集。
ASCII字符集:
ASCI(American Standard Code for lnformation lnterchange,美国信息交换标准代码): 包括了数字、英文、符号
ASCII使用1个字节存储一个字符,一个字节是8位,总共可以表示128个字符信息,对于英文,数字来说是够用的。
比如
01100001= 97 => a
01100010 = 98 => b
GBK:
window系统默认的码表。兼容ASCII码表,也包含了几万个汉字,并支持繁体汉字以及部分日韩文字。
注意: GBK是中国的码表,,一个中文以两个字节的形式存储。但不包含世界上所有国家的文字
Unicode码表:
unicode (又称统一码、万国码、单一码)是计算机科学领域里的一项业界字符编码标准
容纳世界上大多数国家的所有常见文字和符号
由于Unicode会先通过UTF-8,UTF-16,以及 UTF-32的编码成二进制后再存储到计算机,其中最为常见的就是UTF-8
注意
Unicode是万国码,以UTF-8编码后一个中文一般以三个字节的形式存储.
UTF-8也要兼容ASCII编码表。
技术人员都应该使用UTF-8的字符集编码。
编码前和编码后的字符集需要一致,否则会出现中文乱码。
总结
1.字符串常见的字符底层组成是什么样的?
英文和数字等在任何国家的字符集中都占1个字节
GBK字符中一个中文字符占2个字节
UTF-8编码中一个中文1般占3个字节
2. 编码前的字符集和编码好的字符集有什么要求?
必须一致,否则会出现中文字符乱码
英文和数字在任何国家的编码中都不会乱码
字符集的编码、解码操作
String编码
方法名称 |
说明 |
(byte[] bytes) |
使用平台的默认字符集将该 String编码为一系列字节,将结果存储到新的字节数组中 |
(byte[] bytes, [String] |
bytell getBytes(String charsetName)使用指定的字符集将该 string编码为一系列字节,将结果存储到新的字节数组中 |
String解码
构造器 |
说明 |
String(byte[] bytes) |
通过使用平台的默认字符集解码指定的字节数组来构造新的String |
String(bytel] bytes,String charsetName) |
通过指定的字符集解码指定的字节数组来构造新的 String |
//1、编码:把文字转换成字节(使用指定的编码)
String name = "abc我爱你";
//byte[] bytes = name.getBytes();//当前代码默认字符集进行编码(UTF-8)
byte[] bytes = name.getBytes("GBK");//当前代码默认字符集进行编码(UTF-8)
System.out.println(bytes.length);
System.out.println(Arrays.toString(bytes));
//2、解码:把字节转换成对应的中文形式(编码前和编码后的字符集必须一致,否则乱码)
//String rs = new String(bytes);//默认UTF-8
String rs = new String(bytes,"GBK");//指定GBK解码
System.out.println(rs);
五、IO流概述
IO流也称为输入、输出流,就是用来读写数据的。
I表示intput,是数据从硬盘文件读入到内存的过程,称之输入,负责读。
O表示output,是内存程序的数据从内存到写出到硬盘文件的过程,称之输出,负责写。
总结流的四大类:
字节输入流:以内存为基准,来自磁盘文件/网络中的数据以字节的形式读入到内存中去的流称为字节输入流。
字节输出流:以内存为基准,把内存中的数据以字节写出到磁盘文件或者网络中去的流称为字节输出流。
字符输入流:以内存为基准,来自磁盘文件/网络中的数据以字符的形式读入到内存中去的流称为字符输入流。
字符输出流:以内存为基准,把内存中的数据以字符写出到磁盘文件或者网络介质中去的流称为字符输出流。
六、字节流的使用
(一)文件字节输入流: 每次读取一个字节
FilelnputStream
//1、创建一个文件字节输入流管道与源文件接通。
InputStream is = new FileInputStream("file-io-app\\src\\data.txt");
//2、读取一个字节返回(每次读取一滴水)
int b1 = is.read();
System.out.println((char)b1);
int b2 = is.read();
System.out.println((char)b2);
//3、可以使用循环
int b;
while ((b = is.read()) != -1) {
System.out.print((char) b);
}
文件字节输入流,每次读取一个字节的api是哪个?
方法名称 |
说明 |
public int read0 |
每次读取一个字节返回,如果字节已经没有可读的返回-1 |
每次读取一个字节存在什么问题?
性能较慢
读取中文字符输出无法避免乱码问题
(二)文件字节输入流:每次读取一个字节数组
方法名称 |
说明 |
public int read0 |
每次读取一个字节返回,如果字节已经没有可读的返回-1 |
public int read(bytell buffer) |
每次读取一个字节数组返回,如果字节已经没有可读的返回-1 |
InputStream is = new FileInputStream("file-io-app\\src\\data.txt");
byte[] buffer = new byte[5];//5B 读取5个字节
int len = is.read(buffer);
System.out.println("读取了几个字节:"+len);
// String rs = new String(buffer,2,6); //指定读取第几个字符,取多少倒出多少
String rs = new String(buffer);
System.out.println(rs);
//用循环读取
InputStream is = new FileInputStream("file-io-app\\src\\data.txt");
//每次读取1个字节数组
byte[] buffer = new byte[3];
int len;
while ((len = is.read(buffer)) != -1) {
System.out.print(new String(buffer,0,len));
}
每次读取一个字节数组存在什么问题?
读取的性能得到了提升
读取中文字符输出无法避免乱码问题
(三)文件字节输入流:一次读完全部字节
1、如何使用字节输入流读取中文内容输出不乱码呢?
定义一个与文件一样大的字节数组,一次性读取完文件的全部字节
2、直接把文件数据全部读取到一个字节数组可以避免乱码,是否存在问题?
如果文件过大,字节数组可能引起内存溢出。
方式一
自己定义一个字节数组与文件的大小一样大,然后使用读取字节数组的方法,一次性读取完成
方法名称 |
说明 |
public int read(bytell buffer) |
每次读取一个字节数组返回,如果字节已经没有可读的返回-1 |
File f = new File("file-io-app\\src\\data.txt");
InputStream is = new FileInputStream(f);
//定义一个字节数组与文件的大小一样大。
byte[] buffer = new byte[(int) f.length()];
int len = is.read(buffer);
System.out.println(new String(buffer));
方式二
官方为字节输入流Inputstream提供了如下API可以直接把文件的全部数据读取到一个字节数组中
//使用官方提供的AIP readAllBytes
File f = new File("file-io-app\\src\\data.txt");
InputStream is = new FileInputStream(f);
byte[] buffer = is.readAllBytes();
System.out.println(new String(buffer));
(四)文件字节输出流:写字节数据到文件
1. 文件字节输出流:FileOutputStream
作用:以内存为基准,把内存中的数据以字节的形式写出到磁盘文件中去的流
构造器 |
说明 |
public FileOutputStream(File file) |
创建字节输出流管道与源文件对象接通 |
public FileOutputStream(File file,boolean append) |
创建字节输出流管道与源文件对象接通可追加数据 |
public FileOutputstream(string filepath) |
创建字节输出流管道与源文件路径接通 |
public FileOutputStream(String filepath,boolean append) |
创建字节输出流管道与源文件路径接通,可追加数据 |
2. 文件字节输出流(FileOutputStream)写数据出去的API
方法名称 |
说明 |
public void write(int a) |
写一个字节出去 |
public void write(byte[] buffer) |
写一个字节数组出去 |
public void write(byte[] buffer ,int pos ,int len) |
写一个字节数组的一部分出去 |
3.流的关闭与刷新
方法 |
说明 |
flush0 |
刷新流,还可以继续写数据 |
close() |
关闭流,释放资源,但是在关闭之前会先刷新流。一旦关闭,就不能再写数据 |
//1、创建一个文件字节输出流管道与目标文件接通(不用创建,会自动生成)
// OutputStream os = new FileOutputStream("file-io-app/src/out04.txt");//会先清空之前的数据,写入新数据
OutputStream os = new FileOutputStream("file-io-app/src/out04.txt", true);//不清空源文件,追加数据
// 2、写数据出去
// a.public void write(int a):写一个字节出去
os.write('a');
os.write(98);
os.write("\r\n".getBytes());//换行
// b.public void write(byte[] buffer):写一个字节数组出去。
byte[] buffer = {'c', 78, 98, 99};
os.write(buffer);
//写入中文
byte[] buffer2 = "我是中国人".getBytes();
os.write(buffer2);
os.write("\r\n".getBytes());//换行
// c.public void write(byte[] buffer ,int pos ,int Len):写一个字节数组的一部分出去。
byte[] buffer3 = {'f', 'g', 'h', 'j'};
os.write(buffer3, 1, 3);//从第二个字节存入到第四个字节
//os.flush(); //写数据必须,刷新数据 可以继续使用流
os.close();//释放资源,关闭包含了刷新,关闭后流不可以使用了(使用释放资源可以不用刷新)
(五)文件拷贝
需求:
把某个视频复制到其他目录下的“b.avi“
思路:
① 根据数据源创建字节输入流对象
② 根据目的地创建字节输出流对象
③ 读写数据,复制视频
④ 释放资源
try {
//1、创建一个字节输入流管道与源文件接通
InputStream is = new FileInputStream("C:\\Users\\28430\\Pictures\\视频项目\\library – JavaOk.java 2023-02-09 11-32-35.mp4");
//2、创建一个字节流输出流管道与目标文件接通
OutputStream os = new FileOutputStream("D:\\code\\new.mp4");
//3、定义一个字节数组转移数据
byte[] buffer = new byte[1024];//1KB
int len;//记录每次读取的字节数
while ((len = is.read(buffer)) != -1) {
os.write(buffer,0,len);
}
System.out.println("复制完成!");
//4、关闭流
os.close();
is.close();
} catch (Exception e) {
e.printStackTrace();
}
如果前面的代码崩了的话就没有办法执行关闭流了,下一小节资源的释放可以解决这个问题:finally
七、资源释放的方式
try-catch-finally
finally: 在异常处理时提供finally块来执行所有清除操作,比如说IO流中的释放资源
特点: 被finally控制的语句最终一定会执行,除非JVM退出
异常处理标准格式: try....catch...finally
try-catch-finally格式
FileOutputStream fos=null;
try {
fos = new FileOutputStream("a.txt");
fos.write(97);
} catch (IOException e) {
e.printStackTrace();
} finally {
//无论代码是正常结束还是出现异常,都会执行finally
if(fos!=null) //判断fos有没有使用,有的话再关闭
fos.close();
}
try-with-resource
自动释放资源,代码简洁
try(定义流对象){
可能出现异常的代码
}catch(异常类名 变量名){
异常的处理代码
}
资源用完最终自动释放
try(
//这里只能放置对象,用完会自动关闭:自动调用资源对象的cloes方法关闭资源
FileOutputStream fos = new FileOutputStream("a.txt");
) {
fos.write(97);
} catch (IOException e) {
e.printStackTrace();
}
八、字符流的使用
1、字节流读取中文输出可能会存在什么问题?
会乱码。或者内存溢出
2、读取中文输出,哪个流更合适,为什么?
字符流更合适,最小单位是按照单个字符读取的
(一)文件字符输入流-一次读取一个字符
// 1、创建一个字符输入流管道与源文件接通
Reader fr = new FileReader("file-io-app\\src\\data06.txt");
// 2、读取一个字符返回,没有可读的字符了返回-1
// int code = fr.read();
// System.out.print((char)code);
// 3、使用循环读取字符
int code;
while ((code = fr.read()) != -1){
System.out.print((char) code);
}
总结:
1.文件字符输入流,每次读取一个字符的api是哪个?
方法名称 |
说明 |
public int read() |
每次读取一个字符返回,如果字节已经没有可读的返回-1 |
2.字符流的好处,每次读取一个字符存在什么问题?
读取中文字符不会出现乱码(如果代码和文件编码一致)
性能较慢
(二)文件字符输入流-一次读取一个字符数组
文件字符输入流:FileReader
作用:以内存为基准,把磁盘文件中的数据以字符的形式读取到内存中去。
方法名称 |
说明 |
public int read0 |
每次读取一个字节返回,如果字节已经没有可读的返回-1 |
public int read(char[] buffer) |
每次读取一个字节数组返回,如果字节已经没有可读的返回-1 |
// 1、创建一个文件字符输入流与源文件接通
Reader fr = new FileReader("file-io-app/src/data07.txt");
// 2、用循环,每次读取一个字符数组的数据。 1024 + 1024 + 8
char[] buffer = new char[1024]; // 1K字符
int len;
while ((len = fr.read(buffer)) != -1) {
String rs = new String(buffer, 0, len);
System.out.print(rs);
}
每次读取一个字符数组的优势?
读取的性能得到了提升
(三)文件字符输出流
文件字符输出流(FileWriter) 写数据出去的API
方法名称 |
写一个字符 |
void write(int c) |
说明 |
void write(charIl cbuf) |
写入一个字符数组 |
void write(charil cbuf int offint len) |
写入字符数组的一部分 |
void write(String str) |
写一个字符串 |
void write(String str, int off int len) |
写一个字符串的一部分 |
void write(int c) |
写一个字符 |
// 1、创建一个字符输出流管道与目标文件接通
// Writer fw = new FileWriter("file-io-app/src/out08.txt"); // 覆盖管道,每次启动都会清空文件之前的数据
Writer fw = new FileWriter("file-io-app/src/out08.txt", true); // 覆盖管道,每次启动都会清空文件之前的数据
// a.public void write(int c):写一个字符出去
fw.write(98);
fw.write('a');
fw.write('徐'); // 不会出问题了
fw.write("\r\n"); // 换行
// b.public void write(String c)写一个字符串出去
fw.write("abc我是中国人");
fw.write("\r\n"); // 换行
// c.public void write(char[] buffer):写一个字符数组出去
char[] chars = "abc我是中国人".toCharArray();
fw.write(chars);
fw.write("\r\n"); // 换行
// d.public void write(String c ,int pos ,int len):写字符串的一部分出去
fw.write("abc我是中国人", 0, 5);
fw.write("\r\n"); // 换行
// e.public void write(char[] buffer ,int pos ,int len):写字符数组的一部分出去
fw.write(chars, 3, 5);
fw.write("\r\n"); // 换行
// fw.flush();// 刷新后流可以继续使用
fw.close(); // 关闭包含刷线,关闭后流不能使用
九、字节流、字符流如何选择使用?
字节流适合做一切文件数据的拷贝(音视频,文本)字节流不适合读取中文内容输出
字符流适合做文本文件的操作(读,写)