IO流详解
1. File
1.1 File类概述和构造方法
File:它是文件和目录路径名的抽象表示
-
文件和目录是可以通过File封装成对象的
-
对于File而言,其封装的并不是一个真正存在的文件,仅仅是一个路径名。它是可以存在的,也可以不存在的。将来要通过具体的操作把这个路径的内容转换为具体存在
构造方法:
方法名 | 说明 |
---|---|
File(String pathname) | 通过给定的路径名字符串转换为抽象路径名来创建新的File实例 |
File(String parent,String child) | 从父路径名字符串和子路径名字符串创建新的File实例 |
File(File parent,String child) | 从父抽象路径名和子路径字符串创建新的File实例 |
File f1=new File("D:\\FileCast\\java.txt");
System.out.println(f1);
File f2=new File("D:\\FileCast","java.txt");
System.out.println(f2);
File f3= new File("D:\\FileCast");
File f4= new File(f3,"java.txt");
System.out.println(f4);
1.2 File类创建功能
方法名 | 说明 |
---|---|
public boolean createNewFile() | 当具有该名称的文件不存在时,创建一个由该抽象路径名命名的新空文件 |
public boolean mkdir() | 创建由此抽象路径名命名的目录 |
public boolean mkdirs() | 创建由此抽象路径名命名的目录,包括任何必须但不存在的父目录 |
public static void main(String[] args) throws IOException {
File f1 = new File("D:\\FileCast\\java.txt");
System.out.println(f1.createNewFile());//如果文件存在,不创建文件,返回false 如果文件不存在,创建文件,返回true
File f2 = new File("D:\\FileCast\\Java");
System.out.println(f2.mkdir());//如果目录存在,不创建目录,返回false 如果目录不存在,创建目录,返回true
File f3 = new File("D:\\FileCast\\JavaSE\\HTML");
System.out.println(f3.mkdirs());//mkdirs创建多级目录
File f4 = new File("D:\\FileCast\\javase.txt");
System.out.println(f4.mkdir());//true->最终创建了一个目录:javase.txt
}
1.3 File类删除功能
方法名 | 说明 |
---|---|
public boolean delete() | 删除由此抽象路径名表示的文件或目录 |
File f1 = new File("D:\\FileCast\\java.txt");
System.out.println(f1.createNewFile());
System.out.println(f1.delete());//删除目录应当首先删除文件
删除目录时注意事项
如果一个目录中有内容(目录、文件),不能直接删除,应该先删除目录中的内容,最后删除目录
1.4 File类的判断和获取功能
方法名 | 说明 |
---|---|
public boolean isDirectory() | 测试此抽象路径名表示的File是否为目录 |
public boolean isFile() | 测试此抽象路径名表示的File是否为文件 |
public boolean exists() | 测试此抽象路径名表示的File是否存在 |
public String getAbsolutePath() | 返回此抽象路径名的绝对路径名字符串 |
public String getPath() | 将此抽象路径名转换为路径名字符串 |
public String getName() | 返回由此抽象路径名表示的文件和目录的名称 |
public String[] list() | 返回由此抽象路径名表示的文件和目录的名称字符串数组 |
public File[] listFiles() | 返回此抽象路径名表示的目录中的文件和目录的File对象数组 |
1.5 递归
概述:
以编程的角度来看,递归指的是方法定义中调用方法本身的现象
//斐波那契数列
int[] arr = new int[20];
arr[0] = 1;
arr[1] = 1;
for (int i = 2; i < arr.length; i++) {
arr[i] = arr[i - 1] + arr[i - 2];
}
System.out.println(arr[19]);
递归实现:
public static void main(String[] args) {
System.out.println(f(20));
}
public static int f(int n) {
if (n == 1 || n == 2) { //不加if判断://StackOverflowError:堆栈溢出,递归太深
return 1;
} else {
return f(n - 1) + f(n - 2);
}
}
递归解决问题要找到两个内容:
- 递归出口:否则会出现内存溢出
- 递归规则:与原问题相似的规模较小的问题
递归求阶乘:
public static void main(String[] args) {
System.out.println(f(5));
}
public static int f(int n) {
if (n == 1) {
return 1;
}else if (n == 0){
return 1;
} else {
return n * f(n - 1);
}
}
2. IO流
2.1 IO流概述和分类
概述
-
IO:输入/输出
-
流:是一种抽象概念,是对数据传输的总称。也就是说数据在设备间的传输称为流,流的本质是数据传输。
-
IO流就是用来处理设备间数据传输问题的
常见的应用:文件复制;文件上传;文件下载
IO流分类:
-
按照数据的流向:
- 输入流:读数据
- 输出流:写数据
-
按照数据类型来分
- 字节流:字节输入流、字节输出流
- 字符流:字符输入流、字符输出流
一般来说,我们说的IO流的分类是按照数据类型来分的
如果数据通过Windows自带的记事本软件打开,我们可以读出里面的内容,就用字符流,否则使用字节流,如果不知道用哪类型的流,就用字节流。
2.2 字节流写数据
字节流抽象基类
- InputStream:这个抽象类是表示字节输入流的所有类的超类
- OutputStream:这个抽象类是表示字节输出流的所有类的超类
- 子类名特点:子类名称都是以其父类作为子类名的后缀
FileOutputStream(String name):创建文件输出流以指定的名称写入文件
FileOutputStream fos=new FileOutputStream("MyIO\\fos.txt");
/*
* ①调用系统功能创建文件
* ②创建字节输出流对象
* ③让字节输出流对象指向创建好的文件*/
fos.write(97); //void write(int b):将指定的字节写入此文件输出流 输出结果:a 转换为ASCII码对应字符
fos.write(57); 9
fos.write(55); 7
//所有和IO操作相关的内容最终都要释放资源:void close():关闭此输出流并释放与此流关联的所有系统资源。关闭的流无法执行输出操作,也无法重新打开。
fos.close();
2.3 字节流写数据的三种方式
方法名 | 说明 |
---|---|
void write(int b) | 将指定的字节写入此文件输出流 |
void write(byte[] b) | 将b.length字节从指定的字节数组写入此文件输出流,一次写一个字节数组 |
void write(byte[] b,int off,int len) | 将len字节从指定的字节数组开始,从偏移量off开始写入此文件输出流,一次写一个字节数组的部分数据 |
FileOutputStream fos=new FileOutputStream("MyIO\\fos.txt");
//源码: this(name != null ? new File(name) : null, false);
//底层: FileOutputStream ofs=new FileOutputStream(new File("MyIO\\fos.txt"));
/*还可以写:File file=new File("MyIO\\fos.txt");
FileOutputStream fos2=new FileOutputStream(file);*/
//void write(int b):将指定的字节写入此文件输出流
fos.write(97);
fos.write(98);
fos.write(99);
fos.write(100);
fos.write(101);
//void write(byte[] b):将b.length字节从指定的字节数组写入此文件输出流,一次写一个字节数组
byte[] bys={97,98,99,100,101};
fos.write(bys);
//void write(byte[] b,int off,int len) 将len字节从指定的字节数组开始,从偏移量off开始写入此文件输出流,一次写一个字节数组的部分数据
fos.write(bys,0,bys.length);
2.4 字节流写数据的两个问题
字节流写数据如何实现换行
写完数据后加换行符
FileOutputStream fos=new FileOutputStream("MyIO\\fos.txt");
for (int i = 0; i < 10; i++) {
fos.write("hello".getBytes());
fos.write("\n".getBytes());//win:\r\n linux:\n mac:\r
}
fos.close();
字节流写数据如何实现追加写入
public FileOutputStream(File file,boolean append)
创建文件输出流以写入由指定的File对象表示的文件。 如果第二个参数是true ,则字节将写入文件的末尾而不是开头。
FileOutputStream fos=new FileOutputStream("MyIO\\fos.txt",true);
for (int i = 0; i < 10; i++) {
fos.write("hello".getBytes());
fos.write("\n".getBytes());//win:\r\n linux:\n mac:\r
}
fos.close();
2.5 字节流写数据加异常处理
finally:在异常处理时提供finally块来执行所有清楚操作,比如IO流中的释放资源
特点:被finally控制的语句一定会执行,除非JVM退出
try {
可能出现异常的代码
}catch (IOException e){
异常的处理代码
}finally {
执行所有清楚操作
}
FileOutputStream fos = null;
try {
fos = new FileOutputStream("MyIO\\fos.txt");
fos.write("hello".getBytes());
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
2.6 字节流读数据(一次读一个字节数据)
FileInputStream
:从文件系统中的文件获取输入字节
- FileInputStream(String name):通过打开实际文件的连接来创建一个FileInputStream,该文件由文件系统中的路径名name命名
使用字节输入流读数据的步骤:
① 创建字节输入流对象
② 调用字节输入流对象的读数据方法
③ 释放资源
FileInputStream fis = new FileInputStream("MyIO\\fos.txt");
/*
// int read():从该输入流读取一个字节数据
int by =fis.read();
System.out.println(by);//104
System.out.println((char)by);//h
//读取第二个数据
by =fis.read();
System.out.println(by);//104
System.out.println((char)by);//h
by =fis.read();
System.out.println(by);//10:换行
by =fis.read();
System.out.println(by);//-1
*/
/*
//如果到达文件末尾:-1
int by = fis.read();
while (by != -1) {
System.out.print((char) by);
by = fis.read();
}
*/
//⭐⭐⭐优化上面的程序
int by;
while ((by = fis.read()) != -1) {
System.out.print((char) by);
}
/*
* fis.read():读数据
* by = fis.read():把读到的数据复制给by
* by != -1:判断读取到的数据是否为-1
*/
fis.close();
2.7 字节流读数据(一次读一个字节数组数据)
FileInputStream fis=new FileInputStream("MyIO\\fos.txt");
//read(byte[] b) 从输入流中读取最多b.length的字节数并将它们存储到一个字节数组。
byte[] bys=new byte[5];
int len = fis.read(bys);
System.out.println(len);
//System.out.println(new String(bys));
/* 5
hello
*/
System.out.println(new String(bys,0,len));
len = fis.read(bys);
System.out.println(len);
//System.out.println(new String(bys));
/* 5
worl
*/
System.out.println(new String(bys,0,len));
len = fis.read(bys);
System.out.println(len);
//System.out.println(new String(bys));
/* 2
d
orl
* */
System.out.println(new String(bys,0,len));
/*
* 2
* d
*/
//所以之前的应该也改写成System.out.println(new String(bys,0,len));
//⭐⭐⭐优化上面的程序
byte[] bytes =new byte[1024];//1024及其整数倍
int length;
while((length= fis.read(bytes))!=-1){
System.out.print(new String(bytes,0,length));
}
fis.close();
2.8 字节缓冲流
- BufferedOutputStream:该类实现缓冲输出流,通过设置这样的输出流,应用程序可以向底层输出流写入字节,而不必为写入的每个字节导致底层系统的调用。
- bufferedInputStream:创建BufferedInputStream时,将创建一个内部缓冲区数组。 当从流中读取或跳过字节时,内部缓冲区将根据需要从所包含的输入流中重新填充,一次有多个字节。
构造方法
- 字节缓冲输入流:BufferedInputStream(InputStream in)
- 字节缓冲输出流:BufferedoutputStream(OutputStream out)
为什么构造方法需要的是字节流,而不是具体的文件或者路径呢?
- 字节缓冲流仅仅提供缓冲区,而真正读写数据还得依靠基本的字节流对象进行操作
//FileOutputStream fos=new FileOutputStream("MyIO:\\fos.txt");
//BufferedOutputStream bos=new BufferedOutputStream(fos);
BufferedOutputStream bos=new BufferedOutputStream(new FileOutputStream("MyIO\\fos.txt"));
bos.write("hello\r\n".getBytes());
bos.write("world\r\n".getBytes());
bos.close();
BufferedInputStream bis =new BufferedInputStream(new FileInputStream("MyIO\\fos.txt"));
int by;
while((by=bis.read())!=-1) {
System.out.print((char) by);
}
bis.close();
通过四种方式复制视频
public static void main(String[] args) throws IOException {
//记录开始时间
long startTime=System.currentTimeMillis();
//复制视频
//1.基本字节流一次读写一个字节
//method1();
//2.基本字节流一次读写一个字节数组
//method2();
//字节缓冲流一次读写一个字节
//method3();
//字节缓冲流一次读写一个字节数组
method4();
//记录结束时间
long endTime=System.currentTimeMillis();
System.out.println("共耗时:"+(endTime-startTime)+"毫秒");
}
public static void method1() throws IOException {
FileInputStream fis=new FileInputStream("D:\\FileCast\\08.mp4");
FileOutputStream fos=new FileOutputStream("MyIO\\08.mp4");
int by;
while((by=fis.read())!=-1){
fos.write(by);
}
fis.close();
fos.close();
}
public static void method2() throws IOException{
FileInputStream fis=new FileInputStream("D:\\FileCast\\08.mp4");
FileOutputStream fos=new FileOutputStream("MyIO\\08.mp4");
byte[] bys=new byte[1024];
int len;
while((len=fis.read(bys))!=-1){
fos.write(bys,0,len);
}
fis.close();
fos.close();
}
public static void method3() throws IOException{
BufferedInputStream bis=new BufferedInputStream(new FileInputStream("D:\\FileCast\\08.mp4"));
BufferedOutputStream bos=new BufferedOutputStream(new FileOutputStream("MyIO\\08.mp4"));
int by;
while((by=bis.read())!=-1){
bos.write(by);
}
bis.close();
bos.close();
}
public static void method4() throws IOException{
BufferedInputStream bis=new BufferedInputStream(new FileInputStream("D:\\FileCast\\08.mp4"));
BufferedOutputStream bos=new BufferedOutputStream(new FileOutputStream("MyIO\\08.mp4"));
byte[] bys=new byte[1024];
int len;
while ((len= bis.read(bys))!=-1){
bos.write(bys,0,len);
}
bis.close();
bos.close();
}
3.字符流
3.1 为什么会出现字符流
由于字节流操作中文不是特别方便,所以Java就提供了字符流
- 字符流=字节流+编码表
用字节流复制文本时,文本文件也会有中文,但是没有问题,原因时最终底层操作会自动进行字节拼接成中文,如何识别中文?
- 汉字在存储的时候,无论选择那种编码存储,第一个字节都是负数
3.2 字符串中的编码解码问题
编码:
- byte[] getBytes():使用平台的默认字符集将该String编码为一系列字节,将结果存储到新的字节数组中
- byte[] getBytes(String charsetName):使用指定的字符集将该String编码为一系列字节,将结果存储到新的字节数组中
解码:
- String(byte[] bytes):通过使用平台的默认字符集解码指定的字节数组来构造新的String
- String(byte[] bytes,String charsetName):过使用指定的字符集解码指定的字节数组来构造新的String
String s="中国";
byte[] bys = s.getBytes();//[-28, -72, -83, -27, -101, -67]
//byte[] bys = s.getBytes("UTF-8");//[-28, -72, -83, -27, -101, -67]
//byte[] bys = s.getBytes("GBK");//[-42, -48, -71, -6]
//String ss=new String(bys);
String ss=new String(bys,"UTF-8");//中国
//String ss=new String(bys,"GBK");//涓浗
System.out.println(ss);
3.3 字符流中的编码解码问题
字符流抽象基类
- Reader:字符输入流的抽象类
- Writer:字符输出流的抽象类
//FileOutputStream fos=new FileOutputStream("MyIO\\fos.txt");
OutputStreamWriter osw=new OutputStreamWriter(new FileOutputStream("MyIO\\fos.txt"));
osw.write("中国");
osw.close();
InputStreamReader isr=new InputStreamReader(new FileInputStream("MyIO\\fos.txt"));
//一次读取一个字符数据
int ch;
while ((ch=isr.read())!=-1){
System.out.print((char) ch);
}
isr.close();
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现