一、字节流、
1、IO流的顶层父类
字节流
字节输出流: OutputStream
字节输入流: InputStream
字符流
字符输出流: Writer
字符输入流: Reader
注意:IO流的顶层父类全是抽象类。
2、OutputStream 是字节输出流。 可以将Java程序中的数据写入到硬盘(文件)中。(以字节为单位)
OutputStream 是所有字节输出流的顶层父类, 是一个抽象类, 要使用它的子类, 最常用的子类FileOutputStream
FileOutputStream的构造方法
FileOutputStream(File file): 参数需要传递一个File类型的文件。
FileOutputStream(String name):参数需要传递一个字符串类型的文件路径
其他成员方法:
void write(int b):将一个字节写入到文件中
void write(byte[] b):将整个的字节数组写入到文件中
void write(byte[] b, int off, int len): 将字节数组的一部分写入到文件中。 参数off表示起始位置, 参数len表示个数.
void close():关闭流, 释放资源
步骤:
1. 创建FileOutputStream对象, 并且传递一个文件路径
2. 调用write方法,向文件中写数据。
3. 调用close方法,释放资源
注意:在java中一个字符占两个字节,但是如果这个字符是ASCII码表上的字符,那么在操作系统中是占一个字节的。
所以可以使用一次写一个字节的方式写入到文件中。
中文在操作系统中如果采用的是GBK编码,那么占两个字节,如果采用的是UTF-8编码,那么占三个字节。
中文是占多个字节,所以不能采用一次写一个字节的方式写入中文
public class Demo02OutputStream {
public static void main(String[] args) throws IOException {
//创建FileOutputStream对象, 指定一个目的地
/*
创建对象代码做了以下三个事情
1. 调用系统资源,在d盘下创建一个文件file01.txt。 如果文件已经存在,那么就会覆盖掉原来的文件。
2. 会创建一个FileOutputStream字节输出流对象
3. 将这个文件绑定到这个字节输出流对象上,以后通过这个对象操作的就是这个文件了。
*/
OutputStream os = new FileOutputStream("day09\\file01.txt");
//调用write方法,向文件中写数据。
//void write(int b):将一个字节写入到文件中
os.write(100); //d 因为100对应的ASCII码字符是d
//os.write('a');
//os.write('中');
//调用close释放资源。 会解除资源被占用的问题
os.close();
//如果不释放资源,那么如果程序不结束,会导致这个文件一直处于被占用状态。
while(true) {
}
}
}
3、 字符串 -> 字节数组
byte[] getBytes(): 将字符串使用平台默认的编码(IDEA默认编码UTF-8)方式转成字节数组。
字节数组 -> 字符串
String(byte[] bytes): 将整个的字节数组转成一个字符串
String(byte[] bytes, int offset, int length): 将字节数组的一部分转成字符串。 从offset位置开始转, 转length个
public class Demo03Parse {
public static void main(String[] args) {
//字符串 -> 字节数组
String s1 = "abcde";
byte[] bArr1 = s1.getBytes();
System.out.println("bArr1:" + Arrays.toString(bArr1)); // [97, 98, 99, 100, 101]
String s2 = "中国";
byte[] bArr2 = s2.getBytes();
System.out.println("bArr2:" + Arrays.toString(bArr2)); //[-28, -72, -83, -27, -101, -67]
System.out.println("------------------------------");
//字节数组 -> 字符串
String str1 = new String(bArr1);
System.out.println("str1:" + str1); //abcde
//将字节数组的一部分转成字符串
String str2 = new String(bArr1, 1, 3);
System.out.println("str2:" + str2); //bcd
}
}
4、使用字节输出流一次写入一个字节数组。
void write(byte[] b):将整个的字节数组写入到文件中
void write(byte[] b, int off, int len): 将字节数组的一部分写入到文件中。 参数off表示起始位置, 参数len表示个数.
public class Demo04OutputStream {
public static void main(String[] args) throws IOException {
//1. 创建FileOutputStream,指定一个目的地
OutputStream os = new FileOutputStream("d:\\file02.txt");
//2. 调用write方法,写数据
//定义字节数组
//byte[] bArr = "中文".getBytes();
//os.write(bArr);
//写入字节输入的一部分
byte[] bArr = "abcde".getBytes();
os.write(bArr, 1, 3); //bcd
//3. 调用close方法,释放资源
os.close();
}
}
5、 字节输出流的追加写
如果要续写,使用另一个构造方法创建FileOutputStream对象即可
FileOutputStream(File file, boolean append): 第一个参数表示要写入到哪个文件, 第二个参数表示是否续写,如果为true表示续写
FileOutputStream(String name, boolean append):第一个参数表示要写入到哪个文件, 第二个参数表示是否续写,如果为true表示续写
public class Demo05AppendWrite {
public static void main(String[] args) throws IOException {
//创建字节输出流,绑定一个目的地
OutputStream os = new FileOutputStream("d:\\file02.txt", true);//true表示要续写,追加写
//向文件中写入一句诗
os.write("举头望明月".getBytes());
//释放资源
os.close();
}
}
7、 换行写
如果要实现换行效果,那么需要使用换行符
window: \r\n
linux: \n
macOs: \r
public class Demo06WriteLine {
public static void main(String[] args) throws IOException {
//创建一个字节输出流绑定目的地
OutputStream os = new FileOutputStream("d:\\file03.txt");
//调用write方法,向文件中写入两句诗
os.write("白日依山尽\r\n".getBytes());
os.write("黄河入海流".getBytes());
//释放资源
os.close();
}
}
二、字节输入流
1、InputStream表示字节输入流, 可以将文件中的数据读取到Java程序中。(以字节为单位)
InputStream是所有字节输入流的顶层父类, 是一个抽象类, 如果要用需要使用子类,最常用子类: FileInputStream
FileInputStream构造方法:
FileInputStream(File file): 传递一个File类型的文件, 表示要从这个文件中读取数据。
FileInputStream(String name):传递一个字符串类型的文件路径, 表示要从这个文件中读取数据。
其他的成员方法:
int read():读取一个字节并返回。 如果读取结束,返回-1
int read(byte[] b): 读取一个字节数组,返回读取到的有效个数。 如果读取结束,返回值为-1.
使用步骤:
1. 创建字节输入流对象, 绑定一个数据源。
2. 调用read方法,进行读取
3. 释放资源.
一次读取一个字节的方式不能读取中文,因为中文占多个字节.
public class Demo01InputStream {
public static void main(String[] args) throws IOException {
//1. 创建字节输入流对象, 绑定一个数据源。
//创建字节输入流对象的时候如果文件不存在,那么就会抛出异常。
InputStream is = new FileInputStream("day09\\source01.txt");
//2. 调用read方法,从文件中一次读取一个字节数据
//使用循环读取
//定义一个变量,用来接收读取到的字节
int i;
//定义循环,开始读取
while((i = is.read()) != -1) {
/*
条件位置做了三个事情
1. 调用read方法读取了一个字节
2. 把读取到的字节赋值给了变量i
3. 判断变量i不等于-1吗,如果不等于-1表示读取到了数据,那么就在大括号中进行处理。
*/
System.out.print((char)i);
}
/*
int i = is.read(); //read方法会读取到一个字节,并且把这个字节返回赋值给变量i
System.out.println(i);//97
//继续读
i = is.read();
System.out.println(i);//98
//还读
i = is.read();
System.out.println(i);//99
//接着读
i = is.read();
System.out.println(i);//-1
*/
//3. 释放资源
is.close();
}
}
2、使用字节输入流一次读取一个字节数组
int read(byte[] b): 读取一个字节数组,返回读取到的有效个数。 如果读取结束,返回值 为-1.
这种方式读取中文也会有问题
public class Demo02InputStream {
public static void main(String[] args) throws IOException {
//创建一个字节输入流,用来读取
InputStream is = new FileInputStream("day09\\source.txt");
//使用循环,一次读取一个字节数组
//定义一个字节数组,用来保存读取到的字节。
byte[] bArr = new byte[1024]; //这个数组的大小一般是1024的整数倍,最快1024 * 8
//定义变量len,用来保存每次读取到的字节个数.
int len;
//定义while循环,开始读取
while((len = is.read(bArr)) != -1) {
/*
条件做了哪些事情:
1. 通过输入流调用read方法,进行读取,并且把读取到的数据放入到了参数bArr数组中
2. 返回读取到的字节个数,并且把这个个数赋值给变量len。
3. 判断len是否不等于-1,如果不等于-1,那么表示读取到了内容,那么就可以对读取到的内容进行处理。
读取到的字节保存到了bArr数组中。 返回值为读取到的个数。
*/
//读取到了几个内容,就把字节数组中的几个元素转成字符串,然后再打印
System.out.print(new String(bArr, 0, len));
}
/*
//调用read方法,一次读取一个字节数组
byte[] bArr = new byte[2];
//通过输入流进行读取,将数据读取到了bArr这个字节数组中。
//数组的长度是几,那么就最多一次读取几个。
//这个方法返回值为读取到的有效个数.
int len = is.read(bArr);
System.out.println(new String(bArr, 0, len)); //97 98
System.out.println(len); // 2
//接着读
len = is.read(bArr);
System.out.println(new String(bArr, 0, len));//99 100
System.out.println(len); // 2
//接着读
len = is.read(bArr);
System.out.println(new String(bArr, 0, len));//101 100
System.out.println(len); // 1
//接着读
len = is.read(bArr);
System.out.println(new String(bArr));//101 100
System.out.println(len); //-1
`
*/
is.close();
}
}
3、 文件复制。
文件复制本质就是文件的读写。
读取源文件中的字节,每读取一次,就把读取到的字节写入到目的地文件中。
步骤:
1. 创建字节输入流,用来读取。
2. 创建字节输出流,用来写入。
3. 使用字节输入流读取源文件中的字节,一次读取一个字节数组。
4. 将读取到的字节数组写入到目的地中。
5. 释放资源
public class Demo03CopyTest {
public static void main(String[] args) throws IOException {
//创建字节输入流,用来读取。
InputStream is = new FileInputStream("day09\\aa.jpg");
//创建字节输出流,用来写入
OutputStream os = new FileOutputStream("day09\\bb.jpg");
//使用字节输入流读取源文件中的字节,一次读取一个字节数组。
//定义一个字节数组,用来保存读取到的字节
byte[] bArr = new byte[1024];
int len;
//开始读取,每读取到一个字节数组,就将这个字节数组写入到目的地文件中
while ((len = is.read(bArr)) != -1) {
//把读取到的内容写入到目的地文件
os.write(bArr, 0, len); //读取到几个内容,那么就写几个内容
}
//释放资源
os.close();
is.close();
}
}
4、 Reader是字符输入流, 可以将文件中的数据读取到Java程序中。 (以字符为单位)
Reader是字符输入流的顶层父类, 是一个抽象类。 如果要用,需要使用子类。
最常用的子类是: FileReader
FileReader构造方法:
FileReader(File file):参数需要一个File类型的文件
FileReader(String fileName):参数需要一个字符串类型的文件路径
其他方法:
int read(): 一次读取一个字符. 读取结束返回-1.
int read(char[] cbuf):一次读取一个字符数组。 将读取出来的数据保存在字符数组中,返回值为读取到的个数。 如果读取结束返回-1
字符输入流使用步骤:
1. 创建
2. 读取
3. 释放
public class Demo01Reader {
public static void main(String[] args) throws IOException {
//创建字符输入流对象
Reader r = new FileReader("day09\\source02.txt");
//读取
//int read(): 一次读取一个字符.
/*
int i; //定义变量,表示读取到的每一个字符
//开始读取
while((i = r.read()) != -1) {
System.out.print((char)i);
}
*/
//int read(char[] cbuf):一次读取一个字符数组。 将读取出来的数据保存在字符数组中,返回值为读取到的个数。 如果读取结束返回-1
//定义一个字符数组,用来保存读取到的数据
char[] cArr = new char[2];
//len用来接收读到的长度
int len;
//使用循环进行读取
while ((len = r.read(cArr)) != -1) {
//将读取到的内容进行打印, 读取到了几个人内容,就把几个内容打印
System.out.print(new String(cArr, 0, len));
}
//释放资源
r.close();
}
}
三、字符输出流
1、字符输出流 Writer, 可以将Java程序中的数据写入到文件中。(以字符为单位)
Writer 是所有字符输出流的顶层父类, 如果要用需要使用子类。 常用子类FileWriter
FileWriter 构造方法:
FileWriter(File file): 参数需要File类型的文件。
FileWriter(String fileName): 需要传递String类型的文件路径。
其他方法:
void write(String str): 向文件中写入一个字符串。
void flush(): 刷新流。 将内存缓冲区中的数据刷新到文件中
void close(): 关闭流
字符输出流的使用步骤:
1. 创建字符输出流对象, 绑定一个目的地
2. 调用write方法,向文件中写入数据
3. 刷新。 (只有 [字符][输出]流,才需要刷新)
4. 释放资源
public class Demo01FileWriter {
public static void main(String[] args) throws IOException {
//创建FileWriter对象
Writer w = new FileWriter("day09\\file01.txt");
//调用write方法,写一个字符串
w.write("你好"); //并没有将数据直接写入到文件中,而是放到了内存中的字符缓冲区。
//如果要把内存缓冲区中的数据放入到文件中,需要刷新。
w.flush();
//释放资源
w.close();
}
}
2、字符输出流中的其他写的方法
void write(char[] cbuf): 向文件中写入字符数组。
void write(char[] cbuf, int off, int len):向文件中写入字符数组的一部分。 从off 开始写, 写len个
void write(int c):写一个字符数据
void write(String str): 写入一个字符串
void write(String str, int off, int len): 写入字符串的一部分
字节流一般处理非文本文件: 图片, MP3, 视频
字符流用于处理文本文件: 使用windows默认记事本打开能够看得懂的就是文本文件。
public class Demo02FileWriter {
public static void main(String[] args) throws IOException {
//创建一个字符输出流对象
FileWriter fw = new FileWriter("day09\\file02.txt");
//调用write方法,写入
//void write(char[] cbuf): 向文件中写入字符数组。
//char[] cArr = {'h', 'e', 'l', 'l', 'o'};
//fw.write(cArr);
//void write(char[] cbuf, int off, int len):向文件中写入字符数组的一部分。
//char[] cArr = {'h', 'e', 'l', 'l', 'o'};
//fw.write(cArr, 1, 3);
//void write(int c):写一个字符数据
//fw.write(100); //d
//void write(String str): 写入一个字符串
//fw.write("你好");
//void write(String str, int off, int len): 写入字符串的一部分
fw.write("你好吗今天", 1, 3);
//刷新
fw.flush();
//关闭
fw.close();
}
}
3、字符输出流中刷新方法和关闭方法的区别
flash: 刷新。 仅仅是做的刷新操作。 流刷新之后还可以使用
close: 关闭。 先刷新,然后关闭流 流关闭之后就不能使用了
public class Demo03FlushAndClose {
public static void main(String[] args) throws IOException {
//创建字符输出流
Writer w = new FileWriter("day09\\file03.txt");
//调用write方法,写数据
w.write("你好");
w.flush();
w.write("我好");
//释放资源
w.close();
w.write("大家好"); //java.io.IOException: Stream closed
}
}
四、IO中的异常处理
1、IO流中的异常处理(JDK7之前的处理方式)
流创建出来之后无论如何都一定要关闭。
所以要把close关闭流的代码放在finally代码块
public class Demo01Exception {
public static void main(String[] args) {
Writer w = null;
try {
w = new FileWriter("day09\\file04.txt");
w.write("你好");
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
//非空判断,如果w不是null,那么表示这个输出流对象已经创建了,那么才释放资源
if(w != null) {
w.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
2、 在JDK7的时候,多了一种异常的处理方式, try...with...resource。其实就是一种特殊的try...catch语句
格式
try(创建流对象的代码) {
//操作语句
} catch(要捕获的异常 变量名) {
}
try...with...resource 语句的作用是 ,在小括号中创建的对象,无论如何都会调用它的close方法。
并且这个close方法不需要我们自己去调用,系统会自动帮我们去调用
public class Demo02Exception {
public static void main(String[] args) {
try(Writer fw = new FileWriter("day09\\file05.txt"); ) {
fw.write("你好");
} catch (IOException e) {
System.out.println("出现异常啦");
}
}
}
3、 jdk9 try...with...resource的优化
public class Demo04Exception {
public static void main(String[] args) throws IOException {
//创建FileWriter对象
Writer w = new FileWriter("day09\\file06.txt");
try(w) {
w.write("你好");
} catch (Exception e) {
}
}
}
五、属性集(properties)
1、Properties的概念:
Properties是一个双列集合, 是Hashtable的子类。
Properties的特点:
1. Properties实现了Map接口。 所以拥有Map接口中的所有的功能。
2. 这个集合没有泛型,键和值都是String
3. 这个集合支持对流的操作。 可以从流中加载键值对。
常见方法:
(常用)Object setProperty(String key, String value): 将键值对添加到Properties集合
(常用)String getProperty(String key): 根据键获取对应的值。
Set<String> stringPropertyNames():获取Properties集合中所有的key放入到Set集合返回.
public class Demo01Properties {
public static void main(String[] args) {
//创建一个Properties集合
Properties p = new Properties();
//调用setProperty方法,添加键值对
p.setProperty("it001", "王宝强");
p.setProperty("it002", "贾乃亮");
p.setProperty("it003", "陈羽凡");
System.out.println("p:" + p);
//根据key获取指定的value
System.out.println(p.getProperty("it002"));
System.out.println("=========================");
// Set<String> stringPropertyNames():获取Properties集合中所有的key放入到Set集合返回.
Set<String> set = p.stringPropertyNames();
//遍历这个set集合,获取每一个键,根据键获取Properties集合的值
for(String key : set) {
String value = p.getProperty(key);
System.out.println(key + "--" + value);
}
}
}
2、Properties与流相关的方法
void load(InputStream inStream): 参数需要传递一个字节输入流。
void load(Reader reader): 参数需要传递一个字符输入流。
load方法中需要传递一个输入流, 这个输入流会绑定一个文件, load方法可以从该文件中读取键值对。
文件要求:
1. 文件一般以.properties结尾(软性规定)
2. 里面的键值对存储方式必须按照规则 (硬性规定)
键=值
键=值
键=值
...
步骤:
1. 创建一个输入流对象,绑定一个文件。
2. 创建Properties集合对象
3. 调用load方法,传递输入流,就可以将文件中的键值对加载到Properties集合中
4. 释放流的资源
public class Demo02Properties {
public static void main(String[] args) throws IOException {
//创建一个输入流对象,绑定一个文件。
InputStream is = new FileInputStream("day09\\config.properties");
//创建Properties集合对象
Properties p = new Properties();
//调用load方法,就可以将文件中的键值对加载到Properties集合中
p.load(is);
//释放流的资源
is.close();
//打印Properties集合
System.out.println(p);
}
}