Java IO流1(概念及节点流)
- 文件
- IO流原理及流的分类
- InputStream的子类及使用
- OutputStream的子类及使用
- 利用FileInputStream及FileOutputStream进行文件copy
- Reader与Writer的子类及使用
- 节点流和处理流
文件
-
文件是保存数据的地方,例如word文档,txt文档等都是文件。
-
文件流
文件在程序中是以流的形式来操作的
- 流:数据在数据源(文件)和程序(内存)之间经历的路径
- 输入流:数据从数据源(文件)到程序(内存)的路径
- 输出流:数据从程序(内存)到数据源(文件)的路径
-
常用的文件操作
-
使用new File(String pathname)创建
package com.java_io; import org.junit.Test; import java.io.File; import java.io.IOException; public class FileCreate1 { public static void main(String[] args) { } //方法1:使用new File(filePath)创建文件 @Test public void createFile01() throws IOException { //创建的文件路径名称 String filePath = "e:\\news1.txt"; File file = new File(filePath); try { file.createNewFile(); System.out.println("文件创建成功"); } catch (IOException e) { e.printStackTrace(); } } }
-
使用new File(File parent,String child)创建
@Test public void createFile02(){ File parentFile = new File("e:\\"); String fileName = "news2.txt"; //此时的file对象在Java程序(内存)中只是一个对象而已 //只有执行了createNewFile方法才会真正在磁盘创建文件 File file = new File(parentFile,fileName); try { //此时在磁盘中创建了文件 file.createNewFile(); System.out.println("文件创建成功"); } catch (IOException e) { e.printStackTrace(); } }
-
使用new File(String parent,String child)创建
@Test public void createFile03(){ String parentPath = "e:\\"; //String parentPath = "e:/"; 也可以 String fileName = "new3.txt"; File file = new File(parentPath,fileName); try { file.createNewFile(); System.out.println("文件创建成功"); } catch (IOException e) { e.printStackTrace(); } }
-
获取文件信息
- getName():获取文件名字
- getAbsolutePath():获取文件绝对路径
- getParent():获取父级目录
- length():获取文件大小(字节)
- exists():查看文件是否存在
- isFile():检测是不是一个文件
- isDirectory():检测是不是一个目录
代码演示
package com.java_io; import org.junit.Test; import java.io.File; public class fileInformation { public static void main(String[] args) { } @Test public void fileInformation(){ //先创建文件对象 File file = new File("e:\\news1.txt"); //调用对应的方法,得到相应的信息 System.out.println("文件名字="+file.getName()); System.out.println("文件绝对路径="+file.getAbsolutePath()); System.out.println("文件父级目录="+file.getParent()); System.out.println("文件大小(字节)="+file.length()); System.out.println("文件是否存在="+file.exists()); System.out.println("是不是一个文件="+file.isFile()); System.out.println("是不是一个目录="+file.isDirectory()); } }
-
目录的操作
- delete():删除目录。
- mkdir():创建一级目录
- mkdirs():创建多级目录
代码示例
//检查目录下是否存在news2.txt文件,若存在则删除 @Test public void m1(){ File file = new File("e:\\news2.txt"); if (file.exists()) { if (file.delete()) { System.out.println("删除成功"); }else { System.out.println("删除失败"); } }else { System.out.println("找不到改文件..."); } } }
创建目录
//检查e:\\demo\\a\\b\\c是否存在,若不存在则创建 @Test public void m2(){ String directoryPath = "e:\\demo\\a\\b\\c"; File file = new File(directoryPath); if (file.exists()) { System.out.println("改目录存在..."); }else { if (file.mkdirs()) { System.out.println(directoryPath+"目录创建成功"); }else { System.out.println(directoryPath+"目录创建失败"); } } }
IO流原理及流的分类
-
输入input:读取外部数据(磁盘、光盘等存储设备的数据)到程序(内存)中
-
输出output:将程序(内存)数据输出到磁盘、光盘等存储设备中
-
流的分类
- 按操作数据单位不同分为:
- 字节流(8bit):二进制文件使用
- 字符流(按字符):文本文件使用
- 按数据流的流向不同分为:输入流,输出流
- 按流的角色的不同分为:字节流,处理流/包装流
而Java IO流有四个抽象基类
字节流 字符流 输出流 OutputStream Writer 输入流 InputStream Reader 其他的流都是继承于这四大基类的。下图是Java IO流的整体架构图
所理解的流就像快递小哥,在物流中心和客户之间做传递作用
- 按操作数据单位不同分为:
InputStream的子类及使用
-
InputStream的常用子类
- FileInputStream:文件输入流
- BufferedInputStream:缓冲字节输入流
- ObjectInoutStream:对象字节输入流
-
使用FileInputStream读取文件
read()读取单个字节
package com.java_io; import org.junit.Test; import java.io.FileInputStream; import java.io.IOException; public class FileInputStreamTest { public static void main(String[] args) { } @Test public void InputStream(){ String filePath = "e:\\hello.txt"; FileInputStream fileInputStream = null; int readFile = 0; try { //创建FileInputStream对象,用于读取文件 fileInputStream = new FileInputStream(filePath); //read():从输入流中读取一个字节的数据,如果没有输入,则被阻止 //如果返回-1.则表示读取完毕 while ((readFile = fileInputStream.read())!=-1){ System.out.print((char) readFile); } } catch (Exception e) { e.printStackTrace(); }finally { //关闭文件流,释放资源 try { fileInputStream.close(); } catch (IOException e) { e.printStackTrace(); } } } }
read(byte[b])缓冲区读取b个字节
@Test public void InputStream02(){ String filePath = "e:\\hello.txt"; byte [] buf = new byte[8];//每次读取8个字节的数据 FileInputStream fileInputStream = null; int readLen = 0;//记录读取的字节数 try { fileInputStream = new FileInputStream(filePath); /* * 创建FileInputStream对象,用于读取文件 * 从输入流读取最多buf.length字节数据到字节数组,若没有输入,则阻塞此方法 * 如果返回值为-1,表示读取完毕 * 如果读取正常则返回读取的字节数*/ while((readLen = fileInputStream.read(buf))!=-1){ //new Sting(buf,0,len):把一个字节数组从0取到len构造成一个新的String System.out.print(new String(buf,0,readLen)); } } catch (IOException e) { e.printStackTrace(); } finally { try { //关闭流,释放资源 fileInputStream.close(); } catch (IOException e) { e.printStackTrace(); } } }
需要注意的是使用FileInputStream读取文件时,读取到的字符是乱码的
OutputStream的子类及使用
-
FileOutputStream
演示使用FileOutputStream。将数据写到文件中,如果该文件不存在,则创建该文件。
package com.java_io; import org.junit.Test; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; public class FileOutputStreamTest { public static void main(String[] args) { } @Test public void fileOutputStream(){ String filePath = "e:\\hello.txt"; FileOutputStream fileOutputStream = null; try { //创建FileOutputStream对象,对文件操作 fileOutputStream = new FileOutputStream(filePath,true); //向文件写入一个字节的数据 fileOutputStream.write('H'); //向文件写入字符串 //str.getBytes()将字符串转换为字节数组 String str = "Hello,Word!"; fileOutputStream.write(str.getBytes()); System.out.println("写入成功"); } catch (IOException e) { e.printStackTrace(); } finally { try { fileOutputStream.close(); } catch (IOException e) { e.printStackTrace(); } } } }
-
若需要写入指定的字符,使用
//write(byte[] b,int off,int len); fileOutputStream.write(str.getBytes(),0,str.length());
可以将len字节从偏移量off的指定字节数组写入此文件输入流
-
需要注意的是,如果使用
fileOutputStream = new FileOutputStream(File file,boolean append);
时没有将append设置为true的话,系统会默认append的值为false,则会清空记事本再写入新的内容(覆盖之前的内容)。
若需要换行或者添加制表符输入,使用其转义字符即刻
fileOutputStream.write("\n".getBytes());//换行 fileOutputStream.write("\t".getBytes());//增加制表符
-
利用FileInputStream及FileOutputStream进行文件Copy
-
思路分析
- 创建文件的输入流,将文件读到程序中
- 创建文件的输出流,将读取到的文件输入写到指定的文件中
-
代码演示
package com.java_io; import org.junit.Test; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; public class FileCopy { public static void main(String[] args) { } /* * 将"e:\\567.jpg"复制到"d:\\567.jpg"*/ @Test public void fileCopy(){ String srcPath = "e:\\567.jpg"; String destPath = "d:\\567.jpg"; //创建输入流及输出流对象 FileInputStream fileInputStream = null; FileOutputStream fileOutputStream = null; try { //定义字节数组接收,提高效率 byte [] buf = new byte[1024]; int readlen = 0; fileInputStream = new FileInputStream(srcPath); fileOutputStream = new FileOutputStream(destPath); while ((readlen = fileInputStream.read(buf))!=-1){ fileOutputStream.write(buf,0,readlen); } System.out.println("写入成功!"); } catch (IOException e) { e.printStackTrace(); } finally { try { //关闭输入输出流,释放资源 if(fileInputStream==null){ fileInputStream.close(); } if(fileOutputStream==null){ fileOutputStream.close(); } } catch (IOException e) { e.printStackTrace(); } } } }
需要注意的是,写入文件时,必须使用
write(byte[],0,readLength)
写入。readLength为读入数据的长度,这样写可以避免byte数组过大时把其余子集读入造成文件损失
Reader与Writer的子类及使用
-
FileReader的相关方法:
- new FileReader(File/String):创建对象
- read():每次读取单个字符并返回,如果到文件末尾,返回-1
- read(char[]):批量读取多个字符到数组,返回读取到的字符数,如果到文件末尾则返回-1。
相关API
- new String(char[]):将char[] 转换成String
- new String(char[],off,len):将char[]的指定部分转换成String
代码演示
package com.java_io; import org.junit.Test; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; public class FileReaderTest { public static void main(String[] args) { } @Test public void FileReaderTest(){ String filePath = "e:\\story.txt"; FileReader fileReader = null; char [] chars = new char[1024]; try { //新建对象 fileReader = new FileReader(filePath); try { while ((fileReader.read(chars))!=-1){ System.out.println(chars); } } catch (IOException e) { e.printStackTrace(); } } catch (FileNotFoundException e) { e.printStackTrace(); } finally { try { if(fileReader==null){ //关闭流,释放资源 fileReader.close(); } } catch (IOException e) { e.printStackTrace(); } } } }
-
FileWriter的相关方法
- new FileWriter(File/String):覆盖模式,相当于流的指针在首端
- new FileWriter(File/String,true):追加模式,相当于流的指针在尾端
- write(int):写入单个字符
- write(char[]):写入指定数组
- write(char[],off,len):写入指定数组的指定部分
- write(String):写入整个字符串
- write(String,off,len):写入字符串的指定部分
相关API
- toCharArray:将String转换成char[];
注意:
-
FileWriter使用后,必须关闭(close)或刷新(flush),否则写入不到指定的文件
-
FileWriter的类图如下
FileWriter的使用
package com.java_io; import org.junit.Test; import java.io.FileWriter; import java.io.IOException; import java.io.Writer; public class FileWriterTest { public static void main(String[] args) { } @Test public void FileWriter(){ String filePath = "e:\\note.txt"; FileWriter fileWriter = null; String str = "AD"; char [] chars ={'a','b','c','d'}; int writerLen = 3; try { fileWriter = new FileWriter(filePath,true); fileWriter.write('H'); fileWriter.write("\n");//换行 fileWriter.write(str); fileWriter.write("\n"); fileWriter.write(chars); fileWriter.write("\n"); fileWriter.write(chars,0,writerLen); fileWriter.write("\n"); fileWriter.write(str,0,1); } catch (IOException e) { e.printStackTrace(); } finally { try { fileWriter.close(); } catch (IOException e) { e.printStackTrace(); } } } }
-
如果需要换行或者输入制表符则可以
fileWriter.write("\n");//换行 fileWriter.write("\t");//制表符
节点流和处理流
-
节点流可以从一个特定的数据源读写数据
-
处理流(包装流)是“连接”在已存在的流(节点流或处理流)之上,为程序提供更为强大的读写功能,如BufferedReader、BufferedWriter
-
分析BufferedReader处理流
查看BufferedReader的源码可以知道,BufferedReader中定义了一个Reader属性,即他可以封装任意一个节点流,所以使用BufferedReader去操作的时候,其可以访问各种不同的数据源。其功能更为强大
BufferedWriter同理。
-
节点流与处理流的区别和联系
- 节点流是底层流/低级流,直接跟数据源相接。
- 处理流(包装流)包装节点流,既可以消除不同节点流的实现差异,也可以提供更方便的方法来完成输入输出
- 处理流(包装流)对节点流进行包装,使用了修饰器设计模式,不会直接与数据源相连
-
模拟装饰者设计模式来理解处理流BufferedReader/BufferedWriter
创建Reader抽象类(Reader_)
package com.decorator_pattern; public abstract class Reader_ { public void readString(){ } public void readFile(){ } }
创建节点流子类FileReader_
package com.decorator_pattern; public class FileReader_ extends Reader_{ public void readFile(){ System.out.println("readFile"); } }
创建节点流子类StringReader_
package com.decorator_pattern; public class StringReader_ extends Reader_{ public void readString(){ System.out.println("readString"); } }
创建处理流BufferedReader_
package com.decorator_pattern; public class BufferedReader_ extends Reader_{ private Reader_ reader_;//属性时Reader_类型 public BufferedReader_(Reader_ reader_) { this.reader_ = reader_; } //封装方法 public void readFile(){ reader_.readFile(); } //装饰者模式的特性:增加新功能 public void readStrings(int num){ for (int i = 0; i < num; i++) { reader_.readString(); } } }
Test类
package com.decorator_pattern; public class Test { public static void main(String[] args) { //新建bufferedReader_对象,把FileReader_传进去,使方法可以对文件操作 BufferedReader_ bufferedReader_1 = new BufferedReader_(new FileReader_()); bufferedReader_1.readFile(); //必须是相应的对象的方法才会有用 bufferedReader_1.readStrings(10);//无效 //新建bufferedReader_对象,把StringReader_传进去,使方法可以对字符串操作 BufferedReader_ bufferedReader_2 = new BufferedReader_(new StringReader_()); bufferedReader_2.readStrings(10); } }
测试结果
通过上述设计模式可以让BufferedReader包装其他节点流,这样子就能提高对数据源操作的效率
可以使用统一的一个read方法来优化上述代码。如:
在Reader_抽象类中使用抽象方法read
package com.decorator_pattern; public abstract class Reader_ { public abstract void read(); }
然后子类实现此方法即可
package com.decorator_pattern; public class FileReader_ extends Reader_{ public void read(){ System.out.println("readFile"); } }
此时,想要操作相应的数据则需要利用对象的动态绑定机制,绑定到对应的实现子类即可 。
所以Test中的
//多态性,因为bufferedReader_1是new了FileReader_对象,所以去找FileReader_对象 bufferedReader_1.readStrings(10);
能输出readFile。
-
处理流的功能
- 性能的提高:主要以增加缓冲的方式来提高输入输出的效率
- 操作的便捷:处理流可能提供了一系列便捷的方法来一次输入输出大批量的数据,使用更加灵活方便