一、 File和IO流
- File是java.io包下的类, File类的对象,用于代表当前操作系统的文件(可以是文件、或文件夹)
- 注意:File类只能对文件本身进行操作,不能读写文件里面存储的数据
IO流
- 用于读写数据的(可以读写文件,或网络中的数据…)
1. 创建对象
- File对象既可以代表文件、也可以代表文件夹。
- File封装的对象仅仅是一个路径名,这个路径可以是存在的,也允许是不存在的。
package com.file;
import java.io.File;
public class FileTest {
public static void main(String[] args) {
//1.创建一个file对象,指代某个具体的文件 路径使用/或者\\
//File file = new File("E:\\Desktop\\Two\\日期\\202402\\Java\\workspace\\JavaStudy\\day07\\src\\com\\file\\test.txt");
//File file = new File("E:/Desktop/Two/日期/202402/Java/workspace/JavaStudy/day07/src/com/file/test.txt");
File file = new File("E:/Desktop/Two/日期/202402/Java/workspace/JavaStudy/day07/src/com/file/test.txt");
//File file1 = new File("D:" + File.separator + "resource" + File.separator + "ab.txt");
System.out.println(file.length());//文件大小
//可以指代一个文件夹,但是大小仅仅为文件夹本身的大小
File file1 = new File("C:\\Users\\13492\\Desktop\\dir");
System.out.println(file1.length());
//File对象可以指代一个不存在的文件路径
File file2 = new File("C:\\Users\\13492\\Desktop\\dir");
System.out.println(file2.exists());
System.out.println(file2.length());
//绝对路径不推荐,带盘符,相对路径不带盘符,此处的相对路径时相对于工程目录,即idea打开的项目的路径
File file3 = new File("JavaStudy/day07/src/com/file/test.txt");
System.out.println(file3.length());
}
}
2. 常用方法
判断文件类型、获取文件信息
package com.file;
import java.io.File;
import java.text.SimpleDateFormat;
public class FileTest1 {
public static void main(String[] args) {
//1.创建文件对象,代替某个文件
File file = new File("day07/src/com/file/test.txt");
//2.public boolean exists():判断当前文件对象,对应的文件路径是否存在,存在返回true.
System.out.println(file.exists());
//3、public boolean isFile():判断当前文件对象指代的是否是文件,是文件返回true,反之。
System.out.println(file.isFile());
//4、public boolean isDirectory) :判断当前文件对象指代的是否是文件夹,是文件夹返回true,反之。
System.out.println(file.isDirectory());
//5.public String getName():获取文件的名称(包含后缀)
System.out.println(file.getName());
// 6.public long length():获取文件的大小,返回字节个数
System.out.println(file.length());
// 7.public long lastHodified():获取文件的最后修改时间。
long l = file.lastModified();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println(sdf.format(l));
// 8.public String getPath():获取创建文件对象时,使用的路径
System.out.println(file.getPath());
// 9.public String getAbsolutePath():获取绝对路径
System.out.println(file.getAbsoluteFile());
}
}
创建文件、删除文件
// 1、public boolean createNewFile():创建一个新文件(文件内容为空),创建成功返回true,反之。
File file1 = new File("day07/src/com/file/test1.txt");
System.out.println(file1.createNewFile());
// 2、public boolean mkdir():用于创建文件夫,注意:只能创建一级文件夹
File file2 = new File("day07/src/com/file/test");
System.out.println(file2.mkdir());
// 3、public boolean mkdirs():用于创建文件夹,注意:可以创建多级文件夹
File file3 = new File("day07/src/com/file/test/test/test");
System.out.println(file3.mkdirs());
// 4、public boolean delete():删除文件,或者空文件,注意:不能删除非空文件夹。
File file4 = new File("day07/src/com/file/test/test/test");
System.out.println(file4.delete());
遍历文件夹
package com.file;
import java.io.File;
import java.util.Arrays;
public class FileTest2 {
public static void main(String[] args) {
//1、 public String[] list()获取当前目录下所有的一级文件名称到一个字符串数组中去返回。
File file = new File("day07/src/com/file/test/test");
System.out.println(Arrays.toString(file.list()));
//2、public File[]listFiles():(爾点)获取当前目录下所有的"一级文件对象"到一个文件对象数组中去返回(点)
File file1 = new File("day07/src/com/file/test/test");
File[] files = file1.listFiles();
System.out.println(Arrays.toString(files));
}
}
使用listFiles方法时的注意事项:
- 当主调是文件,或者路径不存在时,返回null
- 当主调是空文件夹时,返回一个长度为0的数组
- 当主调是一个有内容的文件夹时,将里面所有一级文件和文件夹的路径放在File数组中返回
- 当主调是一个文件夹,且里面有隐藏文件时,将里面所有文件和文件夹的路径放在File数组中返回,包含隐藏文件
- 当主调是一个文件夹,但是没有权限访问该文件夹时,返回null
3. 方法递归
- 递归是一种算法,在程序设计语言中广泛应用
- 从形式上说:方法调用自身的形式称为方法递归(recursion)
- 直接递归:方法自己调用自己。
- 间接递归:方法调用其他方法,其他方法又回调方法自己
- 递归如果没有控制好终止,会出现递归死循环,导致栈内存溢出错误。
package com.file;
public class Recursion {
public static void main(String[] args) {
System.out.println(test1(6));
System.out.println(eatPeach(1));
}
public static int test1(int a) {
if(a==1) return 1;
return a*test1(a-1);
}
public static int eatPeach(int n){
if(n==10) return 1;
return (eatPeach(n+1)+1)*2;
}
}
递归算法的三要素
- 递归的公式:f(n)= f(n-1)*n;
- 递归的终结点:f(1)
- 递归的方向必须走向终结点:
文件搜索
package com.file;
import java.io.File;
import java.io.IOException;
public class FileTest4 {
public static void main(String[] args) throws IOException {
// File file = new File("day07/src/com/file/test/test");
// System.out.println(Arrays.toString(file.listFiles()));
// String s= "day07\\src\\com\\file\\test\\test\\test.txt";
// System.out.println(Arrays.toString(s.split("\\\\")));
findFile(new File("E:\\InstalledSoftware"), "douyin.exe");
}
/**
* 去目录下搜索某个文件
*
* @param dir 目录
* @param f 要搜索的文件名称
* @return
*/
public static void findFile(File dir, String f) throws IOException {
//1.把非法的情况都拦截住
if (dir == null || !dir.exists()) return;
//2.dir不是null,存在,一定是目录对象。
// 获取当前目录下的全部一级文件对象
File[] files = dir.listFiles();
//3.判断当前目录下是否存在一级文件对象,以及是否可以拿到一级文件对象
if (files != null) {
for (File file : files) {
if (file.isFile() && file.getName().contains(f)) {
System.out.println(file.getAbsolutePath());
Runtime runtime = Runtime.getRuntime();
runtime.exec(file.getAbsolutePath());
} else {
findFile(file, f);
}
}
}
}
}
4. 字符集
- ASClI(American standard Code for Information Interchange): 美国信息交换标准代码,包括了英文、符号等。
- 标准ASCI使用1个字节存储一个字符 首尾是0 总共可表示128个字符,对美国佬来说完全够用。
GBK(汉字内码扩展规范,国标)
-
汉字编码字符集,包含了2万多个汉字等字符 GBK中一个中文字符编码成两个字节的形式存储
-
注意:GBK兼容了ASCII字符集。
-
GBK规定:汉字的第一个字节的第一位必须是1
Unicode字符集(统一码,也叫万国码) -
Unicode是国际组织制定的,可以容纳世界上所有文字、符号的字符集。
-
UTF-32有容乃大我4个字节表示一个字符 奢份!占存储空间,通信效率变低!
-
UTF-8是Unicode字符集的一种编码方案,采取可变长编码方案,共分四个长度区:1个字节,2个字节,3个字节,4个字节
- 英文字符、数字等只占1个字节(兼容标准ASCII编码),汉字字符占用3个字节
- 注意:技术人员在开发时都应该使用UTF-8编码!
- 英文字符、数字等只占1个字节(兼容标准ASCII编码),汉字字符占用3个字节
-
ASCII字符集:只有英文、数字、符号等,占1个字节
-
GBK字符集:汉字占2个字节,英文、数字占1个字节
-
UTF-8字符集:汉字占3个字节,英文、数字占1个字节
-
注意1:字符编码时使用的字符集,和解码时使用的字符集必须一致,否则会出现乱码
-
注意2:英文,数字一般不会乱码,因为很多字符集都兼容了ASCII编码
字符集的编码、解码操作
编码:把字符按照指定字符集编码成字节。
解码:把字节按照指定字符集解码成字符。
Java代码完成对字符的编码
Java代码完成对字符的解码
import java.io.UnsupportedEncodingException;
import java.util.Arrays;
public class FileTest5 {
public static void main(String[] args) throws UnsupportedEncodingException {
//1.encode
String data = "a我b";
byte[] bytes = data.getBytes();//默认是按照平台字符集进行编码的。
System.out.println(Arrays.toString(bytes));
//按照指定字符集进行编码
byte[] bytes1 = data.getBytes("GBK");
System.out.println(Arrays.toString(bytes1));
//2. decode
String s = new String(bytes);//按照平台默认编码解码
System.out.println(s);
String s1 = new String(bytes1,"GBK");
System.out.println(s1);
}
}
5. IO流
- 字节输入流:以内存为基准,来自磁盘文件/网络中的数据以字节的形式读入到内存中去的流
- 字节输出流:以内存为基准,把内存中的数据以字节写出到磁盘文件或者网络中去的流
- 字符输入流:以内存为基准,来自磁盘文件/网络中的数据以字符的形式读入到内存中去的流
- 字符输出流:以内存为基准,把内存中的数据以字符写出到磁盘文件或者网络介质中去的流
文件字节输入流:每次读取一个字节
文件字节输入流:每次读取多个字节
文件字节输入流:一次读取完全部字节
文件字节输出流:写字节出去
5.1 FileInputStream(文件字节输入流)
作用:以内存为基准,可以把磁盘文件中的数据以字节的形式读入到内存中去。
package com.file;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
public class FileTest6 {
public static void main(String[] args) throws IOException {
File file = new File("day07/src/com/file/test.txt");
//创建文件字节输入流管道,与源文件接通
InputStream fileInputStream = new FileInputStream(file);
//简化写法,推荐使用
FileInputStream fileInputStream1 = new FileInputStream("day07/src/com/file/test.txt");
//开始读取文件的字节数据
//每次读取一个字节返回,如果没有数据了,返回-1
// System.out.println((char) fileInputStream.read());
// System.out.println((char) fileInputStream.read());
//使用循环改造上述代码
//读取数据的性能很差
//读取汉字输出会乱码且无法避免的
//流使用完毕之后,必须关闭!释放系统资源!
int i;
while ((i = fileInputStream1.read()) !=- 1){
System.out.print((char)i);
}
fileInputStream.close();
fileInputStream1.close();
}
}
文件字节输入流:每次读取多个字节到字节数组中去,返回读取的字节数量,读取完毕会返回-1
package com.file;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
public class FileTest7 {
public static void main(String[] args) throws IOException {
//1.创建一个字节输入流对象代表字节输入流管道与源文件接通
InputStream is = new FileInputStream("day07/src/com/file/test.txt");
//2.开始读取文件中的字节数据,每次读取多个字节
byte[] bytes = new byte[3];
int read = is.read(bytes);
String s = new String(bytes,0,read);
System.out.println(s);
System.out.println("当次读取的字节个数:"+read);
int read1 = is.read(bytes);
//注意:读取多少,倒出多少
String s1 = new String(bytes,0,read1);
System.out.println(s1);
System.out.println("当次读取的字节个数:"+read1);
//3、使用循环改造 一次读取多个字节 程序性能大幅提升 系统调用次数减少 也不能避免读取汉字会出现乱码的问题
byte[] bytes1 = new byte[3];
int len;
while ((len=is.read(bytes1))!=-1){
String s2 = new String(bytes1, 0, len);
System.out.print(s2);
}
is.close();
}
}
注意事项:使用FileInputStream每次读取多个字节,读取性能得到了提升,但读取汉字输出还是会乱码
1、使用字节流读取中文,如何保证输出不乱码,怎么解决?
定义一个与文件一样大的字节数组,一次性读取完文件的全部字节。
文件字符输入流:一次读取完全部字节 可以解决中文读取乱码的问题
- 方式一:自己定义一个字节数组与被读取的文件大小一样大,然后使用该字节数组,一次读完文件的全部字节。
- 方式二:java官方为InputStream提供了如下方法,可以直接把文件的全部字节读取到一个字节数组中返回。
package com.file;
import java.io.*;
public class FileTest8 {
public static void main(String[] args) throws IOException {
//1、一次性读取完文件的全部字节到一个字节数组中去。
// 创建一个字节给入流管道与源文件接通
InputStream is = new FileInputStream("day07/src/com/file/test.txt");
//2、准备一个字节数姐,大小与文件的大小正好一样大。
File file = new File("day07/src/com/file/test.txt");
long length = file.length();
byte[] bytes = new byte[(int) length];
int read = is.read(bytes);
String s = new String(bytes);
System.out.println(s);
System.out.println(read);
//3、需要注释第二部分代码
byte[] bytes1 = is.readAllBytes();
String s1 = new String(bytes1);
System.out.println(s1);
}
}
1、直接把文件数据全部读取到一个字节数组可以避免乱码,是否存在问题?
- 如果文件过大,创建的字节数组也会过大,可能引起内存溢出。
- 读写文本内容更适合用字符流
- 字节流 适合做数据的转移,如:文件复制等
5.2 FileOutputStream(文件字节输出流)
作用:以内存为基准,把内存中的数据以字节的形式写出到文件中去
package com.file;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
public class FileTest9 {
public static void main(String[] args) throws IOException {
//1、创建一个字节输出流管道与目标文件接通
//覆盖管道:覆盖之前的数据
// OutputStream fileOutputStream = new FileOutputStream("day07/src/com/file/out.txt");
//追加数据的管道
OutputStream fileOutputStream = new FileOutputStream("day07/src/com/file/out.txt",true);
//2、开始写字节数据出去
fileOutputStream.write(97);
fileOutputStream.write('b');
//fileOutputStream.write('是');
byte[] bytes = "我爱你中国abc!".getBytes();
fileOutputStream.write(bytes);
fileOutputStream.write(bytes,0,15);
fileOutputStream.write("\r\n".getBytes());//换行,支持各个平台的换行\r\n
fileOutputStream.close();
}
}
复制文件:可以复制一切文件的
- 字节流非常适合做一切文件的复制操作
- 任何文件的底层都是字节,字节流做复制,是一字不漏的转移完全部字节,只要复制后的文件格式一致就没问题!
package com.file;
import java.io.*;
public class FileTest10 {
public static void main(String[] args) throws IOException {
//需求:复制图片
//1、创建一个字节给入流价道与源文件接通
InputStream is = new FileInputStream("day07/src/com/file/test/49.jpg");
//2、创建一个字节给出流管道与目标文件接通。
OutputStream out = new FileOutputStream("day07/src/com/file/test/50.jpg");
//3、创建一个字节数组,负责转移字节数据。
byte[] bytes = new byte[1024];
//4、从字节输入流中读取字节数据,写出去到字节输出流中。读多少写出去多少。
int len;
while ((len=is.read(bytes))!=-1){
out.write(bytes,0,len);
}
//后创建的流先关掉 注意 可以避免一定问题的
out.close();
is.close();
}
}
5.3 释放资源的方式
中间出异常时,close方法无法被触发,无法释放资源。
try-catch-finally臃肿
- finally代码区的特点:无论try中的程序是正常执行了,还是出现了异常,最后都一定会执行finally区,除非JVM终止。
- 作用:一般用于在程序执行完成后进行资源的释放操作(专业级做法)。
package com.file;
public class FileTest11 {
public static void main(String[] args) {
try {
System.out.println(10/2);
System.out.println(chu(1, 2));
// Runtime runtime = Runtime.getRuntime();
// runtime.exit(0);
return;
}catch (Exception e){
e.printStackTrace();
}
// System.out.println("===执行了一次===");
finally {
System.out.println("===finally执行了一次===");
}
}
public static int chu(int a, int b){
try {
return a/b;
}catch (Exception e){
e.printStackTrace();
return -1;
}finally {//千万不要再finally中return数据
return 111;
}
}
}
package com.file;
import java.io.*;
public class FileTest10 {
public static void main(String[] args) throws IOException {
InputStream is = null;
OutputStream out = null;
try {
//需求:复制图片
//1、创建一个字节给入流价道与源文件接通
is = new FileInputStream("day07/src/com/file/test/49.jpg");
//2、创建一个字节给出流管道与目标文件接通。
out = new FileOutputStream("day07/src/com/file/test/50.jpg");
//3、创建一个字节数组,负责转移字节数据。
byte[] bytes = new byte[1024];
//4、从字节输入流中读取字节数据,写出去到字节输出流中。读多少写出去多少。
int len;
while ((len = is.read(bytes)) != -1) {
out.write(bytes, 0, len);
}
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
//后创建的流先关掉 注意 可以避免一定问题的
try {
if (out != null) {
out.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if (is != null) {
is.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
try-with-resource推荐
- 该资源使用完毕后,会自动调用其close()方法,完成对资源的释放!
package com.file;
import java.io.*;
public class FileTest10 {
public static void main(String[] args) throws IOException {
try (//注意:这里只能放置资源对象。(流对象)
//资源都是会实现一个AutoCloseable接口的
//需求:复制图片
//1、创建一个字节给入流价道与源文件接通
InputStream is = new FileInputStream("day07/src/com/file/test/49.jpg");
//2、创建一个字节给出流管道与目标文件接通。
OutputStream out = new FileOutputStream("day07/src/com/file/test/50.jpg");
) {
//3、创建一个字节数组,负责转移字节数据。
byte[] bytes = new byte[1024];
//4、从字节输入流中读取字节数据,写出去到字节输出流中。读多少写出去多少。
int len;
while ((len = is.read(bytes)) != -1) {
out.write(bytes, 0, len);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}