5. IO流
5.1 File类
5.1.1 文件对象的构造
这个类对一个文件(夹)的描述。
关于路径分隔符:
路径分隔符是对目录的分隔。表示一层嵌套关系。
在windows系统下,分隔符是:\
在UNIX体系下,分隔符是:/
| 构造方法 | 参数 |
|---|---|
| File(String pathName) | 使用一个指定的文件路径来实例化一个File对象 |
| File(String parent, String name) | 给定一个父目录路径和一个子文件名字,系统会自动的将其拼接在一起 |
| File(File parent, String child) | 给定一个父目录和一个子文件的名字,系统自动拼接路径 |
public class FileDemo {public static void main(String[] args) {//File构造函数演示String pathName = "hello.java";File f1 = new File(pathName);System.out.println(f1);File f2 = new File("/Users/IdeaProjects/JavaSE","hello.java");System.out.println(f2);//将parent封装成file对象。File dir = new File("/Users/IdeaProjects/JavaSE");File f3 = new File(dir,"hello.java");System.out.println(f3);}}
5.1.2 文件和文件夹的常用方法
| 常用方 | 描述 |
|---|---|
| boolean exists() | 用来描述指定的路径下到底有没有文件(夹)存在。 |
| boolean isFile() | 判断指定路径的内容是不是一个文件 |
| boolean isDirectory() | 判断指定路径的内容是不是一个文件夹 |
| boolean mkdir() | 在指定的路径创建文件夹,返回值代表创建成功还是失败 |
| boolean mkdirs() | 在指定的路径创建文件夹,可以创建多级目录 |
| boolean createNewFile() | 在指定的路径创建一个空文件,会有一个IOException |
| boolean isHidden() | 判断一个文件是否是隐藏的 |
public static void main(String[] args) throws IOException {// 对文件或者文件加进行操作。File file = new File("/Users/IdeaProjects/JavaSE/file.txt");//获取文件的绝对路径,即全路径String absPath = file.getAbsolutePath();//File中封装的路径是什么获取到的就是什么。String path = file.getPath();//获取文件名称String filename = file.getName();//获取文件大小long size = file.length();System.out.println("absPath="+absPath);System.out.println("path="+path);System.out.println("filename="+filename);System.out.println("size="+size);// 创建文件,如果文件不存在,创建 true 如果文件存在,则不创建 false。// 如果路径错误,IOException。boolean file1 = file.createNewFile();System.out.println("file1=" + file1);//-----------删除文件操作-------注意:不去回收站。慎用------boolean flag = file.delete();System.out.println("flag="+flag);//-----------需要判断文件是否存在------------boolean falg1 = file.exists();System.out.println("falg1="+falg1);//-----------对目录操作 创建,删除,判断------------File dir = new File("/Users/IdeaProjects/JavaSE/");//mkdir()创建单个目录。//dir.mkdirs();创建多级目录boolean flag2 = dir.mkdir();System.out.println("flag2="+flag2);//删除目录时,如果目录中有内容,无法直接删除。boolean flag3 = dir.delete();//只有将目录中的内容都删除后,保证该目录为空,这个目录才可以删除。System.out.println("flag3=" + flag3);//-----------判断文件,目录------------File file3 = new File("/Users/IdeaProjects/JavaSE/");// 要判断是否是文件还是目录,必须先判断存在。System.out.println(file3.isFile());System.out.println(file3.isDirectory());//-----------目录遍历------------File dir1 = new File("/Users/IdeaProjects");//获取的是目录下的当前的文件以及文件夹的名称。String[] names = dir.list();for(String name : names){System.out.println(name);}//获取目录下当前文件以及文件对象,只要拿到了文件对象,那么就可以获取其中想要的信息File[] files = dir.listFiles();for(File file4 : files){System.out.println(file4);}}
5.1.3 文件过滤器
实现自己的文件过滤器
public class FileDemo{public static void main(String[] args) {//获取扩展名为.java所有文件//创建File对象File file = new File("/Users/IdeaProjects");//获取指定目录下的文件夹File[] files = file.listFiles(new FileFileterByDir());//遍历获取到的所有符合条件的文件for (File f : files) {System.out.println(f);}}}自定义类继承FileFilter过滤器接口//文件过滤器class FileFileterByDir implements FileFilter{public boolean accept(File pathname) {return pathname.isDirectory();}}
5.2 流的分类
分类:
方向:输入流和输出流
数据单元:字节流和字符流
InputStream: 字节输入流的超类
OutputStream: 字节输出流的超类
Reader: 字符输入流
Writer: 字符输出流
无论是使用什么流,在使用结束之后,一定要记得关闭这个流。
5.3 字节流
InputStream:
int read(byte[] arr):
参数:从流中读取数据,需要使用到一个字节数组。read方法会将读取到的数据填充到这个字节数组中。所以,读取完成后,我们直接从这个字节数组中取数据即可。
返回值:每次读取数据后,返回这一次读取到了多少个字节的数据。如果这个方法返回值为-1,说明本次没有读取到任何数据,读取完成。
class FileInputStreamDemo {public static void main(String[] args) throws Exception {// 先声明InputStream is = null;try {// 实例化一个流is = new FileInputStream(new File("/Users/IdeaProjects/JavaSE/src/kaikeba/com/ArraysDemo.java"));// InputStream: 字节输入流,数据是以字节为单位// 从管道中读取数据:// 实例化一个数组,用来存储每次读取到的数据byte[] contents = new byte[100];// 用来记录每次读取到了多少数据int length = 0;// 循环读取,将每次读取到的数据长度给length赋值,并判断是否为-1while ((length = is.read(contents)) != -1) {// 通过一个字节数组实例化一个字符串String str = new String(contents, 0, length);System.out.print(str);}} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();} finally {// 判断是否为空,因为如果最开始实例化的时候,文件不存在,则is实例化失败,依然为null// 此时,如果is是null,那么再去关闭的时候,就会出现NullPointerExceptionif (is != null) {try {is.close();} catch (IOException e) {e.printStackTrace();}}}}}
outputStream:操作的数据都是字节,定义了输出字节流的基本共性功能方法。
OutputStream有很多子类,其中子类FileOutputStream可用来写入数据到文件。
FileOutputStream类,即文件输出流,是用于将数据写入 File的输出流。
public class FileOutputStreamDemo2 {public static void main(String[] args) throws Exception {File file = new File("myfile.txt");FileOutputStream fos = new FileOutputStream(file, true);String str = "\r\n"+"kaikeba.com";fos.write(str.getBytes());fos.close();}}
5.4 字符流
Reader:字符输入流
Writer:字符输出流
写操作
public static void main(String[] args) throws IOException {//1.创建FileWriter的对象并关联对应的文件//注意点://一:如果只写文件的名字,不写具体路径,默认路径是当前的工程//二:对于关联的文件,如果之前不存在,程序会自动创建一个,如果存在,会将原来的内容覆盖//三:可以自己指定路径,但是必须保证路径是真实存在的,否则报异常---FileNotFountException(系统找不到指定的路径。)FileWriter fileWriter = new FileWriter("fileWrite.txt");//调用写入方法//注意点四:在执行write方法时,数据被临时放到了流对象的内部数组中,这个数组是一个字节数组,会默认去查编码表fileWriter.write("晨晨");//刷新---将临时数组中的数据放入磁盘//fileWriter.flush();//4.关闭流--两个功能:a:关闭流 b:刷新//第五个注意点:流对象使用完后必须关闭fileWriter.close();//第六个注意点:当流对象关闭之后,不能再进行操作,否则会报异常:Stream closed//fileWriter.write("haha");}
读操作
public static void main(String[] args) throws IOException {//1.创建文件读入流FileReader fileReader = new FileReader("/Users/IdeaProjects/JavaSE/src/kaikeba/com/ArraysDemo.java");//开始读操作/** read():一个字符一个字符的读,每次读出一个字符*///a:将当前的字符读出 b:将当前的指针后移一位int value;while ((value = fileReader.read()) != -1) {System.out.println((char)value);}fileReader.close();}public static void main(String[] args) throws IOException {//1.创建文件读入流FileReader fileReader = new FileReader("/Users/liyunxia/IdeaProjects/JavaSE/src/kaikeba/com/ArraysDemo.java");//开始读操作/** read(数组):一次可以读出多个字符 ,数组的作用:每次会将读出的字符临时放到这个数组中*//* 数组是临时存放数据的地方,我们会将读到的字符放到临时数组中,数组的大小决定了我们一次可以读到的字符个数.* 一般这个数组的大小<=1kB* 返回值代表本次读到的真实的字符个数,如果返回值是-1代表读完了.*/char[] cbuf = new char[2];int num ;while ((num = fileReader.read(cbuf)) != -1) {//System.out.println(new String(cbuf)+" num:"+num);System.out.println(new String(cbuf,0,num)+" num:"+num);}fileReader.close();}
5.5 转换流
InputStreamReader、OutputStreamWriter
字节=》字符=》字节
转换流:保留了字符流对文本进行操作时候的便利性,保留了字节流的安全性。产生的一种新的流,主要用来做关于文本文件的处理。还可以解决:关于采用了不同字符集的文本之间的处理问题。可以使用指定的字符集来读取一个文本,也可以使用指定的字符集来写一个文本。
OutputStreamWriter
public class InputStreamReaderDemo {public static void writeCN() throws Exception {//创建与文件关联的字节输出流对象FileOutputStream fos = new FileOutputStream("OutputStreamWriter.txt");//创建可以把字符转成字节的转换流对象,并指定编码OutputStreamWriter osw = new OutputStreamWriter(fos,"utf-8");//调用转换流,把文字写出去,其实是写到转换流的缓冲区中osw.write("你好");//写入缓冲区。osw.close();}}
InputStreamReader
public class InputStreamReaderDemo {public static void main(String[] args) throws IOException {//演示字节转字符流的转换流readCN();}public static void readCN() throws IOException{//创建读取文件的字节流对象InputStream in = new FileInputStream("/Users/IdeaProjects/JavaSE/src/kaikeba/com/ArraysDemo.java");//创建转换流对象//InputStreamReader isr = new InputStreamReader(in);用本地默认码表读取,可能发生解码的错误InputStreamReader isr = new InputStreamReader(in,"utf-8");//使用转换流去读字节流中的字节int ch = 0;while((ch = isr.read())!=-1){System.out.println((char)ch);}//关闭流isr.close();}}
5.6 缓冲流
Java提供一套缓冲流,可提高IO流的读写速度;
缓冲流,根据流的分类分类字节缓冲流与字符缓冲流。
BufferedInputStream、BufferedOutputStream、BufferedReader、BufferedWriter
BufferInputStream、BufferedOutputStream:
public class BufferedStreamDemo {public static void main(String[] args) throws IOException {//写数据到文件的方法write();read();}private static void write() throws IOException {//创建基本的字节输出流FileOutputStream fileOut = new FileOutputStream("bufferedStream.txt");//使用高效的流,把基本的流进行封装,实现速度的提升BufferedOutputStream out = new BufferedOutputStream(fileOut);//2,写数据out.write("hello".getBytes());//3,关闭流fileOut.closeout.close();}private static void read() throws IOException {//1,创建缓冲流对象FileInputStream fileIn = new FileInputStream("bufferedStream.txt");//把基本的流包装成高效的流BufferedInputStream in = new BufferedInputStream(fileIn);//2,读数据int ch = -1;while ( (ch = in.read()) != -1 ) {//打印System.out.print((char)ch);}//3,关闭in.close();}}
BufferedReader、BufferedWriter
public class BufferedWriterReaderDemo {public static void main(String[] args) throws IOException {write();read();}private static void write() throws IOException {//创建流//基本字符输出流FileWriter fileOut = new FileWriter("file.txt");//把基本的流进行包装BufferedWriter out = new BufferedWriter(fileOut);//2,写数据for (int i=0; i<5; i++) {out.write("hello");out.newLine();}//3,关闭流out.close();}private static void read() throws IOException {//1,创建流BufferedReader in = new BufferedReader(new FileReader("file.txt"));//2,读数据//一次一个字符//一次一个字符数组//一次读取文本中一行的字符串内容String line = null;while( (line = in.readLine()) != null ){System.out.println(line);}//3,关闭流in.close();}}
5.7 序列化和反序列化流
用于从流中读取对象的操作流 ObjectInputStream 称为反序列化流
用于向流中写入对象的操作流 ObjectOutputStream 称为序列化流
特点:用于操作对象。可以将对象写入到文件中,也可以从文件中读取对象
import java.io.*;class Person implements Serializable {private String name;private int age;public Person() {super();}public Person(String name, int age) {super();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;}@Overridepublic String toString() {return "Person [name=" + name + ", age=" + age + "]";}}class ObjectStreamDemo {public static void main(String[] args) throws IOException, ClassNotFoundException {writeObj();//对象的序列化。readObj();//对象的反序列化。}public static void writeObj() throws IOException {//1,明确存储对象的文件。FileOutputStream fos = new FileOutputStream("object.txt");//2,给操作文件对象加入写入对象功能。ObjectOutputStream oos = new ObjectOutputStream(fos);//3,调用了写入对象的方法。oos.writeObject(new Person("wangcai",20));//关闭资源。oos.close();}public static void readObj() throws IOException, ClassNotFoundException {//1,定义流对象关联存储了对象文件。FileInputStream fis = new FileInputStream("object.txt");//2,建立用于读取对象的功能对象。ObjectInputStream ois = new ObjectInputStream(fis);Person obj = (Person)ois.readObject();System.out.println(obj.toString());}}
注意:
一个对象要能被序列化,对象所属的类必须实现Serializable接口。否则会发生NotSerializableException异常。
当反序列化对象时,如果对象所属的class文件在序列化之后进行的修改,那么进行反序列化也会发生异常InvalidClassException。发生这个异常的原因如下:该类的序列版本号与从流中读取的类描述符的版本号不匹配/该类包含未知数据类型/该类没有可访问的无参数构造方法.
Serializable标记接口,给需要序列化的类,提供了一个序列版本号:serialVersionUID,该版本号的目的在于验证序列化的对象和对应类是否版本匹配。
静态修饰也不会被序列化,因为序列化是把对象数据进行持久化存储,而静态的属于类加载时的数据,不会被序列化。
当一个类的对象需要被序列化时,某些属性不需要被序列化,这时不需要序列化的属性可以使用关键字transient修饰。只要被transient修饰了,序列化时这个属性就不会琲序列化了。
5.8 标准输入输出流
System类,定义了一系列有用的属性和方法
System.in、System.out
PrintStream
System.in
结束控制台输入方案:
- 在Windows环境下,需要输入Ctrl+Z;在Linux/Unix/MAC环境下,需要输入Ctrl+D;
- 使用结束条件;
public static void main(String[] args) throws IOException {//创建输入流对象InputStream is = System.in;//创建输出流对象FileWriter fw = new FileWriter("file.txt");//读取数据byte[] bys = new byte[1024];int len;while((len = is.read(bys))!=-1){fw.write(new String(bys, 0, len));fw.flush();}fw.close();is.close();}
System.out
public static void main(String[] args) throws IOException {//创建输入输出流对象BufferedReader br=new BufferedReader(new FileReader("file.txt"));OutputStream os=System.out;String line;//用于存储读取到的数据while((line=br.readLine())!=null) {os.write(line.getBytes());os.write("\r\n".getBytes());}//释放资源os.close();br.close();}
由于标准输出流是一个字节输出流,所以只能输出字节或者字节数组,但是我们读到的数据则是字符串,如果想进行输出,还需要转换成字节数组,非常麻烦。我们要想通过标准输出流输出字符串,把标准输出流转换成一种字符输出流即可。可以使用转换流OutputStreamWriter。
public static void main(String[] args) throws IOException {//创建输入输出流对象BufferedReader br=new BufferedReader(new FileReader("file.txt"));Writer w=new OutputStreamWriter(System.out);//多态,父类引用指向子类对象//进行数据读写String line;while((line=br.readLine())!=null) {w.write(line);w.write("\r\n");}//释放资源w.close();br.close();}
还可以结合高效缓冲流使用:
public static void main(String[] args) throws IOException {//创建输入输出流对象BufferedReader br=new BufferedReader(new FileReader("file.txt"));BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(System.out));//进行数据读写String line;while((line=br.readLine())!=null) {bw.write(line);bw.newLine();}//释放资源bw.close();br.close();}
5.9 打印流
打印流是输出信息最方便的类,注意包含字节打印流:PrintStream和字符打印流:PrintWriter
通过定义的构造方法可以发现,有一个构造方法可以直接接收OutputStream类的实例,与OutputStream相比起来,PrintStream可以更方便的输出数据,相当于把OutputStream类重新包装了一下,使之输出更方便。
JAVA对PrintStream功能进行了扩充,增加了格式化输出功能。直接使用Print即可。但是输出的时候需要指定输出的数据类型
- 提供了一系列重载的print()和println()方法,用于多种数据类型的输出。
- PrintStream和PrintWriter的输出不会抛出IOException异常。
- PrintStream和PrintWriter有自动flush功能。
- PrintStream 打印的所有字符都使用平台的默认字符编码转换为字节。 在需要写入字符而不是写入字节的情况下,应该使用 PrintWriter 类。
- System.out返回的是PrintStream的实例。
PrintStream
public static void main(String[] args) throws IOException {//创建流,绑定输出的目的地PrintStream ps=new PrintStream("a.txt");//使用write方法写数据,保持内容不变ps.write(97);ps.println();//使用print方法,会根据平台编码,进行转码工作ps.println(97);//97->aps.println("HelloWorld");//HelloWorld//释放资源ps.close();}
PrintWriter
public static void main(String[] args) throws IOException {//创建流//PrintWriter out = new PrintWriter(new FileWriter("printFile.txt"));PrintWriter out = new PrintWriter("a.txt");//2,写数据for (int i=0; i<5; i++) {out.println("helloWorld");}//3,关闭流out.close();}
5.10 Properties类
Properties 类表示了一个持久的属性集。Properties 可保存在流中或从流中加载。属性列表中每个键及其对应值都是一个字符串。
特点:
Hashtable的子类,map集合中的方法都可以用。
该集合没有泛型。键值都是字符串。
它是一个可以持久化的属性集。
唯一一个能与IO流交互的集合
有和流技术相结合的方法。
load(InputStream) 把指定流所对应的文件中的数据,读取出来,保存到Propertie集合中
load(Reader)
store(OutputStream,commonts)把集合中的数据,保存到指定的流所对应的文件中,参数commonts代表对描述信息
store(Writer,comments);常用方法
- public Object setProperty(String key, String value)调用 Hashtable 的方法 put。
- public Set stringPropertyNames()返回此属性列表中的键集,
- public String getProperty(String key)用指定的键在此属性列表中搜索属性
//Properties的使用public class PropertiesDemo01 {public static void main(String[] args) {//创建集合对象Properties prop = new Properties();//添加元素到集合//prop.put(key, value);prop.setProperty("小丽", "大学生");prop.setProperty("小美", "研究生");prop.setProperty("小芳", "博士生");//遍历集合Set<String> keys = prop.stringPropertyNames();for (String key : keys) {//通过键 找值String value = prop.getProperty(key);System.out.println(key+"==" +value);}}}
使用Properties集合,完成把集合内容存储到IO流所对应文件中的操作
public class PropertiesDemo02 {public static void main(String[] args) throws IOException {//1,创建Properties集合Properties prop = new Properties();//2,添加元素到集合prop.setProperty("小丽", "大学生");prop.setProperty("小美", "研究生");prop.setProperty("小芳", "博士生");//3,创建流FileWriter out = new FileWriter("prop.properties");//4,把集合中的数据存储到流所对应的文件中prop.store(out, "save data");//5,关闭流out.close();}}
需求:从属性集文件prop.properties 中取出数据,保存到集合中
public class PropertiesDemo03 {public static void main(String[] args) throws IOException {//1,创建集合Properties prop = new Properties();//2,创建流对象FileReader in = new FileReader("prop.properties");//3,把流所对应文件中的数据 读取到集合中prop.load(in);//4,关闭流in.close();//5,显示集合中的数据System.out.println(prop);}}
5.11 NIO和NIO.2
NIO(New IO):
从 JDK 1.4 开始引入的一个用来替代传统IO的API。NIO与传统的IO具有相同的作用,但是使用的方式是不一样的。NIO是面向缓冲区(Buffe)的、基于通道(Channel)。
从 JDK 1.7 开始加入了一些新的元素。被称作 NIO.2
NIO 和 IO 有什么区别:
- IO是面向流(Stream)的,NIO是面向缓冲区(Buffer)的。
- IO是阻塞型的,NIO是非阻塞型的。
缓冲区Buffer:是一个用来存储基本数据类型的容器。
按照其存储的数据类型不同,缓冲区有着不同的分类:(没有boolean)
ByteBuffer、ShortBuffer、IntBuffer、LongBuffer、FloatBuffer、DoubleBuffer、CharBuffer
以上,这些缓冲区有着相同的方法来进行获取和数据的管理。因为所有以上的缓冲区都是继承自Buffer类的。
常用属性和方法:
1,获取缓冲区:不是通过new的方式,而是通过静态方法 allocate(int capacity) 来获取缓冲区。
2,属性:
- capacity: 容量。表示缓冲区最多可以存储多少数据。一旦设置后,将不能改变。
- limit: 界限。表示缓冲区可以操作的数据数量。(实际上存储了多少数据)。
- position: 位置。缓冲区中正在进行操作的数据的位置。
- mark: 使用mark()方法标记的位置。
- mark <= position <= limit <= capacity
3,常用方法:
put():将数据放入缓冲区
get():从缓冲区中获取数据
flip():切换成读模式
rewind():重新读取(position重置为0)
clear():清空缓冲区。将缓冲区中limit和position重置为allocate之后的状态。
clear方法只是重置了一下标记,缓冲区中的数据还在。
mark():在指定的position做一个标记
public class FileDemo{public static void main(String[] args) throws IOException {// 设置输入源 & 输出目的 = 文件String infile = "a.txt";String outfile = "b.txt";// 1. 获取数据源 和 目标传输地的输入输出流(此处以数据源 = 文件为例)FileInputStream fin = new FileInputStream(infile);FileOutputStream fout = new FileOutputStream(outfile);// 2. 获取数据源的输入输出通道FileChannel fcin = fin.getChannel();FileChannel fcout = fout.getChannel();// 3. 创建缓冲区对象ByteBuffer buff = ByteBuffer.allocate(1024);while (true) {// 4. 从通道读取数据 & 写入到缓冲区// 注:若 以读取到该通道数据的末尾,则返回-1int r = fcin.read(buff);if (r == -1) {break;}// 5. 传出数据准备:调用flip()方法buff.flip();// 6. 从 Buffer 中读取数据 & 传出数据到通道fcout.write(buff);// 7. 重置缓冲区buff.clear();}}}
