IO流
主要涉及文件的操作
File类
位于java.io.
File
类中的一个对象通常代表一个目录或者一个文件
File
类适用于文件的创建、删除操作,如果读取、写入需要IO流
构造器
目录无特殊说明,都是字符串
构造器1
方式1:File 对象名 = new File(目录)
目录可以为相对目录也可以为绝对目录
相对路径:相对于当前程序的目录,如果时main()
中的File
类,那么此时路径是相当于整个项目文件的
绝对路径:加上盘符的完整的路径
使用\\
标识每级路径或者/
可以print
文件类的对象名,默认是输出填写的目录名
构造器2
方式2:File 对象名 = new File(上级目录, 下级目录)
构造器3
方式3:File 对象名 = new File(File类型的目录, 下级目录的字符串)
方法
方法 | 含义 |
---|---|
f.getAbsolutePath() | 返回String 类型,获取对象的绝对路径 |
f.getPath() | 返回String 类型,填写的路径 |
f.getName() | 返回String 类型,获取文件名 |
f.getParent() | 返回String 类型,获取上级目录 |
f.length() | 返回long 类型,获取文件大小 |
f.lastModified() | 返回long 类型,最后的修改时间,毫秒数 |
f.list() | 返回String[] ,获取f 所有的文件、目录名称 |
f.listFiles() | 返回File[] ,获取f 所有的文件、目录 |
f.renameTo(File类型) | 返回布尔类型,将文件改名和移动到File的位置 |
f.isDirectory() | 返回布尔类型,判断是否是个目录 |
f.isFile() | 返回布尔类型,判断是否是个文件 |
f.exists() | 返回布尔类型,判断是否存在,中文为存在,读音iɡˈzists |
f.canRead() | 返回布尔类型,判断是否可读 |
f.canWrite() | 返回布尔类型,判断是否可写 |
f.isHidden() | 返回布尔类型,判断是否隐藏 |
f.createNewFile() | 返回布尔类型,仅当f 不存在时创建文件,需要使用try-catch 环绕,并且目录必须要存在 |
f.mkdir() | 返回布尔类型,创建文件目录,仅当上层文件目录存在时创建,如果目录存在或者上层目录不存在都不会创建 |
f.mkdirs() | 返回布尔类型,创建文件目录,如果不存在上层目录,将一并创建 |
f.delete() | 返回布尔类型,删除文件夹,这个目录内不能有文件或者文件目录 |
renameTo
将文件改名和移动到File的位置,名字改为File类型所打开的文件名
需保证形式参数所打开的文件不存在才能修改名称+移动 成功
创建文件夹
有两个方法mkdir
和mkdirs
,在new
对象中即使填入带后缀的文件名也会当成文件夹处理
例如
File fp2 = new File("D:6.txt");
fp2.mkdir();
实际效果
流stream
Java中对数据的输入/输出操作以流的形式进行
分类:
- 按照数据单位的不同,分为
- 字节流8bit,可以读二进制,按照字节读
- 字符流16bit
- 按照数据流向的不同,分为
- 输入流(输入到程序)
- 输出流(程序输出到数据)
- 按照角色的不同,分为
- 节点流
- 处理流
流的抽象基类
抽象类 | 字节流 | 字符流 |
---|---|---|
输入流 | InputStream | Reader |
输出流 | OutputStream | Writer |
Java的IO流涉及到40多个类,但都是从以上4个类中派生的
节点流(文件流)
分为
FileInputStream
类FileOutputStream
类FileReader
类FileWriter
类
FileReader类
针对于字符的读取
FileReader 变量名 = new FileReader(File类型)
,如果文件不存在,返回null
实例化FileReader
类时,需要使用try-catch
环绕或者让实例化的函数抛出异常
方法 | 含义 |
---|---|
f.read() | 会抛出异常,返回int ,读取一个字符并返回,如果读取到末尾,则返回-1 ,可以使用强制类型转换来输出char 类型的数据 |
f.close() | 关闭流,JVM不会自动回收已经打开的流,也会抛出异常 |
f.read(char[]数组) | 也会抛出异常,功能是每次读取char[] 数组长度的数据,并存入到数组中,返回int 每次读取的长度,当读取到末尾时,返回-1,如果最后一个读取的长度小于数组的长度,那么,超出的部分时上次的内容,即数组内容不会被清空,只会被替换 |
为保证f.close()
方法始终被执行,可以采用try-catch-finally
的方式进行环绕
格式
try{
FileReader 变量名 = new FileReader(参数);//这里读入的可能为null
//其他的相关操作
}catch(IOException e){
e.printStackTrace();
}finally{
try{
if(f != null){
f.close();//如果为空时,不能直接关闭
}
}catch (IOException e) {
e.printStackTrace();
}
}
读取数据
int data;
while ((data = fr.read()) != -1) {
System.out.println((char) data);
}
FileWriter类
针对于字符的写入
FileWriter 变量名 = new FileWriter(File类型)
FileWriter 变量名 = new FileWriter(File类型, 布尔类型)
布尔类型会决定如果文件中存在内容时 写入文件是否要替换,当为true
时追加,为false
时,替换文件
方法 | 含义 |
---|---|
f.write(字符串) | 也会抛出异常,将内容写入到文件,如果文件不存在,则创建新文件并写入,如果文件中存在内容,按照构造器给出的参数决定是否替换。在未执行f.close() 方法前,每次写入数据都写入到末尾,如果文件夹不存在,则抛出异常 |
f.close() | 关闭流,JVM不会自动回收已经打开的流,也会抛出异常 |
f.write(char[]数组, 开始位置, 个数) | 即在char[] 数组中的开始位置下标处读取相应的个数 |
FileInputStream类
逐字节读入,如果在内存层面操作过,读中文可能会乱码
f.read()
方法包含多个重载
f.read()
读取一个字节,返回int
,当读取到末尾时返回-1
f.read(byte[]数组, 开始位置, 长度)
,在byte[]
数组中,每次读取相应的长度,从开始位置开始存放
f.readAllBytes()
返回一个byte[]
数组,包含所有的信息
FileOutputStream类
逐字节写入
f.write()
方法也有多个重载
f.write(int)
,写入一个字节
f.write(byte[]数组)
,将一个byte[]
数组写入
f.write(byte[]数组, 开始位置, 写入长度)
在byte[]
数组中的开始位置下标处读取相应的个数
处理流
包裹着已有的流的基础上的流
缓冲流
是处理流的一种,是为了提高文件读写的效率
处理字节的BufferedInputStream
、BufferedOutputStream
处理字符的BufferedWriter
、BufferedReader
步骤:
- 打开文件
- 造一个节点流/文件流
- 造缓冲流
- 相应的操作
- 关闭流,关闭流时,可以只关闭外部的流,内部的流会自动关闭
public static void main(String[] args) throws IOException {
//打开文件
File file = new File("C:\\Users\\singx\\OneDrive\\图片\\背景图片\\wallhaven-8oky1j.jpg");
File file2 = new File("C:\\Users\\singx\\Desktop\\新建文件夹\\2.jpg");
//造节点流/文件流
FileInputStream fileInputStream = new FileInputStream(file);
FileOutputStream fileOutputStream = new FileOutputStream(file2);
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fileOutputStream);
//其他的操作
int b;
while((b = fileInputStream.read())!=-1){
fileOutputStream.write(b);
}
fileInputStream.close();//关闭外部的流
fileOutputStream.close();//关闭外部的流
}
使用数组
public static void main(String[] args) throws IOException {
File file = new File("C:\\Users\\singx\\OneDrive\\图片\\背景图片\\wallhaven-8oky1j.jpg");
File file2 = new File("C:\\Users\\singx\\Desktop\\新建文件夹\\22.jpg");
FileInputStream fileInputStream = new FileInputStream(file);
FileOutputStream fileOutputStream = new FileOutputStream(file2);
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fileOutputStream);
byte[] b = new byte[512];
int len;
while((len = fileInputStream.read(b)) != -1){
fileOutputStream.write(b, 0, len);
}
fileInputStream.close();
fileOutputStream.close();
}
f.flush()
写入到文件并清空缓冲区,flush中文未冲洗,缓冲流快的原因是其内部有一个大小为8192的数组,当达到这个数组的大小时,一次性的写出
BufferedWriter
、BufferedReader
与BufferedInputStream
、BufferedOutputStream
步骤一致,造文件、造节点/缓冲流、执行操作、关闭文件,但只能处理文本文件
也可以使用套娃的方式创建
BufferedXXXX 对象名 = newBuffered(new FileXXXX(new File(路径)))
BufferedReader
可以一次读一行,即使用.readLine()
方法,返回String
类型,当读到最后时,返回null
,使用此方法无法读入换行- 在写入时需要手动添加换行或者是用
BufferedWriter
类的实例中的nextLine()
方法进行写入一个换行
- 在写入时需要手动添加换行或者是用
public static void main(String[] args) throws IOException {
File file = new File("C:\\Users\\singx\\OneDrive\\C\\1.c");
File file2 = new File("C:\\Users\\singx\\Desktop\\新建文件夹\\222.c");
BufferedReader bufferedReader = new BufferedReader(new FileReader(file));
BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(file2));
String data;
int cnt = 0;
while ((data = bufferedReader.readLine()) != null) {//每次读一行
bufferedWriter.write(data + "\n");//手动添加换行
}
bufferedReader.close();
bufferedWriter.close();
}
转换流
提供了字节流和字符流之间的转换,也是属于处理流,还属于字符流,目的时转换格式
有两个,分别为
InputStreamReader
:将一个字节的输入流,转换为字符的输入流
OutputStreamWriter
:将字符的输出流,转换为字节的输出流
使用InputStreamReader
:
- 造文件
- 造文件流/字节流
- 造转换流:
InputStreamReader 对象名 = new InputStreamReader(字节流/文件流, 文件编码格式字符串)
,文件编码格式可以省略,默认为系统指定的 - 相应的操作
- 关闭文件
public static void main(String[] args) throws IOException {
File file = new File("C:\\Users\\singx\\OneDrive\\C\\1.c");
File file2 = new File("C:\\Users\\singx\\Desktop\\新建文件夹\\222.c");
InputStreamReader inputStreamReader = new InputStreamReader(new FileInputStream(file), "UTF-8");
int a;
while((a = inputStreamReader.read()) != -1){
System.out.print((char)a);
}
inputStreamReader.close();
}
使用特定的字符集进行存储
public class IoTest {
public static void main(String[] args) throws IOException {
File file = new File("C:\\Users\\singx\\OneDrive\\C\\1.c");
File file2 = new File("C:\\Users\\singx\\Desktop\\新建文件夹\\2.c");
InputStreamReader inputStreamReader = new InputStreamReader(new FileInputStream(file), "UTF-8");
OutputStreamWriter outputStreamWriter = new OutputStreamWriter(new FileOutputStream(file2), "gbk");//存储为GBK格式
int a;
while((a = inputStreamReader.read()) != -1){
outputStreamWriter.write(a);
}
inputStreamReader.close();
// outputStreamWriter.close();
}
}
标准输入/输出流
System
中有System.out
标准输出流、System.in
标准输入流
System.in
标准输入流:默认从键盘输入:
- 可通过
System
类中的setIn
指定输入的流
System.out
标准输出流:默认输出到控制台:
- 可通过
System
类中的setOut
指定输出的流
Scanner
也是可以读取文件的
Scanner 变量名 = new Scanner(File类型)
后续调用变量名.nextXXX()
就可以读取了
打印流
PrintStream
和PrintWriter
在Java中的System.out.printXXX()
方法就是一个打印流
用法:
- 造文件
PrintStream 变量名 = new PrintStream(文件);
有多个重载的构造函数- 可以使用
变量名.printXXX(内容)
的形式写入到文件
使用打印流也可以指定System.out.printXXX()
指定输出流
System.setOut(printStream)
例如
public static void main(String[] args) throws IOException {
File file = new File("C:\\Users\\singx\\Desktop\\新建文件夹\\1.txt");
PrintStream ps = new PrintStream(file);
System.setOut(ps);//指定输出流,此后的所有System.out.printXXX()都输出到文件
System.out.println("ddddddddddd");
}
数据流
作用是操作基本数据类型和String
的
分别为DataInputStream
、DataOutputStream
变量值写入到文件(二进制)变量值从文件读取(二进制)
也就是保存变量中的值,运行期间变量是在内存当中,一旦结束运行,变量的值就自动释放,可以使用数据量进行保存
读取顺序要和写入相同!
例子
public class IoTest {
public static void main(String[] args) throws IOException {
File file = new File("C:\\Users\\singx\\Desktop\\新建文件夹\\1.txt");
FileOutputStream fileOutputStream = new FileOutputStream(file);
DataOutputStream dataOutputStream = new DataOutputStream(fileOutputStream);
dataOutputStream.writeInt(3);//写入
dataOutputStream.writeDouble(77.8);//写入
dataOutputStream.close();
}
}
class hhhh{
public static void main(String[] args) throws IOException {
File file = new File("C:\\Users\\singx\\Desktop\\新建文件夹\\1.txt");
FileInputStream fileInputStream = new FileInputStream(file);
DataInputStream dataInputStream = new DataInputStream(fileInputStream);
int a = dataInputStream.readInt();//按顺序读取
double b = dataInputStream.readDouble();//按顺序读取
System.out.println(a);
System.out.println(b);
dataInputStream.close();
}
}
对象流
用来存储/读取对象、基本数据类型的值,以二进制形式存储,与数据流对应
ObjectInputStream
和ObjectOutputStream
,属于字节流
可以将对象写入到文件中
序列化:用ObjectOutputStream
类保存基本数据类型或者对象
反序列化:用ObjectInputStream
类读取保存的基本数据类型或者对象
不能序列化static
修饰的成员变量
序列化过程
一个类想要被序列化,需要满足实现
-
其内部属性是可序列化的
-
Serializable
接口(位于java.io. ) -
或者实现
Externalizable
接口 -
还需要提供一个全局常量,自定义异常类中的UID
public static final long serialVersionUID = XXXXXL
,UID可以不写,Java会自动随机生成 -
当不写UID时,如果类保存后,类中的代码修改了或者增加了其他属性,那么,该类再次读取文件时,会出错
-
如果写了UID,可以正常读取,并且新增的属性会自动置为默认值
Serial中文为顺序的,连续的,依次的,读音ˈsirēəl
,Serializable
中文为可序列化的
External中文为外部的、对外的,读音为ikˈstərnl
,Externalizable
中文为可外部的
Serializable
接口中没有任何的抽象方法,因此无需重写任何的方法
class 类名 implements Serializable{
//无需重写任何的方法
}
使用ObjectOutputStream
进行存储到本地
- 造文件
- 造文件流
- new一个
ObjectOutputStream
类的对象 - 调用
.writeObject(对象)
方法,将文件写入到硬盘中 - 关闭外层流
反序列化的过程
使用ObjectInputStream
进行存储到本地
- 造文件
- 造文件流
- new一个
ObjectInputStream
类的对象 - 调用
.readObject(对象)
方法,读取,该方法返回Object
类型 - 关闭外层流
读取时也是按照写入的顺序读取
例子
public class IoTest {
public static void main(String[] args) throws IOException {
File file = new File("C:\\Users\\singx\\Desktop\\新建文件夹\\object.txt");
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream(file));
Dog dog = new Dog("狗狗", 12, true);
objectOutputStream.writeObject(dog);
objectOutputStream.close();
}
}
class hhhh{
public static void main(String[] args) throws IOException, ClassNotFoundException {
File file = new File("C:\\Users\\singx\\Desktop\\新建文件夹\\object.txt");
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(file));
Dog s = (Dog)(objectInputStream.readObject());
System.out.println(s);
objectInputStream.close();
}
}
class Dog implements Serializable{
public static final long serialVersionUID = 98089897L;
String name;
int weight;
boolean female;
public Dog() {
}
public Dog(String name, int weight, boolean female) {
this.name = name;
this.weight = weight;
this.female = female;
}
@Override
public String toString() {
return "Dog{" +
"name='" + name + '\'' +
", weight=" + weight +
", female=" + female +
'}';
}
}
RandomAccessFile类
access,中文为使用权,读音ˈakˌses
,也是在java.io.
下,直接继承于Object
类,实现了DataInput
和DataOutput
接口,这个类既可以读又可以写
如果文件不存在,创建文件并写入,如果存在,从头挨个覆盖
-
造文件
-
不需要造文件流/字节流,直接使用FIle
-
RandomAccessFile 变量名 = new RandomAccessFile(FIle对象/文件路径, 模式字符串)
-
模式 含义 r
只读的方式打开 rw
读写的方式打开,不会立即写入到硬盘,如果发生异常,数据会丢失 rwd
读写的方式打开,立即写入到硬盘,如果发生异常,会被保存好数据 rws
读写的方式打开,
-
public static void main(String[] args) throws IOException, ClassNotFoundException {
RandomAccessFile raf1 = new RandomAccessFile("C:\\Users\\singx\\Desktop\\新建文件夹\\2.jpg", "r");
RandomAccessFile raf2 = new RandomAccessFile("C:\\Users\\singx\\Desktop\\新建文件夹\\23.jpg", "rw");
byte[] b = new byte[256];
int len;
while ((len = raf1.read(b)) != -1) {
raf2.write(b, 0, len);
}
raf1.close();
raf2.close();
}
例子
文件1
1234567
调用该类写入abc后,文件1会变为
abc456
该类中的实例变量还有其他方法:
-
设置文件指针的方法:
.seek(long)
,如果设置的文件指针比原有的文件最后的指针要长,那么,从原有文件最后到设置的指针之前要用空格填入 -
获取当前文件指针的方法:
.getFilePointer()
,返回long
-
获取文件大小(最大的指针长度)的方法:
.length()
,同样返回long
将数据插入到某个位置:
思路是先将文件指针定位到需要插入的位置,该位置之后的数据全部保存下来,再将指针定位到开始插入的位置,将需要写的数据插入,再将保存好的之后的数据插入
RandomAccessFile raf = new RandomAccessFile("路径", "rw");
//先将文件指针定位到需要插入的位置
raf.seek(需要插入的位置);
//使用byte数组存储该位置之后的数据
byte[] b;
//获取该位置之后的数据,长度就是文件的字节数-需要插入的位置
raf.read((b = new byte[(int)(raf.length() - 需要插入的位置)]));
//文件指针回滚,将指针定位到开始插入的位置
raf.seek(需要插入的位置);
//插入需要插入的数据
raf.write(需要插入的数据);
//再插入保存下来的之前得数据
raf.write(b);
raf.close();
Q.E.D.