赞
踩
IO
:Input/Output即输入&输出,也称之为流(河流,水流),指的是数据从一个地点到另一个地点过程;对于计算机来说文件的拷贝过程,文件的编写保存,显示功能都是使用到IO;将数据的传输过程看做一个输入输出。现实角度考虑可以将耳朵作为输入流,将嘴巴当做输出流
Java针对IO操作提供了相应的API,Java中几乎所有的IO操作都需要使用java.io
包;java中对于流的分类包含各种方式:
流虽然内容繁多,但是规律性很强,几乎所有的流都从四个基本流继承而来:
输入流 | 输出流 | |
---|---|---|
字节流 | java.io.InputStream | java.io.OutputStream |
字符流 | java.io.Reader | java.io.Writer |
以上四个流是所有java流的顶层父类,都是抽象类;
流的类型识别规律性很强:一般以Stream
结尾的都是字节流;一般以Reader/Writer
结尾的流都是字符流
在计算机系统中,一切都是字节:系统中存储的各种文件(文本文档,图片,视频,音频)的存储在计算机低层都是以字节的形式存储的,因此对于任何的文件操作都是可以使用一个一个字节进行操作的(读,写);java.io中的字节流顶层父类是:InputStream/OutputStream
.
java中字节输入流都是从java.io.InputStream
继承而来。由于该类为抽象类,因此无法实例化的,所以jdk对于字节输入提供了一些能够直接使用子类:
FileInputStream
ByteArrayInputStream
BufferedInputStream
ObjectInputStream
InputStream常用方法
int available()
:获取流中可读字节数int read()
:从流中读取一个字节,返回当前读取的字节数据int read(byte[] b)
:将读取的字节数据存储到字节缓冲区,并返回实际的读取字节总数skip(int b)
:跳过指定个字节发生下一次读取FileInputStream
是一个针对字节输入流的实现流,主要用于对文件进行读取操作,内部的方法主要是针对父类的实现,
常见构造器
FileInputStream(File file)
:根据提供的文件构建的对象FileInputStream(String filePath)
:根据提供的文件路径构建对象使用FileInputStream
进行文件读取操作
基本读取(每次读取一个字节)
//创建File对象
File file = new File("C:\\Users\\Administrator\\Desktop\\新建文本文档.txt");
//基于File创建字节输入流
InputStream in = new FileInputStream(file);
//读取一个字节
int i = in.read();
System.out.println((char)i);
Java的IO只能对标准文件发生读写操作,不允许直接对一个目录创建输入或者输出流(会导致
java.io.IOException(拒绝访问)
)
使用字节缓冲区读取(缓冲区大小为总可读字节数)
由于以上的读取方式是每次一个字节读取,因此,读取效率很低,所以实际开发中一般会使用一个字节缓冲区,提高读取效率:
//创建File对象
File file = new File("C:\\Users\\Administrator\\Desktop\\新建文本文档.txt");
//基于File创建字节输入流
InputStream in = new FileInputStream(file);
//创建字节缓冲区(大小为总可读字节数)
byte[] arr = new byte[i];
//将流中读取的字节内容存储到数组中
int total = in.read(arr);
//将字节数组转换为String字符串
System.out.println(new String(arr));
使用合适大小字节缓冲区读取
以上的读取是一次性将文件中的数据读取到缓冲区中,因此,缓冲区中容量可能会很大,如果针对一个大文件文件的读取,使用一个过大的缓冲区,可能会造成空间的消耗,从而影响其他程序的执行,所以,需要一个合适大小的缓冲区进行反复读取
//创建文件输入流对象(文件:一缸水)
InputStream in = new FileInputStream("C:\\Users\\Administrator\\Desktop\\Hero.java");
//创建缓冲区(购买一个大小适中的水桶)
byte[] b = new byte[1024];
//声明临时变量表示每次读取的实际字节数
int len = 0;
while((len = in.read(b)) != -1){
String s = new String(b,0,len);
System.out.println(s);
}
以上的读取方式就是对于与字节流的读取常规解决方案
根据数据的流向除了可以进行读取(输入)操作之外,数据的写(输出)操作也是十分常见,java中的字节输出流都是从java.io.OutputStream
继承过来
由于OutputStream
是一个抽象类,无法实例化,因此jdk也提供了针对该流的子类:
FileOutputStream
ByteArrayOutputStream
BufferedOutputStream
ObjectOutputStream
PrintStream
OutputStream类的常见方法:
write(int b)
:将一个字节通过输出流写出到目标输出源write(byte[] b)
:将一个字节数组通过输出流写出到目标输出源write(byte[] b,int offset,int len)
将一个数组的offset开始写出len个字节到目标输出源FileOutputStream
是一个针对字节输出流的实现流,主要用于对文件进行写入操作,内部的方法主要是针对父类的实现
常用构造器
FileOutputStream(String filePath)
:基于一个标准文件的路径创建对其操作的输出流FileOutputStream(String filePath,boolean append)
:基于一个标准文件的路径创建对其操作的输出流,使用追加模式FileOutputStream(File file)
:基于一个标准文件的路径创建对其操作的输出流FileOutputStream(File file,boolean append)
基于一个标准文件对象创建对其操作的输出流,使用追加模式使用FileOutputStream
进行文件写出操作
//try...with语句:JDK1.7
//针对所有的第三方资源不再需要手动回收(关闭)
//因为从jdk1.7开始,很多资源类都实现过Closeable接口,但凡实现过该接口类
//只要将其在try()中创建,不再手动关闭资源,会由JVM自动回收
try(OutputStream os = new FileOutputStream("readme.txt",true);){
//写出一个字节到文件中
// os.write(101);
//荀子·劝学篇
String msg = "不积小流无以成江海,不积跬步无以至千里";
os.write(msg.getBytes());
}catch (IOException e){
e.printStackTrace();
}
/** * 将一个源文件拷贝到一个目标目录中 * @param src 源文件 * @param targetDir 目标目录 */ public static void copyFile(File src, File targetDir) throws IOException { InputStream is = null; OutputStream os = null; try { //获取源文件的输入流 is = new FileInputStream(src); //获取目标文件的输出流(目标文件是由:目录+源文件名称构成):文件输出流可以创建文件(前提:父目录必须存在) os = new FileOutputStream(new File(targetDir,src.getName())); //声明字节缓冲区(缓冲区越大,拷贝效率越高,但是带来空间损耗也越大) byte[] b = new byte[1024*1024]; //临时变量表示实际读取的字节数 int len = 0; System.out.println("开始拷贝..."); while((len = is.read(b)) != -1){ //写出读取的内容到输出流 os.write(b,0,len); } System.out.println("拷贝完成!"); } finally{ if(os != null){ os.close(); } if(is != null){ is.close(); } } } //测试 public static void main(String[] args) throws IOException { //源文件 File src = new File("D:\\素材\\视频\\短视频\\test.mp4"); //目标目录 File targetDir = new File("C:\\Users\\Administrator\\Desktop"); //文件拷贝 copyFile(src,targetDir); }
在文件拷贝的基础上实现目录拷贝:
public class FileCopy { /** * 将一个源目录拷贝到另一个目标目录中 * @param srcDir * @param targetDir */ public static void copyDir(File srcDir,File targetDir) throws IOException { //获取新目录对象 targetDir = new File(targetDir,srcDir.getName()); //如果新目录不存在,则创建 if(!targetDir.exists()){ targetDir.mkdirs(); } File[] files = srcDir.listFiles(); if(Objects.nonNull(files)){ for (File file : files) { if(file.isDirectory()){ //目录递归拷贝 copyDir(file,targetDir); }else{ //执行文件拷贝 copyFile(file,targetDir); } } } } /** * 将一个源文件拷贝到一个目标目录中 * @param src 源文件 * @param targetDir 目标目录 */ public static void copyFile(File src, File targetDir) throws IOException { InputStream is = null; OutputStream os = null; try { //获取源文件的输入流 is = new FileInputStream(src); //获取目标文件的输出流(目标文件是由:目录+源文件名称构成) os = new FileOutputStream(new File(targetDir,src.getName())); //声明字节缓冲区(缓冲区越大,拷贝效率越高,但是带来空间损耗也越大) byte[] b = new byte[1024*1024]; //临时变量表示实际读取的字节数 int len = 0; System.out.println("开始拷贝..."); while((len = is.read(b)) != -1){ //写出读取的内容到输出流 os.write(b,0,len); } System.out.println("拷贝完成!"); } finally{ if(os != null){ os.close(); } if(is != null){ is.close(); } } } public static void main(String[] args) throws IOException { //源目录 File srcDir = new File("D:\\素材\\视频"); //目标目录 File targetDir = new File("C:\\Users\\Administrator\\Desktop\\video"); //目录拷贝 copyDir(srcDir,targetDir); } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
通常在文本文件
中,文件内容的存在形式一般是以一个个字符(一个中文汉字,一个英文字母,一个符号)的形式存在,在GBK编码模式下通常1个字符=2个字节
;字符流一般适用于对文本数据的处理,java中所有的字符流几乎都是以Reader/Writer
结尾,并且都是从两个基本的抽象类中继承:
java.io.Reader
:字符输入流java.io.Writer
:字符输出流字符输入流一般用于对文本数据进行读取操作,常见的子类:
InputStreamReader
FileReader
BufferedReader
CharArrayReader
FileReader是一个以字符流的形式进行文件内容读取的流,从InputStreamReader
继承而来,内部没有多余的方法(与父类的API一致),提供一下常见的构造方法:
常用构造方法
FileReader(File file)
:根据提供的文件对象获取文件字符输入流FileReader(String path)
:根据提供的文件路径获取文件字符输入流具体使用
//创建文件字符输入流 FileReader fr = new FileReader("D:\\文档资料\\电子书\\书籍推荐.txt"); //获取当前流使用的默认编码模式(并非目标文件的编码) // String encoding = fr.getEncoding(); // System.out.println(encoding); //读取一个字符(以int类型存在) // int read = fr.read(); // System.out.println((char)read); // int c = 0; // while((c = fr.read()) != -1) // { // System.out.print((char)c); // } //使用字符缓冲区 char[] ch = new char[100]; int len = 0; while((len = fr.read(ch)) != -1){ String s = new String(ch,0,len); System.out.print(s); }
字符输出流一般用于对文本数据进行写出操作,常见的子类:
OutputStreamWriter
BufferedWriter
CharArrayWriter
FileWriter
PrintWriter
FileWriter是一个以字符流的形式进行文件内容写出的流,从OutputStreamWriter
继承而来,内部没有多余的方法(与父类的API一致),提供一下常见的构造方法:
常用构造方法
FileWriter(File file)
:根据提供的文件对象获取文件字符输入流FileWriter(String path)
:根据提供的文件路径获取文件字符输入流FileWriter(File file,boolean append)
:根据提供的文件对象获取文件字符输入流(使用追加模式)FileWriter(String path,boolean append)
:根据提供的文件路径获取文件字符输入流(使用追加模式)具体使用
//基于指定的文件创建字符输出流
FileWriter fw = new FileWriter("readme.txt");
fw.write("路漫漫其修远兮,吾将上下而求索");
//允许在流未关闭之前,强制将字符缓冲区中的数据写出到目标输出源
fw.flush();
Thread.sleep(10000);
fw.write("好好xio习,天天up!!!");
fw.close();
对于字符输出流,内部使用到了一个
字符缓冲区(字符数组)
,在进行数据写出时,通常是将需要写出的数据缓存存在了字符数组中,然后在关闭流时(或者缓冲区存满时),一次性将缓冲区的数据写出到目标输出源, 如果需要在流未关闭前(或者缓冲区未满时)强制的将字符缓冲区中的数据写出,可以手动调用flush()
强制输出。
对于流的处理类型(功能)来分又分为节点流和处理流:
节点流
也称之为低级流,直接跟输入输出源沟通的流(例如:FileReader,FileWriter,FileInputStream,FileOutputStream)
处理流
处理流也称之为高级流或包装流,即可以用于对其他节点流进行包装,以实现流的类型转换
或者效率的提升。处理流主要由缓冲流
和转换流
构成
包装通常使用到了一种设计模式(装饰器模式)
缓冲流是一种自带缓冲区的流,主要由以下四种构成
BufferedInputStream
:字节缓冲输入流BufferedOutputStream
:字节缓冲输出流BufferedReader
:字符缓冲输入流BufferedWriter
:字符缓冲输出流long start = System.currentTimeMillis(); try( //获取输入流 InputStream in = new FileInputStream("D:\\集团资料\\宣讲\\video\\云计算&大数据\\阿里云.mp4"); //包装节点流 BufferedInputStream bis = new BufferedInputStream(in); //获取输出流 OutputStream os = new FileOutputStream("C:\\Users\\Administrator\\Desktop\\阿里云.mp4"); //包装节点流 BufferedOutputStream bos = new BufferedOutputStream(os); ) { System.out.println("开始拷贝"); byte[] b = new byte[1024*1024*8]; int len = 0; while((len = bis.read(b)) != -1){ bos.write(b,0,len); } System.out.println("拷贝结束"); }catch (IOException e){ e.printStackTrace(); } long end = System.currentTimeMillis(); System.out.println("耗时:"+(end-start));
缓冲流内部实现原理使用了了一个默认大小为
8kb
的字节缓冲区,每次将缓冲区的空间存满之后再将数据通过流对象完成读写操作
在一些需求中经常会需要将一个字节流的数据转换为字符流;又或者需要将一个字符流转换为字节流,此时就需要使用转换流来完成功能,转换流一般应用于:文件转码,从网络中读取的数据为字节流时,但是该流包含内容是字符时,可以使用转换流实现转换;java.io中转换流主要由以下两个类构成:
InputStreamReader
:用于将字节输入流转换为字符输入流(字节->字符)OutputStreamWriter
:用于将字符输出流转换为字节输出流(字符->字节)使用实例:
//获取标准输入流
InputStream is = System.in;
//将字节流转换为字符流
Reader isr = new InputStreamReader(is);
//将字符节点流包装为缓冲流
BufferedReader br = new BufferedReader(isr);
//读取一行
String line = br.readLine();
System.out.println("输入的内容--->"+line);
转换流只能对流的类型转换,不能对流向转换
日常的文件拷贝中由于多个编辑器(系统)的编码模式存在差异,因此极有可能出现文件乱码问题(对于文本文件较为常见),通过转换流可以实现文件转码的功能:
public class FileCharacterConvert { /** * 将一个目标文件的编码转换为新的编码 * @param file 原始文件 * @param targetDir 目标目录 * @param oldEncoding 原始编码 * @param newEncoding 新编码 */ public static void convert(File file,File targetDir, String oldEncoding, String newEncoding) throws IOException { //使用特定的编码获取文件的输入流 FileInputStream fis = new FileInputStream(file); Reader reader = new InputStreamReader(fis,oldEncoding); BufferedReader br = new BufferedReader(reader); //使用特定的编码获取文件的输出流 FileOutputStream fow = new FileOutputStream(new File(targetDir,file.getName())); Writer writer = new OutputStreamWriter(fow,newEncoding); BufferedWriter bw = new BufferedWriter(writer); //开始读写 String line = ""; //循环读取每一行文本以换行符作为一行的结束标记(但是换行符不会被作为内容读取) while((line = br.readLine()) != null){ //写入读取的一行文本() bw.write(line); //手动加入一个换行标记到文件,否则所有内容会在同一行显示 bw.newLine(); //将缓冲区的数据强制输出到目标输出源 bw.flush(); } bw.close(); br.close(); } public static void main(String[] args) throws IOException { //准备需要转换的文件 File f = new File("C:\\Users\\Administrator\\Desktop\\GamePanel.java"); convert(f,new File("C:\\Users\\Administrator\\Desktop\\temp"),"gbk","utf-8"); } }
java.io,对于数据的输出提供了两个特殊的流:打印流,打印流只有输出没有输入,并且可以直接针对输出源创建,也可以将其他输出流包装,打印流主要包含以下两个流:
PrintStream
:字节打印流PrintWriter
:字符打印流PrintStream
是继承自java.io.OutputStream
,是一个用于进行字节数据输出的流,内包含了大量的print/prinln
重载方法;并且System.out
实际上就是一个PrintStream
常见构造器
PrintStream(File file)
:基于指定的文件创建打印流PrintStream(OutputStream os)
:将其他字节输出流包装PrintStream(OutputStream os,boolean autoFlush)
:将其他字节输出流包装,可以自动刷新流中数据常用方法
print(...)
println(...)
基本使用
// PrintStream ps = new PrintStream(new File("readme.txt"),true);
OutputStream os = new FileOutputStream("readme.txt", true);
PrintStream ps = new PrintStream(os,true);
ps.println("你好中国");
PrintWriter
和PrintStream
区别在于,PrintWriter
是基于字符的打印流(包含字符缓冲区),其API与PrintStream
极其相似;在使用PrintWriter
打印数据的时候记得执行flush()
方法
FileWriter fw = new FileWriter("readme.txt",true);
PrintWriter pw = new PrintWriter(fw);
pw.println("哪里有彩虹告诉过我!!!");
pw.flush();
pw.close();
在后期框架的学习过程中,经常会涉及到一些配置文件的编写,其中属性文件是很常见的一种文件类型,java中对于属性文件的读写提供了一个java.util.Properties
类:
InputStream in = PropertiesDemo.class.getResourceAsStream("/jdbc.properties"); //创建属性对象 Properties prop = new Properties(); // Properties prop = System.getProperties(); //加载流到属性对象中 prop.load(in); String user = prop.getProperty("user"); String url = prop.getProperty("url"); System.out.println(user); System.out.println(url); prop.setProperty("maxActive","10"); File f = new File("D:\\带班资料\\2021\\J2106\\课程资料\\code\\part1-javase\\java高级\\lesson_01_IO\\resources\\jdbc.properties"); FileWriter fw = new FileWriter(f); prop.store(fw,"this is jdbc config file"); fw.close();
input
、read
output
、write
stream
结尾reader
或writer
结尾Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。