IO概述
字符流回顾
代码案例:
public class CharStreamDemo {
public static void main(String[] args) throws IOException {
FileReader fr = new FileReader("a.txt");
//一次读取一个数组的数据
char[] chs=new char[10];
int len=-1;
while((len=fr.read(chs))!=-1){
System.out.print(new String(chs,0,len));
}
fr.close();
System.out.println("==============");
FileReader r = new FileReader("a.txt"); //这里若继续使用上面定义的fr 则输出的结果则是空
//一次读取一个字符
int ch;
while((ch=r.read())!=-1){
System.out.print((char)ch);
}
r.close();
}
}
运行结果:
Hello World!
Hello World!
Hello World!
==============
Hello World!
Hello World!
Hello World!
数据的本质
数据在传输的过程中,一切数据(文本,图像,声音等)最终存储的均为一个个字节,即二进制数字。所以数据传输过程中,使用二进制数据可以完成任意数据的传输
我们想一个文件中存储一定的数据,如果使用文本方式打开,则会以文本的方式解释数据。如果以视频的方式打开则会以视频的方式解释数据。音频,可执行文件等都是如此。所以在文件传输过程中,我们要时刻明确,传输的始终是二进制数据。
字节输入流和字节输出流
字节输出流
输出流:OutputStream抽象类 FileOutputStream基本输出流
构造方法:
public FileOutputStream(String name) 创建输出流操作得分目标
public FileOutputStream(String name,boolean append) append为true时代表每次写入都向文件末尾追加,为False则覆盖原有内容
操作方法:
write(int n) 输出一个字节;
write(byte[] b) 输出一个字节数组
write(byte[] b,int off,int len) 输出字节数组的一部分
close() 释放IO占用的windows底层资源
代码案例:
public class OutputStreamDemo {
public static void main(String[] args) {
try {
OutputStream os=new FileOutputStream("test.txt",true); //true表示输出内容是在文件末尾追加内容的
os.write(97); //输出的是字符a
byte[] b={97,98,99}; //输出的是abc
os.write(b);
os.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
字节输入流
输入流: InputStream 抽象类 FileInputStream 基本输入流
构造方法:
public FileInputStream(String name)
操作方法:
int read(); 读取一个字节 返回字节内容本身 读取到文件末尾返回-1
int read(byte[] b) 读取一个字节数组
close() 释放资源
代码案例:
public class InputStreamDemo {
public static void main(String[] args) throws IOException {
InputStream fis=new FileInputStream("test.txt");
//一次读取一个字节 也可以一次读取一个字节数组
System.out.println(fis.read());
int c=-1;
while((c=fis.read())!=-1){
System.out.println(c);
}
}
}
运行结果:
97
97
98
99
文件复制与高效流
文件复制
方式1:不使用字节数组 即一次读取一个字节
方式2:使用字节数组 一次读取一个字节数组
高效字节流
Java在常规IO的基础上,提供了更为高效的缓冲流,如下:
高效字节缓冲流:BufferedInputStream/BufferedOutputStream
高效流使用普通流对象作为构造方法参数。将普通流包装,提供高效的装饰
高效流write写出数据时,写出位置为缓冲区,并非目标资源。需要通过flush刷新方法将缓冲区的内容写出到目标文件中。
并且高效输出流的关闭方法会自动调用flush方法
高效原理:缓冲区临时存储多个数据,然后一次调用底层资源,减少代用底层资源的次数,也即调用中断次数,从而提高程序运行速度。
代码案例:
public class BufferedByteStreamDemo {
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream("test.txt");
BufferedInputStream bis = new BufferedInputStream(fis);
int c = bis.read();
System.out.println(c);
byte[] b=new byte[10];
int len=-1;
while((len=bis.read(b))!=-1){
System.out.println(new String(b,0,len));
}
}
}
运行结果:
97
abc
转换流
操作纯文本文件时,使用字节流对于程序来说过于繁琐,程序员更偏好操作字符。所以当面对一些字节流的操作时,我们可以将其转换成字符流再进行操作,这样便非常方便了。
Reader:
InputStreamReader可以完成字节输入流转换成字符流
Writer:
OutputStreamWriter可以完成字节输出流转换成字符输出流。
由上边可以知道,转换流是字符流的一种,创建对象是传入对于的字节流对象即可完成转换动作。
转换流同样使用了包装的思想,其构造方法接受的同样为IO流对象,并非某个文件资源。关闭转换流的同时即关闭了对应的字节流。
OutputStreamWriter
//利用转换流向test.txt中写入“hello:中国”
public class OutputStreamWriterDemo {
public static void main(String[] args) throws IOException {
//创建字节输出流
FileOutputStream fos = new FileOutputStream("test.txt", true);
OutputStreamWriter osw = new OutputStreamWriter(fos);
osw.write("hello:中国"); //向字节流中写入字符串
osw.close();
}
}
InputStreamReader
需求:已经具备字节流,绑定了IO资源。需要将字节流转换字符流
此时,可以使用转换流,接受一个字节流对象,进行字符流的操作,转换流本身就是字符流。
public class InputStreamReaderDemo {
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream("test.txt");
InputStreamReader isr = new InputStreamReader(fis);
int b=-1;
while((b=isr.read())!=-1){
System.out.println((char)b);
}
fis.close();
}
}
运行结果:
a
a
b
c
h
e
l
l
o
:
中
国
编码表
编码表概述
我们知道计算机底层数据存储的都是二进制数据,而我们生活中各种各样的数据,如何才能和计算机中存储的二进制数据对应起来呢??
这时美国就把他们的每一个字符和一个整数对应起来,就形成了一张编码表,美国他们的编码表就是ASCII表。其中就是各个英文字符对应的编码
编码表:就是生活中字符和计算机二进制的对应关系表。
-
ASCII:一个字节中的7位表示。对应的字节就是正数:0xxxxxxx。
-
ISO-8859-1:拉丁Latin码表,用一个字节8位表示。1xxxxxxx:负数
-
GB2312:简体中文码表。包含6000-7000中文和符号。用两个字节表示。两个字节第一个字节就是负数,第二个字节可能是正数
GBK:目前最常用的中文码表,2万的中文和符号。用两个字节表示,其中一部分文字,第一个字节开头是1,第二个字节开头是0.
GB18030:最新的中文码表,目前还没有正式使用。
-
Unicode:国际标准码表:无论是什么文字,都用两个字节存储。
Java中char类型用的就是这个码表:char c=‘a’ ;占用两个字节
-
UTF-8:基于Unicode,一个字节就可以存储数据,不用两个字节存储,而且这个码更加标准化,中文一般使用3个字节
能识别中文的码表:GB2312,GBK,Unicode,UTF-8
乱码问题
A:乱码:当字符与字节转换过程中使用了不同的码表,会造成乱码的情况
B:在字符串中:当我们将字符串转为对应的数字字节时,需要指定码表,则存储为该字符该码表对应的数字字节,如果使用了其他码表重写翻译回字符串,则拼写的新字符串回出现乱码
C:在IO中:与字符串编码表使用类似,当以某个码表写出字节数据时,又使用另外的码表展示,就会出现乱码。
将字节流设置指定编码的字节流:
public Output StreamWriter(OutputStream out,String charsetName);
public Input StreamReader(InputStream in,String charsetName);
代码实例:
public class CharSetDemo {
public static void main(String[] args) throws IOException {
//使用UTF-8的方式将字符输出到文件中
FileOutputStream fos = new FileOutputStream("test.txt");
OutputStreamWriter osw = new OutputStreamWriter(fos, "UTF-8");
osw.write("你好");
osw.close(); //注意这里需要关闭osw(在关闭对象资源之前会先清空缓存) 否则下面可能不会输出任何内容
//如果使用UTF-8的方式写出两个汉字(6个字节),在那个写出的文件中观看时乱码了 这时在使用UTF-8的方式从那个文件中读回就不会乱码了
FileInputStream fis = new FileInputStream("test.txt");
InputStreamReader isr = new InputStreamReader(fis, "UTF-8"); //这里若使用GBK的方式读会就会出现乱码
int ch=-1;
while((ch=isr.read())!=-1){
System.out.print((char)ch);
}
isr.close();
}
}
其他流
序列化和反序列化
概述
用于向流中写入对象的操作流ObjectOutputStream 称为序列化流。
用于从流中读取对象的操作流 ObjectInputStream 称为反序列化流。
特点:用于操作对象。可以将对象写入到文件中,也可以从文件中读取对象
ObjectOutputStream 序列化流
public final void writeObject(Object o);
ObjectInputStream 反序列化流
public final Object readObject();
每个被序列化操作的类型必须实现Serializable接口(使其具备序列化功能的标记接口,不需要实现任何方法)。
对象序列化流ObjectOutputStream
ObjectOutputStream将Java对象写入到OutputStream,利用输出流将对象写入到指定文件中。
代码案例:
public class ObjectOutputStreamDemo {
public static void main(String[] args) throws IOException, ClassNotFoundException {
Person person = new Person("Jack","22");
//利用OutputStream将Person对象写入到指定文件中
FileOutputStream fos = new FileOutputStream("personObject.txt");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(person);
oos.close();
//利用InputStream从泰国指定文件中读取对象
FileInputStream fis = new FileInputStream("personObject.txt");
ObjectInputStream ois = new ObjectInputStream(fis);
Person o = (Person)ois.readObject();
System.out.println(o.getName());
System.out.println(o.getAge());
}
}
打印流
打印流添加输出数据的功能,使他们能够方便打印各种数据值表示形式
打印流只有输出流,没有输入流:
PrintStream 字节打印流 调用println 方法时自动刷新
PrintlnWriter 字符打印流 指定自动刷新开关后,调用prinln方法时自动刷新,无序手动调用flush() 方法
第三方工具CommonsIO
导入classpath
A:加入classpath的第三方jar包内的class文件才能在项目使用
B:创建lib文件夹
将commons-io.jar拷贝到lib文件夹
右键点击Commons-io.jar,Build Parth ---->Add to Build Path
Filename Utils
这个工具是用来处理文件名(包含文件路径)的,他可以轻松解决不同不同操作系统文件名称规范不同的问题
常用方法:
getExtension(String path) 获取文件的扩展名
getName() 获取文件名
isExtension(String fileName,String ext) 判断fileName是否是ext后缀名
FileUtils
提供文件操作(移动文件,读取文件,检查文件是否存在等等)的方法。
常用方法:
readFileToString(File file) 读取文件内容,并返回一个String
writeStringtoFile(File file,String content) 将内容content写入到file中
copyDirectoryToDirectory(File srcDir,File destDir) 文件夹复制
copyFile(File srcFile,File destFile) 文件复制