一,IO流
1.什么是IO流
IO流是存取和读取数据的解决方案
2.IO流的作用
IO流用于读写数据,这些数据包括本地文件和网络上的一些数据;比如读写本地文件的时候需要用到文件读写的IO流,读写网络上的数据时需要通过Socket套接字来调用数据流来进行网络数据的读写
3.IO流的分类
如何判断一个文件是不是纯文本文件:可以将该文件用记事本的方式打开,观察其是不是乱码,如果是乱码说明该文件不是纯文本文件,否则就是!!!
分别以word文件和txt文件举例(分别在文件中保存一段文字“我爱中国”):
word:
word文件用记事本打开是乱码,所以不是纯文本文件
txt:
txt文件用记事本打开显示“我爱中国”,所以是纯文本文件
二,字节流(InputStream/OutputStream)
1.字节输出流(OutputStream)
输出流指的是往内存上写入数据,每次写的时候按照字节为单位进行书写;
OutputStream是一个抽象类,在实例化对象时需要使用向上转型,采用子类FileOutputStram(这里主要是操作文件)来创建对象。
OutputStream相关方法
修饰符及返回值类型 | 方法签名 | 说明 |
void | write(int b) | 写入一个整数,在文件中显示对应ASCII对应的字符 |
void | write(byte[] b) | 写入一个字节数组 |
int | write(byte[] b,int off,int len) | 写入对应条件长度的字节数组 |
void | close() | 关闭字节流 |
void | flush() | 刷新缓冲区 |
OutputStream中最重要的方法是write,write方法有三种形式,分别进行代码演示,在进行代码示例之前我们需要知道字节流写数据时的相关细节:
字节流写数据
1.如果文件不存在会自动创建一个文件,但是要保证父亲路径存在
2.如果文件存在会先清空文件,否则需要在实例化OutputStream对象时另加一个参数设置为true(该参数的意思是支持续写)
3.write方法的三种用法
1) write(int b):写入一个整数,但是存储的ASCII值,所以也可以直接写一个字符,字符也是一个整数
2)write(byte[] b):写入一个字节数组,所以用字节流想要写入字符串的时候需要将字符串先转换成字节数组
3)write(byte[] b, int off, int len):将字符数组从off下标开始写len个长度的数据元素
/**
* 在当前项目的路径下创建一个a.txt文件
*/
public static void main(String[] args) throws IOException {
//创建对象
OutputStream outputStream = new FileOutputStream("./a.txt");
//写数据
//1.写入一个整数,文件中存储的是ASCII值
outputStream.write(97);
outputStream.write(98);
//2.写入一个字节数组或者字符串,如果写入的是字符串的话也需要转换成字节数组
byte[] bytes = {97, 98, 99, 100};
outputStream.write(bytes);
String str1 = "abc";
outputStream.write(str1.getBytes());
//3.写入一个字符数组,从off下标开始写len个长度的数据元素
String str2 = "abc";
outputStream.write(str2.getBytes(), 0, 3);
//关闭资源
outputStream.close();
}
第一种写法:写入一个整数,文件中存储的是ASCII值
第二种写法:写入一个字节数组或者字符串,如果写入的是字符串的话也需要转换成字节数组
第三种写法:写入一个字符数组,从off下标开始写len个长度的数据元素
2.字节输入流(InputStream)
输入流指的是从内存上读取数据,每次读取的时候按照字节为单位进行读取;
InputStream是一个抽象类,在实例化对象时需要使用向上转型,采用子类FileInputStream(这里主要是操作文件)来创建对象。
InputStream相关方法
修饰符及返回值类型 | 方法签名 | 说明 |
int | read() | 读取一个字节的数据 |
int | read(byte[] b) | 最多读取b.length子节点额数据到b中,返回实际读到的数量 |
int | read(byte[] b,int off,int len) | 最多读取len-off字节的数据到b中,放在从off开始,返回实际读到的数量 |
int | close() | 关闭字节流 |
InputStream中最重要的方法是read,read方法有三种形式,这里主要讲解前两种,在进行代码示例之前我们需要知道字节流读取数据时的相关细节:
字节流读数据
1.如果文件不存在会直接报错,和输出流写数据区分开
2.read方法
read():一次读取一个字节
1)一次读一个字节,返回的是该数据在ASCII表上所对应的数字,如果想返回字符本身需要强转成char类型
2)当读取的数据很多时,需要循环读取的时候,判断此时读到的数据是不是-1即可,否则就继续读
read(byte[] b):一次读取多个字节
当读取的文件很大,一次读取一个字节很费事的时候,可以采用一次读取多个字节
设置一个缓存数组,一般数组创建的越大,拷贝速度越快(但是数组创建的太大会占用内存资源),所以一般设置成1024的整数倍
/**
* 读取当前项目的路径下的一个a.txt文件,假设存储的是abc
*/
public static void main(String[] args) throws IOException {
//创建对象
InputStream inputStream = new FileInputStream("./a.txt");
//读取数据
//1.一个字节一个字节的读
int a = inputStream.read();
System.out.println((char) a);
//2.循环读取
int b = 0;//变量b一定要定义
while ((b = inputStream.read()) != -1) {
System.out.print((char) b);
}
//3.一次读取多个字节,用缓存数组
byte[] bytes = new byte[2];//一次读取多少个字节和数组的长度有关
int len = 0;
while ((len = inputStream.read(bytes)) != -1) {
//每次读取之后会替换之前的字节数组
//注意这里一定要写读取的数组的长度,因为可能某一次读取的长度不足数组的长度,所以只需要读len的长度即可
System.out.println(new String(bytes, 0, len));
}
//关闭资源
inputStream.close();
}
第一种读取方法:一个字节一个字节的读
第二种方法:循环读取
第三种方法:一次读取多个字节,用缓存数组
- 单个字节和多个字节效率区别(使用拷贝文件的案例演示)
假设有一个照片的路径为:C:/Users/14611/Videos/Captures/a.png,大小为:6,866,962 字节;
需要拷贝到路径为:D:/b.png下并命名为b.png
观察单个字节拷贝和多个字节拷贝下的效率区别!
单子字节:
public static void main(String[] args) throws IOException {
//创建对象,需要创建两个对象
//输入流:用来读取文件
//输出流:用来将读取到的数据写入文件中
InputStream inputStream = new FileInputStream("C:/Users/14611/Videos/Captures/a.png");
OutputStream outputStream = new FileOutputStream("D:/b.png");
//进行拷贝
long beg = System.currentTimeMillis();
int b = 0;
while ((b = inputStream.read()) != -1) {
outputStream.write(b);
}
long end = System.currentTimeMillis();
//关闭资源(先创建的流最后关闭)
outputStream.close();
inputStream.close();
System.out.println("拷贝花费时间为:" + (end - beg) + "ms");
}
多个字节:
public static void main(String[] args) throws IOException {
//创建对象,需要创建两个对象
//输入流:用来读取文件
//输出流:用来将读取到的数据写入文件中
InputStream inputStream = new FileInputStream("C:/Users/14611/Videos/Captures/a.png");
OutputStream outputStream = new FileOutputStream("D:/c.png");
//进行拷贝
long beg = System.currentTimeMillis();
byte[] bytes = new byte[1024 * 1024 * 5];
int len = 0;
while ((len = inputStream.read(bytes)) != -1) {
outputStream.write(bytes, 0, len);//写的时候要设置长度,不能把整个数组都读
}
long end = System.currentTimeMillis();
//关闭资源(先创建的流最后关闭)
outputStream.close();
inputStream.close();
System.out.println("拷贝花费时间为:" + (end - beg) + "ms");
}
两个方法均可以完全拷贝出相同的照片文件,但是每次读取写入多个字节的方法的时间效率明显由于单个字节的方法!
三,字符流(Reader/Write)
- 字符输入流(Reader)
字符输入流在进读取数据的时候,每次都是读取一个字节,遇到中文时,GBK编码读两个字节,读UTF-8编码读三个字节
Reader相关方法
修饰符及返回值类型 | 方法签名 | 说明 |
int | read() | 读取一个数据 |
int | read(char[] buffer) | 读取多个数据 |
void | close() | 关闭字符流 |
Reader中最重要的方法是read,read方法有两种形式,这里主要讲解前两种,在进行代码示例之前我们需要知道字符流读取数据时的相关细节:
字符流读数据,底层就是字节流
输入流:一次读一个字节,遇到中文时,一次就会读取多个字节
输出流:底层会把数据按照指定的编码方式进行编码,变成字节再写到文件中
适用于纯文本文件进行读写操作
1.如果文件不存在,会直接报错
2.read方法:方法读取完之后会自动解码并转成十进制,所以读取汉字的时候需要强转
read():按字节进行读取,遇到中文,一次读多个字节,读取后解码,返回一个整数(GBK一次读两个字节,UTF-8一次读三个字节)
read(char[] buffer):一次读取多个并放到字符数组中
/**
* 读取当前项目的路径下的一个a.txt文件,假设存储的是abc我爱中国
*/
public static void main(String[] args) throws IOException {
//创建对象
Reader reader = new FileReader("./a.txt");
//读取数据
//1.无参的read方法
int ch = 0;
while ((ch = reader.read()) != -1) {
System.out.println(ch);
}
//2.有参的read方法
char[] chars = new char[2];//表示一次读两个数据
int len = 0;
int count = 0;
while ((len = reader.read(chars)) != -1) {
count++;
//把数组的数据变成字符串再进行打印
System.out.println(new String(chars, 0, len));
System.out.println(count);
}
//关闭资源
reader.close();
}
第一种方法:无参的read方法
第二种方法:有参的read方法(每次读两个数据并换行)
2.字符输出流(Writer)
字符输出流在进写数据的时候,每次都是写入一个字节,遇到中文时,GBK编码写入两个字节,读UTF-8编码写入三个字节
Writer相关方法
Writer也有类似的几种写数据的方法,但是用的最多的就是写入字符串(只需了解这一种用法即可)
write方法的相关细节:
字符流写数据
1.文件不存在会创建一个新的路径,但是要保证父亲路径存在
2.如果文件已经存在,则会清空文件,如果不想清空需要打开续写开关,即设置成true即可
3.如果write方法的参数是整数,但是实际上写到本地文件中的是整数在字符集上对应的字符
用的最多的方法是写一个字符串
/**
* 在当前项目的路径下创建一个a.txt文件
*/
public static void main(String[] args) throws IOException {
Writer writer = new FileWriter("./a.txt");
writer.write("你好中国");//是12个字节
writer.close();
}