一、 File类概述
(1)File类概述
- File类在包java.io.File下,代表操作系统的文件对象(文件、文件夹)
- File类提供了诸如:定位文件、获取文件本身信息、删除文件、创建文件(文件夹)等功能
(2)File类创建对象
//1.创建File对象
//路径写法:C:\Users\Pictures\20200514200511.jpg
// C:/Users/Pictures/20200514200511.jpg
// File.separator
File f = new File("C:\\Users\\Pictures\\20200514200511.jpg");
long size = f.length();//是文件的字节大小
System.out.println(size);
//2.File创建对象,可以支持绝对路径,也可以支持相对路径
//相对路径:一般定位模块中的文件,相对到工程下
File f2 = new File("file-io/src/data.txt");
System.out.println(f2.length());
//3.File创建对象,可以是文件也可以是文件夹
File f3 = new File("D:\\resources");
System.out.println(f3.exists());//判断这个文件夹是否存在
二、File类的常用API
(1)判断文件类型,获取文件信息
//获取它的绝对路径
System.out.println(f3.getAbsolutePath());
//获取文件定义时使用的路径
System.out.println(f3.getPath());
//获取文件的名称,带后缀
System.out.println(f3.getName());
//获取文件的最后修改时间
long time = f3.lastModified();
System.out.println("最后修改时间:"+ new SimpleDateFormat("yyyy/MM/dd HH:mm:ss").format(time));
(2)创建文件、删除文件功能
//创建文件,成功返回true
File f1 = new File("file-io/src/data01.txt");
System.out.println(f1.createNewFile()); //几乎不用,因为以后文件自动创建
//mkdir创建一级目录
File f4 = new File("D:/Code/aaa");
System.out.println(f4.mkdir());
//mkdirs创建多级目录
File f5 = new File("D:/Code/bbb/ccc/ddd");
System.out.println(f5.mkdirs());
//删除文件或者空文件
System.out.println(f4.delete());
//只删除空文件夹,不能删非空
File f6 = new File("D/Code/aaa");
System.out.println(f6.delete());
(3)遍历文件夹
//1.定位一个目录
File f7 = new File("D/Code");
String[] names = f7.list();
for (String name : names) {
System.out.println(name);
}
//2.一级文件对象
//获取当前目录下所有的"一级文件对象"到一个文件对象数组中去返回
File[] files = f7.listFiles();
for (File ff : files) {
System.out.println(ff.getAbsolutePath());
}
listFiles 注意事项:
- 当调用者不存在时,返回null
- 当调用者是一个文件时,返回null
- 当调用者是一个空文件夹时,返回一个长度为0的数组
- 当调用者是一个有内容的文件夹时,将里面所有文件和文件夹的路径放在File数组中返回
- 当调用者是一个有隐藏的文件夹时,将里面所有的文件和文件夹的路径放在File数组中返回,包含隐藏内容。
三、方法递归
(1)递归的形式和特点
- 什么是方法递归?
- 方法直接调用自己或者间接调用自己的形式
- 递归作为一种算法在程序设计语言中广泛应用
- 递归的形式
- 直接递归:方法自己调用自己
- 间接递归:方法调用其他方法,其他方法又回调方法自己
(2)递归的算法流程、核心要素
- 把一个复杂的问题层层转换为一个与原问题相似的规模较小的问题来求解
- 递归算法三要素:
a. 递归公式
b. 递归的终结点
c. 递归的方向必须走向终结点
(3)递归常见案例
计算1-n的阶乘
public static int f(int n) {
if(n == 1){
return 1;
}else{
return f(n-1) *n;
}
}
计算1-n的和
public static int f2(int n){
if(n == 1){
return 1;
}else{
return f2(n-1)+n;
}
}
(4)递归的经典问题
猴子吃桃问题
public static int f3(int n){
if(n == 10){
return 1;
}else{
return 2 * f3(n-1) + 2;
}
}
(5)非规律化递归案例-文件搜索
从C:盘中,搜索出某个文件名称并输出绝对路径
public class Demo2 {
public static void main(String[] args) {
//传目录和文件名
searchFile(new File("D:/"),"Code");
}
/**
* 搜索某个目录下的全部文件,找到我们想要的文件
* @param dir 被搜索的原目录
* @param filename 被搜索的文件名称
*/
public static void searchFile(File dir,String filename){
//判断是否是目录
if(dir != null && dir.isDirectory()){
//可以找了
//提取当前目录下的一级文件对象
File[] file = dir.listFiles();
//判断是否存在一级文件对象
if(file != null && file.length>0){
for (File file1 : file) {
//判断当前遍历的一级文件对象是文件还是目录
if(file1.isFile()){
if(file1.getName().contains(filename)){
System.out.println("找到了"+file1.getAbsolutePath());
}
}else{
//是文件夹,需要继续做递归寻找
searchFile(file1,filename);
}
}
}
}else{
System.out.println("当前搜索的不是文件夹");
}
}
}
(6)非规律化递归案例-啤酒问题
public class Demo3 {
//定义一个静态成员变量用于存储可以买的酒的数量
public static int totalNumber; //总数量
public static int lastBottleNumber; //剩余瓶子个数
public static int lastCoverNumber; //剩余盖子个数
public static void main(String[] args) {
buy(10);
System.out.println("总数:"+totalNumber);
System.out.println("剩余盖子数:"+lastCoverNumber);
System.out.println("剩余瓶子数:"+lastBottleNumber);
}
private static void buy(int money) {
int buyNumber = money / 2;
totalNumber += buyNumber;
//把盖子和瓶子换算成钱
//统计本轮总的盖子数 和 瓶子数
int coverNumber = lastCoverNumber + buyNumber;
int bottleNumber = lastBottleNumber + buyNumber;
//统计可换算的钱
int allMoney = 0;
if(coverNumber >= 4){
allMoney += (coverNumber/4) *2;
}
lastCoverNumber = coverNumber % 4;
if(bottleNumber>=2){
allMoney += (bottleNumber /2) *2;
}
lastBottleNumber = bottleNumber % 2 ;
if(allMoney>=2){
buy(allMoney);
}
}
}
四、字符集
(1)常见字符集介绍
- 字符集基础知识
- 计算机底层不能直接存储字符,计算机底层中只能存储二进制
- 二进制可以转换成十进制
- 结论:计算机底层可以表示十进制编号。计算机可以给人类字符进行编号存储,这套编号规则就是字符集
- ASCII字符集:
- ASCII:包括了数字、英文、符号
- ASCII使用1个字节存储一个字符,一个字节是8位,总共可以表示128个字符信息,对于英文,数字来说够用
- GBK:
- window系统默认的码表,兼容ASCII码表,也包含了几万个汉字,并支持繁体汉字以及部分日韩文字
- GBK是中国的码表,一个中文以两个字节的形式存储,但不包括世界上所有国家的文字
- Unicode码表:
- unicode(统一码,万国码,单一码)是计算机学科领域的一项业界字符编码标准
- 容纳世界上大部分国家的所有常见文字和符号
- 由于Unicode会先通过UTF-8,UTF-16,以及UTF-32的编码成二进制后再存储到计算机,其中最常见的就是UTF-8
注意:
- Unicode是万国码,以UTF-8编码后一个中文一般以三个字节的形式存储;
- UTF-8也要兼容ASCII编码表;
- 技术人员应该使用UTF-8的字符集编码;
- 编码前和编码后的字符集需要一致,否则出现中文乱码。
(2)字符集的编码、解码操作
public static void main(String[] args) throws UnsupportedEncodingException {
//1.编码
String name = "abc我爱你中国";
//byte[] bytes = name.getBytes(); //以当前默认字符集进行编码(UTF-8)
byte[] bytes = name.getBytes("GBK"); //指定编码
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表示input,是数据从硬盘文件读入到内存的过程,称之为输入,负责读
- O表示output,是内存程序的数据从内存到写出到硬盘文件的过程,称之为输出,负责写
IO流的分类
- 按流的方向
- 按流中数据最小单位
总结:流的四大类
- 字节输入流:以内存为基准,来自磁盘文件/网络中的数据以字节的形式读入到内存中去的流称为字节输入流
- 字节输出流:以内存为基准,把内存中的数据以字节写出到磁盘文件或者网络中去的流称为字节输出流。
- 字符输入流:以内存为基准,来自磁盘文件/网络中的数据以字符的形式读入到内存中去的流称为字符输入流
- 字符输出流:以内存为基准,内存中的数据以字符写出到磁盘文件或者网络中去的流称为字符输出流。
六、字节流的使用
(1)文件字节输入流:每次读取一个字节
public static void main(String[] args) throws Exception {
//1.创建一个文件字节输入流管道与原文件接通
InputStream is = new FileInputStream("file-io\\src\\data.txt");
//2.读取一个字节返回
int b1 = is.read();
System.out.println((char)b1);
int b2 = is.read();
System.out.println((char)b2);
}
(2)文件字节输入流:每次读取一个字节数组
public static void main(String[] args) throws Exception {
//1.创建一个文件字节输入流管道与源文件接通
InputStream is = Files.newInputStream(Paths.get("D:\\file-io\\src\\data01.txt"));
//2.定义一个字节数组,用于读取字节数组
// byte[] byffer = new byte[3];
// int len = is.read(byffer);
// System.out.println("读取了几个字节:"+len);
// String rs = new String(byffer,0);
// System.out.println(rs);
//
// int len1 = is.read(byffer);
// System.out.println("读取了几个字节:"+len1);
// //读多少倒多少
// String rs1 = new String(byffer,0,len1);
// System.out.println(rs1);
//3. 改进使用循环,每次读取一个字节数组
byte[] buffer1 = new byte[3];
int length; //记录每次读取的字节数
while((length = is.read(buffer1)) != -1){
System.out.print(new String(buffer1,0,length));
}
}
(3)文件字节输入流:一次读完全部字节
- 如何使用字节输入流读取中文内容输出不乱码?
- 定义一个与文件一样大的字节数组,一次性读取完文件的全部字节
- 直接把文件数据全部读取到一个字节数组可以避免乱码,是否存在问题?
- 如果文件过大,字节数组可能引起内存溢出
public static void main(String[] args) throws Exception {
//1.创建一个文件字节输入流管道与源文件接通
File f = new File("D:/file-io\\src\\data01.txt");
InputStream is = new FileInputStream(f);
//2.定义一个字节数组,与文件大小一样大
// byte[] buffer = new byte[(int) f.length()];
// int len = is.read(buffer);
// System.out.println("读取了多少个字节:"+len);
// System.out.println("文件大小:"+f.length());
// System.out.println(new String(buffer));
byte[] buffer = is.readALLBytes(); //jdk9之后才有
System.out.println(new String(buffer));
}
(4)文件字节输出流:写字节数据到文件
public static void main(String[] args) throws Exception {
//1.创建一个文件字节输出流管道与目标文件接通
OutputStream os = new FileOutputStream("D:/file-io/src/out04.txt");
//2.写数据出去
//a.public void write(int a);写一个字节出去
os.write('a');
os.write(98);
//os.write('徐');
os.write("\r\n".getBytes()); //换行
//b.public void write(byte[] buffer);写一个字节数组出去
byte[] buffer = {'a',97,98,99};
os.write(buffer);
//byte[] buffer2 = "我是中国人".getBytes("GBK");
byte[] buffer2 = "我是中国人".getBytes();
os.write(buffer2);
//c.public void write(byte[] buffer, int pos, int len); 写一个字节数组的一部分出去
byte[] buffer3 = {'a',97,99,100};
os.write(buffer3,0,3);
//写数据必须刷新数据
//os.flush(); //可以继续用流
os.close(); //释放资源,包含了刷新 ,不可以使用流了
}
七、资源释放的方式
(1)try-catch-finally
- finally:在异常处理时提供finally块来执行所有清除操作,比如IO流中的释放资源
- 特点:被finally控制的语句最终一定会执行,除非JVM退出
- 异常处理标准格式:try…catch…finally
(2)try-catch-resource
finally太繁琐
resource自动释放资源,代码简洁