一:IO流
IO流概述
IO流分类
IO流是一块很大的知识体系,让我们一鼓作气学完吧。我认为要真正的学好一个知识点,清晰的框架是必不可少的。那么这里我们就先认识一下IO流体系。
总括:IO流体系
首先,我们先通过一张图片去认识IO流体系:
怎么样?是不是清晰明了,循序渐进。可见IO流大体分为两种流:字节流和字符流.
字节流可分为:字节输入流(InputStream)和字节输出流(OutputStream)。
字符流可分为:字符输入流(Reader)和字符输出流(Writer)。
以上的InputStream、OutputStream、Reader、Writer都是抽象类,以下的几种流:文件流(File)、缓冲流(Buffered)、字符转换流[(Input/Out)Stream(Reader/Writer)]、对象字节流(Object)、打印流(Print)都相当于它们四个的实现类。
接下来我们将会依次介绍这些流:
1.1 文件流
1.1.1文件字节流
文件字节输入流
实例:
(1)读取字节:
(1)读取字节:
/*1. 创建一个 文件字节输入流管道 与 源文件 接通 */
//InputStream is=new FileInputStream(new File(".\\Phase2\\resource\\data01.txt"));
//简化写法:
InputStream is=new FileInputStream(".\\Phase2\\resource\\data01.txt");
/*2. 读取一个字节返回(读取完毕返回-1 !!!)*/
int b1=is.read(); //read() 每次接一滴水
System.out.println("第一个字节的编码为:"+b1);
System.out.println("第一个字节为:"+(char)b1);
/*3. 使用循环改进,定义一个变量记录每次读取的字节
* w 2 国
* o o [ooo] -->几个字节(UTF-8) */
int b;
while( ( b=is.read() ) != -1){ //一次只读取一个字节,因此遇到三个字节的汉字会报错
System.out.print((char)b);
}
(2)读取字节数组:
(2)读取字节数组(读取完毕返回-1):
InputStream is=new FileInputStream(".\\Phase2\\resource\\data02.txt");
//data里有5个字节
//定义一个字节数组,用于读取字节数组
byte[] buffer =new byte[3];//每次读取3个字节(1个桶每次装3滴水)
int len1=is.read(buffer);
System.out.println("第一次读取了几个字节:"+len1);
String rs1=new String(buffer);
System.out.println(rs1);
int len2=is.read(buffer);//这里只剩余2个字节了,打印出来的第3个字节是上一次读取的第3个字节
System.out.println("第三次读取了几个字节:"+len2);
String rs2=new String(buffer,0,len2);//但是,可以改进!!! 0的意思是从第1位开始读取,每次读 len3 个字节
System.out.println(rs2);
//改进使用循环,每次读取一个字符数组
byte[] buffer2=new byte[3];
int len;
while( (len=is.read(buffer2)) !=-1){
System.out.print(new String(buffer2,0,len));
}
(3)读取全部字节:
public static void main(String[] args) throws IOException {
(3)一次性读取所有字节
/* 方式1: 自定义字节数组*/
//1.创建一个 文件字节输入流管道 与 源文件 接通
File f=new File(".\\Phase2\\resource\\data03.txt");
InputStream is=new FileInputStream(f);
//2.定义一个字节数组与文件的大小一样大
byte[] buffer=new byte[(int) f.length()];
int len=is.read(buffer);
System.out.println("读取的字节为:"+len);
System.out.println(new String(buffer));
/*方式2: 用API读取字节数组*/
byte[] buffer2=is.readAllBytes();
System.out.println(new String(buffer));
}//main方法的括号
文件字节输出流
实例:
public static void main(String[] args) throws IOException {
/*1. 创建一个 文件字节输出流管道 与 目标文件 接通*/
//加true,不覆盖管道(也就是每次运行不清除之前的数据)
//OutputStream os=new FileOutputStream(".\\Phase2\\resource\\out01.txt",true);
OutputStream os=new FileOutputStream(".\\Phase2\\resource\\out01.txt");
/*2. 写数据出去*/
//2.1 写一个字节出去
os.write('x');
os.write("\r\n".getBytes());(Charset编码API)
//1.回车换行(换行) 2.兼容性更强 3.这里需要把"/r/n"字符串转换成字节数组再输出
//2.2 写一个字节数组出去
byte[] buffer1={101,102,103};
os.write(buffer1);
byte[] buffer2="我是中国人".getBytes();
//byte[] buffer2="我是中国人".getBytes("GBK"); 用GBK进行编码
os.write(buffer2);
byte[] buffer3={'a','b',99,100};
os.write(buffer3,0,3);//写一个数组的一部分出去
/*3. 刷新/关闭 流*/
//os.flush(); 写数据,必须刷新数据,可以继续使用流
os.close();//可以释放资源,包含了刷新!!!关闭了流后不可以继续使用
1.1.2 文件字符流
文件字符输入流
实例:
public static void main(String[] args) throws IOException {
(1)一次读取一个字符
/*1. 创建一个 字符输入流管道 与 源文件 接通*/
Reader fr=new FileReader(".\\Phase2\\resource\\data04.txt");
/*2. 读取一个字符返回,没有可读的字符则返回-1*/
int code1=fr.read();
System.out.println((char)code1);
/*3. 定义循环读取字符*/
int code;
while ( ( code=fr.read() ) !=-1 ) {
System.out.print((char)code);//不换行
}
(2)一次读取一个字符数组
char[] buffer=new char[1024];//1k字符
int len;
while ( (len=fr.read(buffer) ) !=-1 ){
String rs=new String(buffer,0,len);
System.out.println(rs);
}
}
文件字符输出流
实例:
public static void main(String[] args) throws IOException {
/*1. 创建一个 字符输出流管道 与 目标文件 接通*/
//Writer fw=new FileWriter(".\\Phase2\\resource\\out02.txt");·//覆盖管道,每次启动都会清空之前的数据
Writer fw=new FileWriter(".\\Phase2\\resource\\out02.txt",true);//继续写
//2.写一个字符出去
fw.write(98);
fw.write('a');
fw.write('赵');//汉字不会出问题了
fw.write("\r\n"); //换行
//3.写一个字符串出去
fw.write("abc清澈的爱为中国");
fw.write("abc清澈的爱为中国",3,7);//从第3个字节开始,写7个字节
//4.写一个字符数组出去
char[] chars="abc清澈的爱为中国".toCharArray();
fw.write(chars);
fw.write(chars,3,7);
//5.
//fw.flush(); //刷新流后,还可以继续使用
fw.close(); //关闭流后,不能再继续使用
}
补充:try-catch-finally和try-catch-resource
try-catch-finally:
try-catch-resource:
1.2 缓冲流
1.2.1 文件字节缓冲流
文件字节缓冲输入/输出流
实例:
public static void main(String[] args) {
//1. 创建一个文件输入流管道 与 原图片 相通
try( /*try() (括号里面只能放资源对象)无视异常,强行关闭资源*/
InputStream is = new FileInputStream("D:\\JavaPoint\\Fighting.jpg");//必须有后缀
InputStream bis= new BufferedInputStream(is);
/*把原始的 字节输入流 包装成 高级的缓冲字节输入流*/
//2. 创建一个字节输出流管道 与 目标文件接通
OutputStream os = new FileOutputStream("D:\\JavaPoint\\BufferedFighting.jpg");
OutputStream bos=new BufferedOutputStream(os);/*把原始的 字节输出流 包装成 高级的缓冲字节输出流*/
){
//3. 定义一个 字节数组 转移数据
byte[] buffer = new byte[1024];
int len; //记录每次读取的字节数
while ((len = bis.read(buffer)) != -1) {
bos.write(buffer, 0, len);
}
System.out.println("图片复制完成了");
//try-catch-resorce已经关闭流了,不用再释放资源了
} catch (Exception e) {
e.printStackTrace();
}
}
1.2.2 文件字符缓冲流
文件字符缓冲输入流
实例:
try {
Reader fr=new FileReader("D:\\idea\\HM\\Phase2\\resource\\data05.txt");
BufferedReader br=new BufferedReader(fr);//这里要使用BufferedReader的特有功能
br.readLine();//读一行字符,空行也读,直到读完返回null
String line;
while( (line = br.readLine()) !=null ){
System.out.println(line);
}
} catch (Exception e) {
e.printStackTrace();
}
文件字符缓冲输出流
实例:
public static void main(String[] args) throws IOException {
/*1. 创建一个 字符输出流管道 与 目标文件 接通*/
Writer fw=new FileWriter(".\\Phase2\\resource\\out02.txt",true);//不覆盖管道
//2.写一个字符出去
fw.write(98);
fw.write('a');
fw.write('赵');//汉字不会出问题了
fw.write("\r\n"); //换行
//3.写一个字符串出去
fw.write("abc清澈的爱为中国");
fw.write("\r\n");
fw.write("abc清澈的爱为中国",3,7);//部分
//4.写一个字符数组出去
char[] chars="abc清澈的爱为中国".toCharArray();
fw.write(chars);
fw.write(chars,3,7);
//5.
//fw.flush(); //刷新流后,还可以继续使用
fw.close(); //关闭流后,不能再继续使用
}
小结:字符缓冲流
1.3 字符转换流
转换流其实就是把字符流转换成字节流。字节流因为是一个字节一个字节读取的,不会出bug,也就没有字节转换流。
1.3.1字符转换输入流
为什么要使用字符转换输入流?
实例:
代码UFF-8 文件 GBK("D:\idea\HM\Phase2\resource\GBK_data.txt")
public static void main(String[] args) throws Exception {
/*1. 提取GBK文件的原始文件字节流*/
InputStream is=new FileInputStream(".\\Phase2\\resource\\GBK_data.txt");
/*2. 把 原始文件字节流 转换成 字符输入流 */
Reader isr=new InputStreamReader(is,"GBK");//以指定的GBK编码转换成字符输入流,完美的的解决了乱码问题
BufferedReader br=new BufferedReader(isr);
String line;
while( (line=br.readLine()) !=null ){
System.out.println(line);
}
}
1.3.2 字符转换输出流
为什么要使用字符转换输出流?
实例:
public static void main(String[] args) throws IOException {
/*1. 定义一个字节输出流*/
OutputStream os=new FileOutputStream(".\\Phase2\\resource\\out04.txt");
/*2. 把原始的字节输出流 转换成 字符输出流*/
Writer osw=new OutputStreamWriter(os);
//以默认的UTF-8写字符出去,直接写跟FileWriter一样
/*3. 把低级的字符输出流 包装成 高级的缓冲字符串输出流 */
BufferedWriter bw=new BufferedWriter(osw);
bw.write("我爱中国1");
bw.write("我爱中国2");
}
1.4 对象字节流
对象序列化与反系列化
序列化是指把对象数据存储到文件中。
反序列化:
实例:序劣化的三个重点
1.对象要序列化,必须实现Serializable接口
2.申明序列化的版本号码(序列化与反序列化的版本号必须一致才不会报错!!!)
3.transient 修饰的成员变量不参与序列化
public class serializable { //序列化
public static void main(String[] args) throws Exception {
/*1. 创建学生对象*/
Student s1=new Student("山姆","sam","123",34);
/*2. 对象序列化:使用 对象字节输出流管道 包装 低级的文件字节输出流管道*/
ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream(".\\Phase2\\resource\\obj.txt"));
/*3.直接调用序列化方法*/
oos.writeObject(s1);
oos.close();//关闭资源
//=================反序列化================(序列化和反序列化操作路径一致)
/*1. 创建 对象字节输入流管道 包装 低级的文件字节输入流*/
ObjectInputStream ois =new ObjectInputStream(new FileInputStream(".\\Phase2\\resource\\obj.txt"));
/*2. 调用对象字节输入流的反序列方法*/
Student s2=(Student) ois.readObject();
System.out.println(s2);
}
}
class Student implements Serializable {
//1.对象要序列化,必须实现Serializable接口
private static final long serialVersionUID=1;
//2.申明序列化的版本号码(序列化与反序列化的版本号必须一致才不会报错!!!)
private String name;
private String loginName;
private transient String password;//3.transient 修饰的成员变量不参与序列化
private int age;
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", loginName='" + loginName + '\'' +
", password='" + password + '\'' +
", age=" + age +
'}';
}
public Student() {
}
public Student(String name, String loginName, String password, int age) {
this.name = name;
this.loginName = loginName;
this.password = password;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getLoginName() {
return loginName;
}
public void setLoginName(String loginName) {
this.loginName = loginName;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
1.5 打印流
实例:
一个路径只能创建一个打印流对象。
public static void main(String[] args) throws Exception {
/* 创建一个打印流对象 */
PrintStream ps1=new PrintStream(new FileOutputStream(".\\Phase2\\resource\\ps1.txt"));
//使用PrintWriter也行
ps1.println("我是打印流,可以打印任何类型的数据");
ps1.close();
}
补充:重定向
//改变输入语句的位置(重定向)
PrintStream ps3=new PrintStream(".\\Phase2\\resource\\ps3.txt");
System.out.println("我在重定向前面,还是会打印到控制台");
System.setOut(ps3);//把系统的打印流改成我们自己的打印流
System.out.println("我是我们自己的打印流,不会打印到控制台,而是会打印到文件中");
二:Properties
2.1 Properties的归属及作用
2.2 Properties与IO流结合
这便是为什么我们要在IO流这里讲Properties的原因。其中load和store方法是我们着重要掌握的。
实例:
public static void main(String[] args) throws IOException {
Properties properties=new Properties();//1.实质上就是一个Map集合,但是可以与IO流结合
System.out.println(properties);
properties.setProperty("jack","1314");
properties.setProperty("rose","520");
System.out.println(properties);
//2. 把键值文件存到属性文件中
properties.store(new FileWriter("./Phase2/resource/user.properties"),
"我是注释:看到了v我50");
//参数一:保存管道(文件路径) 参数二:文件保存注释
//3.加载属性文件中的键值对信息数据到属性对象properties中去
properties.load(new FileReader("./Phase2/resource/user.properties"));
System.out.println(properties);
String rs=properties.getProperty("jack");//4.获取键的值
System.out.println(rs);
}
三:commons-io类库
3.1 导入commons-io jar包
3.2 commons-io概述
3.3 commons-io API
实例:
以下所有 " " 中应填的东西是路径,这里省略。
public static void main(String[] args) throws IOException {
(1)IOUtils类
//完成 文件复制
IOUtils.copy(new FileInputStream(""),new FileOutputStream(""));
(2)FileUtils类
//1.完成 文件 复制到 文件夹 下
FileUtils.copyFileToDirectory(new File(""),new File(""));
//2.完成 文件夹 复制到 文件夹下
FileUtils.copyDirectoryToDirectory(new File(""),new File(""));
//3..删除文件夹(!!!)
FileUtils.deleteDirectory(new File(""));
(3)java 自己提供的完成代码复制的操作(New IO的技术):
Files.copy(Path.of(""),Path.of(""));
}