Java - day18 - File、递归、IO流
File类概述
File类概述
- File类在包java.io.File下、代表操作系统的文件对象(文件、文件夹)。
- File类提供了诸如:定位文件,获取文件本身的信息、删除文件、创建文件(文件夹)等功能,但不能读写文件内容。
File类创建对象
绝对路径和相对路径
实例
import java.io.File;
/**
目标:学会创建File对象定位操作系统的文件(文件 文件夹的)
*/
public class FileDemo {
public static void main(String[] args) {
// 1、创建File对象(指定了文件的路径)
// 路径写法: D:\resources\xueshan.jpeg
// D:/resources/xueshan.jpeg
// File.separator
// File f = new File("D:\\resources\\xueshan.jpeg");
// File f = new File("D:/resources/xueshan.jpeg");
File f = new File("D:" + File.separator+"Java-workspace\\workspace\\day18\\resource"+ File.separator +"beauty.jpg");
//D:\Java-workspace\workspace\day18\resource
long size = f.length(); // 是文件的字节大小
System.out.println(size);
// 2、File创建对象,支持绝对路径 支持相对路径(重点)
File f1 = new File("D:\\Java-workspace\\workspace\\day18\\resource\\beauty.jpg"); // 绝对路径
System.out.println(f1.length());
// 相对路径:一般定位模块中的文件的。 相对到工程下!!
File f2 = new File("src/data.txt");
System.out.println(f2.length());
// 3、File创建对象 ,可以是文件也可以是文件夹
File f3 = new File("D:\\resource");
System.out.println(f3.exists()); // 判断这个路径是否存在,这个文件夹存在否
}
}
File类的常用API
判断文件类型、获取文件信息
实例
import java.io.File;
import java.text.SimpleDateFormat;
/**
目标:File类的获取功能的API
- public String getAbsolutePath() :返回此File的绝对路径名字符串。
- public String getPath() : 获取创建文件对象的时候用的路径
- public String getName() : 返回由此File表示的文件或目录的名称。
- public long length() : 返回由此File表示的文件的长度。
*/
public class FileDemo02 {
public static void main(String[] args) {
// 1.绝对路径创建一个文件对象
File f1 = new File("D:\\Java-workspace\\workspace\\day18\\resource\\beauty.jpg");
// a.获取它的绝对路径。
System.out.println(f1.getAbsolutePath());
// b.获取文件定义的时候使用的路径。
System.out.println(f1.getPath());
// c.获取文件的名称:带后缀。
System.out.println(f1.getName());
// d.获取文件的大小:字节个数。
System.out.println(f1.length()); // 字节大小
// e.获取文件的最后修改时间
long time = f1.lastModified();
System.out.println("最后修改时间:" + new SimpleDateFormat("yyyy/MM/dd HH:mm:ss").format(time));
// f、判断文件是文件还是文件夹
System.out.println(f1.isFile()); // true
System.out.println(f1.isDirectory()); // false
System.out.println("-------------------------");
File f2 = new File("src\\data.txt");
// a.获取它的绝对路径。
System.out.println(f2.getAbsolutePath());
// b.获取文件定义的时候使用的路径。
System.out.println(f2.getPath());
// c.获取文件的名称:带后缀。
System.out.println(f2.getName());
// d.获取文件的大小:字节个数。
System.out.println(f2.length()); // 字节大小
// e.获取文件的最后修改时间
long time1 = f2.lastModified();
System.out.println("最后修改时间:" + new SimpleDateFormat("yyyy/MM/dd HH:mm:ss").format(time1));
// f、判断文件是文件还是文件夹
System.out.println(f2.isFile()); // true
System.out.println(f2.isDirectory()); // false
System.out.println(f2.exists()); // true
File file = new File("D:/");
System.out.println(file.isFile()); // false
System.out.println(file.isDirectory()); // true
System.out.println(file.exists()); // true
File file1 = new File("D:/aaa");
System.out.println(file1.isFile()); // false
System.out.println(file1.isDirectory()); // false
System.out.println(file1.exists()); // false
}
}
创建文件、删除文件功能
File类创建文件的功能
File类删除文件的功能
实例
import java.io.File;
import java.io.IOException;
/**
目标:File类的创建和删除的方法
- public boolean createNewFile() :当且仅当具有该名称的文件尚不存在时,
创建一个新的空文件。 (几乎不用的,因为以后文件都是自动创建的!)
- public boolean delete() :删除由此File表示的文件或目录。 (只能删除空目录)
- public boolean mkdir() :创建由此File表示的目录。(只能创建一级目录)
- public boolean mkdirs() :可以创建多级目录(建议使用的)
*/
public class FileDemo03 {
public static void main(String[] args) throws IOException {
File f = new File("src\\data2.txt");
// a.创建新文件,创建成功返回true ,反之 ,不需要这个,以后文件写出去的时候都会自动创建
System.out.println(f.createNewFile());
File f1 = new File("src\\data02.txt");
System.out.println(f1.createNewFile()); // (几乎不用的,因为以后文件都是自动创建的!)
// b.mkdir创建一级目录
File f2 = new File("D:\\Java-workspace\\workspace\\day18\\resource2");
System.out.println(f2.mkdir());
// c.mkdirs创建多级目录(重点)
File f3 = new File("D:\\Java-workspace\\workspace\\day18\\resource\\99\\22");
// System.out.println(f3.mkdir());
System.out.println(f3.mkdirs()); // 支持多级创建
// d.删除文件或者空文件夹
System.out.println(f1.delete());
File f4 = new File("D:\\Java-workspace\\workspace\\day18\\resource\\yu.jpg");
System.out.println(f4.delete()); // 占用一样可以删除
// 只能删除空文件夹,不能删除非空文件夹.
File f5 = new File("D:/resource/aaa");
System.out.println(f5.delete());
}
}
遍历文件夹
File类的遍历功能
实例
import java.io.File;
import java.util.Arrays;
/**
目标:File针对目录的遍历
- public String[] list():
获取当前目录下所有的"一级文件名称"到一个字符串数组中去返回。
- public File[] listFiles()(常用):
获取当前目录下所有的"一级文件对象"到一个文件对象数组中去返回(重点)
*/
public class FileDemo04 {
public static void main(String[] args) {
// 1、定位一个目录
File f1 = new File("D:\\Java-workspace\\workspace\\day18\\resource");
String[] names = f1.list();
for (String name : names) {
System.out.println(name);
}
// 2.一级文件对象
// 获取当前目录下所有的"一级文件对象"到一个文件对象数组中去返回(重点)
File[] files = f1.listFiles();
for (File f : files) {
System.out.println(f.getAbsolutePath());
}
// 注意事项
File dir = new File("D:\\Java-workspace\\workspace\\day18\\resource");
File[] files1 = dir.listFiles();
System.out.println(Arrays.toString(files1));
}
}
方法递归
递归的形式和特点
什么是方法递归?
- 方法直接调用自己或者间接调用自己的形式称为方法递归(recursion)。
- 递归做为一种算法在程序设计语言中广泛应用。
递归的形式
- 直接递归:方法自己调用自己。
- 间接递归:方法调用其他方法,其他方法又回调方法自己。
什么是递归死循环?
- 递归的方法无限调用自己,无法终止,出现栈内存溢出。
递归的算法流程、核心要素
实例
/**
目标:递归的算法和执行流程
*/
public class RecursionDemo02 {
public static void main(String[] args) {
System.out.println(f(5)); //120
}
public static int f(int n){
if(n == 1){
return 1;
}else {
return f(n - 1) * n;
}
}
}
递归解决问题的思路:
- 把一个复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解。
递归算法三要素大体可以总结为:
- 递归的公式:f(n) = f(n-1)*n;
- 递归的终结点:f(1)
- 递归的方向必须走向终结点。
递归常见案例
实例
/**
目标:1 - n求和
*/
public class RecursionDemo03 {
public static void main(String[] args) {
System.out.println(f(5));
}
public static int f(int n){
if(n == 1){
return 1;
}else {
return f(n - 1) + n;
}
}
}
递归的经典问题
实例
public class RecursionDemo04 {
public static void main(String[] args) {
System.out.println(f(1)); //1534
System.out.println(f(2)); //766
System.out.println(f(3)); //382
}
public static int f(int n){
if(n == 10){
return 1;
}else {
return 2 * f(n + 1) + 2;
}
}
}
非规律化递归案例 - 文件搜索
实例
import java.io.File;
import java.io.IOException;
/**
目标:去D判断搜索 eDiary.exe文件
*/
public class RecursionDemo05 {
public static void main(String[] args) {
// 2、传入目录 和 文件名称
searchFile(new File("D:/") , "eDiary.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());
// 启动它。
try {
Runtime r = Runtime.getRuntime();
r.exec(file.getAbsolutePath());
} catch (IOException e) {
e.printStackTrace();
}
}
}else {
// 8、是文件夹,需要继续递归寻找
searchFile(file, fileName);
}
}
}
}else {
System.out.println("对不起,当前搜索的位置不是文件夹!");
}
}
}
非规律化递归案例 - 啤酒问题
实例
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
/**
目标:啤酒2元1瓶,4个盖子可以换一瓶,2个空瓶可以换一瓶,
请问10元钱可以喝多少瓶酒,剩余多少空瓶和盖子。
答案:15瓶 3盖子 1瓶子
*/
public class RecursionDemo06 {
// 定义一个静态的成员变量用于存储可以买的酒数量
public static int totalNumber; // 总数量
public static int lastBottleNumber; // 记录每次剩余的瓶子个数
public static int lastCoverNumber; // 记录每次剩余的盖子个数
public static void main(String[] args) {
// 1、拿钱买酒
buy(10);
System.out.println("总数:" + totalNumber);
System.out.println("剩余盖子数:" + lastCoverNumber);
System.out.println("剩余瓶子数:" + lastBottleNumber);
}
public static void buy(int money){
// 2、看可以立马买多少瓶
int buyNumber = money / 2; // 5
totalNumber += buyNumber;
// 3、把盖子 和瓶子换算成钱
// 统计本轮总的盖子数 和 瓶子数
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);
}
Integer[] arr2 = new Integer[]{11, 22, 33};
Arrays.sort(arr2);
}
}
字符集
常见字符集介绍
字符集基础知识
- 计算机底层不可以直接存储字符的。
- 计算机中底层只能存储二进制(0、1)。
- 二进制是可以转换成十进制的。
- 结论:计算机底层可以表示十进制编号。计算机可以给人类字符进行编号存储,这套编号规则就是字符集。
ASCII字符集
ASCII使用1个字节存储一个字符,一个字节是8位,总共可以表示128个字符信息。
GBK
- GBK是中国的码表,一个中文以两个字节的形式存储。
Unicode码表
- Unicode是万国码,以UTF-8编码后一个中文一般以三个字节的形式存储。
- UTF-8也要兼容ASCIl编码表。
- 技术人员都应该使用UTF-8的字符集编码。
- 编码前和编码后的字符集需要一致,否则会出现中文乱码。
字符集的编码、解码操作
String编码
String解码
实例
import java.io.UnsupportedEncodingException;
import java.util.Arrays;
/**
目标:学会自己进行文字的编码和解码,为以后可能用到的场景做准备。
*/
public class Test {
public static void main(String[] args) throws Exception {
// 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流概述
- I0流也称为输入、输出流,就是用来读写数据的。
- I表示intput,是数据从硬盘文件读入到内存的过程,称之输入,负责读。
- O表示output,是内存程序的数据从内存到写出到硬盘文件的过程,称之输出,负责写。
IO流的分类
- 字节输入流:以内存为基准,来自磁盘文件/网络中的数据以字节的形式读入到内存中去的流称为字节输入流。
- 字节输出流:以内存为基准,把内存中的数据以字节写出到磁盘文件或者网络中去的流称为字节输出流。
- 字符输入流:以内存为基准,来自磁盘文件/网络中的数据以字符的形式读入到内存中去的流称为字符输入流。
- 字符输出流:以内存为基准,把内存中的数据以字符写出到磁盘文件或者网络介质中去的流称为字符输出流。
字节流的使用
文件字节输入流:每次读取一个字节
文件字节输入流:FilelnputStream
实例
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
public class FileInputStreamDemo01 {
public static void main(String[] args) throws Exception {
InputStream is = new FileInputStream("src\\data.txt");
int b;
while (( b = is.read() ) != -1){
System.out.print((char) b);
}
}
}
文件字节输入流:每次读取一个字节数组
文件字节输入流:FilelnputStream
实例
import java.io.FileInputStream;
import java.io.InputStream;
/**
目标:使用文件字节输入流每次读取一个字节数组的数据。
*/
public class FileInputStreamDemo02 {
public static void main(String[] args) throws Exception {
// 1、创建一个文件字节输入流管道与源文件接通
InputStream is = new FileInputStream("src/data.txt");
// 3、改进使用循环,每次读取一个字节数组
byte[] buffer = new byte[3];
int len; // 记录每次读取的字节数。
while ((len = is.read(buffer)) != -1) {
// 读取多少倒出多少
System.out.print(new String(buffer, 0 , len));
}
}
}
文件字节输入流:一次读完全部字节
如何使用字节输入流读取中文内容输出不乱码呢?
直接把文件数据全部读取到一个字节数组可以避免乱码,是否存在问题?
- 如果文件过大,字节数组可能引起内存溢出。
实例
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
/**
目标:使用文件字节输入流一次读完文件的全部字节。可以解决乱码问题。
*/
public class FileInputStreamDemo03 {
public static void main(String[] args) throws Exception {
// 1、创建一个文件字节输入流管道与源文件接通
File f = new File("file-io-app/src/data03.txt");
InputStream is = new FileInputStream(f);
// 读取全部字节数组
byte[] buffer = is.readAllBytes();
System.out.println(new String(buffer));
}
}
文件字节输出流:写字节数据到文件
文件字节输出流: FileOutputStream
文件字节输出流(FileOutputStream)写数据出去的API
流的关闭与刷新
实例
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.OutputStream;
public class OutputStreamDemo04 {
public static void main(String[] args) throws Exception {
// 1、创建一个文件字节输出流管道与目标文件接通
OutputStream os = new FileOutputStream("src/out.txt" , true); // 追加数据管道
// OutputStream os = new FileOutputStream("file-io-app/src/out04.txt"); // 先清空之前的数据,写新数据进入
// 2、写数据出去
// a.public void write(int a):写一个字节出去
os.write('a');
os.write(98);
os.write("\r\n".getBytes()); // 换行
// os.write('徐'); // [ooo]
// b.public void write(byte[] buffer):写一个字节数组出去。
byte[] buffer = {'a' , 97, 98, 99};
os.write(buffer);
os.write("\r\n".getBytes()); // 换行
byte[] buffer2 = "我是中国人".getBytes();
// byte[] buffer2 = "我是中国人".getBytes("GBK");
os.write(buffer2);
os.write("\r\n".getBytes()); // 换行
// c. public void write(byte[] buffer , int pos , int len):写一个字节数组的一部分出去。
byte[] buffer3 = {'a',97, 98, 99};
os.write(buffer3, 0 , 3);
os.write("\r\n".getBytes()); // 换行
// os.flush(); // 写数据必须,刷新数据 可以继续使用流
os.close(); // 释放资源,包含了刷新的!关闭后流不可以使用了
}
}
文件拷贝
字节流适合做一切文件数据的拷贝吗?
实例
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
/**
* 目标:学会使用字节流完成文件的复制(支持一切文件类型的复制)
*/
public class CopyDemo05 {
public static void main(String[] args) {
try {
// 1、创建一个字节输入流管道与原视频接通
InputStream is = new FileInputStream("src/out.txt");
// 2、创建一个字节输出流管道与目标文件接通
OutputStream os = new FileOutputStream("src/out02.txt");
// 3、定义一个字节数组转移数据
byte[] buffer = new byte[1024];
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();
}
}
}
资源释放的方式
try-catch-finally
- finally:在异常处理时提供finally块来执行所有清除操作,比如说IO流中的释放资源。
- 特点:被finally控制的语句最终一定会执行,除非JVM退出。
- 异常处理标准格式:
try....catch...finally
try-catch-finally 格式
实例
import java.io.*;
/**
* 目标:学会使用finally释放资源。
*/
public class TryCatchFinallyDemo1 {
public static void main(String[] args) {
InputStream is = null;
OutputStream os = null;
try {
// System.out.println(10/ 0);
// 1、创建一个字节输入流管道与原视频接通
is = new FileInputStream("src/out.txt");
// 2、创建一个字节输出流管道与目标文件接通
os = new FileOutputStream("src/out03.txt");
// 3、定义一个字节数组转移数据
byte[] buffer = new byte[1024];
int len; // 记录每次读取的字节数。
while ((len = is.read(buffer)) != -1){
os.write(buffer, 0 , len);
}
System.out.println("复制完成了!");
// System.out.println( 10 / 0);
} catch (Exception e){
e.printStackTrace();
} finally {
// 无论代码是正常结束,还是出现异常都要最后执行这里
System.out.println("========finally=========");
try {
// 4、关闭流。
if(os!=null)os.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
if(is != null) is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
System.out.println(test(10, 2));
}
public static int test(int a , int b){
try {
int c = a / b;
return c;
}catch (Exception e){
e.printStackTrace();
return -111111; // 计算出现bug.
}finally {
System.out.println("--finally--");
// 哪怕上面有return语句执行,也必须先执行完这里才可以!
// 开发中不建议在这里加return ,如果加了,返回的永远是这里的数据了,这样会出问题!
return 100;
}
}
}
try-with-resource
- finally虽然可以用于释放资源,但是释放资源的代码过于繁琐,有没有办法简化?
JDK 7和JDK9中都简化了资源释放操作
try-catch-resource的作用
- 自动释放资源、代码简洁.
实例
import java.io.*;
/**
* 目标:JDK 9释放资源的方式:可以了解下。
*/
public class TryCatchResouceDemo3 {
public static void main(String[] args) throws Exception {
// 这里面只能放置资源对象,用完会自动关闭:自动调用资源对象的close方法关闭资源(即使出现异常也会做关闭操作)
// 1、创建一个字节输入流管道与原视频接通
InputStream is = new FileInputStream("src/out.txt");
// 2、创建一个字节输出流管道与目标文件接通
OutputStream os = new FileOutputStream("src/out4.txt");
try ( is ; os ) {
// 3、定义一个字节数组转移数据
byte[] buffer = new byte[1024];
int len; // 记录每次读取的字节数。
while ((len = is.read(buffer)) != -1){
os.write(buffer, 0 , len);
}
System.out.println("复制完成了!");
} catch (Exception e){
e.printStackTrace();
}
}
}
字符流的使用
字节流读取中文输出可能会存在什么问题?
- 会乱码。或者内存溢出。
读取中文输出,哪个流更合适,为什么?
- 字符流更合适,最小单位是按照单个字符读取的。
文件字符输入流 - 一次读取一个字符
文件字符输入流:Reader
实例
import java.io.File;
import java.io.FileReader;
import java.io.Reader;
public class FileReaderDemo01 {
public static void main(String[] args) throws Exception {
// 目标:每次读取一个字符。
// 1、创建一个字符输入流管道与源文件接通
Reader fr = new FileReader("src\\data.txt");
// 2、读取一个字符返回,没有可读的字符了返回-1
// int code = fr.read();
// System.out.print((char)code);
//
// int code1 = fr.read();
// System.out.print((char)code1);
// 3、使用循环读取字符
int code;
while ((code = fr.read()) != -1){
System.out.print((char) code);
}
}
}
文件字符输入流 - 一次读取一个字符数组
文件字符输入流:FileReader
实例
import java.io.FileReader;
import java.io.Reader;
public class FileReaderDemo02 {
public static void main(String[] args) throws Exception {
// 1、创建一个文件字符输入流与源文件接通
Reader fr = new FileReader("src/data.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
流的关闭与刷新
实例
import java.io.File;
import java.io.FileWriter;
import java.io.Writer;
public class FileWriterDemo03 {
public static void main(String[] args) throws Exception {
// 1、创建一个字符输出流管道与目标文件接通
// Writer fw = new FileWriter("file-io-app/src/out08.txt"); // 覆盖管道,每次启动都会清空文件之前的数据
Writer fw = new FileWriter("src/out9.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(); // 关闭包含刷线,关闭后流不能使用
}
}
Comments | NOTHING