1 字节缓冲流
BufferInputStream 将创建一个内部的缓冲区数组,内部缓冲区数组将根据需要从包含的输入流中重新填充,一次可以读取多个字节
BufferOutputStream 该类实现缓冲输出流。 通过设置这样的输出流,应用程序可以向底层输出流写入字节,而不必为写入的每个字节导致底层系统的调用
构造方法
方法 | 作用 |
---|---|
BufferedOutputStream(OutputStream out) | 创建一个新的缓冲输出流,以将数据写入指定的底层输出流。 |
BufferedOutputStream(OutputStream out, int size) | 创建一个新的缓冲输出流,以便以指定的缓冲区大小将数据写入指定的底层输出流。 |
-
BufferedInputStream(InputStream in)
创建一个BufferedInputStream
并保存其参数,输入流in
,供以后使用。BufferedInputStream(InputStream in, int size)
创建BufferedInputStream
具有指定缓冲区大小,并保存其参数,输入流in
,供以后使用。
public static void main(String[] args) throws IOException {
// 创建字节缓冲输出流
OutputStream os = new FileOutputStream("day_13/dir/bos.txt");
BufferedOutputStream bos = new BufferedOutputStream(os);
//写数据
bos.write("hello\r\n".getBytes(StandardCharsets.UTF_8));
bos.write("hello\r\n".getBytes(StandardCharsets.UTF_8));
/*
BufferedOutputStream 内部带有缓冲区 写数据的时候 先写出到缓冲区,缓冲区写满的时候 ,才会将缓冲区的内容写出到磁盘
调用flush方法 只刷新缓冲流 但不释放资源
close方法 在关闭流 释放资源之前 会先刷新缓冲流
*/
// bos.flush();//刷新缓冲输出流
bos.close();
// 字节缓冲输入流
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("day_13/dir/bos.txt"));
byte[] bytes = new byte[1024];
int len;
while((len=bis.read(bytes))!= -1){
System.out.println(new String(bytes,0,len,StandardCharsets.UTF_8));
}
bis.close();
}
为什么缓冲流的构造方法中需要的是一个字节流,而不是具体的文件或者路径呢?
字节缓冲流仅仅提供缓冲区,而真正的读写数据还的移开基本的字节流对象进行操作。
- 使用字节缓冲流完成视频的复制
public class BufferedStreamDemo2 {
public static void main(String[] args) throws IOException {
// 1 创建字节输入流
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("D:\\IO\\mp4.mp4"));
// 2 创建字节输出流
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("day_13/dir/aa.mp4"));
// 3 创建缓冲数组
byte[] buff = new byte[1024*1024];
// 每次读取的字节数
int len;
while((len = bis.read(buff))!= -1){
bos.write(buff,0,len);
}
bos.close();
bis.close();
}
}
2 字符流
2.1 为什么出现字符流
由于字节流操作中文不是特别方便,所以就出现了字符流
字符流 = 字节流 +字符集
中文字节存储方式
用字节流复制文本文件时,文本文件也会有中文,但是没有问题,原因就是最终底层操作会自动的进行字节拼接成中文。
如何识别中文?
汉字在存储的时候 无论使用那种编码存储 第一个字节都是负数。
2.2. 字符串中的编码和解码的问题
编码就指的是将字符转换成字节
-
-
byte[]
getBytes()
使用平台的默认字符集将此String
编码为字节序列,将结果存储到新的字节数组中。
-
-
-
byte[]
getBytes(String charsetName)
使用命名的字符集将此String
编码为字节序列,将结果存储到新的字节数组中。
-
解码: 将字节数组转换为字符
-
String(byte[] bytes, String charsetName)
构造一个新的String
由指定用指定的字节的数组解码charset 。
-
String(byte[] bytes, String charsetName)
构造一个新的String
由指定用指定的字节的数组解码charset 。
public static void main(String[] args) throws UnsupportedEncodingException {
String s = "河南加油";
// 编码 采用不同的字符集进行编码
byte[] b1 = s.getBytes();// 默认的编码 UTF-8
byte[] b2 = s.getBytes("GBK");
for(byte b : b1){
System.out.print(b +" ");
}
System.out.println();
for(byte b : b2){
System.out.print(b +" ");
}
System.out.println();
// 解码 编码和解码必须使用相同的码表 否则会出现中文乱码
String str1 = new String(b1);//使用默认编码 UTF-8
System.out.println(str1);
String str2 = new String(b2,"gbk");
System.out.println(str2);
}
2.3. 字符流中的编码问题
字符流的抽象基类:
Reader 字符输入流的抽象基类 编码
Writer 字符输出流的抽象基类 解码
字符流中和编码相关的类:
-
InputStreamReader是从字节流到字符流的桥:它读取字节,并使用指定的
charset
将其解码为字符 。 它使用的字符集可以由名称指定,也可以被明确指定,或者可以接受平台的默认字符集。 -
OutputStreamWriter是字符的桥梁流以字节流:向其写入的字符编码成使用指定的字节
charset
。 它使用的字符集可以由名称指定,也可以被明确指定,或者可以接受平台的默认字符集。
构造方法:
-
OutputStreamWriter(OutputStream out)
创建一个使用默认字符编码的OutputStreamWriter。 -
OutputStreamWriter(OutputStream out, String charsetName)
创建一个使用命名字符集的OutputStreamWriter。 -
InputStreamReader(InputStream in)
创建一个使用默认字符集的InputStreamReader。 -
InputStreamReader(InputStream in, String charsetName)
创建一个使用命名字符集的InputStreamReader。使用字符流完成对于中文的写和读
public class RWDemo1 { public static void main(String[] args) throws IOException { // 写数据 OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream("day_13/dir/writer.txt"),"UTF-8"); writer.write("中国"); writer.close(); // 读数据 InputStreamReader reader = new InputStreamReader(new FileInputStream("day_13/dir/writer.txt")); int ch; while((ch = reader.read())!= -1){ System.out.println((char) ch); } reader.close(); } }
2.4 字符流读写的方式
写的方式
-
void
write(char[] cbuf, int off, int len)
写入字符数组的一部分。void
write(int c)
写一个字符void
write(String str, int off, int len)
写一个字符串的一部分。 -
void
write(String str)
写一个字符串 -
void
write(char[] cbuf)
写入一个字符数组。
刷新和关闭
-
void
close()
关闭流,先刷新。void
flush()
刷新流。
读的方式
-
int
read()
读一个字符int
read(char[] cbuf, int offset, int length)
将字符读入数组的一部分。 -
int
read(char[] cbuf)
将字符读入数组。
使用字符流完成文件的复制
public class RWDemo2{
public static void main(String[] args) throws IOException {
Reader r = new InputStreamReader(new FileInputStream("day_13/dir/窗里窗外.txt"));
Writer w = new OutputStreamWriter(new FileOutputStream("day_13/dir/aa.txt"));
char[] buff = new char[1024];
int len;
while((len = r.read(buff)) != -1){
w.write(buff,0,len);
}
r.close();
w.close();
}
}
字符流自带缓冲区
2.5 字符流操作的便捷类
FileReader 是InputStreamReader的简洁形式
FileWriter 是OutputStreamReader的简洁形式
public class RWDemo3 {
public static void main(String[] args) throws IOException {
Reader r = new FileReader("day_13/src/cn/lanqiao/charac/RWDemo2.java");
Writer w = new FileWriter("day_13/dir/demo.java");
char[] cbuff = new char[1024];
int len;
while((len=r.read(cbuff))!= -1){
w.write(cbuff,0,len);
}
r.close();
w.close();
}
}
2.6 字符缓冲流
BufferedReader
-
从字符输入流读取文本,缓冲字符,以提供字符,数组和行的高效读取。可以指定缓冲区大小,或者可以使用默认大小。
BufferedReader(Reader in)
创建使用默认大小的输入缓冲区的缓冲字符输入流。-
-
String
readLine()
读一行文字。
-
-
BufferedWriter
-
将文本写入字符输出流,缓冲字符,以提供单个字符,数组和字符串的高效写入。
可以指定缓冲区大小,或者可以接受默认大小。
BufferedWriter(Writer out)
创建使用默认大小的输出缓冲区的缓冲字符输出流。 -
void
newLine()
写一行行分隔符。
public class RWDemo4 {
public static void main(String[] args) throws IOException {
Reader r = new FileReader("day_13/src/cn/lanqiao/charac/RWDemo2.java");
Writer w = new FileWriter("day_13/dir/demo1.java");
BufferedReader br = new BufferedReader(r);
BufferedWriter bw = new BufferedWriter(w);
String line;
while((line = br.readLine())!=null){// 每次读取一行 当为null的时候 说明到达文件末尾
bw.write(line);
bw.newLine();//写行分隔符
bw.flush();
}
br.close();
bw.close();
}
}
2.7 IO流小结
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-w4QRODti-1627558360018)(assest/image-20210729105134952.png)]
字节流可以赋值文件数据,有四种方式一般采用字节缓冲流一次去屑一个字节数组的形式。
2.8 练习:
点名器:
我有一个文件里面存储了班级同学的姓名,每个姓名占一行,要求通过程序实现随机点名器
思路:1 使用字符缓冲输入流 特有的方法 readLine()
2 将每一个同学姓名作为List集合的一个元素
3 采用Random产生一个随机数 (1–28);
4 获取随机数所对应的姓名。
import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
public class RandomSelect {
public static void main(String[] args) throws IOException {
//创建缓冲字符输入流对象
BufferedReader reader = new BufferedReader(new FileReader("day_13\\namelist.txt"));
//定义list数组存放读取的名字
List<String> names = new ArrayList<>();
//定义字符串保存每次读取的名字
String name;
//使用缓冲字符流特有的readline方法 每次读取一个名字,并存入list中,当读取结束,即读取到的字符串为空时,结束
while((name = reader.readLine()) != null){
names.add(name);
}
System.out.println(names);
//创建Random对象 生成随机数
Random random = new Random();
int select = random.nextInt(names.size());
//输出这个数对应索引的名字
System.out.println(names.get(select));
}
}
案例: 复制单级目录
需求:把D://IO这个文件夹复制到E盘
分析:
1 数据源是 D:\IO
2 目的地:E:
3 判断数据源是目录还是文件
是文件 直接复制 字节流
是目录
在目的地创建该目录
遍历源目录中的所有的文件 获得File数组,得到的每一个File对象 在回到3继续 (递归)
public static void main(String[] args) throws IOException {
// 创建数据源目录的File对象
File srcDir = new File("day_13/dir");
//获取数据源目录的名称
String srcDirName = srcDir.getName();
// 创建目的地File对象
File destDir = new File("E:\\",srcDirName);
//判断目录是否存在
if(!destDir.exists()){
destDir.mkdirs();
}
// 遍历源目录 得到File数组
File[] listFiles = srcDir.listFiles();
//遍历数组
for(File srcFile : listFiles){
//获取列表中文件的名称
String srcFileName = srcFile.getName();
// 创建目的地文件的File对象
File destFile = new File(destDir,srcFileName);
//复制文件
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(srcFile));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(destFile));
byte[] buff = new byte[1024];
int len;
while((len = bis.read(buff))!= -1){
bos.write(buff,0,len);
}
bis.close();
bos.close();
}
}
案例: 复制多级目录
public static void main(String[] args) throws IOException {
// 创建数据源目录的File对象
File srcDir = new File("D:\\IO");
// 创建目的地File对象
File destDir = new File("E:\\");
copyDir(srcDir,destDir);
}
public static void copyDir(File srcFile,File destFile) throws IOException {
// 判断 数据源是目录还是文件
if(srcFile.isDirectory()){
// 在目的地创建目录
String srcFileName = srcFile.getName();
File newDilr = new File(destFile,srcFileName);
//判断目的地是否存在该目录
if(!newDilr.exists()){
newDilr.mkdirs();
}
//获取数据源下的所有的File列表
File[] listFiles = srcFile.listFiles();
// 遍历列表
for(File file : listFiles){
copyDir(file,newDilr);
}
}else{
//说明此时File对象表示的是文件 直接使用字节流复制
File newFile = new File(destFile,srcFile.getName());
//复制文件
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(srcFile));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(newFile));
byte[] buff = new byte[1024];
int len;
while((len = bis.read(buff))!= -1){
bos.write(buff,0,len);
}
bis.close();
bos.close();
}
}
练习:删除多级目录 删除其中所有的子目录及文件 包括IO根目录都要删除(删除无用目录 因为删除不经过回收站,删除之后 不可找回)
3 标准输入输出流
-
static PrintStream
err
“标准”错误输出流。static InputStream
in
“标准”输入流。 默认指的键盘static PrintStream
out
“标准”输出流。 默认是屏幕
3.1.标准输入流
public static void main(String[] args) throws IOException {
//字节流
InputStream is = System.in;// 字节输入流的数据来源来自标准输入设备键盘
// int by;
// while((by = is.read())!= -1){
// System.out.println((char)by);
// }
// 将字节流转换为字符流
InputStreamReader isr = new InputStreamReader(is);//字符流的数据来源来自标准输入设备
BufferedReader br = new BufferedReader(isr);
System.out.println("请输入一个字符串:");
String line = br.readLine();
System.out.println("您输入的字符串为:" + line);
// 让用户输入一个整数
System.out.println("请输入一个整数:");
int i= Integer.parseInt(br.readLine());
System.out.println("您输入的整数为:"+(i + 1));
//以上代码就是自己实现了键盘录入字符串和整数的方法 这样写起来有点麻烦,因此Java提供了
Scanner sc = new Scanner(System.in);
}
3.2. 标准输出流
public static void main(String[] args) throws IOException {
//PrintStream 本质就是字节输出流
PrintStream ps = System.out;
ps.println("hello");
ps.println(100);
}
4 打印流
打印流分为:
- 字节 打印流 PrintStream
- 字符打印流 PrintWtriter
打印流的特点:
只负责数据的输出 不能读取数据
有一些特有的方法
public static void main(String[] args) throws FileNotFoundException {
PrintStream ps = new PrintStream("day_13/dir/ps.txt");
ps.println("aaaa");
ps.println(23);
ps.println(true);
//释放资源
ps.close();
}
public static void main(String[] args) throws FileNotFoundException {
PrintWriter pw = new PrintWriter("day_13/dir/pw.txt");
pw.print("hello");
pw.println("aaaaaaaaaaaaaaaaaa");
pw.close();
}
使用打印流实现文件的复制
public class PrintStreamDemo {
public static void main(String[] args) throws IOException {
// 创建一个字符输入流
BufferedReader br = new BufferedReader(new FileReader("day_13/dir/demo.java"));
// 创建一个字符输入流
// BufferedWriter bw = new BufferedWriter(new FileWriter("day_13/dir/pw.java"));
PrintWriter pw = new PrintWriter(new FileWriter("day_13/dir/pw.java"));
//读写数据
String line;
while((line =br.readLine())!=null){
pw.println(line);
}
br.close();
pw.close();
}
5 对象的序列化流
对象序列化: 就是将对象保存到磁盘或者在网络中传输对象 为了对象保存的正确性 和传输的安全性,需要对对象进行编码处理,那么把这种处理方式称为对象的序列化
反序列化:将序列化的对象解析为原对象的过程 就称为反序列化
-
ObjectOutputStream
-
ObjectInputStream
对于对象传输 对象必须实现java.io.Serializable接口
public static void main(String[] args) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("day_13/dir/oos.txt"));
//创建学生对象
Student stu1 = new Student("张三",22);
oos.writeObject(stu1);//写出一个对象
oos.close();
}
反序列化
public static void main(String[] args) throws IOException, ClassNotFoundException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("day_13/dir/oos.txt"));
//创建学生对象
Student stu1 = new Student("张三",22);
oos.writeObject(stu1);//写出一个对象
oos.close();
// 反序列化
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("day_13/dir/oos.txt"));
Object obj = ois.readObject();
Student stu = (Student) obj;
System.out.println(stu);
}
6 Porperties
Porperties是Map集合的一个实现类
Porperties可以保存到流中或者从流中加载。 属性列表中的键及其对应的值都是字符串
public static void main(String[] args) {
Properties prop = new Properties();
//存储元素
prop.put("0001","张三");
prop.put("0002","李四");
prop.put("0003","王五");
// 遍历集合
Set<Map.Entry<Object,Object>> set = prop.entrySet();
for(Map.Entry<Object,Object> entry : set){
System.out.println(entry.getKey() +"--"+ entry.getValue());
}
}
Porperties和IO流结合的方法
-
void
load(InputStream inStream)
从输入字节流读取属性列表(键和元素对)。void
load(Reader reader)
以简单的线性格式从输入字符流读取属性列表(关键字和元素对)。 -
void
store(OutputStream out, String comments)
将此属性列表(键和元素对)写入此Properties
表中,以适合于使用load(InputStream)
方法加载到Properties
表中的格式输出流。void
store(Writer writer, String comments)
将此属性列表(键和元素对)写入此Properties
表中,以适合使用load(Reader)
方法的格式输出到输出字符流。
public class ProPertiesDemo {
public static void main(String[] args) throws IOException {
// myLoad();
myStore();
}
public static void myLoad() throws IOException {
Properties prop = new Properties();
Reader r = new FileReader("day_13/dir/test.properties");
prop.load(r);
r.close();
System.out.println(prop.get("username"));
}
public static void myStore() throws IOException {
Properties prop = new Properties();
prop.setProperty("001","张三");
prop.setProperty("002","李四");
prop.setProperty("003","王五");
prop.setProperty("004","admin");
FileWriter fw = new FileWriter("day_13/dir/test.properties");
prop.store(fw,"这是存储一些对象");
fw.close();
}
}
练习:游戏次数
需求:请编写程序实现猜数字小游戏,只能试完3次,如果达到3次,还想继续玩,提示:游戏试完已结束,想继续,请充值(www.lanqiao.cn)
分析;
1 编写一个游戏类,里面存储一个猜数字的小游戏。
2 写一个测试类,在测试类中调用猜数字游戏
3 玩过几次游戏,记录次数,用文件保存,而且文件应该一开始就提供,初始次数0
4 每次玩的时候,先从文件中读取,看看你玩了几次
如果达到3次 给出提示
如果没有达到3次 把次数+1,重新写会文件,开始玩游戏
public class GueesNumberTest {
public static void main(String[] args) throws IOException {
//从文件中读取数据到Properties集合
Properties prop= new Properties();
FileReader fr = new FileReader("day_13/dir/game.properties");
prop.load(fr);
fr.close();
//获取玩过的次数
String count = prop.getProperty("count");
int number = Integer.valueOf(count);
if(number > 3){
System.out.println("游戏试完结束,请充值(www.lanqiao.cn)");
}else{
GuessNumber.start();
number++;
prop.setProperty("count",String.valueOf(number));
FileWriter fw = new FileWriter("day_13/dir/game.properties");
prop.store(fw,null);
fw.close();
}
}
}