一、初识I/O
1.1 理解输入(Input)与输出(Output)
1.I/O特点
- I/O,所有程序都必须有;
- 输入机制:一个Java程序,要接受来自键盘命令的输入,或者读取外部载体的数据(包括磁盘、光盘)
- 输出机制:通过运算,在显示器上输出"Hello world",或者将结果保存在本地磁盘、光盘等存储设备中
2.java.io包
java的 IO是通过java.io包下的类和接口来支持的。其中,java.io包下的类的分类:
- 按照出入方向:输入(Input)与输出(Output)
- 按照内容类型:字节流、字符流
1.2 File类及其常用方法
1.File类特点
File类:java.io包下的一个类,代表与平台无关的文件和目录
用途:程序中操作文件和目录,都可以通过File类来完成。可以新建、删除、重命名文件和目录
缺点:File类只可以操作文件本身,但不能修改文件的内容。
要想访问修改文件内的内容,就需要输入/输出流。
2.createNewFile()
方法来创建文件
- 成功创建文件
public class FileSample {
//演示最常见的File类的方法
public static void main(String[] args) {
File f = new File("d:/b.txt"); //表示多级路径,大多数平台都是用反斜杠/
//File f = new File("d:\\b.txt"); //windows平台也可用这个,但麻烦
try {
boolean r1 = f.createNewFile(); //创建文件 //如果上述地址不存在,就会出现异常
System.out.println(f.getPath()+"文件创建是否成功:"+r1); //获取文件的路径,以检查文件是否创建成功
} catch (IOException e) {
e.printStackTrace();
}
}
}
--->
d:\b.txt文件创建是否成功:true
- 因为windows平台上,c盘是系统盘,需要管理员授权的。所以,禁止程序直接写入C盘根路径。
public class FileSample {
//演示最常见的File类的方法
public static void main(String[] args) {
File f = new File("c:/b.txt");
try {
boolean r1 = f.createNewFile();
System.out.println(f.getPath()+"文件创建是否成功:"+r1);
} catch (IOException e) {
e.printStackTrace();
}
}
}
--->
java.io.IOException: 拒绝访问。
at java.io.WinNTFileSystem.createFileExclusively(Native Method)
at java.io.File.createNewFile(File.java:1023)
at com.imooc.io.FileSample.main(FileSample.java:13)
- 如果再运行一次
File f = new File("d:/b.txt");
来创建同名文件,也会报错。因为已存在。 - 如果指定一个不存在的目录,也会抛出异常。
3.获得文件的基本参数的方法:
public class FileSample {
public static void main(String[] args) {
File f = new File("d:/b.txt");
try {
boolean r1 = f.createNewFile();
System.out.println(f.getPath()+"文件创建是否成功:"+r1);
System.out.println(f.getPath() + "是否存在:" + f.exists());
System.out.println(f.getPath()+"是否是目录:"+f.isDirectory());
System.out.println(f.getPath() + "是否是文件:" + f.isFile()); //不是文件,就是目录,二选一
System.out.println(f.getPath()+"的大小:"+f.length()); //字节数量
System.out.println(f.getPath()+"的文件名:"+f.getName());
boolean r2 = f.delete();
System.out.println(f.getPath()+"文件是否删除成功:"+r2);
System.out.println(f.getPath() + "是否存在:" + f.exists());
} catch (IOException e) {
e.printStackTrace();
}
}
}
--->
d:\b.txt文件创建是否成功:true
d:\b.txt是否存在:true
d:\b.txt是否是目录:false
d:\b.txt是否是文件:true
d:\b.txt的大小:0
d:\b.txt的文件名:b.txt
d:\b.txt文件是否删除成功:true
d:\b.txt是否存在:false
4.上述是对文件的操作,下面是对目录的创建:mkdirs()方法:
public class FileSample {
public static void main(String[] args) {
File d = new File("d:/电影/华语/大陆");
try {
boolean r3 = d.mkdirs();
System.out.println("[" + d.getPath() + "]目录是否创建成功:" + r3);
} catch (IOException e) {
e.printStackTrace();
}
}
}
--->
[d:\电影\华语\大陆]目录是否创建成功:true
如果再运行一次上述代码,就会出现:因为该目录已经存在的。
[d:\电影\华语\大陆]目录是否创建成功:false
二、通过字节、字符流实现文件的读取与写入
上面完成了File类操作文件本身。接下来,介绍读取与写入文件的内容。
2.1 流与四种抽象类
1.流
把要处理的数据,统一称为流。
读取原文件,写入目标新文件
2.四种抽象类
分类 | 字节流 | 字符流 |
---|---|---|
输入方向 | InputStream | Reader 读 |
输出方向 | OutputStream | Writer 写 |
对象 | 二进制数据,例如图片 | 文本文件 |
2.2 通过字节输入流读取文件
1.InputStream
类的特点
InputStream
是所有字节输入流的父类InputStream
提供核心方法:read()
,用于读取字节数据- 其中,专用的
FileInputStream
类,专用于读取二进制文件
2.InputStream
类的通用开发模式
(1)实例化InputStream
对象
(2)利用read
方法循环读取字节数据,并进行处理
(3)调用close
方法,关闭InputStream
对象
3.用字节输入流,读取E盘根目录下的demo.txt文件
- 新建一个文件
- 查看源码,验证相关接口和具体实现类之间的关系
- 用字节输入流,读取E盘根目录下的demo.txt文件
public class FileCopySample {
public static void main(String[] args) {
//用字节输入流,读取E盘根目录下的demo.txt文件
File source = new File("e:/demo.txt"); //指定一个路径
InputStream fis=null; //为什么放在外面?因为流有开就有关.fis对象的生存周期,既要在try块打开,也要在finally块关闭
try {
//实例化一个InputStream对象
fis = new FileInputStream(source); //将数据源头的对象传入构造方法
byte[] bs = new byte[1024]; //专用于保存字节信息的数组 //在内存中创建一个1024字节大小(1k内存)的数组
int len;
while ((len = fis.read(bs)) != -1) { //一次性读取1024字节的数据,存储到字节数组bs中
System.out.println(len);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fis != null) { //当前对象是已实例化过的
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
--->
7 //对应E盘根目录下的demo.txt文件:abcdefg内容的长度
注意:read()
方法读完文件时,会返回-1,表示文件已读取完毕
- 如果用字节输入流,读取E盘根目录下的demo.jpg文件:
public static void main(String[] args) {
File source = new File("e:/demo.jpg"); //指定一个路径
byte[] bs = new byte[1024];
...
--->
1024 //一块一块的读取,局部读取
1024
...
1024
127 //不够整除了,有个小余数
- 换个更大的方式,去读取:
public static void main(String[] args) {
File source = new File("e:/demo.jpg"); //指定一个路径
byte[] bs = new byte[1024*1024]; //如果是1m的读取
...
--->
1048576
1048576
1048576
1048576
1048576
473215
2.3 通过字节输出流实现文件复制
1.OuputStream
类的特点
OuputStream
是所有字节输出流的父类OuputStream
提供核心方法:write()
,用于向指定输出流输出字节数组- 其中,专用的
FileOutputStream
类,专用于写入二进制文件
相关接口和具体实现类之间的关系图示:
其中,Flushable
接口的作用:立即将缓存中的数据,写入到文件或其他输出流中。
2.OuputStream
类的通用开发模式
(1)实例化OuputStream
对象
(2)利用write
方法循环写入字节数据
(3)调用close
方法,关闭OuputStream
对象
3.实现文件复制的功能
public class FileCopySample {
public static void main(String[] args) {
File source = new File("e:/demo.jpg");
File target = new File("e:/demo1.jpg"); //定义要输出到哪个文件
InputStream fis=null;
OutputStream fos = null;
try {
fis = new FileInputStream(source); //读取指定文件
fos = new FileOutputStream(target); //写入目标文件
byte[] bs = new byte[1024];
int len;
while ((len = fis.read(bs)) != -1) { //如果还有内容
System.out.println(len);
fos.write(bs,0,len); //起始点 //最后一次的长度,往往不是1024,所以用len
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fos != null) {
try {
fos.close(); //关闭输出流
} catch (IOException e) {
e.printStackTrace();
}
}
if (fis != null) { //如果当前对象是已实例化过的
try {
fis.close(); //关闭输入流
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
--->
1024
1024
...
1024
127
查看电脑路径,成功复制了图片:
上述代码,不仅可以复制图片,还可以复制文档。
2.4 通过字符输入输出流实现文本读取与写入
上述字节的输入输出流,是针对所有字节的操作。但是,实际开发中,多是对文本内容的操作。
1.字符输入输出流的特点
Reader
是所有字符输入流的抽象父类Write
是所有字符输出流的抽象父类FileReader
:读取文本文件FileWriter
:写入文本文件
2.写入文本文件
public class TextFileSample {
//实例方法,写入文本文件
public void writeTextFile(){
Writer writer = null;
try{
File file = new File("e:/test.txt");
if (!file.exists()) {
file.createNewFile();
}
writer = new FileWriter(file);
writer.write("这是一个新文件");
}catch(IOException e){
e.printStackTrace();
}finally{
if (writer != null) {
try {
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
TextFileSample sample = new TextFileSample();
sample.writeTextFile();
}
}
--->
已经成功创建了test.txt文本文件,内容为:“这是一个新文件”
- 如果对已有文件进行写入,相当于直接覆盖:
writer.write("这是一个新文件");
--->
内容为:“这是一个新文件new”
- 如果想在文本内容末尾追加:
writer.write("这是一个新文件new");
writer.append(":Append内容");
--->
内容为:“这是一个新文件new:Append内容”
- 查看源码,验证相关接口和具体实现类之间的关系
3.读取文本文件
public class TextFileSample {
//实例方法,读取文本文件
public void readTextFile(){
Reader reader=null;
try {
File file = new File("e:/test.txt");
reader = new FileReader(file); //实例化FileReader对象
int ch = 0;
while ((ch = reader.read())!= -1) { //调用对象的read方法,读取每一个字符的编码数字
System.out.println(ch);
}
}catch (IOException e) {
e.printStackTrace();
}finally {
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
TextFileSample sample = new TextFileSample();
sample.readTextFile();
}
}
--->
36825 //UTF-8编码集中的编码
26159
...
100
20869
23481
- UTF-8编码集相当于一个包含全世界语言的代号大字典,其中的编码其实就是个int代号。它可以与所代表的对应字符进行转换。
System.out.print((char) ch); //将UTF-8编码集中的编码转型为对应的字符
--->
这是一个新文件new:Append内容
- 查看源码,验证相关接口和具体实现类之间的关系
三、I/O辅助类
3.1 辅助类:将字节流转换成字符流
1.两个转换流的类
不管是网络传输,还是读取文件,最根本的还是二进制的数据。
但是,实际人肉眼读取文本文件,需要对字符进行处理。
因此,需要转换:
- 将字节流转换成字符流
InputStreamReader
:将字节输入流--->字符输入流OutputStreamWriter
:将字节输出流--->字符输出流
字符是字节的子集,所以I/O不提供字符再转回到字节。
2.利用InputStreamReader
读取文本文件
public class TextFileSample {
public void isrSample(){
FileInputStream fis=null;
InputStreamReader isr=null;
try {
File file = new File("e:/test.txt");
fis = new FileInputStream(file);
isr = new InputStreamReader(fis, "UTF-8");
while (isr.ready()) { //如果后面有没有其他字符
System.out.println(isr.read());
}
} catch (IOException e) {
e.printStackTrace();
}finally {
if (isr != null) {
try {
isr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
TextFileSample sample = new TextFileSample();
sample.isrSample();
}
}
--->
36825 //也成功读取到了UTF-8编码集中的编码
26159
...
100
20869
23481
- 将UTF-8编码集中的编码转型为对应的字符,肉眼能认识的:
System.out.print((char) isr.read()); //将UTF-8编码集中的编码转型为对应的字符
--->
这是一个新文件new:Append内容
- 如果我们不想直接输出,而是将其保存下来:
public void isrSample(){
FileInputStream fis=null;
InputStreamReader isr=null;
try {
File file = new File("e:/test.txt");
fis = new FileInputStream(file);
isr = new InputStreamReader(fis, "UTF-8");
StringBuffer buffer = new StringBuffer();
while (isr.ready()) {
buffer.append((char) isr.read()); //保存进buffer对象
}
System.out.println(buffer.toString());
} catch (IOException e) {
e.printStackTrace();
}finally {
if (isr != null) {
try {
isr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
实际应用中,InputStreamReader
类其实用的不多,而是直接用已经封装好的FileReader
类。
因为,FileReader
类是InputStreamReader
类的子类。
3.利用OutputStreamWriter
写入文本文件
public class TextFileSample {
public void oswSample(){
FileOutputStream fos = null;
OutputStreamWriter osw = null;
try {
File file = new File("E:/test.txt");
//创建文件
if (!file.exists()) {
file.createNewFile();
}
fos = new FileOutputStream(file);
osw = new OutputStreamWriter(fos, "UTF-8");
osw.write("这是一个新文件!");
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (osw != null) {
osw.close();
}
if (fos != null) {
fos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
TextFileSample sample = new TextFileSample();
sample.oswSample();
}
}
--->
这是一个新文件!
3.2 辅助类:缓冲区与应用
默认文件的读取与写入,都是逐个字节/字符完成的。但这种处理方法并不高效。那应该怎么优化呢?
缓冲区,应运而生。
1.概念
将读取或写入的数据整块在内存中缓存,一次性批量读取、写入,便可以有效提高数据交互效率。
优化输入输出的执行过程
比如:去菜市场买菜:
- 方式1:跑第一趟买一个萝卜,第二趟买一个白菜,第三趟买一块肉
- 方式2:拿个筐去,跑一趟搞定
2.分类
BufferedInputStream
与BufferedOutputStream
用于缓冲字节的输入、输出流BufferedReader
与BufferedWriter
用于缓冲字符的输入、输出流
3.利用BufferedReader
实现文本的整行读取
public class TextFileSample {
public void readerBuffer(){
Reader reader=null;
BufferedReader br = null;
try {
File file = new File("e:/FileSample.java");
reader = new FileReader(file);
br = new BufferedReader(reader);
String line = null; //整行的文本
while ((line = br.readLine()) != null) { //每次读一行,直到下一行没有新数据
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
if (br != null) {
br.close();
}
if (reader != null) {
reader.close(); //依次关闭,养成良好的编码习惯
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
TextFileSample sample = new TextFileSample();
sample.readerBuffer();
}
}
--->
public class FileSample { //成功一行为单位,读取了java文件中的内容
public static void main(String[] args) {
...
四、初识其他流的应用
4.1 初识其他常用的字节流
4.2 通过URLConnection对象获取网络资源
如何利用URLConnection对象从网络获取文件、下载到本地硬盘?
该图片保存在远程服务器上,用浏览器加载:https://manongbiji.oss-cn-beijing.aliyuncs.com/images/weixin.jpg
1.如何通过网络,获取某一个文件的字节呢?
URL(Unified Resource Location),统一资源定位符,大白话就是,网址。
public class URLConnectionSample {
public static void main(String[] args) {
InputStream is = null;
try {
URL url = new URL("https://manongbiji.oss-cn-beijing.aliyuncs.com/images/weixin.jpg"); //实例化一个网址对象
URLConnection connection = url.openConnection(); //本机与远程服务器,建立通信的管道
is = connection.getInputStream();
byte[] bs = new byte[1024];
int len = 0;
while ((len = is.read(bs)) != -1) {
System.out.println(len);
}
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
if (is != null) {
is.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
--->
942 //源文件,读取成功!
1024 //根据网络的状态、服务器通信的情况,可能会出现没有把数组填满的情况。
376
...
847
2.写入到本地硬盘
public class URLConnectionSample {
public static void main(String[] args) {
InputStream is = null;
OutputStream os = null;
try {
URL url = new URL("https://manongbiji.oss-cn-beijing.aliyuncs.com/images/weixin.jpg");
URLConnection connection = url.openConnection();
is = connection.getInputStream();
os = new FileOutputStream("e:/weixin.jpg"); //保存到本地的文件中
byte[] bs = new byte[1024];
int len = 0;
while ((len = is.read(bs)) != -1) {
os.write(bs, 0, len); //从网上获得一点字节数据,这里就写入一点字节数据。直到字节数组bs全部写完
}
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
if (os != null) {
os.close();
}
if (is != null) {
is.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
成功保存到了本地硬盘:
总结:
开发时,如果要获取一些数据,就是想办法获得各种各样的输入流。
当进行写操作时,输出时,就要通过OutputStream
或Writer
对象来进行数据的输出。这就是I/O操作的常规过程。
五、总结
读进脑子里
写出来
Comments | NOTHING