Io流
1. IO流原理
1.1 IO流理解
IO:Input 和 Output
IO流主要是针对于内存来作为参照物来理解的:
- 内存拿硬盘中的文件就是输入流,过程也叫做读(inputStream)
- 内存的数据文件拿出去给硬盘就是输出流,过程也叫写(outStream)
- IO管道中传输的就是数据文件
1.2 IO分类
分类原则:
- 按照流的方向进行分类
- 按照字节来读取
- 按照字符来读取
1.2.1 按照流的方向进行分类
以内存为参照物,往内存存数据叫做输入or读,从内存中出来叫做输出or写
1.2.2 按照读取数据方式分类
2.2.1 按照字节
万能流(可以读取视频、文本文件、图片等)
Stream结尾
概念:
1、可以直接操作字节信息的流对象
2、根据流向,可以分为:字节输入流和字节输出流
3、顶层抽象父类分别是:InputStream 和 OutputStream
4、根据不同的交互设备,有不同的具体子类
方法:
1、int available();返回剩余的可以读取的字节数
- 解释:就是文件中还没有被读取的字节有多少就会输出他们的总数
- 例:一个全新文件,里面数据的字节数一共有12个,也就是长度为12,一次都没有被读取过,
2、int
2.2.2 按照字符
Reader结尾:输入流
Writer结尾:输出流
只能读取txt文件
注意事项:
字符流的本质其实还是一个字符数组,当拿到一个字符信息的时候,会将其先转换成对应的字符数组,然后对字符数组进行对应的操作,操作过后还需要再将其字符数组再转一次转成字符串,其实感觉好像没有太大的必要,啊哈哈,相反还麻烦了,不如直接用字节
1.3 Java.IO流四大家族
- java.io.InputStream
- java.io.OutputStream
- java.io.Reader
- java.io.Writer
1.4 IO流的使用
1、new一个对象
2、使用对象去调用方法
3、流使用完后一定要关闭流, 对象 . close()
4、在使用OutputStream 和 writer的时候,一定要记得flush
5、flush作用:
- flush就是将管道内未输出完的一个数据进行强制的输出,将其输出完
- 不试用flush,可能会导致丢失数据
1.5 IO 字节流读文件入门案例
1.5.1 最简单的案例
package com.xiaowang;
import org.junit.Test;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
/**
* @Author 小王
* @DATE: 2022/8/2
*/
@SuppressWarnings("all")
public class IoStreamTestOne {
/*搭建一个文件IO输入流框架
* 1.先创建一个输入流对象
* 2.使用输入流对象调方法(这里可能会有异常,要进行处理)
* 3.然后要进行输入流对象的关闭
* */
@Test//单独一个一个字节的读取
public void Test(){
FileInputStream fis = null;
try {
fis = new FileInputStream("hello.txt");//传入要执行文件的路径,这里有异常要 处理一下
System.out.println(fis.read());//直接读取文件,按照一个字节一个字节的来找,因为是字节所以会是返回字节码
//要是要读取完全部文件就必须调文件中指定长度的个数
//当读取完了后,后面要是没有数据了,那么就会返回-1
System.out.println(fis.read());
System.out.println(fis.read());//注意:这里read读到的是文件里面的ASSIC码
System.out.println(fis.read());
System.out.println(fis.read());
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fis != null){
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
@Test//用循环对多个字节的读取
public void Test1(){
//创建一个输入流对象
FileInputStream fis1 = null;
try {
fis1 = new FileInputStream("hello.txt");
//因为要是数据没有了返回的是-1,所以我们可以通过去判断读取到的数据是不是-1来进行循环,这样就可以减少输出代码量
while (true){//进行循环
int read = fis1.read();//用一个变量去存读取到的数据
if (read==-1){//当读到的数据没有了的时候,就会返回-1去提醒你已经读完了,所以这个时候就可以结束循环了
break;
}
System.out.println(read);//然后输出即可
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fis1 != null){
try {
fis1.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
@Test//简写循环
public void Test3(){
FileInputStream fis =null;
try {
fis = new FileInputStream("hello.txt");
int read = 0;
while ((read = fis.read())!=-1){
System.out.print(read+"\t");
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fis !=null){
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
1.5.2 基于byte数组的案例
package com.xiaowang;
import org.junit.Test;
import org.omg.CORBA.PUBLIC_MEMBER;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
/**
* @Author 小王
* @DATE: 2022/8/2
*/
/*使用一个byte数组去输出文件数据
* 1.先创建一个对象
* 2.异常处理
* 3.finally里面关闭对象输入流
* 4.创建数组,存要读入的数据字节个数
* 5.对象调用read(bytes)方法,传入bytes这个参数,目的是将读取的数据存入到数组中,这样可以一次读取多个
* 6.调用read(bytes)方法后,返回的是实际要读入的字节个数,而不是具体数据
* 注意:如果文件字节数为8,数组长度定义为5,每一次调用情况:
* 6.1:第一次:read(bytes) == 5;
* 6.2:第二次:read(bytes) == 3;
* 所以得出结论:read(bytes)返回的是实际具体获得的
* 7.使用字符串new String()去输出具体的数据
* */
public class IoStreamTestByte {
@Test//单次
public void Test(){
FileInputStream fis = null;
try {
fis = new FileInputStream("hello.txt");
byte[] bytes = new byte[5];//创建一个byte数组,主要就是用来
int read = fis.read(bytes);//注意这里read接收的是世纪获取到的字节数,最多为数组长度
System.out.println(read);
//但是我们要的应该是输出具体数据类容,所以我们需要处理一下获取的字节,将其用字符输出
System.out.println(new String(bytes,0,read));
//读取的是bytes数组的,从数组索引为0的位置开始读取,读取长度为read,之间的数据
//这样也是只读取了数组长度个数,如果文件里还有,则还需要再读取一次,就会将后面剩下的继续读出来
read= fis.read(bytes);
System.out.println(read);
System.out.println(new String(bytes,0,read));
//要是很多的话也挺麻烦,所以我们可以将其用循环来输出
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
@Test//使用循环
public void Test1(){
FileInputStream fis = null;
try {
fis = new FileInputStream("hello.txt");
byte[] bytes = new byte[5];
int read = 0;
while ((read=fis.read(bytes))!=-1){
System.out.print(new String(bytes,0,read));
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fis!=null){
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
1.5.3 输入流的最终版
其实就是流对象+数组+循环
public void Test1(){
FileInputStream fis = null;
try {
fis = new FileInputStream("hello.txt");
byte[] bytes = new byte[5];
int read = 0;
while ((read=fis.read(bytes))!=-1){
System.out.print(new String(bytes,0,read));
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fis!=null){
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
1.5.3 总结
5.3.1 基本框架
/*使用一个byte数组去输出文件数据
- 1.先创建一个对象
- 2.异常处理
- 3.finally里面关闭对象输入流
- 4.创建数组,存要读入的数据字节个数
- 5.对象调用read(bytes)方法,传入bytes这个参数,目的是将读取的数据存入到数组中,这样可以一次读取多个
- 6.调用read(bytes)方法后,返回的是实际要读入的字节个数,而不是具体数据
- 注意:如果文件字节数为8,数组长度定义为5,每一次调用情况:
- 6.1:第一次:read(bytes) == 5;
- 6.2:第二次:read(bytes) == 3;
- 所以得出结论:read(bytes)返回的是实际具体获得的
- 7.使用字符串new String()去输出具体的数据
- */``
5.3.2 注意事项
-
关闭时,判断null是判断对象是不是空
读取到最后没有数据返回-1 -
-1是没有数据了的提示标识
-
数组读取的时候:
3.1 read返回的是读取到的实际字节数,
- 也就是要是数组长度大于文件数据字节长度,那么就输出具体谈的文件数据个数
- 要是数组长度等于文件里面数据字节长度,那就输出全部字节数
- 要是数组长度小于文件里面数据字节长度,那就输出数组能存的字节数数量,相当于数组长度
3.2、再使用String去转换成字符串
- new String(byte,0,count)//byte是数组,0是从哪开始,count是转换个数
1.6 字节流IO 写文件入门案例
1、new对象的时候,加上true原文件内容就不会被覆盖
步骤框架和读文件其实差不多,只不过就是创建的对象类型不一样,然后因为是写操作,需要有flush的刷新操作。
package com.xiaowang;
import org.junit.Test;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
/**
* @Author 小王
* @DATE: 2022/8/2
*/
/*字节流的写入
* 1.创建对象,处理异常,finally执行close
* 2.try里面最后要有flush操作
* 3.try里面创建对象的时候,如果要保留原文件有的信息,就要在后面加true,如果不写true,就会被覆盖
*
* */
// @SuppressWarnings("all")
public class IOOutStreamTest {
public static void main(String[] args) {
FileOutputStream fos = null;
try {
//这里在后面加上true,就是为了保存文件里面之前还存在的数据,如果不加上true,那么里面的数据会被覆盖
fos = new FileOutputStream("hello.txt", true);
//创建一个要写入的字节数组,然后将其数据存进去
byte [] bytes = {97,98,99,100,101};
//写入bytes数组
fos.write(bytes);
//因为字节流是万能的,什么类型都可以传,那我们试试写入一个字符串
String str = "小王";
//因为是字节,所以要将其先转换为字节数组
byte[] bytes1 = str.getBytes();
//转换为byte数组后,就阔以直接进行写入了
fos.write(bytes1);
fos.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fos != null){
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
1.7 文件copy
也就是将一个盘里的东西复制到另外个盘里面,也就是平时用的ctrl+c 和ctrl+v
案例:
package com.xiaowang.filecopy;
import java.io.*;
/**
* @Author 小王
* @DATE: 2022/8/2
*/
/*对文件进行copy(一边读一遍写)
* 1.先创建文件输入输出流,把一般框架搭好(也就是try-catch啥的)
* 2.在finally位置要记得将输入流和输出流的关闭给分开去try-catch,这样就不会使其相互影响
* 3.关闭过后要记得在try里面将写的操作进行flush刷新操作,记得后面代码要写在这句前面哈
* 4.创建一个字节数组,定义一个int长度为0,这是用来后面读的时候接收读取的字节数的总字节数
* 5.开始进行读的操作,判断里面要记得判断条件是,读取的时候有有效数据字节,也就是不能为-1
* 6.能成功读到,那就直接进行写的操作,使用writer方法去写fos.write(bytes,0,读取到的字节个数);
* */
public class IOFileCopy {
public static void main(String[] args) {
FileInputStream fis =null;
OutputStream fos = null;
try {
//创建对象
fis = new FileInputStream("hello.txt");
fos = new FileOutputStream("hello1.txt");
byte[] bytes = new byte[1024 * 1024];
//读:
int readCount = 0;
while ((readCount=fis.read(bytes))!=-1){//读操作
//成功读取了就同时开始写
fos.write(bytes,0,readCount);
}
//因为有写操作,所以要刷新一下
fos.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fis != null){
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}if (fos !=null){
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
1.8 字符流
字符流和字节流的区别就是,字节流里面的数组是byte类型的,字符流里面的是char类型的,其他的都一样,这里直接用字符流的copy来进行讲解
package com.xiaowang.iozifu;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
/**
* @Author 小王
* @DATE: 2022/8/2
*/
public class IOReadCopyWriter {
public static void main(String[] args) {
//创建两个对象
FileReader fr = null;
FileWriter fw = null;
try {
//实例化两个流对象
fr = new FileReader("hello.txt");
//如果要写入的文件不存在,会自动创建一个文件,true是为了保证文件中原有的数据不会被覆盖
fw = new FileWriter("Hello2.txt",true);
//定义字符数组,用来暂存数据
char [] chars = new char[5];
//定义变量来记录要读入or写入的个数
int charsCount = 0;
while ((charsCount=fr.read(chars))!=-1){//当读的时候,只要没有返回-1.就证明文件里面还有数据,还要继续读
fw.write(new String(chars,0,charsCount));//将读入的数据给写到另一个文件中去
}
fw.flush();//写的操作,一定要去刷新一下,防止数据丢失
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fr!= null){//确保读对象不为null,因为为null,就没必要读了,就不知道该读什么
try {
fr.close();
} catch (IOException e) {
e.printStackTrace();
}
}if (fw != null){//确保写对象不为null,因为为null,就没必要写入了,都不知道写啥了
try {
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
1.9 缓冲流
1.9.1 缓冲流介绍
- 自带缓存(自带的缓存是一个数组,每次执行read或者write方法的时候,会先一次性读取8192个字节,然后根据读写次数一次次的输出,就不用每读取一次就去内存里面再拿一次,当8192个字节都用完后,再进行下一个8192字节的读取)
- 不需要自定义char / byte数组(每一次的读取就会直接读取8192个字节在数组中)
1.9.2 节点流&处理流
- 节点流:当一个流的构造方法中需要一个流的时候,这个被传进来的流(参数),就是节点流
- 处理流:外部负责包装节点流的这个包装流,就是处理流,也叫包装流
注意:参数和构造类名都是流
1.9.3 缓冲流入门案例
1.9.3.1 字节缓存流
字节缓冲输入流过程:
- 1.创建bufferedInputStream,注意里面传的参数是InputStream类型的,一般都是FileInputStream
- 2.因为缓存流buffer自带了byte数组,所以就不需要自己定义byte数组去接收字节数然后输出
- 3.直接调用read方法读取数据,用一个int类型的变量去接收,这个int变量主要的作用是判断文件中还有没有数据,没有了就是-1
- 4.while循环中就进行int变量的赋值(通过调用文件的read方法)要判断是不是为-1
- 5.当while循环中一致满足不是-1的时候,就证明还有数据,直接输出即可
字节缓冲输出流过程:
- 1.先创建bufferedOutputStream,注意里面传的参数是OutputStream类型的,一般采用FileOutputStream
- 2.定义一个要写入的byte数组
- 3.直接通过缓冲流的write方法,将刚刚写入的byte数组给写进去即可
- 4.因为是输出流,所以要flush刷新一下
- 5.最后还得要关闭缓冲流
代码案例:
package com.xiaowang.iobuffered;
import org.junit.Test;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
/**
* @Author 小王
* @DATE: 2022/8/3
*/
/*
* 字节流案例总结:
* 字节缓冲输入流过程:
* 1.创建bufferedInputStream,注意里面传的参数是InputStream类型的,一般都是FileInputStream
* 2.因为缓冲流buffer自带了byte数组,所以就不需要自己定义byte数组去接收字节数然后输出
* 3.直接调用read方法读取数据,用一个int类型的变量去接收,这个int变量主要的作用是判断文件中还有没有数据,没有了就是-1
* 4.while循环中就进行int变量的赋值(通过调用文件的read方法)要判断是不是为-1
* 5.当while循环中一致满足不是-1的时候,就证明还有数据,直接输出即可
* 6.最后记得关闭缓冲流
*
* 字节缓冲输出流过程
* 1.先创建bufferedOutputStream,注意里面传的参数是OutputStream类型的,一般采用FileOutputStream
* 2.定义一个要写入的byte数组
* 3.直接通过缓冲流的write方法,将刚刚写入的byte数组给写进去即可
* 4.因为是输出流,所以要flush刷新一下
* 5.最后还得要关闭缓冲流
* */
public class BufferedTest2 {
@Test//字节缓存输入流(bufferedInputStream)读进来
public void Test() throws Exception{//先把异常抛了好看代码
/*//1.创建一个字节流,和缓存流
FileInputStream fileInputStream = new FileInputStream("hello.txt");
//注意:buffered的构造器里面传的参数,是对应buffer类型的字节输入流or字节输出流
BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream);*/
//一句话创建字节缓存输入流
BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream("hello.txt"));
//2.读文件,因为buffer自带了byte数组缓存,所以直接输出即可
int readCount = 0;
while ((readCount = bufferedInputStream.read())!=-1){
System.out.print(readCount+"\t");
}
//3.关闭缓存流
}
@Test//字节缓存输出流(BufferedOutStream)写出去
public void Test1() throws Exception{
//1.创建一个buffer字节输出流对象,直接就一句话搞定了,所以buffer里面的参数是OutputStram类型
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(new FileOutputStream("hello.txt"));
//3.定义一个要写入的数组
byte[] bytes = {98,99,100,101,102};
bufferedOutputStream.write(bytes);
//4.单个的写入
bufferedOutputStream.write(97);
//2.刷新缓存流,关闭缓存流
bufferedOutputStream.flush();
bufferedOutputStream.close();
}
}
1.9.3.2 字符缓冲流
字符缓冲输入流案例:
解析在代码中:
package com.xiaowang.iobuffered;
import org.junit.Test;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.InputStreamReader;
/**
* @Author 小王
* @DATE: 2022/8/3
*/
/*
* 字符缓冲流案例
* 字符缓冲输入流过程
* 1.创建对应的缓冲输入流,传入的类型记得是字符型的,如果不是字符型的要用转换流(InputStreamReader)去转换成字符流
* 2.定义一个接收读取到的信息的变量str去暂存读取的信息
* 3.通过多行or单个读取数据赋值给str,使用循环去全部读完
* 4.最后记得关闭缓冲流
* */
public class BufferedTest3 {
@Test//读的就是字符流的时候
public void Test() throws Exception{
//1.创建一个bufferedReader的字符缓冲流对象
BufferedReader bufferedReader = new BufferedReader(new FileReader("hello.txt"));
//2.定义一个String类型的变量来接收读取到的数据
//3.字符的读取,单独读取一个read(),一次性读取多行readline()
//4.读取一个的时候bufferedReader.read()
System.out.println(bufferedReader.read());
//5.读取多行,使用循环去判断是否还要读
String str = null;
while ((str = bufferedReader.readLine())!=null){
System.out.println(str);
}
//6.关闭缓冲流
bufferedReader.close();
}
@Test//读的是一个字节流
public void Test1() throws Exception{
//1.定义一个字节输入流
FileInputStream fileInputStream = new FileInputStream("hello.txt");
//2.将其转为字符流(这里就会涉及到转换流)
//InputStreamReader:将字节输入流转换成字符输入流
InputStreamReader inputStreamReader = new InputStreamReader(fileInputStream);
//3.创建缓冲流对象
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
//4.读取信息,按整行读取
String str = null;
while ((str = bufferedReader.readLine())!=null){
System.out.println(str);
}
//缓冲流关闭
bufferedReader.close();
}
}
字符缓冲输出流:
package com.xiaowang.iobuffered;
import org.junit.Test;
import java.io.*;
/**
* @Author 小王
* @DATE: 2022/8/3
*/
/*
* 情况1:传入的就是一个简单的字节流的时候
* 1.
* */
public class BufferedTest4 {
@Test//传入的就是字符输出流
public void Test() throws Exception {
BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter("hello.txt"));
String str = "a";
String str1 = "b";
String str2 = "c";
bufferedWriter.write(str);
bufferedWriter.write(str1);
bufferedWriter.newLine();//这个代表另起一行,也就是这句前面的就在一行,后面的和前面的换行了,要是没有这句就是都在一行
bufferedWriter.write(str2);
bufferedWriter.flush();
bufferedWriter.close();
}
@Test//传递字节流,将其转换成字符流在操作
public void Test2() throws Exception {
/*//分开创建对象去完成一个缓冲输出流
FileOutputStream fileOutputStream = new FileOutputStream("hello.txt");//先创建字节输出流
OutputStreamWriter outputStreamWriter = new OutputStreamWriter(fileOutputStream);//创建字节转换字符的转换流
BufferedWriter bufferedWriter = new BufferedWriter(outputStreamWriter);*///创建一个缓冲流
//总结一句话完成:
BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("hello.txt")));
bufferedWriter.write("小");
bufferedWriter.write("王");
bufferedWriter.write("吖");
//刷新缓冲流
bufferedWriter.flush();
//关闭缓冲流
bufferedWriter.close();
}
}
1.10 转换流
概念:就是将字节流和字符流之间的转化
- OutputStreamWriter:字符–>字节(主要是编码作用:utf-8 和 GBK 的原因)
- InputStreamReader:字节–>字符
1.10.1 编码
- GBK:国标码,定义的是英文字符和中文字符,在GBK编码表中,英文字符占一个字节,中文字符占两个字节
- UTF-8:万国码,定义了全球所有的符号,英文字符占一个字节,中文字符占三个字节
案例:
@Test//读的是一个字节流
public void Test1() throws Exception{
//1.定义一个字节输入流
FileInputStream fileInputStream = new FileInputStream("hello.txt");
//2.将其转为字符流(这里就会涉及到转换流)
//InputStreamReader:将字节输入流转换成字符输入流
InputStreamReader inputStreamReader = new InputStreamReader(fileInputStream);
//3.创建缓冲流对象
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
//4.读取信息,按整行读取
String str = null;
while ((str = bufferedReader.readLine())!=null){
System.out.println(str);
}
//缓冲流关闭
bufferedReader.close();
}
1.11 标准字节输出流
概念:
其实就是我们平时的sout方法(System.out.println(输出内容))
输出位置:
setOut()修改输出方向,不修改就是控制台,修改了就是对应位置
案例:
package com.xiaowang.sout;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintStream;
/**
* @Author 小王
* @DATE: 2022/8/3
*/
public class SoutTest {
public static void main(String[] args) {
//输出到控制台:
System.out.println("123");
System.out.println("ABC");
//输出到指定的文件里面
PrintStream printStream =null;
//注意:1、new FileOutputStream()构造器参数是一个OutputStream对象
// (可以理解为:是要在一个文件里面去输出内容,相当于就是将要输出的内容给写入这个文件)
// 2、标准输出流是不需要自己去关闭流的
try {
printStream = new PrintStream(new FileOutputStream("hello2.txt"));
System.setOut(printStream);//setOut()方法就是用来转换输出位置的
System.out.println("123");
System.out.println("ABC");
System.out.println("POI");
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
}
1.12 File文件操作
1、先实例化一个文件对象(文件名or路径,可以是多重)
2、文件对象 . exits()方法去查文件存不存在
3、不存在,就按照文件去创建
- createFile()创建一个文件
4、不存在,就按照目录去创建一个文件夹目录 - mkdir()创建文件夹
5、获取路径
获取父路径:
文件对象 . getParent()
获取绝对路径
- 先获得文件路径:文件对象 . getParentFile()
- 实际去获取绝对路径:. getAbsoluteFile()
```java
package com.xiaowang.file_;
import java.io.File;
/**
* @Author 小王
* @DATE: 2022/8/3
*/
public class FileTest {
public static void main(String[] args) throws Exception{
//1.先在盘里面创建一个文件,判断存不存在,不存在就创建文件or文件目录
// File file = new File("G:\\小王\\a");
/* //2.显示判断下存不存在
System.out.println(file.exists());*/
//3.不存在,就在该目录下创建一个该文件
/* if (!file.exists()){
file.createNewFile();
}*/
//4.不存在的情况下,创建一个文件目录(相当于一个文件夹)这里直接创建多重目录
/* File file1 = new File("F:\\bb\\b");
//System.out.println(file1.exists());
if (!file1.exists()){
file1.mkdirs();
}*/
File file = new File("F:\\bb\\b");
//获取该文件父路径
System.out.println(file.getParent());
System.out.println(file.getParentFile());
//获取绝对路径
System.out.println(file.getAbsoluteFile());
System.out.println(file.getAbsolutePath());
}
}
1.12.2文件拷贝练习
要求:
将一个盘里面的文件(包含文件、文件夹)copy到另外一个盘中
题目分析:
将一个盘里的文件拷贝到另外个盘里面,需要考虑的是,要拷贝的一个文件,里面可能既有文件夹(目录),也有文件,所以就的考虑不同的情况去拷贝
- 当拷贝文件的时候,我们可以直接通过一边读一边写搞定,
- 要是是要拷贝一个文件夹的话,那就需要去考虑文件夹路径的改变,也就是盘符要改变
实现过程:
- 1.先创建拷贝源文件和文件要被存放的位置文件,也就是不同路径的两个文件
- 2.拷贝操作,需要分拷贝文件和拷贝文件夹(目录)两个情况
- 3.当要拷贝的文件是一个文件的时候,那我们就使用文件输入输出流,进行一边读,一边写即可
- 4.当拷贝的是一个文件夹里面的文件的时候,那我们就通过截取要拷贝文件的绝对路径,然后和要拷贝到的位置的就对路径进行拼接
- 5.最后通过递归,就可以全部copy完了
注意: - 1.我们截取的要拷贝文件的绝对路径,是要截取到不要盘符的路径,这样就可以直接通过拼接要拷贝到的位置的盘符路径上去即可
案例代码:
package com.xiaowang;
import java.io.*;
/**
* @Author 小王
* @DATE: 2022/8/4
*/
/*整个文件拷贝过程
* 题目分析:将一个盘里的文件拷贝到另外个盘里面,需要考虑的是,要拷贝的一个文件,里面可能既有文件夹(目录),也有文件,所以就的考虑不同的情况去拷贝
* 当拷贝文件的时候,我们可以直接通过一边读一边写搞定,要是是要拷贝一个文件夹的话,那就需要去考虑文件夹路径的改变,也就是盘符要改变
* 实现过程:
* 1.先创建拷贝源文件,文件要被存放的位置,也就是梁文不同路径的文件
* 2.拷贝操作,需要分拷贝文件和拷贝文件夹(目录)两个情况
* 3.当要拷贝的文件是一个文件的时候,那我们就使用文件输入输出流,进行一边读,一边写即可
* 4.当拷贝的是一个文件夹里面的文件的时候,那我们就通过截取要拷贝文件的绝对路径,然后和要拷贝到的位置的就对路径进行拼接
* 注意:
* 1.我们截取的要拷贝文件的绝对路径,是要截取到不要盘符的路径,这样就可以直接通过拼接要拷贝到的位置的盘符路径上去即可
* 2.通过递归,就可以全部copy完了
*
* */
public class FileCopy {
public static void main(String[] args) {
//1.先创建两个文件,一个是拷贝源,一个是拷贝后的位置
File file1 = new File("E:\\表白");//拷贝源
File file2 = new File("G:\\小王");//拷贝到的位置
copy(file1,file2);
}
private static void copy(File file1, File file2) {
//如果是一个文件。那么就对文件进行读写进去,相当于文件的copy就直接通过读和写去进行
if (file1.isFile()){
FileOutputStream fos =null;
FileInputStream fis =null;
try {
fis = new FileInputStream(file1);
//要将写入的地址重新修改一次
String path = file2.getAbsolutePath()+file1.getAbsolutePath().substring(2);
fos = new FileOutputStream(path);
byte[] bytes = new byte[1024 * 1024];
int readCount = 0;
while ((readCount = fis.read(bytes))!=-1){
fos.write(bytes,0,readCount);
}
fos.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fis !=null){
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}if (fos !=null){
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return;//结束递归的条件就是,当文件都读取完
}
//对文件夹进行操作,文件夹里面会有很多子文件,所以要先拿到子文件
File[] files = file1.listFiles();
//System.out.println(files.length);输出一下长度看拿到没有
//因为哟啊将其目录拷贝过去,那么需要文件的绝对路径
for (File file: files
) {
if (file.isDirectory()){//先判断是不是一个文件目录
// System.out.println(file.getAbsolutePath());//显示看一下那倒没有
String fiDir = file.getAbsolutePath();
//拿到路径过后,将其除去盘符,然后把剩下的和要拷贝到的位置的盘符给拼接即可
String f2Dir = file2.getAbsolutePath()+fiDir.substring(2);//截取2是因为在idea中有一个\为转义字符,主要是将盘符截取出来
// System.out.println(f2Dir);//查看是否截取拼接成功,路径拼接成功后,将其写入盘中,相当于就是创建一个文件
File fileNew = new File(f2Dir);
if (!fileNew.exists()){//如果这个文件不存在,也就说明不是一个文件,是一个文件目录,所以就创建一个目录
fileNew.mkdirs();
}
}
//通过循环来递归,不断的进行copy行为
copy(file,file2);
}
}
}
1.13 序列化和反序列化
1.13.1 序列化介绍
概念:
- 序列化:将内存中的对象储存到硬盘中(这时候会把对象给拆分了)
- 反序列化:将硬盘里面被拆分的对象给组装成一个对象
示意图:
1.13.2 序列化实现
13.2.1 单个对象序列化
单个对象的序列化,
案例:
已经有了一个完整的Student类了,这里就不写上去了,怪麻烦的,特别注意,这个类一定要去实现序列化接口,不然不能被实例化,直接上序列化和反序列化代码:
序列化:
package com.xiaowang.serializable;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
/**
* @Author 小王
* @DATE: 2022/8/4
*/
/*
* 步骤:
* 1.先实例化一个对象new Student(注意这个类一定是要实现了Serializable接口)
* 2.序列化准备,new ObjectOutputStream对象
* 3.进行序列化:序列化对象去调用 WriteObject()方法,参数传入要序列化的对象
* 4.刷新、关闭流
* */
public class TestOut {
public static void main(String[] args) {
//创建一个学生对象进行操作
Student student = new Student("小王", 21);
//序列化
ObjectOutputStream oos =null;
try {
oos = new ObjectOutputStream(new FileOutputStream("students.txt"));
oos.writeObject(student);
oos.flush();
} catch (IOException e) {
e.printStackTrace();
}finally {
if (oos != null) {
try {
oos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
最后在students.txt文件中就可以看到,我们写入的对象信息
反序列化:
package com.xiaowang.serializable;
import java.io.*;
/**
* @Author 小王
* @DATE: 2022/8/4
*/
/*
* 步骤:
* 1.创建反序列化对象流,new ObjectInputStream(),参数就是对应的File的文件输入流
* 2.定义一个对象去接收通过反序列化回来,重新组装的对象
* 3.输出对象即可
* 4.关闭流
* */
public class TestIn {
public static void main(String[] args) {
//反序列化;
// 注意,反序列化和序列化一定是要在一个序列化版本,也就是序列化过后,不能修改类了,再去反序列化一遍
ObjectInputStream ois =null;
try {
ois = new ObjectInputStream(new FileInputStream("students.txt"));
Object object = ois.readObject();//反序列化回来,接收的是一个学生对象,所以需要接收
System.out.println(object);
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} finally {
if (ois != null) {
try {
ois.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
最后控制台就会输出我们从文件students.txt中读取到的对象信息
13.2.2 多个对象序列化
思路:
其实要对多个对象同时进行序列化反序列化操作,使用集合+对象就可以搞定,因为集合也实现了序列化的接口,所以可以直接被序列化和反序列化
案例:
已经有了一个完整的Usert类了,这里就不写上去了,怪麻烦的,特别注意,这个类一定要去实现序列化接口,不然不能被实例化,直接上序列化和反序列化代码:
序列化代码:
package com.xiaowang.serializable;
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import java.util.*;
/**
* @Author 小王
* @DATE: 2022/8/4
*/
/*
* 一次性序列化多个对象,使用集合去完成,因为集合也实现了序列化接口,所以可以直接进行序列化
* */
public class ListObjectOutStream {
public static void main(String[] args) throws Exception{
//创建一个集合,然后存入多个user对象
List<User> list = new ArrayList<>();
list.add(new User("小王",21));
list.add(new User("小李",22));
list.add(new User("小陈",23));
list.add(new User("小余",24));
//准备序列化
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("users.txt"));
//进行序列化
oos.writeObject(list);
oos.flush();
oos.close();
}
}
反序列化代码:
package com.xiaowang.serializable;
import java.io.FileInputStream;
import java.io.ObjectInputStream;
import java.util.List;
/**
* @Author 小王
* @DATE: 2022/8/4
*/
public class ListObjectInputStream {
public static void main(String[] args) throws Exception{
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("users.txt"));
//反序列化回来的对象,存入集合中
List<User> list = (List<User>)ois.readObject();//因为读出来的是一个对象,要将其强转成集合去封装在一起,这样就实现了反序列化多个对象
//通过集合的遍历去输出,就可以看出来反序列化
for (User user: list
) {
System.out.println(user);
}
}
}
1.13.2 序列化注意事项
- 要序列化的对象类一定要实现序列化接口(serialiazable)
- 如果想对象的某一个属性不参与序列化,使用 transient 关键字去修饰
- java虚拟机,在一个类实现序列化接口的时候,会自动给这个类生成一个序列化版本号,如果版本号改变了,就不能进行序列化,所以我们最好是自己去写死序列化版本号
1.13.2 写死序列化版本号
写死序列化版本号的原因:
在一个类实现序列化接口的时候,java虚拟机,会自动给这个类生成一个序列化版本号,序列化版本号不同,那我们就不能将其进行序列化和反序列化操作
小案例理解:
当我们写好一个类,并且实现类序列化接口后,我们将其进行序列化操作,序列化操作完了过后,不去立马反序列化,而是去修改类的属性或者其他操作,去修改后,直接进行反序列化就会报错,这样的情况就相当于,类被修改了,所以Java虚拟机就会重新生成一个序列化版本号,所以这时候再去反序列化的版本号和之前的序列化的版本号就不是一个了,就不能成功
方法:
步骤1:
步骤2:
IO流就彻底end了哦,消化消化迎接线程吧~~~~