一、File 类
1.1 File概述
- FIle 类是
** java.io **包下代表和平台无关的文件和目录,换言之,如果希望在程序中操作文件和目录都可以使用 File 类来完成,File 类能新建、删除、重命名文件和目录。 - 在 API 中 File 的解释是
**文件和目录路径名的抽象表示形式**,即 File 类是文件或目录的路径,而不是文件本身,因此 File 类不能直接访问文件内容本身,如果需要访问文件内容本身,就需要使用 IO 流了。 - 对于File而言,其封装的并不是一个真正存在的文件,仅仅是一个路径名(路径字符串而已)。它可以是存在的,也可以是不存在的;将来是要通过具体的操作把这个路径的内容转换为具体存在的
- File 类代表磁盘或网络中某个文件或目录的路径名称,例如:
E:\\Develop\\java.txt `` E:\\lhl。 - 不能直接通过 File 对象读取和写入数据,如果要操作数据,需要 IO 流。
- File 类代表磁盘或网络中某个文件或目录的路径名称,例如:
1.2 File 类的构造方法
| 方法名 | 说明 |
|---|---|
| File(String pathname) | 通过将给定的路径名字符串转换为抽象路径名来创建新的 File实例 |
| File(String parent, String child) | 从父路径名字符串和子路径名字符串创建新的 File实例 |
| File(File parent, String child) | 从父抽象路径名和子路径名字符串创建新的 File实例 |
package com.lhl.file;import java.io.File;import java.net.URI;/*** @author lhl* @ClassName FileDemo01* @description TODO File类的构造方法* @data 2022/03/19 20:14* @Version V1.0**/public class FileDemo01 {public static void main(String[] args) {//File(String pathname): 通过将给定的路径名字符串转换为抽象路径名来创建新的 File实例File f1 = new File("E:/Develop/java.txt");System.out.println("file = " + f1);//File(String parent, String child): 从父路径名字符串和子路径名字符串创建新的 File实例File f2 = new File("E:/lhl","靓仔.jpg");//两个路径的拼接,String类型System.out.println("file = " + f2);//File(File parent, String child): 从父抽象路径名和子路径名字符串创建新的 File实例File f3 = new File("E:/lhl");File f4 = new File(f3,"IO.md");//两个路径的拼接,file类型跟String类型System.out.println("file = " + f4);//File(URI uri) 通过将给定的 file: URI转换为抽象路径名来创建新的 File实例。URI uri = URI.create("file:///E:/Develop/Java/jdk1.8.0_221/bin");//uri语法自行学习嗷~,File f5 = new File(uri);System.out.println("file = " + f5);}}
输出:
file = E:\Develop\java.txtfile = E:\lhl\靓仔.jpgfile = E:\lhl\IO.mdfile = E:\Develop\Java\jdk1.8.0_221\bin
1.3 绝对路径和相对路径
- 绝对路径
是一个完整的路径,从盘符开始 - 相对路径
是一个简化的路径,相对当前项目下的路径 示例代码
public class FileDemo02 {public static void main(String[] args) {// 是一个完整的路径,从盘符开始File file1 = new File("E:\\lhl\\a.txt");// 是一个简化的路径,从当前项目根目录开始File file2 = new File("a.txt");File file3 = new File("模块名\\a.txt");}}
1. 4 File 的常用方法
1.4.1 File 的创建功能
| 方法名 | 说明 |
|---|---|
| public boolean createNewFile() | 当具有该名称的文件不存在时,创建一个由该抽象路径名命名的新空文件 |
| public boolean mkdir() | 创建由此抽象路径名命名的目录 |
| public boolean mkdirs() | 创建由此抽象路径名命名的目录,包括任何必需但不存在的父目录 |
package com.lhl.file;import java.io.File;import java.io.IOException;/*** @author lhl* @ClassName FileDemo02* @description TODO File类的创建功能* @data 2022/03/19 21:28* @Version V1.0**/public class FileDemo02 {public static void main(String[] args) throws IOException {//需求1:我要在E:\\lhl目录下创建一个文件java.txtFile f1 = new File("E:\\lhl\\java.txt");System.out.println(f1.createNewFile());System.out.println("--------");//需求2:我要在E:\\lhl目录下创建一个目录JavaSEFile f2 = new File("E:\\lhl\\JavaSE");System.out.println(f2.mkdir());System.out.println("--------");//需求3:我要在E:\\lhl目录下创建一个多级目录JavaWEB\\HTMLFile f3 = new File("E:\\lhl\\JavaWEB\\HTML");//System.out.println(f3.mkdir());System.out.println(f3.mkdirs());System.out.println("--------");//需求4:我要在E:\\lhl目录下创建一个文件javaee.txtFile f4 = new File("E:\\lhl\\javaee.txt");//System.out.println(f4.mkdir());System.out.println(f4.createNewFile());}}
1.4.2 File类的删除功能
| 方法名 | 说明 |
|---|---|
| public boolean delete() | 删除由此抽象路径名表示的文件或目录 |
示例:
package com.lhl.file;import java.io.File;import java.io.IOException;/*** @author lhl* @ClassName FileDemo03* @description TODO File类的删除功能* @data 2022/03/19 21:44* @Version V1.0**/public class FileDemo03 {public static void main(String[] args) throws IOException {//需求1:在当前模块目录下创建java.txt文件File f1 = new File("JavaSE\\java.txt");//System.out.println(f1.createNewFile());//需求2:删除当前模块目录下的java.txt文件System.out.println(f1.delete());System.out.println("--------");//需求3:在当前模块目录下创建 美女目录File f2 = new File("JavaSE\\美女");System.out.println(f2.mkdir());//需求4:删除当前模块目录下的 美女目录System.out.println(f2.delete());System.out.println("--------");//需求5:在当前模块下创建一个目录aaa,然后在该目录下创建一个文件java.txtFile f3 = new File("JavaSE\\aaa");System.out.println(f3.mkdir());File f4 = new File(f3,"java.txt");System.out.println(f4.createNewFile());//需求6:删除当前模块下的目录aaaSystem.out.println(f4.delete()); //得先删除目录里的文件才能删除目录System.out.println(f3.delete());}}
1.4.3 File类判断和获取功能
判断功能 | 方法名 | 说明 | | —- | —- | | public boolean isDirectory() | 测试此抽象路径名表示的File是否为目录 | | public boolean isFile() | 测试此抽象路径名表示的File是否为文件 | | public boolean exists() | 测试此抽象路径名表示的File是否存在 |
获取功能 | 方法名 | 说明 | | —- | —- | | public String getAbsolutePath() | 返回此抽象路径名的绝对路径名字符串 | | public String getPath() | 将此抽象路径名转换为路径名字符串 | | public String getName() | 返回由此抽象路径名表示的文件或目录的名称 | | public File[] listFiles() | 返回此抽象路径名表示的目录中的文件和目录的File对象数组 |
示例代码:
package com.lhl.file;import java.io.File;/*** @author lhl* @ClassName FileDemo04* @description TODO File类的判断和获取功能* @data 2022/03/19 21:57* @Version V1.0**/public class FileDemo04 {public static void main(String[] args) {//创建一个File对象File f = new File("JavaSE\\java.txt");//public boolean isDirectory():测试此抽象路径名表示的File是否为目录//public boolean isFile():测试此抽象路径名表示的File是否为文件//public boolean exists():测试此抽象路径名表示的File是否存在System.out.println(f.isDirectory());System.out.println(f.isFile());System.out.println(f.exists());//public String getAbsolutePath():返回此抽象路径名的绝对路径名字符串//public String getPath():将此抽象路径名转换为路径名字符串//public String getName():返回由此抽象路径名表示的文件或目录的名称System.out.println(f.getAbsolutePath());System.out.println(f.getPath());System.out.println(f.getName());System.out.println("--------");//public File[] listFiles():返回此抽象路径名表示的目录中的文件和目录的File对象数组File f2 = new File("E:\\lhl");File[] fileArray = f2.listFiles();for(File file : fileArray) {//System.out.println(file);//System.out.println(file.getName());if (file.isFile()) {System.out.println(file.getName());}}}}
1.4.4 练习
练习1:
- 案例需求
在当前模块下的aaa文件夹中创建一个a.txt文件 - 实现步骤
- 创建File对象,指向aaa文件夹
- 判断aaa文件夹是否存在,如果不存在则创建
- 创建File对象,指向aaa文件夹下的a.txt文件
- 创建这个文件 ```java package com.lhl.file.test;
import java.io.File; import java.io.IOException;
/**
- @author lhl
- @ClassName Test1
- @description TODO
- @data 2022/03/20 01:08
@Version V1.0 **/ public class Test1 { public static void main(String[] args) throws IOException {
//1.创建File对象,指向aaa文件夹File file = new File("filemodule/aaa");//2.判断aaa文件夹是否存在,如果文件夹不存在就创建//注意:文件所在的目录必须存在if (!file.exists()){file.mkdir();}//3.创建File对象,指向aaa文件夹下面的a.txt文件File file1 = new File(file, "a.txt");//4.创建这个文件file1.createNewFile();
} } ```
练习2:
- 案例需求
删除一个多级文件夹 - 实现步骤
- 定义一个方法,接收一个File对象
- 遍历这个File对象,获取它下边的每个文件和文件夹对象
- 判断当前遍历到的File对象是文件还是文件夹
- 如果是文件,直接删除
- 如果是文件夹,递归调用自己,将当前遍历到的File对象当做参数传递
- 参数传递过来的文件夹File对象已经处理完成,最后直接删除这个空文件夹
```java
package day13.File;
import java.io.File;
import java.util.Scanner;
public class Demo02 {
static Scanner sc=new Scanner(System.in);
public static void main(String[] args) {
/已创建的/
//D:\ccc\bbb\aaa\f\g—-空文件夹
//D:\ccc\bbb\aaa\f\h\d.txt
//D:\ccc\bbb\aaa\f\h\e.txt
System.out.println(“请输入一个多级目录:”);
String str=sc.nextLine();
File file1=new File(str);
System.out.println(method(file1));
}
public static int method(File file1){
//打印当前文件名
System.out.println(file1.getName());
File[] files = file1.listFiles();
if(file1.exists()){
} //判断是否为文件 if(file1.isFile()){//文件for(File s:files){ System.out.println(s); }
} else{//递归调用 //文件夹file1.delete(); return 1;
} } }int leng= files.length; for (int i = 0; i <leng ; i++) { //System.out.println(files[i]); files[i].delete(); } return -1;
```java
package com.lhl.file.test;
import java.io.File;
import java.util.Arrays;
/**
* @author lhl
* @ClassName Test2
* @description TODO
* @data 2022/03/20 13:22
* @Version V1.0
**/
public class Test2 {
public static void main(String[] args) {
File file = new File("C:\\Users\\Tul\\Desktop\\JavaSE");
deleteDir(file);
}
//定义一个方法,接收一个File对象
public static void deleteDir(File file) {
//1.先删除掉这个文件夹里的所有东西
//递归,在方法中自己调用自己
//2.遍历这个File对象,获取他下面的每个文件和文件夹对象
File[] files = file.listFiles();
System.out.println(Arrays.toString(files));
//3.判断当前遍历到的File
for (File f : files) {
//4.如果是文件,直接删除
if(f.isFile()){
f.delete();
}else{
//5.如果是文件夹,递归调用自己,将当前遍历到的File对象当做参数传递
deleteDir(f);//参数一定要是src文件夹里面的文件夹File对象
}
}
//6.参数传递过来的文件夹File对象已经处理完成,最后直接删除这个空文件夹
file.delete();
}
}
练习3:
- 案例需求
统计一个文件夹中每种文件的个数并打印
打印格式如下:
txt:3个
doc:4个
jpg:6个<br />
- 实现步骤
- 定义一个方法,参数是HashMap集合用来统计次数和File对象要统计的文件夹
- 遍历File对象,获取它下边的每一个文件和文件夹对象
- 判断当前File对象是文件还是文件夹
- 如果是文件,判断这种类型文件后缀名在HashMap集合中是否出现过
- 没出现过,将这种类型文件的后缀名存入集合中,次数存1
- 出现过,获取这种类型文件的后缀名出现的次数,对其+1,在存回集合中
- 如果是文件夹,递归调用自己,HashMap集合就是参数集合,File对象是当前文件夹对象 ```java package com.lhl.file.test;
import java.io.File; import java.util.HashMap;
/**
- @author lhl
- @ClassName Test3
- @description TODO
- @data 2022/03/20 14:30
@Version V1.0 **/ public class Test3 { public static void main(String[] args) {
//统计一个文件夹中,每种文件出现的次数. //统计 --- 定义一个变量用来统计. ---- 弊端:同时只能统计一种文件 //利用map集合进行数据统计,键 --- 文件后缀名 值 ---- 次数 File file = new File("JavaSE"); HashMap<String, Integer> hm = new HashMap<>(); getCount(hm, file); System.out.println(hm);}
//1.定义一个方法,参数是HashMap集合用来统计次数和File对象要统计的文件夹 private static void getCount(HashMap
hm, File file) { //2.遍历File对象,获取它下边的每一个文件和文件夹对象 File[] files = file.listFiles(); for (File f : files) { //3.判断当前File对象是文件还是文件夹 if(f.isFile()){ //如果是文件,判断这种类型文件后缀名在HashMap集合中是否出现过 String fileName = f.getName(); String[] fileNameArr = fileName.split("\\."); //有些文件是没有后缀名的,譬如aaa,或者存在,a.a.txt,我们先不做考虑,只统计长度为2的 if(fileNameArr.length == 2){ //获取到文件的后缀名 String fileEndName = fileNameArr[1]; if(hm.containsKey(fileEndName)){ //出现过,获取这种类型文件的后缀名出现的次数,对其+1,在存回集合中 Integer count = hm.get(fileEndName); //这种文件又出现了一次. count++; //把已经出现的次数给覆盖掉. hm.put(fileEndName,count); }else{ // 没出现过,将这种类型文件的后缀名存入集合中,次数存1 hm.put(fileEndName,1); } } }else{ //如果是文件夹,递归调用自己,HashMap集合就是参数集合,File对象是当前文件夹对象代码实现 getCount(hm,f); } }}
}
<a name="hHCkF"></a>
# 二、IO流
<a name="LxohY"></a>
## 2.1 IO流概述
- I/O 是 Input 和 Output 的缩写,IO 技术是非常实用的技术,用于 `处理设备之间的数据传输` ,如:读写文件、文件复制,文件上传下载,网络通讯等。
- Java 程序中,对于数据的输入/输出操作以 `流stream` 的方式进行。
- java.io 包下提供了各种 `流` 类和接口,用于获取不同种类的数据,并通过 `标准的方法` 输入或输出数据。
- **流**(`Stream`):是一种抽象概念, 是指一连串的数据(字符或字节),以先进先出的方式发送信息的通道;也就是说数据在设备间的传输称为流,**流的本质也就是是数据传输**;
> 流的特性:
> - 先进先出:最先写入输出流的数据最先被输入流读取到。
> - 顺序存取:可以一个接一个地往流中写入一串字节,读出时也将按写入顺序读取一串字节,不能随机访问中间的数据。(RandomAccessFile除外)
> - 只读或只写:每个流只能是输入流或输出流的一种,不能同时具备两个功能,输入流只能进行读操作,对输出流只能进行写操作。在一个数据传输通道中,如果既要写入数据,又要读取数据,则要分别提供两个流。
<a name="Alvoe"></a>
## 2.2 IO的原理
- IO流的方向:以内存为参照,进行读写
- 输入( Input ):读取外部数据(磁盘、U 盘等存储设备的数据)到程序(内存)中。
- 输出( Output ):将程序(内存)数据输出到磁盘、U 盘等存储设备中。

<a name="PVIbW"></a>
## 2.3 IO流的分类
- 根据流的流向分类:
- 输入流:将数据从 `其他设备` 上读取到 `内存` 中的流。以 InputStream 、Reader 结尾。
- 输出流:将数据从 `内存` 中写出到 `其他设备` 上的流。以 OutputStream 和 Writer 结尾。
- 根据数据的类型分类:
- 字节流:以字节为单位,读写数据的流。以 InputStream 和 OutputStream 结尾。
- 字符流:以字符为单位,读写数据的流。以 Reader 和 Writer 结尾。
- 根据 IO 流的角色不同分类:
- 节点流:可以从或向一个特定的地方(节点)读取数据。如 FileReader 。
- 处理流:对一个已经存在的流进行连接和封装,通过锁封装的流的功能实现数据读写。如:BufferReader ,处理流的构造方法总是要带一个其它的流对象做参数。一个流对象经过多次其它流的多次包装,称为流的链接。
- 常用的节点流:
- 文件:FileInputStream 、FileOutputStream 、FileReader 、FileWriter 文件进行处理的节点流。
- 字符串:StringReader 、StringWriter 对字符串进行处理的节点流。
- 数组: ByteArrayInputStream 、ByteArrayOutputStream、CharArrayReader 、CharArrayWriter 对数组进行处理的节点流(对应的不再是文件,而是内存中的一个数组)。
- 管道:PipedInputStream 、PipedOutputStream 、PipedReader 、PipedWriter 对管道进行处理的节点流。
- 常用处理流:
- 缓冲流:BufferedInputStream 、BufferedOutputStream 、BufferedReader 、BufferedWriter ---增加缓冲功能,避免频繁读写硬盘。
- 转换流:InputStreamReader 、OutputStreamReader --- 实现字节流和字符流之间的转换。
- 数据流:DataInputStream 、DataOutputStream - 提供读写 Java 基础数据类型功能。
- 对象流:ObjectInputStream 、ObjectOutputStream -- 提供直接读写 Java 对象功能。
<a name="c59ddff7"></a>
## 2.4 四大顶级抽象流父类
| | **输入流** | **输出流** |
| --- | --- | --- |
| **字节流** | 字节输入流 **InputStream** | 字节输出流 **OutputStream** |
| **字符流** | 字符输入流 **Reader** | 字符输出流 **Writer** |

> **4个顶级类都是抽象类,并且是所有流类型的父类**
- IO流的使用场景
- 如果操作的是图片、视频、音频等二进制文件,优先使用字节流
- 如果操作的是纯文本文件,优先使用字符流
- 如果不确定文件类型,优先使用字节流.字节流是万能的流
- 字节流与字符流区别
- 字节流和字符流的用法几乎完成全一样,区别在于字节流和字符流所操作的数据单元不同,字节流操作的单元是数据单元是8位的字节,字符流操作的是数据单元为16位的字符。
<a name="48bb1d58"></a>
## 2.5 字节流
<a name="504dfcb7"></a>
### 2.5.1 概述
- 一切文件数据(文本、图片、视频等)在存储的时候,都是以二进制的形式保存的,都是一个个的字节;同样,在传输的时候依然如此。所以,字节流可以传输任意文件数据。
- 在操作流的时候,我们要时刻明确,无论使用什么样的流对象,底层传输始终是二进制数据。
<a name="tIf0L"></a>
### 2.5.2 继承体系
- 字节流抽象基类
- InputStream:这个抽象类是表示**字节输入流**的所有类的超类
- OutputStream:这个抽象类是表示**字节输出流**的所有类的超类
- `**子类名特点:子类名称都是以其父类名作为子类名的后缀**`
<br />
<a name="sLzYt"></a>
### 2.5.3 字节流写数据
**FileOutputStream**
- FileOutputStream 是文件输出流,用于将数据写出到文件中。
- 构造函数:
| 方法名 | 说明 |
| --- | --- |
| FileOutputStream(String name) | 创建文件输出流以指定的名称写入文件 |
| FileOutputStream(File file) | 创建文件输出流以写入由指定的 File对象表示的文件 |
| FileOutputStream(String name,boolean append) | 创建文件输出流以写入由指定的 File对象表示的文件。 |
| FileOutputStream((File file,boolean append) | 创建文件输出流以指定的名称写入文件。 |
> 注意:`FileOutputStream((File file,boolean append)`,如果第二个参数为true ,则字节将写入文件的末尾而不是开头
使用字节输出流写数据的步骤
- 创建字节输出流对象(调用系统功能创建了文件,创建字节输出流对象,让字节输出流对象指向文件)
- 调用字节输出流对象的写数据方法
- 释放资源(关闭此文件输出流并释放与此流相关联的任何系统资源)
入门示例:
```java
public class FileOutputStreamDemo01 {
public static void main(String[] args) throws IOException {
//创建字节输出流对象
/*
注意点:
1.如果文件不存在,会帮我们创建
2.如果文件存在,会把文件清空
*/
//FileOutputStream(String name):创建文件输出流以指定的名称写入文件
FileOutputStream fos = new FileOutputStream("myByteStream\\fos.txt");
//void write(int b):将指定的字节写入此文件输出流
fos.write(97);
//fos.write(57);
//fos.write(55);
//最后都要释放资源
//void close():关闭此文件输出流并释放与此流相关联的任何系统资源。
fos.close();
}
}
2.5.3.1 字节流写数据的三种方式
| 方法名 | 说明 |
|---|---|
| void write(int b) | 将指定的字节写入此文件输出流 一次写一个字节数据 |
| void write(byte[] b) | 将 b.length字节从指定的字节数组写入此文件输出流 一次写一个字节数组数据 |
| void write(byte[] b, int off, int len) | 将 len字节从指定的字节数组开始,从偏移量off开始写入此文件输出流 一次写一个字节数组的部分数据 |
| void flush() | 刷新此输出流并强制写出所有缓冲的输出字节 |
| void close() | 关闭此输出流并释放与此流相关的所有系统资源 |
package com.lhl.output;
import java.io.FileOutputStream;
import java.io.IOException;
/**
* @author lhl
* @ClassName FileOutputStreamDemo02
* @description TODO
* @data 2022/03/20 14:38
* @Version V1.0
**/
public class FileOutputStreamDemo02 {
public static void main(String[] args) throws IOException {
//FileOutputStream(String name):创建文件输出流以指定的名称写入文件
FileOutputStream fos = new FileOutputStream("myByteStream\\fos.txt");
//FileOutputStream(File file):创建文件输出流以写入由指定的 File对象表示的文件
//FileOutputStream fos = new FileOutputStream(new File("myByteStream\\fos.txt"));
//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};
//byte[] getBytes():返回字符串对应的字节数组
byte[] bys = "abcde".getBytes();
//fos.write(bys);
//void write(byte[] b, int off, int len):将 len字节从指定的字节数组开始,从偏移量off开始写入此文件输出流
//fos.write(bys,0,bys.length);
fos.write(bys,1,3);
//释放资源
fos.close();
}
}
2.5.3.2 字节流写数据的两个小问题
- 字节流写数据如何实现换行
- windows:\r\n
- linux:\n
- mac:\r
- 字节流写数据如何实现追加写入
- public FileOutputStream(String name,boolean append)
- 创建文件输出流以指定的名称写入文件。如果第二个参数为true ,则字节将写入文件的末尾而不是开头
package com.lhl.output;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
/**
* @author lhl
* @ClassName FileOutputStreamDemo03
* @description TODO
* @data 2022/03/20 14:52
* @Version V1.0
**/
public class FileOutputStreamDemo03 {
public static void main(String[] args) throws IOException {
//创建字节输出流对象
//FileOutputStream fos = new FileOutputStream("myByteStream\\fos.txt");
FileOutputStream fos = new FileOutputStream("myByteStream\\fos.txt",true);
//写数据
for (int i = 0; i < 10; i++) {
fos.write("hello".getBytes());
fos.write("\r\n".getBytes());
}
//释放资源
fos.close();
}
}
2.5.3.3 字节流写数据的异常处理
异常处理格式
try-catch-finally
try{ 可能出现异常的代码; }catch(异常类名 变量名){ 异常的处理代码; }finally{ 执行所有清除操作; }finally特点
- 被finally控制的语句一定会执行,除非JVM退出
public class FileOutputStreamDemo04 { public static void main(String[] args) { //加入finally来实现释放资源 FileOutputStream fos = null; try { fos = new FileOutputStream("myByteStream\\fos.txt"); fos.write("hello".getBytes()); } catch (IOException e) { e.printStackTrace(); } finally { if(fos != null) { try { fos.close(); } catch (IOException e) { e.printStackTrace(); } } } } }2.5.4 字节流读数据
FileInputStream
- 被finally控制的语句一定会执行,除非JVM退出
java.io.FileInputStream类是文件输入流,从文件中读取字节。- 构造方法:
| 方法名 | 说明 |
| —- | —- |
| FileInputStream(File file) | 通过打开与实际文件的连接来创建一个
FileInputStream
,该文件由文件系统中的File对象file命名。 | | FileInputStream(String name) | 通过打开与实际文件的连接来创建一个FileInputStream
,该文件由文件系统中的路径名name命名。 |
2.5.4.1 字节流写数据的三种方式
| 方法名 | 说明 |
|---|---|
| public int read() | 从该输入流读取一个字节的数据。数据的下一个字节,如果达到文件的末尾, -1 |
| public int read(byte[] b) | 从输入流读取最多b.length个字节的数据 |
| public int read(byte[] b, int off, int len) | 读取从此输入流中的数据len个字节到字节数组,开始在目标数组b的偏移。 |
一次读一个字节:
package com.lhl.input;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
/**
* @author lhl
* @ClassName FileInputStreamDemo01
* @description TODO
* @data 2022/03/21 13:51
* @Version V1.0
**/
public class FileInputStreamDemo01 {
public static void main(String[] args){
FileInputStream fis = null;
try {
//创建字节输入流对象
//FileInputStream(String name)
fis = new FileInputStream("myByteStream\\aaa\\fos.txt");
int by;
/*
fis.read():读数据
by=fis.read():把读取到的数据赋值给by
by != -1:判断读取到的数据是否是-1
*/
while ((by=fis.read())!=-1) {
System.out.print((char)by);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
//释放资源
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
思考:为什么read()方法返回值为什么是int?而不是byte 因为字节输入流可以操作任意类型的文件,比如图片音频等,这些文件底层都是以二进制形式存储的。如果每次读取都返回byte;有可能在读到中间的时候遇到11111111,而byte类型的8个1的十进制正好是-1,我们的程序是遇到-1就会停止不读了,后面的数据就读不到了,所以在读取的时候用int类型接收。如果是11111111就会在其前面补上24个0凑足4个字节,那么byte类型的-1就会变成int类型的255了,这样就可以保证整个数据读完,而结束标记的-1就是int类型。
一次读一个数组:
package com.lhl.input;
import java.io.FileInputStream;
import java.io.IOException;
/**
* @author lhl
* @ClassName FileInputStreamDemo02
* @description TODO
* @data 2022/03/21 19:34
* @Version V1.0
**/
public class FileInputStreamDemo02 {
public static void main(String[] args) {
FileInputStream fis = null;
try {
//创建字节输入流对象
fis = new FileInputStream("myByteStream\\aaa\\fos.txt");
byte[] bys = new byte[1024]; //1024及其整数倍
int len;//本次读到的有效字节个数 -- 这次读了几个字节
//循环读取
while ((len=fis.read(bys))!=-1) {
System.out.print(new String(bys,0,len));//读几个就打印出几个
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (fis!= null){
//释放资源
fis.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
2.5.5 字节流复制文件(练习)
文件复制的本质是字节的搬家。
- 案例需求
把“E:\lhl\窗里窗外.txt”复制到模块目录下的“窗里窗外.txt” (文件可以是任意文件) - 实现步骤
- 复制文本文件,其实就把文本文件的内容从一个文件中读取出来(数据源),然后写入到另一个文件中(目的地)
- 数据源:
E:\lhl\窗里窗外.txt —- 读数据 —- InputStream —- FileInputStream - 目的地:
myByteStream\窗里窗外.txt —- 写数据 —- OutputStream —- FileOutputStream
- 代码实现 ```java package com.lhl.copydemo;
import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException;
/**
- @author lhl
- @ClassName CopyDemo
- @description TODO
- @data 2022/03/21 19:58
@Version V1.0 **/ public class CopyDemo { public static void main(String[] args) throws IOException {
//根据数据源创建字节输入流对象 FileInputStream fis = new FileInputStream("E:\\lhl\\嘿嘿嘿.avi"); //根据目的地创建字节输出流对象 FileOutputStream fos = new FileOutputStream("myByteStream\\嘿嘿嘿.avi"); //读写数据,复制图片(一次读取一个字节数组,一次写入一个字节数组) byte[] bys = new byte[1024]; int len; while ((len=fis.read(bys))!=-1) { fos.write(bys,0,len); } //释放资源 fos.close(); fis.close();2.6 字节缓冲流
2.6.1 缓冲流概述
- 缓冲流,也叫高效流,按照数据类型分类:
- 字节缓冲流:BufferedOutputStream 、BufferedInputStream 。
- 字符缓冲流:BufferedReader 、BufferedWriter 。
- 缓冲流会在内部创建一个缓冲区数组,缺省使用 8192 个字节(8kb)的缓冲区。
缓冲流仅仅提供缓冲区,而真正的读写数据还得依靠基本的字节流、字符流对象进行操作
public class BufferedInputStream extends FilterInputStream { private static int DEFAULT_BUFFER_SIZE = 8192; ... }缓冲流的基本原理:
当读取数据的时候,数据按块读入缓冲区,其后的读操作则直接访问缓冲区。
- 当使用 BufferedInputStream 读取字节文件时,BufferedInputStream 会一次性的从文件中读取 8192 个,存储在缓冲区中,直到缓冲区中装满了,才重新从文件中读取下一个 8192 个字节数组。
- 向流中写入字节的时候,不会直接写到文件中,先写到缓冲区中直到缓冲区中写满,BufferedOutputStream 才会将缓冲区中的数据一次性的写到文件里。使用 flush() 方法可以强制将缓冲区的内容全部写入输出流。

- 关闭流的顺序和打开流的顺序相反,只需要关闭最外层流即可,因为关闭最外层流的同时也会关闭对应的内层流。
- flush() 方法的使用:手动将 buffer 中内容写入文件。
- 如果是带缓冲区的流对象的 close() 方法,不但会关闭流,还会在关闭流之前刷新缓冲区,关闭后不能再写出
2.6.2 字节缓冲流
字节缓冲流介绍
- lBufferOutputStream:该类实现缓冲输出流.通过设置这样的输出流,应用程序可以向底层输出流写入字节,而不必为写入的每个字节导致底层系统的调用
- lBufferedInputStream:创建BufferedInputStream将创建一个内部缓冲区数组.当从流中读取或跳过字节时,内部缓冲区将根据需要从所包含的输入流中重新填充,一次很多字节
构造方法:
| 方法名 | 说明 |
|---|---|
| BufferedOutputStream(OutputStream out) | 创建字节缓冲输出流对象 |
| BufferedInputStream(InputStream in) | 创建字节缓冲输入流对象 |
package com.lhl.BufferStream;
import java.io.*;
/**
* @author lhl
* @ClassName BufferStreamDemo
* @description TODO
* @data 2022/03/21 20:52
* @Version V1.0
**/
public class BufferStreamDemo {
public static void main(String[] args) throws IOException {
//字节缓冲输出流:BufferedOutputStream(OutputStream out)
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("myByteStream\\bos.txt"));
//写数据
bos.write("hello\r\n".getBytes());
bos.write("world\r\n".getBytes());
//释放资源
bos.close();
//字节缓冲输入流:BufferedInputStream(InputStream in)
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("myByteStream\\bos.txt"));
//一次读取一个字节数据
//int by;
//while ((by=bis.read())!=-1) {
// System.out.print((char)by);
//}
//一次读取一个字节数组数据
byte[] bys = new byte[1024];
int len;
while ((len=bis.read(bys))!=-1) {
System.out.print(new String(bys,0,len));
}
//释放资源
bis.close();
}
}
2.6.3 字节缓冲流复制视频
- 案例需求
把“E:\lhl\字节流复制图片.avi”复制到模块目录下的“字节流复制图片.avi” - 实现步骤
- 根据数据源创建字节输入流对象
- 根据目的地创建字节输出流对象
- 读写数据,复制视频
- 释放资源 ```java package com.lhl.BufferStream;
import java.io.*;
/**
- @author lhl
- @ClassName CopyAviDemo
- @description TODO
- @data 2022/03/21 21:09
@Version V1.0 **/ public class CopyAviDemo { public static void main(String[] args) throws IOException {
//复制视频 //method1(); method2();}
//字节缓冲流一次读写一个字节数组 public static void method2() throws IOException {
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("E:\\lhl\\字节流复制图片.avi")); BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("myByteStream\\字节流复制图片.avi")); byte[] bys = new byte[1024]; int len; while ((len=bis.read(bys))!=-1) { bos.write(bys,0,len); } bos.close(); bis.close();}
//字节缓冲流一次读写一个字节 public static void method1() throws IOException {
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("E:\\lhl\\字节流复制图片.avi")); BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("myByteStream\\字节流复制图片.avi")); int by; while ((by=bis.read())!=-1) { bos.write(by); } bos.close(); bis.close();}
2.7 字符流
思考:既然字节流可以操作所有文件,那么为什么还要学习字符流?
- 如果利用字节流,把文本文件的中文,读取到内存中,有可能还会出现乱码
- 如果利用字节流,把中文写到文本文件中,也有可能出现乱码
了解原因之前,前来学习下编码表这个概念
2.7.1 编码表
2.7.1.1 基础知识
- 计算机中存储的信息都用二机制表示的
- 按照某种规则,将字符变成二进制,再存储到计算机中,称为编码
- 按照同样的规则,将存储在计算机中的二进制数据解析出来,称为解码
- 编码和解码的方式必须一致,否则会导致乱码
简单理解:存储一个字符a,首先需要在码表中查到对应数字是97,然后转换成二进制进行存储
读取的时候,先把二进制解析出来,再转换成97,通过97查找到对应字符是a;
字符编码( CharacterEncoding ):就是一套自然语言的字符和二进制数之间的对应规则。
2.7.1.2 码表
又叫做字符集,是一个系统支持的所有字符的集合,包括各国家文字、标点符号、图形符号、数字等
- 计算机要准确的存储和识别各种字符集符号,就需要进行字符编码,一套字符集必然至少有一套字符编码。常见字符集有ASCII字符集、GBXXX字符集、Unicode字符集等

- 可见,当指定了编码,它所对应的字符集自然就指定了,所以编码才是我们最终要关心的。
2.7.1.3 常见的码表
- ASCII字符集(American Standard Code for Information Interchange,美国信息交换标准代码):
- 是基于拉丁字母的一套电脑编码系统,用于显示现代英语,主要包括控制字符(回车键、退格、换行键等)和可显示字符(英文大小写字符、阿拉伯数字和西文符号)
- 基本的ASCII字符集,使用7位表示一个字符,共128字符。ASCII的扩展字符集使用8位表示一个字符,共256字符,方便支持欧洲常用字符。是一个系统支持的所有字符的集合,包括各国家文字、标点符号、图形符号、数字等
- ASCII码表中是没有中文的。
- ISO-8859
- ISO-8859实在ASCII码上的扩展,其中包含128个ASCII码字符,并增加了128个用于西欧国家的字符。
- GBXXX字符集:
- GB2312又称为GB2312-80字符集,全称为《信息交换用汉字编码字符集·基本集》,GB2312收录简化汉字及一般符号、序号、数字、拉丁字母、日文假名、希腊字母、俄文字母、汉语拼音符号、汉语注音字母,共 7445 个图形字符。其中包括6763个汉字。
- GBK:windows默认的码表,兼容ASCII码表,最常用的中文码表。是在GB2312标准基础上的扩展规范,使用了双字节编码方案,共收录了21003个汉字,完全兼容GB2312标准,同时支持繁体汉字以及日韩汉字等
- Unicode字符集:
- Unicode是一个字符集,这个字符集就厉害了,他想包含所有的字符,由国际组织ISO指定,是统一的万国码,基本上包括了世界上所有语言的常见字符。
- 但是因为表示的字符太多了,所以Unicode码表中的数组不会直接以二进制的的形式存储到计算机中,会先通过UTF-7,UTF-7.5,UTF-8,UTF-16,以及UTF-32进行编码,再存储到计算机,其中最常见的就是UTF-8
- UTF-8编码:可以用来表示Unicode标准中任意字符,它是电子邮件、网页及其他存储或传送文字的应用中优先采用的编码。互联网工程工作小组(IETF)要求所有互联网协议都必须支持UTF-8编码。它使用一至四个字节为每个字符编码
- 编码规则:
- 128个US-ASCII字符,只需一个字节编码
- 拉丁文等字符,需要二个字节编码
- 大部分常用字(含中文),使用三个字节编码
- 其他极少使用的Unicode辅助字符,使用四字节编码
注意:UTF-8 不是一张码表,他只是一种编码格式
2.7.2 字符流读取中文乱码原因
2.7.2.1 汉字存储和展示过程解析
2.7.2.2 字节流读取纯文本文件到内存,乱码原因
因为字节流一次读一个字节,而不管GBK还是UTF-8一个中文都是多个字节,用字节流每次只能读其中的一部分,所以就会出现乱码问题。
2.7.2.3 字节流复制纯文本文件未出现乱码原因
原因是最终底层操作会自动进行字节拼接成中文,那么计算机是如何识别中文的呢?
汉字在存储的时候,无论选择哪种编码存储,第一个字节都是负数
2.7.3 字符流概述
字符流的介绍
由于字节流操作中文不是特别的方便,所以Java就提供字符流
字符流 = 字节流 + 编码表
三、路径中的”/“ 和 “\”
正斜杠,又称左斜杠,符号是 “/“; 反斜杠,也称右斜杠,符号是”\”。
在Linux系统中
Linux系统中只能使用正斜杠:
在Linux系统中使用反斜杠,路径是不可识别的:
windows系统
1、windows系统中默认使用反斜杠
2、同时,在windows系统中,也可以使用正斜杠,访问文件目录
3、在DOS命令中
\ 是用于路径的, 如 C:\users\user ,
/ 用于命令的参数,如dos命令 dir /a 表示显示当前目录下的文件,包括隐藏文件
网络路径
网络路径必须使用正斜杠
