IO流的概述
IO流用来处理设备之间的数据传输, 在Java中对数据的操作是通过流的方式进行的,Java用于操作流的对象都在IO包中,因此要导入IO包。
File类
File类是文件和目录路径名的抽象表现形式,它既可以用来表示目录,也可以用来表示文件。
代码演示
判断桌面是否有 .png 文件,有就输出该文件的绝对路径。
import java.io.File;
public class File1 {
public static void main(String[] args) {
//根据一个路径得到File对象
File file = new File("C:\\Users\\Administrator\\Desktop");
//调用listFiles()方法,来获取该目录下的所有文件
File[] files = file.listFiles();
//遍历文件数组
for (File file1 : files) {
//判断文件名是否以.png结尾,getName()是获得文件的名字,endsWith()是判断是否以某字符串结尾
if(file1.getName().endsWith(".png")){
//getAbsolutePath()方法获取绝对路径
System.out.println(file1.getAbsolutePath());
}
}
}
}
结果
C:\Users\Administrator\Desktop\File类.png
C:\Users\Administrator\Desktop\IO流.png
C:\Users\Administrator\Desktop\Map集合.png
C:\Users\Administrator\Desktop\异常.png
注:相对路径:没有带盘符的路径;绝对路径:带有盘符的路径;
字节流
FileOutputStream类
文件输出流(FileOutputStream)是用于将数据写入文件中的输出流,与FileInputStream一起使用。
FileInputStream
文件输入流(FileInputStream)是用于将文件中的数据读出来的流,它是与FileOutputStream一起使用的。
代码演示
把C:\Users\Administrator\Desktop\FileInputStream类.png的内容复制到C:\Users\Administrator\Desktop\FileInputStream类2.png中
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
//在输入输出流中可能会出现一些输入输出错误,因此要解决这些错误,你可以用到try..catch或throws
//要是不写的话IDE会报错
public class Demo {
public static void main(String[] args) throws IOException{
//创建一个输入流对象
FileInputStream in = new FileInputStream("C:\Users\Administrator\Desktop\FileInputStream类.png");
//创建一个输出流对象,如果该对象的文件不存在,就会创建这个文件
FileOutputStream out = new FileOutputStream("C:\Users\Administrator\Desktop\FileInputStream类2.png");
byte[] b=new byte[10];
int len=0;
//输入流对象用read方法把数据读入数组中,如果读到数据就返回数据的长度,如果没读到就返回-1
while((len=in.read(b))!=-1){
//输出流对象调用write(数组名,读这个数据从哪个位置写入,数据的长度 )方法,把数据写入文件中
out.write(b,0,len);
}
//关闭流
in.close();
out.close();
}
}
结果
注:用完流时,要关闭流,因为不关闭的话会占用资源(也可以说是被认为还在使用中,因此不能进行垃圾回收),长久下来就容易造成内存溢出。
BufferedOutputStream类
该类实现缓冲的输出流,通过设置这种输出流,应用程序就可以将各个字节写入底层输出流中,而不必针对每次字节写入调用底层系统,一般也适合它配对的输入流一起使用(BufferedInputStream类)。
BufferedInputStream类
该类实现缓冲的输入流,可以通过此流读数据。(在创建 BufferedInputStream
时,会创建一个内部缓冲区数组。在读取或跳过流中的字节时,可根据需要从包含的输入流再次填充该内部缓冲区,一次填充多个字节。)。
代码演示
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.FileInputStream;
import java.io.FileOutputStream;
public class File11 {
public static void main(String[] args) throws IOException {
BufferedInputStream inputStream = new BufferedInputStream(new FileInputStream("a.txt"));
BufferedOutputStream outputStream = new BufferedOutputStream(new FileOutputStream("fox.txt"));
byte[] bytes = new byte[100];
int len=0;
//把数据读到数组中,如果没有读到数据会返回-1
while ((len=inputStream.read(bytes))!=-1){
//调用write方法把数组中的数据写入到文件中,len为写入的个数,0为从该数据的开头位置
outputStream.write(bytes,0,len);
//该输出流必须刷新,不然数据不会写到文件中,其实不刷新也可以,最后关闭流时,会自动刷新,不过建议每写一次刷新一次,因为如果写入的东西太多,最后关闭流时刷新,会丢失数据
outputStream.flush();
}
inputStream.close();
outputStream.close();
}
}
结果
字符流
字符流的出现是由于字节流操作中文不是特别方便,所以,java就提供了字符流。字符流:字符流 = 字节流 + 编码表
注:字符流写入时都需要刷新一下,否则数据写不到文件中。
OutputStreamWriter类
OutputStreamWriter 是字符流通向字节流的桥梁,它可使用指定的
将要写入流中的字符编码成字节。charset(如:UTF-8,GB2312等编码)
InputStreamReader类
InputStreamReader 是字节流通向字符流的桥梁:它使用指定的
读取字节并将其解码为字符。charset
代码演示
键盘录入5个学生信息(姓名,语文成绩,数学成绩,英语成绩),按照总分从高到低存入文本文件
public class Student {
private String name;
private int chinese;
private int math;
private int english;
private int totalSocre;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getChinese() {
return chinese;
}
public void setChinese(int chinese) {
this.chinese = chinese;
}
public int getMath() {
return math;
}
public void setMath(int math) {
this.math = math;
}
import java.io.*;
import java.util.Comparator;
import java.util.Scanner;
import java.util.TreeSet;
public class File9 {
public static void main(String[] args) throws IOException {
//创建输出流对象,FileOutputStream里的true表示可在数据后面追加数据,不会覆盖先前的数据
OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream("a.txt",true));
//创建一个TreeSet对象,用来存放Student对象,并重写比较器,按总分从高到低排
TreeSet<Student> set = new TreeSet<>(new Comparator<Student>() {
@Override
public int compare(Student t1, Student t2) {
int v = t1.getTotalSocre() - t2.getTotalSocre();
return (v==0)?(t1.getName().compareTo(t2.getName())):v;
}
});
Scanner sc = new Scanner(System.in);
for (int i = 1; i <= 5; i++) {
Student student = new Student();
System.out.println("请输入第"+i+"个学生的姓名:");
student.setName(sc.next());
System.out.println("请输入第"+i+"个学生的语文成绩:");
student.setChinese(sc.nextInt());
System.out.println("请输入第"+i+"个学生的数学成绩 :");
student.setMath(sc.nextInt());
System.out.println("请输入第"+i+"个学生的英语成绩:");
student.setEnglish(sc.nextInt());
set.add(student);
}
writer.write("姓名"+" "+"语文成绩"+" "+"数学成绩"+" "+"英语成绩"+" "+"总分");
//写入一个回车换行,这样数据不会挤在一起,看着舒服些
writer.write("\r\n");
writer.flush();
//遍历集合
for (Student stu : set) {
writer.write(stu.getName()+" "+stu.getChinese()+" "+stu.getMath()+" "+stu.getEnglish()+" "+stu.getTotalSocre());
writer.write("\r\n");
writer.flush();
}
writer.close();
}
}
结果
请输入第1个学生的姓名:
jack
请输入第1个学生的语文成绩:
67
请输入第1个学生的数学成绩 :
78
请输入第1个学生的英语成绩:
89
请输入第2个学生的姓名:
sam
请输入第2个学生的语文成绩:
89
请输入第2个学生的数学成绩 :
67
请输入第2个学生的英语成绩:
56
请输入第3个学生的姓名:
jim
请输入第3个学生的语文成绩:
78
请输入第3个学生的数学成绩 :
89
请输入第3个学生的英语成绩:
34
请输入第4个学生的姓名:
lucy
请输入第4个学生的语文成绩:
67
请输入第4个学生的数学成绩 :
98
请输入第4个学生的英语成绩:
83
请输入第5个学生的姓名:
rose
请输入第5个学生的语文成绩:
78
请输入第5个学生的数学成绩 :
43
请输入第5个学生的英语成绩:
91
便捷类
OutputStreamWriter和的InputStreamReader名字比较长,而我们常见的操作都是按照本地默认编码实现的,所以,为了简化我们的书写,提供了对应的子类FileWriter和 FileReader,因此一样的用法。
字符缓冲流
BufferedWriter类
BufferedWriter写出数据,它是高效的字符输出流。
BufferedReader类
BufferedReader读取数据 ,它是高效的字符输入流。
代码演示
复制文件
import java.io.*;
public class File7 {
public static void main(String[] args) throws IOException {
//创建输入流对象,读的文件地址
BufferedReader reader = new BufferedReader(new FileReader("C:\\Users\\Administrator\\Desktop\\CopyFolderDemo.java"));
//创建输出流对象,写的文件地址
BufferedWriter writer = new BufferedWriter(new FileWriter("C:\\Users\\Administrator\\Desktop\\CopyFolderDemo2.java"));
//读入一行数据(BufferedReader特有方法),如果没有的话就返回null值,并结束循环
while (reader.readLine()!=null){
//写入一行数据
writer.write(reader.readLine());
//写入回车换行(BufferedWriter特有方法)
writer.newLine();
//要刷新哟,不然数据写不进去
writer.flush();
}
reader.close();
writer.close();
}
}
结果
打印流
打印流只能操作目的地,不能操作数据源(不能进行读取数据),可以操作任意类型的数据,调用print() 方法可以写任意数据类型,如果启用自动刷新,那么在调用println、printf 或 format 方法中的一个方法的时候,会完成自动刷新。
PrintWriter类
代码演示
import java.io.*;
public class File10 {
public static void main(String[] args) throws IOException {
//创建一个BufferedReader对象
BufferedReader reader = new BufferedReader(new FileReader("a.txt"));
//创建一个打印流对象
PrintWriter print = new PrintWriter("C:\\Users\\Administrator\\Desktop\\fox.txt");
//定义一个字符串变量
String line=null;
//把上面的字符串变量用来接收读到问一行数据,如果文件中没有数据就还是null,那就结束循环
while ((line=reader.readLine())!=null){
String s = line;
//打印字符串到文件中
print.println(s);
//刷新流,不然写不进文件哟
print.flush();
}
//关闭流
reader.close();
print.close();
}
}
结果
随机访问流
RandomAccessFile类
RandomAccessFile最大的特点是能读能写,也可以操作任意数据类型的数据,它不属于流,是Object类的子类,但它融合了InputStream和OutputStream的功能,因此它支持对随机访问文件的读取和写入。
代码演示
import java.io.IOException;
import java.io.RandomAccessFile;
public class IODemo {
public static void main(String[] args) throws IOException {
//RandomAccessFile概述 最大特点 能读能写 能够获取文件指针的位置,也能够定位指针的位置
writeData();
redeData();
}
private static void redeData() throws IOException {
RandomAccessFile rw = new RandomAccessFile("f.txt", "rw");
//你怎么写的就怎么读取,读的格式和写的格式一样,如:先写了int类型,那就先读int类型
int i = rw.readInt();
long pointer = rw.getFilePointer();
System.out.println("指针位置"+pointer);
System.out.println(i);
boolean b = rw.readBoolean();
pointer = rw.getFilePointer();
System.out.println("指针位置" + pointer);
System.out.println(b);
byte b1 = rw.readByte();
pointer = rw.getFilePointer();
System.out.println("指针位置" + pointer);
System.out.println(b1);
String s = rw.readUTF();
pointer = rw.getFilePointer();
System.out.println("指针位置" + pointer);
System.out.println(s);
System.out.println("-------------------");
rw.seek(0);//定位指针的位置
int and= rw.readInt();
System.out.println(and);
rw.seek(6);
String s1 = rw.readUTF();
System.out.println(s1);
}
private static void writeData() throws IOException {
RandomAccessFile rw = new RandomAccessFile("f.txt", "rw");
//首先会先写入两个字节,然后才会把该数据写入,因此位置最后定位到4
rw.writeInt(100);
rw.writeBoolean(false);
rw.writeByte(10);
rw.writeUTF("你好");
rw.close();
}
}
结果
指针位置4
100
指针位置5
false
指针位置6
10
指针位置14
你好
-------------------
100
你好
序列化流和反序列化流
ObjectOutputStream类
序列化流(ObjectOutputStream)也就是把对象通过流存储到文件中,它也可以操作任意类型数据,还要注意的一点是必须要重写Serializable接口(这个接口中没有任何方法,只是起到了标记作用)才能被序列化,如果你不想要序列化,transient关键字可以声明不需要序列化的成员变量。
ObjectinputStream类
反序列化流(ObjectinputStream)也就是把文件中存储的对象以流的形式还原为对象,可以读任意类型数据。
代码演示
1、通过序列化流与反序列化流完成从file.txt文件存取基本数据类型的操作
import java.io.*;
public class MyDemo {
public static void main(String[] args) throws IOException {
//创建序列化流
ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("C:\\Users\\Administrator\\Desktop\\file.txt"));
//调用write方法写入数据,记得要刷新哟,不然写不进去哟
outputStream.writeBoolean(true);
outputStream.writeDouble(3.14);
outputStream.writeInt(100);
outputStream.flush();
//创建反序列化流
ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream("C:\\Users\\Administrator\\Desktop\\file.txt"));
//怎么写就怎么读出来,按着写的顺序读出来
System.out.println(inputStream.readBoolean());
System.out.println(inputStream.readDouble());
System.out.println(inputStream.readInt());
}
}
结果
true
3.14
100
注:文件里存放的是乱码,只有读出来的时候才会还原成原来的数据。你可能会疑惑,不是要继承Serializable接口吗?为什么这里没有继承呢?,因为那是在对对象进行操作时才用到,这里是对基本数据类型进行操作。
2、通过序列化流与反序列化流完成从file.txt文件存取对象的操作
import java.io.Serializable;
//因为在主函数中要操作该对象,因此要实现这个接口,不过这个接口只是做标记作用,因此只要写上就好了
class Person implements Serializable {
private String name;
private transient int age;
public Person (String name, int age) {
this.name = name;
this.age = age;
}
public void setName (String name) {
this.name = name;
}
public String getName () {
return name;
}
public void setAge (int age) {
this.age = age;
}
public int getAge(){
return age;
}
//重写toString方法,以便于按照想要的格式输出
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
}
import java.io.*;
class Test4 {
public static void main(String[] args) throws IOException, ClassNotFoundException {
write();
read();
}
public static void write() throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("C:\\Users\\Administrator\\Desktop\\file.txt"));
//写入Person对象
oos.writeObject(new Person("Tom", 20));
oos.close();
}
//readObject()这个方法会抛出ClassNotFoundException这个错误,因此要加上
public static void read() throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("C:\\Users\\Administrator\\Desktop\\file.txt"));
//读出Person对象
Object obj = ois.readObject();
//这里会自动调用toString方法,这个方法在上面的Person类中有重写,因此会按照重写的格式输出
System.out.println(obj);
ois.close();
}
}
结果
Person [name=Tom, age=0]
你可能会疑惑,明明把年龄都输入为20了怎么输出是0呢?因为transient修饰了age,transient修饰的变量不参与序列化过程(也就是写入的过程没参与),因此它没赋上值,所以为0,存到的文件夹里的数据也是乱码哟。
Properties类的概述
Properties 类表示了一个持久的属性集,它可保存在流中或从流中加载,它的列表中每个键及其对应值都是一个字符串,并且其父类是Hashtable,属于双列集合,这个集合中的键和值都是字符串 ,但是它不能指定泛型。
代码演示
有一个file.txt文件,判断是否有"lisi"这样的键存在,如果有就改变其实为"100"
file.txt文件内容如下:
zhangsan = 90
lisi = 80
wangwu = 85
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Properties;
public class MyDemo2 {
public static void main(String[] args) throws IOException {
//创建一个Properties对象
Properties properties = new Properties();
//加载路径下的文件
properties.load(new FileInputStream("C:\\Users\\Administrator\\Desktop\\file.txt"));
//判断该文件是否有lisi这个键值存在,有就把对应的值改成100
if(properties.containsKey("lisi")){
//因为文件中存的是字符串,因此要把int类型转换成String类型,然后用put方法把值给覆盖
properties.put("lisi", String.valueOf(100));
//覆盖好值之后,要把值给存到文件中,这后面的null是注释,当你不知道要写什么你可以null
properties.store(new FileOutputStream("C:\\Users\\Administrator\\Desktop\\file.txt"),null);
System.out.println("修改成功");
}
}
}
结果
修改成功
注:这上面的那一串时间就是你填写null值得结果,你可以随意些一个字符串,写进文件时会变为 #你写的字符串 这样的格式。