day10【File类、递归、IO流、字节流、字符流】
今日目标:
File类(了解)
递归(了解)
IO流(概念)
字节流(重点,**复制文件案例**)
字符流(重点)
一.File类
1.File类的作用
代表磁盘上某个文件或者某个文件夹
2.File类的构造
"public File(String path); -- 以指定的路径创建一个File对象
public File(String parent,String child); -- 以父路径和子路径创建一个File对象
public File(File parent,String child); -- 以父File对象和子路径创建一个File对象
/**
* File的构造方法
*/
public class FileConstructorDemo {
public static void main(String[] args) {
//1.public File(String path);
File f1 = new File("C:\\Users\\Administrator\\Desktop\\temp\\aaa\\2.java");
System.out.println(f1);
File f2 = new File("C:\\Users\\Administrator\\Desktop\\temp\\aaa\\bbb");
System.out.println(f2);
//2.public File(String parent,String child);
File f3 = new File("C:\\Users\\Administrator\\Desktop\\temp", "aaa\\2.java");
System.out.println(f3);
//3.public File(File parent,String child);
File parent = new File("C:\\Users\\Administrator\\Desktop\\temp");
File f4 = new File(parent, "aaa\\2.java");
System.out.println(f4);
}
}
3.相对路径和绝对路径的概念
绝对路径: 以盘符开头的路径,比如:"C:\\Users\\Administrator\\Desktop\\temp"
相对路径: 不需要盘符开头,直接写路径: "aaa.txt",相对路径默认从当前项目的根目录下开始
假设当前项目的根目录为: "E:\heima126"
那么以下两种写法都是正确的:
File f5 = new File("E:\\heima126\\abc\\aaa.txt");
File f6 = new File("abc\\aaa.txt");
4.File类的获取方法
public String getName(); 获取名字(不带路径)
public String getAbsolutePath(); 获取绝对路径
public String getPath(); 获取构造时传入的路径
public long length(); 获取文件的大小(单位是字节) 该方法只能用于获取文件的大小,不能获取文件夹大小
/**
* File的获取方法
*/
public class FileGetMethodDemo {
public static void main(String[] args) {
//1.创建File对象
File f1 = new File("E:\\heima126\\abc\\aaa.txt");
//2.获取文件名
String name = f1.getName();
System.out.println(name);
//3.获取绝对路径
String absolutePath = f1.getAbsolutePath();
System.out.println(absolutePath);
//4.获取路径
String path = f1.getPath();
System.out.println(path);
//5.获取大小(字节)
long len = f1.length();
System.out.println(len);
}
}
5.File类的判断方法
public boolean exists(); 判断是否存在
注意: 只有存在之后,再去判断是文件还是文件夹才有意义!!!否则都会返回false!!!
public boolean isFile(); 判断是否为文件
public boolean isDirectory(); 判断是否为文件夹
/**
* File的判断方法
*/
public class FileBooleanMethodDemo {
public static void main(String[] args) {
//1.创建File对象
File f1 = new File("E:\\heima126\\abc\\ccc");
//2.判断是否存在
boolean exists = f1.exists();
System.out.println("该文件是否存在:"+exists);
//3.判断是否文件
boolean isFile = f1.isFile();
System.out.println("是文件吗?" + isFile);
//4.判断是否文件夹
boolean isDirectory = f1.isDirectory();
System.out.println("是文件夹吗?"+isDirectory);
}
}
6.File类的创建删除方法
public boolean createNewFile(); 创建文件,返回成功或者失败
public boolean mkdir(); 创建文件夹,返回成功或者失败
public boolean delete(); 删除文件或者文件夹
/**
* File的创建和删除方法
*/
public class FileCreateAndDeleteDemo {
public static void main(String[] args) throws IOException {
//1.创建File对象
File f1 = new File("E:\\heima126\\abc\\123.txt");
File f2 = new File("E:\\heima126\\abc\\aaa");
//2.创建文件
boolean newFile = f1.createNewFile();
System.out.println("是否成功:"+newFile);
//3.创建文件夹
boolean mkdir = f2.mkdir();
System.out.println("是否成功:"+mkdir);
//4.删除文件或者文件夹
boolean delete = f1.delete();
System.out.println("删除文件是否成功"+delete);
boolean delete1 = f2.delete();
System.out.println("删除文件夹是否成功"+delete1);
//注意:删除文件夹时,只能删除空文件夹
}
}
7.File类遍历目录的方法
public String[] list(); 遍历出当前目录下所有的文件和文件夹的名字
"public File[] listFiles(); 遍历出当前目录下所有的文件和文件夹的File对象
/**
* File的遍历目录方法
*/
public class FileForeachDemo {
public static void main(String[] args) {
//1.创建File对象,必须代表文件夹
File fileDir = new File("E:\\heima126\\abc");
//2.遍历名字
String[] filenames = fileDir.list();
for (String filename : filenames) {
System.out.println(filename);
}
//3.遍历File对象
File[] files = fileDir.listFiles();
for (File file : files) {
System.out.println(file);
System.out.println("是文件夹吗?"+file.isDirectory());
System.out.println("是文件吗?"+file.isFile());
}
}
}
二.递归
1.什么是递归?
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lNKbGSal-1578312610326)(img/image-20191229101827367.png)]
什么是递归????
在一个方法中调用方法本身,称为递归调用(recursion)
递归案例:
public class RecursionDemo01 {
public static void main(String[] args) {
method();
}
/**
* java.lang.Stack(栈)Overflow(溢出)Error(错误)
*/
public static void method() {
System.out.println("method....");
method();
}
}
无限递归最终会导致栈内存溢出,为什么???
因为方法不断入栈,最终栈内存不足,导致溢出
我们需要合理的利用递归,满足以下两个条件
a.不能是无限递归,递归必须有一个结束点
public class RecursionDemo01 {
public static void main(String[] args) {
method(10);
}
/**
* java.lang.Stack(栈)Overflow(溢出)Error(错误)
*/
public static void method(int n) {
if (n == 0) {
//递归的出口
return;
}
System.out.println("method....");
method(n-1);
}
}
b.递归到底出口之前,递归次数不能太多(大概1w次左右)
2.递归求和案例
需求:求1+2+3..n的和,使用递归
使用递归的步骤:
a.定义一个方法
b.找规律,调用方法自己
c.给递归一个出口
/**
* 递归求和案例
*/
public class RecursionDemo02 {
public static void main(String[] args) {
//需求:求1+2+3..n的和,使用递归
//调用方法
int sum = getSum(100);
System.out.println("总和:"+sum);
}
/**
* 使用递归的步骤:
* a.定义一个方法
* b.找规律,调用方法自己
* c.给递归一个出口
*/
//a.定义一个方法
public static int getSum(int n) {
//c.给递归一个出口
if (n == 1) {
return 1;
}
//b.找规律,调用方法自己
//1+2+3...n == (1+2...n-1)+n
//getSum(n) == getSum(n-1)+n
return getSum(n-1) + n;
}
/**
* 使用循环
*/
public static int getSum01(int n) {
int sum = 0;
for (int i = 1; i <= n; i++) {
sum += i;
}
return sum;
}
}
- 求和案例的过程图
3.递归求积案例
需求:1*2*3...n的积,使用递归
使用递归的步骤:
a.定义一个方法
b.找规律,调用方法自己
c.给递归一个出口
/**
* 递归求积案例
*/
public class RecursionDemo03 {
public static void main(String[] args) {
//需求:1*2*3...n的积,使用递归
int ji = getJi(6);
System.out.println("总积:"+ji);
}
/**
* 使用递归的步骤:
* a.定义一个方法
* b.找规律,调用方法自己
* c.给递归一个出口
* 人迭代 神递归
*/
//a.定义一个方法
public static int getJi(int n) {
//c.给递归一个出口
if (n == 1) {
return 1;
}
//b.找规律,调用方法自己
//1*2*3..n = (1*2*3..n-1)*n
//getJi(n) = getJi(n-1)*n
return getJi(n-1)*n;
}
/**
* 使用循环
*/
public static int getJi01(int n) {
int ji = 1;
for (int i = 1; i <= n; i++) {
ji *= i;
}
return ji;
}
}
4.文件搜索案例
需求:
搜索 C:\Users\Administrator\Desktop\temp\aaa 目录中的 .txt 文件。
public class RecursionDemo04 {
public static void main(String[] args) {
//需求:
//搜索 C:\Users\Administrator\Desktop\temp\aaa 目录中的 .txt 文件。
File fileDir = new File("C:\\Users\\Administrator\\Desktop\\temp\\aaa");
//调用方法
printTxtFiles(fileDir);
}
/**
* 定义方法,在指定的目录下,查找.txt文件
*/
public static void printTxtFiles(File fileDir) {
//1.遍历目录
File[] files = fileDir.listFiles();
//2.遍历数组
for (File file : files) {
//3.判断是否为.txt文件
if (file.isFile() && file.getName().endsWith(".txt")) {
//4.找到了一个txt文件
System.out.println("找到了一个txt文件:"+file);
} else if (file.isDirectory()) {
//5.进入该文件夹继续遍历
printTxtFiles(file);
}
}
}
}
三.IO流的概述
1.什么是IO流
流:一种比喻,把数据传输的过程比喻成流
I: Input,输入流,数据从外部设备到内存,所以我们也称为读流
O: Output,输出流,数据从内存到外部设备,所以我们也称为写流
2.IO流的分类
按照流的方向分:
输出流
输入流
按照流中数据类型分:
字节流
字符流
3.Java中IO的四大流
字节输出流:顶层父类OutputStream(抽象类)
字节输入流:顶层父类InputStream(抽象类)
字符输出流:顶层父类Writer(抽象类)
字符输入流:顶层父类Reader(抽象类)
扩展:Java中所有的IO流,命名是非常规范:
规范: 功能+父类名
比如: FileInputStream 文件的字节输入流,从文件读取字节数据的
FileWriter 文件的字符输出流,向文件中写字符数据的
四.字节流
1.万物皆对象和IO流一切皆字节
Java面向对象的语言: 思想,万物皆对象(万事万物都可以使用对象来描述)
IO流中:思想,一切皆字节(电脑中所有的文件,都是由字节组成)
问题1:既然文件是由字节组成的,那么为什么打开之后看到的不是0101???
因为我们使用软件打开的,不同软件会对0101进行不同的解析
问题2:能不能打开文件时,不解析,就想看到0101这些字节数据???
可以,使用专门的二进制查看器即可(比如:Binary Viewe.exe这个二进制查看器)
2.字节输出流
顶层父类:OutputStream(抽象类)
public void flush(); -- 刷新缓冲区(对于字节输出流来说没用,空方法)
public void close(); -- 关闭资源
public void write(int b); 写出一个字节
public void write(byte[] bs); 写出一个字节数组
public void write(byte[] bs,int startIndex,int len); 写出一个字节数组的一部分
3.FileOutputStream类的使用
文件的字节输出流: 向文件中写出字节数据
-
a.构造方法
public FileOutputStream(String path); -- 创建文件字节输出流,指定要写的文件 public FileOutputStream(File file); -- 创建文件字节输出流,指定要写的文件 public class FileOutputStreamDemo01 { public static void main(String[] args) throws Exception { //1.创建一个FileOutputStream对象 FileOutputStream fos = new FileOutputStream("1.txt"); /** * 以上构造干了三件事!!! * a.创建对象fos * b.判断文件是否存在 * 如果不存在,则会自动创建一个空文件 * 如果存在,则会清空文件的内容 * c.让对象fos和文件1.txt绑定 */ } }
-
b.写字节数据的三个方法
public void write(int b); 写出一个字节 public void write(byte[] bs); 写出一个字节数组 public void write(byte[] bs,int startIndex,int len); 写出一个字节数组的一部分 public class FileOutputStreamDemo02 { public static void main(String[] args) throws Exception { //1.创建一个FileOutputStream对象 FileOutputStream fos = new FileOutputStream("1.txt"); //2.调用write方法,向文件中写数据 //a.一个字节 fos.write(97); // 0110 0001 //问题: 现在我想打开文件之后,看到97,怎么办?? fos.write(57); fos.write(55); //b.一个字节数组 // byte[] bs = {97,98,99,100}; byte[] bs = "Java".getBytes(); fos.write(bs); //c.一个字节数组的一部分 fos.write(bs,1,2); } }
-
c.如何追加续写
非常简单,只要使用以下两个构造即可 public FileOutputStream(String path,boolean append); -- 创建文件字节输出流,指定要写的文件 public FileOutputStream(File file,boolean append); -- 创建文件字节输出流,指定要写的文件 public class FileOutputStreamDemo03 { public static void main(String[] args) throws Exception { //1.创建一个FileOutputStream对象,true表示追加(续写) FileOutputStream fos = new FileOutputStream("1.txt", true); //2.写数据 fos.write("java".getBytes()); } }
-
d.如何换行
非常简单,只要向文件中写一个代表换行的符号即可: windows \r\n Linux \n MacOS \r (如果是MacOSX以后版本,是\n) public class FileOutputStreamDemo04 { public static void main(String[] args) throws Exception { //1.创建一个FileOutputStream对象,true表示追加(续写) FileOutputStream fos = new FileOutputStream("1.txt"); //2.写数据 for (int i = 0; i < 10; i++) { fos.write("php\r\n".getBytes()); //写一个换行符 // fos.write("\r\n".getBytes()); } } }
-
关闭和刷新
a.flush()方法,刷新缓冲区(对于字节输出流来说没用,空方法) b.close()方法,释放资源(注意流一旦使用完毕,一定要立刻释放资源) public class FileOutputStreamDemo04 { public static void main(String[] args) throws Exception { //1.创建一个FileOutputStream对象,true表示追加(续写) FileOutputStream fos = new FileOutputStream("1.txt"); //2.写数据 fos.write("php\r\n".getBytes()); //3.刷新 //fos.flush(); //4.释放资源 fos.close(); } }
4.字节输入流
顶层父类:InputStream(抽象类)
public void close(); -- 释放资源
public int read(); -- 读一个字节
public int read(byte[] bs); -- 读一个字节数组,返回值表示实际读取的字节数
5.FileInputStream类的使用
文件的字节输入流:从文件中读字节数据的
-
a.构造方法
public FileInputStream(String path); -- 创建文件的字节输入流,指定文件的路径 public FileInputStream(File file); -- 创建文件的字节输入流,指定文件的路径 public class FileInputStreamDemo01 { public static void main(String[] args) throws Exception { //1.创建一个FileInputStream对象 FileInputStream fis = new FileInputStream("1.txt"); /** * 以上构造干了三件事!!!! * a.创建对象fis * b.判断文件是否存在 * 如果不存在,直接抛出异常,FileNotFoundException(不创建!!!) * 如果存在,不清空!! * c.绑定对象fis和文件1.txt */ } }
-
b.读取一个字节
public class FileInputStreamDemo02 { public static void main(String[] args) throws Exception { //1.创建一个FileInputStream对象 FileInputStream fis = new FileInputStream("1.txt"); //2.读数据 //a.读一个字节 // int b = fis.read(); // System.out.println((char) b); //=========一次读取一个字节的标准循环代码=========== int b = 0; // 用来保存读取到的字节 /** * (b = fis.read()) != -1 * 以上代码干了三件事!!! * a.先读 fis.read(); * b.赋值 b = 读取到的字节 * c.判断 b != -1 */ while ((b = fis.read()) != -1) { System.out.print((char) b); } //3.释放资源 fis.close(); } }
-
c.读取一个字节数组
public class FileInputStreamDemo03 { public static void main(String[] args) throws Exception { //1.创建一个FileInputStream对象 FileInputStream fis = new FileInputStream("1.txt"); //2.读数据 //b.一次读取一个字节数组 // byte[] bs = new byte[4]; // int len = fis.read(bs); // System.out.println("实际读取"+len+"个字节"); // System.out.println(new String(bs,0,len)); //===========一次读取一个字节数组的标准循环代码=========== byte[] bs = new byte[4]; //用来保存读到的字节 int len = 0; // 用来保存实际读取的字节个数 /** * (len = fis.read(bs)) != -1 * 以上代码干了三件事!!! * a.读取 fis.read(bs); * b.赋值 len = 读取的实际长度 * c.判断 len != -1 */ while ((len = fis.read(bs)) != -1) { System.out.print(new String(bs,0,len)); } //3.释放资源 fis.close(); } }
-
一次读取一个字节数组的原理
-
6.字节流练习:复制图片
-
a.复制文件的过程(画图)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ooiEEIKJ-1578312610327)(img/image-20191229161527843.png)]
-
b.代码实现(代码演示)
/** * 复制文件 */ public class CopyFileDemo { public static void main(String[] args) throws Exception { long start = System.currentTimeMillis(); // copyPic();//复制成功,耗时:39817毫秒 copyPic02();//复制成功,耗时:15毫秒 long end = System.currentTimeMillis(); System.out.println("复制成功,耗时:" + (end - start) + "毫秒"); } public static void copyPic02() throws IOException { //1.源文件,使用FileInputStream FileInputStream fis = new FileInputStream("G:\\upload\\1.png"); //2.目标文件,使用FileOutputStream FileOutputStream fos = new FileOutputStream("copy.png"); //3.复制文件 //b.一个一个字节数组快快复制 byte[] bs = new byte[1024 * 10]; int len = 0; while ((len = fis.read(bs)) != -1) { fos.write(bs, 0, len); } //4.释放资源 fos.close(); fis.close(); } public static void copyPic() throws Exception { //1.源文件,使用FileInputStream FileInputStream fis = new FileInputStream("G:\\upload\\1.png"); //2.目标文件,使用FileOutputStream FileOutputStream fos = new FileOutputStream("copy.png"); //3.复制文件 //a.一个一个字节慢慢复制 int b = 0; //保存字节 while ((b = fis.read()) != -1) { fos.write(b); } //4.释放资源 fos.close(); fis.close(); } }
五.字符流
1.为什么要用字符流
字节流是以字节为单位来操作数据
而字符中有中文字符,中文字符占2-3个字节
如果是字节流操作含有中文的文件时,会出现只读取某个中文一部分的情况
所以说:如果读存英文文件,使用字节流足够了,但是读含有中文的文化,为了防止乱码必须使用字符流
2.字符输入流
顶层父类:Reader(抽象类)
public void close(); -- 释放资源
public int read(); -- 读一个字符
public int read(char[] chs); -- 读一个字符数组,返回值代表实际读取的字符个数
3.FileReader类的使用
文件的字符输入流:从文件中读取字符数据的
-
a.构造方法
public FileReader(String path); -- 创建文件字符输入流,指定文件的路径 public FileReader(File file); -- 创建文件字符输入流,指定文件的路径 public class FileReaderDemo01 { public static void main(String[] args) throws Exception { //1.创建一个FileReader对象 FileReader fr = new FileReader("1.txt"); /** * 以上构造干了三件事!!! * a.创建对象fr * b.判断文件是否存在 * 如果不存在,直接抛出异常,FileNotFoundException * 如果存在,不会清空!!! * c.绑定fr和1.txt */ //3.释放资源 fr.close(); } }
-
b.读取一个字符
public class FileReaderDemo02 { public static void main(String[] args) throws Exception { //1.创建一个FileReader对象 FileReader fr = new FileReader("1.txt"); //2.读数据 //a.一次读一个字符 // int ch = fr.read(); // System.out.println((char) ch); //========一次读取一个字符的标准循环代码========= int ch = 0; //用来保存读取到的字符 /** * (ch = fr.read()) != -1 * 以上代码干了三件事! * a.先读 fr.read(); * b.赋值 ch = 读到的字符数据 * c.判断 ch != -1 */ while ((ch = fr.read()) != -1) { System.out.print((char) ch); } //3.释放资源 fr.close(); } }
-
c.读取一个字符数组
public class FileReaderDemo03 { public static void main(String[] args) throws Exception { //1.创建一个FileReader对象 FileReader fr = new FileReader("1.txt"); //2.读数据 //b.一次读一个字符数组 //char[] chs = new char[4]; //int len = fr.read(chs); //System.out.println("实际读取" + len + "个字符"); //System.out.println(new String(chs,0,len)); //===========一次读取一个字符数组的标准循环代码========== char[] chs = new char[4]; int len = 0; /** * (len = fr.read(chs)) != -1 * 以上代码干了三件事!!! * a.先读 fr.read(chs); * b.赋值 len = 实际读取的字符数 * c.判断 len != -1 */ while ((len = fr.read(chs)) != -1) { System.out.println(new String(chs, 0, len)); } //3.释放资源 fr.close(); } }
4.字符输出流
顶层父类:Writer(抽象类)
public void flush(); -- 刷新缓冲区(有用!!!)
public void close(); -- 释放资源
public void write(int ch); -- 写一个字符
public void write(char[] chs); -- 写一个字符数组
public void write(char[] chs,int startIndex,int len); -- 写一个字符数组一部分
public void write(String str); -- 写一个字符串
public void write(String str,int startIndex,int len); -- 写一个字符串的一部分
5.FileWriter类的使用
-
a.构造方法
public FileWriter(String path); -- 创建文件的字符输出流,指定文件的路径 public FileWriter(File file); -- 创建文件的字符输出流,指定文件的路径 public class FileWriterDemo01 { public static void main(String[] args) throws IOException { //1.创建一个FileWriter对象 FileWriter fw = new FileWriter(new File("2.txt")); /** * 以上构造干了三件事!! * a.创建对象fw * b.判断文件是否存在, * 如果不存在,创建文件 * 如果存在,清空文件 * c.绑定fw和2.txt */ //3.释放资源 fw.close(); } }
-
b.写出字符数据的三组方法
public void write(int ch); -- 写一个字符 public void write(char[] chs); -- 写一个字符数组 public void write(char[] chs,int startIndex,int len); -- 写一个字符数组一部分 public void write(String str); -- 写一个字符串 public void write(String str,int startIndex,int len); -- 写一个字符串的一部分 public class FileWriterDemo02 { public static void main(String[] args) throws IOException { //1.创建一个FileWriter对象 FileWriter fw = new FileWriter(new File("2.txt")); //2.写字符数据 //a.写一个字符 fw.write('a'); fw.write('中'); fw.write('国'); //b.写一个字符数组 char[] chs = {'中', '国', '万', '岁'}; fw.write(chs); //c.写一个字符数组的一部分 fw.write(chs,2,2); //d.写一个字符串 fw.write("Java万岁中国也万岁!"); //e.写一个字符串的部分 fw.write("Java万岁中国也万岁!",6,2); //3.释放资源 fw.close(); } }
-
c.关闭和刷新的区别
a.flush(),只是刷新缓冲区,流依然可以继续使用 b.close(),也有刷新缓冲区的功能,但是还会关闭流,流不能继续使用 public class FileWriterDemo04 { public static void main(String[] args) throws IOException { //1.创建一个FileWriter对象 FileWriter fw = new FileWriter(new File("2.txt")); //2.写字符数据 fw.write("你不好"); //3.刷新 fw.flush(); fw.write("讨厌"); //流正常使用 //4.释放资源 fw.close(); fw.write("讨厌死了");//流已经关闭 } }
-
d.续写和换行
如何续写: 非常简单,只要使用以下构造即可 public FileWriter(String path,boolean append); -- 创建文件的字符输出流,指定文件的路径 public FileWriter(File file,boolean append); -- 创建文件的字符输出流,指定文件的路径 如何换行:非常简单,只要向文件中写一个代表换行的符号即可 windows \r\n Linux \n MacOS \r public class FileWriterDemo03 { public static void main(String[] args) throws IOException { //1.创建一个FileWriter对象,true代表续写 FileWriter fw = new FileWriter(new File("2.txt"),true); //2.写字符数据 fw.write("你好\r\n中国"); //3.释放资源 fw.close(); } }
总结
能够说出File对象的创建方式 能够使用File类常用方法 能够遍历文件夹 "能够辨别相对路径和绝对路径 能够解释递归的含义 能够使用递归的方式计算5!的阶乘(1*2*3*4*5) 能够说出使用递归会内存溢出隐患的原因 "能够说出IO流的分类和功能 "能够使用字节输出流写出数据到文件 "能够使用字节输入流读取数据到程序 "能够理解读取数据read(byte[])方法的原理 =========能够使用字节流完成文件的复制====== "能够使用FileWriter写数据的5个方法 "能够说出FileWriter中关闭和刷新方法的区别 "能够使用FileWriter写数据实现换行和追加写 "能够使用FileReader读数据一次一个字符 "能够使用FileReader读数据一次一个字符数组