对象流"/>
Java基础(三)IO流和对象流
文件及IO流
File类
1、来自于java.io包
2、是文件和目录路径名的抽象表示
3、文件和目录可以通过File封装为对象
4、对于File而言,封装的不是真正存在的文件,仅仅是封装一个路径,路径不一定存在,将来可以通过具体的方法,把这个路径封装的内容转换成具体存在。
5、路径:
用于描述文件或者文件夹所在位置的字符串
路径分类:
相对路径:是一个简化路径,相对于某个路径而言
绝对路径:是一个完整路径,从盘符开始
File类的构造方法
File f = new File("D:\\Java");File f1 = new File("D:\\Java\\test");File f2 = new File(f,"\\test");System.out.println(f);System.out.println(f1);System.out.println(f2);
输出结果为:
File类的创建方法
1、creatNewFile():创建当前File对象所描述的路径下的文件
2、mkdir():创建当前File对象所描述的文件夹(如果路径中包含不存在的父级路径,那么不会自动创建父级路径)路径中不能包含不存在的文件夹,只能创建最后一级
3、mkdirs():创建当前FIle对象所描述的文件夹(如果父级路径不存,自动创建父级路径)
File f3 = new File("D:\\Java\\test\\file.txt");f3.createNewFile();//创建当前路径下的文件(记得写后缀名)File f4 = new File("D:\\Java\\test\\file");f4.mkdir();//创建当前目录下的文件夹File f5 = new File("D:\\Java\\test\\mkdirs\\file");f5.mkdirs();//创建当前目录下的文件夹,若无父级目录,则自动创建父级目录
代码执行结果:
File类型的删除方法
1、delete():删除调用者所描述的文件或者文件夹,文件存在/文件夹为空才能删除成功
2、如果删除文件夹,只能删除空文件夹(里面不能有任何内容,包括空文件夹),删除文件的时候,文件有没有内容不影响
3、delete方法不走回收站
f5.delete();f4.delete();f3.delete();
执行结果:
File类型的判断方法
1、exists():判断当前调用者描述的路径是否存在,存在返回true
2、isFile():判断当前调用者是否是文件
3、isDirectory():判断当前调用者是否是文件夹
还是接之前的代码
boolean b5 = f5.exists();boolean b4 = f4.exists();boolean b3 = f3.exists();boolean b1 = f1.exists();//D:\Java\testboolean b = f.exists();//D:\JavaSystem.out.println(b5);System.out.println(b4);System.out.println(b3);System.out.println(b1);System.out.println(b);
判断结果:3
File类型获取功能
1、getAbsolutePath():获取File对象的绝对路径,与路径是否存在无关
2、getPath():获取当前File中封装的路径
3、getName():获取当前File对象描述的文件或文件夹的名称,仅仅获取路径的最后一级
4、list():获取当前文件夹下所有的文件和文件夹名称,到一个字符串数组中(只获取当前文件夹子一级的内容,子一级文件夹中的内容不会获取)返回String[]类型数组
5、listFiles():获取当前文件夹中所有的文件和文件夹的File对象,当一个File数组中,特点和list相同,只能获取当前文件夹内
附上完整代码:
File f = new File("D:\\Java");File f1 = new File("D:\\Java\\test");File f2 = new File(f,"\\test");System.out.println(f);System.out.println(f1);System.out.println(f2);File f3 = new File("D:\\Java\\test\\file.txt");f3.createNewFile();//创建当前路径下的文件(记得写后缀名)File f4 = new File("D:\\Java\\test\\file");f4.mkdir();//创建当前目录下的文件夹File f5 = new File("D:\\Java\\test\\mkdirs\\file");f5.mkdirs();//创建当前目录下的文件夹,若无父级目录,则自动创建父级目录f5.delete();f4.delete();f3.delete();boolean b5 = f5.exists();boolean b4 = f4.exists();boolean b3 = f3.exists();boolean b1 = f1.exists();//D:\Java\testboolean b = f.exists();//D:\JavaSystem.out.println(b5);System.out.println(b4);System.out.println(b3);System.out.println(b1);System.out.println(b);File f6 = new File("\\java");String p6 = f6.getAbsolutePath();String p5 = f5.getAbsolutePath();//路径不存在,但依然能获取String p1 = f1.getAbsolutePath();System.out.println(p6);System.out.println(p5);System.out.println(p1);String pp6 = f6.getPath();String pp5 = f5.getPath();//获取当前封装路径String pp1 = f1.getPath();System.out.println(pp5);System.out.println(pp1);System.out.println(pp6);String[] list = f6.list();//获取当前文件夹下所有的文件和文件夹名称,存到一个字符串数组中for (String s : list) {System.out.print(s+"\t");}System.out.println();File[] files = f6.listFiles();//获取当前文件夹中所有的文件和文件夹的File对象for (File file : files) {System.out.print(file+"\t");}
IO流
字节流
1、字节流的抽象基类:以字节读取数据
InputStream:字节输入流的所有类的超类
OutputStream:字节输出流的所有类的超类
2、根据交互的设备不同,有不同的子类
FileInputStream:文件字节输入流
1、是InputStream的子类,用于和磁盘上的文件进行交互
2、该类型不仅可以一次读取一个字节,也可以一次读取多个字节,不仅可以读取文本文件,也可以读取视频、音频、图片等非纯文本文件,一切的数据在计算机中都是以字节为单位存储的
3、构造方法:
FileInputStream(File f):将一个File对象所表示的文件封装到一个字节输入流中,未来都是以字节的方式读取文件中的内容
FileInputStream(String path):将一个字符串表示的文件文件路径封装到一个字节输入流中,未来都是以字节的方式读取文件的内容
注意事项:
无论使用哪个构造方法,都只能封装文件的路径(封装文件夹的路径没有任何意义,文件夹本身没有任何数据,所以不能使用流对象读取)
4、读取文件的方法:
int read():从当前输入流中,读取并返回一个字节,返回值为int类型,表示读取到的字节对应的整数信息,如果返回-1表示读取到文件末尾了
int read(byte[] arr):读取数组大小个数的字节信息,存储到数组中,返回值结果为int,表示本次读取到的字节的个数,如果读取到-1,表示读取完毕
void close():关闭流对象
File file = new File("D:\\Java\\test\\test.txt");FileInputStream fis = new FileInputStream(file);//文件字节输入流//FileInputStream fis = new FileInputStream("D:\\Java\\test\\test.txt");int read;while ((read=fis.read())!=-1){System.out.println(read);}fis.close();
在txt文件中写入“114514”后读取,输出如下:
FileOutputStream:文件字节输出流
1、可以将字节数据写出到指定文件中
2、构造方法:
FileOutputStream(File f):将file对象封装为字节输出流对象
FileOutputStream(String path):将path描述的路径封装成字节输出流对象
FileOutputStream(String path,boolean append):如果第二个参数为true(拼接),这次写出的内容就直接与原文件中的内容进行拼接,不写第二个参数或者为false(新建/覆盖)的时候,每次都是相当于新建了一个文件,写入内容,原文件中的内容不存在
3、字节流写出数据的方式:
write(int b):将指定的字节写出到目标文件中,一次写出一个字节信息
*输出的是单字节信息,b=‘a’和b=97是一样的
*write(‘暴’.getBytes());如果想要输出一个字符串可以将字符串转为字符数组然后输出,
write(byte[] b):一次像一个数组的内容写入到指定文件中
*byte[] b = {65,66,67,68}
write(byte[] b,int offset,int len):将len个字节内容,从指定字节数组b中的off索引开始,写出到文件中
文件复制
FileInputStream(File f):用于把想要复制的文件封装到字节输入流中
FileOutputStream(File f):把输入流中的文件输出到指定文件中
FileInputStream fis = new FileInputStream("D:\\Java\\test\\test.txt");FileOutputStream fos = new FileOutputStream("D:\\Java\\test\\1.txt");int write;while ((write=fis.read())!=-1){fos.write(write);}/*写到这里时发现,若存在同一fis对象的多个read()方法,则只有第一个生效,猜测是复制文件时,先把文件存入到fis对象中,当触发read()函数时会释放fis对象中的文件*///把目标文件内容输出出来FileInputStream fis1 = new FileInputStream("D:\\Java\\test\\test.txt");int read;while ((read=fis1.read())!=-1){System.out.println(read);}//先关输出流,再关输入流fos.close();fis.close();
高效字节缓冲流
1、BufferedOutputStream:是OutputStream的子类,表示高效字节缓冲输出流,代码底层其实是一个8182(1028*8)个字节的数组,提供了数组缓冲区,写出的时候也是,不直接写到磁盘上,而是写出到缓冲数组中,直到数组写满了或者调用flush方法(刷新),将内容刷新到目标文件中
2、BufferedInputStream:是InputStream的子类,高效缓冲输入流,代码低层也是一个8192个字节的数组,每次调用read()方法 一次读取8192个字节的内容,放入到缓冲数组中,但是每次调用read方法只读取一个,此时就是将缓冲数组中的内容,给你一个,直到你读取够8192个字节时,再次访问磁盘读取8192个字节信息
3、这两个流是包装类型,本身不具备任何读写功能,需要作用于某个流的基础上,对其进行增强,例如FileInputStream和FileOutputStream,原本效率比较低,加强之后效率就高
4、构造方法:
BufferedOutputStream(OutputStream):创建字节缓冲输出流对象
BufferedIutputStream(IutputStream):创建字节缓冲输入流对象
下面是使用高效字节缓冲流复制文件:
//高效字节缓冲流BufferedInputStream bis = new BufferedInputStream(new FileInputStream("D:\\Java\\test\\meeting.mp4"));//准备复制的文件路径BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("D:\\Java\\test\\1.mp4"));//准备拷贝的目标文件路径,若目标文件已存在,则会覆盖int write;while ((write=bis.read())!=-1){bos.write(write);}bos.flush();//调用flush()函数手动刷新,将缓存区中的内容刷新到目标文件夹中。bos.close();bis.close();
IO流如何保证流对象关闭
若忘记关闭流对象,或者代码出现问题没能执行关闭流对象时,可能会导致资源泄露问题,调用过的资源一直存在不被释放,会占用资源,也可能导致依赖这个资源的后续代码无法执行,甚至可能导致程序崩溃。
使用finally可以轻松解决这个问题,格式如下:
try{流对象的使用
}catch{ }finally{流对象的关闭
}
字符流
字符流写入数据:
1、字符输出流顶层父类Writer:
用于写出字符流的抽象父类
FileWriter:用于将字符写出到文件的常用子类
2、构造方法:
FileWriter(File f):根据File描述的路径创建一个字符输出流对象
FileWriter(String path):根据描述的路径,创建一个字符输出流对象
FileWriter(String path,boolean append):第二个参数表示拼接或覆盖
3、输出的方法:
write(int c):写出一个字符
write(char[] c):写出一个字符数组
write(char[] c,int off,in len):写出字符数组的一部分,从off开始len个
write(String str):写出一个字符串
write(String str,int off,int len):写出字符串的一部分,从off开始len个
FileWriter fw = new FileWriter(new File("D:\\Java\\test\\1.txt"));fw.write("你好,世界!");fw.write("hello world!");fw.write("114514");fw.close();
字符流读取数据:
1、字符输入流Reader
用于读取字符的抽象父类
FileReader:用于读取字符流的常用子类
2、FileReader的构造方法:
FileReader(File f):使用File对象描述的路径创建一个字符输入流对象
FileReader(String path):使用path描述的路径创建一个字符输入流对象
3、读取的方法:
int read():一次读取一个字符,返回值int类型,返回的就是这个字符在编码表中对应的整数,如果读取到-1表示到了文件末尾
int read(char[] arr):一次读取字符数组大小个字符,将文件中读取的字符放到参数数组中,返回值结果是int,表示读取到的字符个数,如果读取到了-1表示到了文件末尾
读取txt文件中的格式:
第一种方法:
FileReader fr = new FileReader("D:\\Java\\test\\1.txt");String line="";//可以将字符存在line中int len;while ((len=fr.read())!=-1){line+=(char)len;//将字节型转换成字符型System.out.print((char) len);}fr.close();
第二种方法:
FileReader fr = new FileReader("D:\\Java\\test\\1.txt");int len;char[] c=new char[1000];//将字符存储到定长的char数组中while ((len=fr.read(c))!=-1){System.out.println(new String(c,0,len));}fr.close();
高效缓冲字符流特有的方法
*其他方法见高效字节缓冲流
*高效的原理:减少了IO(输入输出)的次数
1、BufferedReader:
readLine():可以从输入流中一次读取一行的数据,返回一个字符串,如果到达文件末尾,则返回null
2、BufferedWriter:
newLine():作用是提供一个换行符
BufferedWriter bw = new BufferedWriter(new FileWriter("D:\\Java\\test\\bw.txt"));BufferedReader br = new BufferedReader(new FileReader("D:\\Java\\test\\br.txt"));bw.write("114514"+"\n");bw.write("helloworld");bw.newLine();bw.write("你好,世界!"+"\n");String str = "";while ((str=br.readLine())!=null){System.out.println(str);}bw.close();br.close();
转换流
1、编码表:
GBK:中文占两个字节,英文占一个字节
UTF-8:中文占三个字节,英文占一个字节
2、转换流作用:针对于不同编码集的文件,有正确的读写过程
3、OutputStreamWriter:字符流到字节流的桥梁,可以指定编码形式
构造方法 :OutputStreamWriter(OutputStream os,String charsetName)
创建一个转换流对象,可以将接收到的字符,通过指定的编码表charsetName,编码字节信息在通过指定的字节流os,将字节信息输出
使用方法:直接使用Writer类中的方法,该类是Writer的子类
4、InputStreamReader:字节流到字符流的桥梁,可以指定编码形式
构造方法:InputStreamReader(InputStream is,String charsetName);
创建一个转换流对象,可以使用is这个字节流,从磁盘中读取字节信息,通过指定的编码表,将字节信息转换为对应的字符信息,返回给调用者
该类是Reader的子类,直接使用Reader的方法即可
以gbk编码表为例,中文占两个字节,英文占一个字节,当读取utf-8格式的txt文件时会出现乱码
InputStreamReader isr = new InputStreamReader(new FileInputStream("D:\\Java\\test\\1.txt"),"gbk");int read;while ((read=isr.read())!=-1){System.out.print((char) read);}
而如果使用转换流将其用gbk编码格式写入后再读取,则不会出现乱码情况,即保证读写时使用的编码格式相同即可。
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("D:\\Java\\test\\2.txt"),"gbk");osw.write("114514"+"\n");osw.write("一一四五一四"+"\n");osw.write("AADEAD"+"\n");osw.close();
对象流
对象序列化
对象流
1、对象流:将一个对象写出到文件中(序列化),将文件中的对象读取(反序列化)
2、序列化:就是将对象写入到文件的过程
3、反序列化:将对象和对象的内容从文件中获取到
4、对象:表示任意对象,Object的子类,都可以读写
序列化的类需要接口serialVersion
以Person类为例:
import java.io.Serializable;public class Person implements Serializable {private String name;private int age;public Person(String name, int age) {this.name = name;this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}
}
对象输出流
1、ObjectOutputStream:对象输出流
功能:将对象写入到文件中
2、构造方法:
ObjectOutputStream(OutputStream o):需要绑定一个写出的位置,比如向文件中写出
3、常用方法:
writeObject(Object o):向目标位置写出一个对象
4、注意事项:
写出的对象,必须实现Serializable序列化接口,如果没有实现接口,就会报错
5、对象写出到目标文件中,内容无法人为阅读,因为计算机将对象转换成了字节信息存储,只有计算机能够读懂,当反序列化的时候,能够将对象成功读取
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("D:\\Java\\test\\Output.txt"));oos.writeObject(new Person("珂朵莉",15));oos.writeObject(new Person("577",17));oos.writeObject(new Person("Abigail",19));oos.close();
对象输入流
1、ObjectInputSteam:对象输入流,是InputStream的子类
2、功能:将对象从文件中读取
3、构造方法:
ObjectInputStream(InputStream in):从指定的某个流中读取
4、常用方法:
readObject():将对象从文件中读取出来,每次读取一个对象,返回值类型Object类型,如果文件中没有对象还要继续读取,就会抛出异常
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("D:\\Java\\test\\Output.txt"));Person p;while ((p=(Person) ois.readObject())!=null){System.out.println(p.getName()+":"+p.getAge()+"岁");}ois.close();
运行后会出现异常,这里是正常现象,不影响代码运行,若不想看到红色提示可以用try…catch来捕获while函数的异常
序列号serialVersionUID和transient关键字
1、类每次被编译成.class文件的时候,都会有一个版本号long类型的数字
2、类有任何改动,都会重新编译.class文件,生成一个新的版本
3、两次的数字如果不相等,进行对象序列化的时候,会记录class文件的版本号long数字,反序列化的时候,会检测当前.class文件的版本号和序列化时是否一致,如果不一致就会报错,无效的类异常InvalidClassException
4、解决方案:
手动添加一个成员序列号,序列号是一个固定的值,不管改不改代码,序列号都是一致的,当我们认为类中的内容改动比较大,就可以去修改序列号,此时在这之前存储的对象都不能使用了
private static final long serialVersionUID = 1l;
*相当于手动添加了版本号
private static final long serialVersionUID = 10000;
5、transient关键字:
如果一个对象中的成员变量值,不希望跟随序列化被保存,就可以使用transient关键字来修饰,修饰之后,该成员变量的值,就不会被序列化,不会写出到文件中。
*transient修饰的内容,不会写出到目标文件中,相当于不参与序列化
比如把类中的成员变量age用transient修饰:
更多推荐
Java基础(三)IO流和对象流
发布评论