File文件类
1.File文件类的作用
在Java中,File就代表一个文件类,这里的文件既包含了普通文件,又包含了文件目录。
File文件类可实现对文件的基本操作(创建文件、删除文件、获取文件长度、更改文件的名字等等)。
2.File文件类常用的方法
3. 递归删除层级目录
public class Exercise2 {
public static void main(String[] args) {
File dir = new File("D:/aaa/javase");
forceDeleteDir(dir);
}
public static void forceDeleteDir(File dir) {
if (dir != null && dir.isDirectory()) {
File[] listFiles = dir.listFiles();
if(listFiles!=null){
for (File sub : listFiles) {
forceDeleteDir(sub);
}
}
}
dir.delete();
}
}
4.File文件类总结
Java中的流
1.流的概念
在Java中,利用程序完成对数据的读、写(存储)操作所使用的工具就是流。
2.字节输出流
/* 需求:
* 采用字节流,指定路径下的文件。写入数据
* 1.实例化字节流对象(输出流(写入操作)),指定文件路径
* 2.调用方法,写入数据
*/
public class Test01 {
public static void main(String args[]) throws Exception {
//实例化字节流对象,指定文件路径
FileOutputStream fs = new FileOutputStream(new File("D://a.txt"), true);
/**
* 调用方法,写入数据
* 是以最基本的单位字节 进行数据写入的
*
* 指定字符数据
*/
String value = "热烈庆祝中华人民共和国成立70周年";
//将指定的字符数据转换成字节数据
byte[] b = value.getBytes();
//将得到的字节数据,作为参数,写入到指定文件中
fs.write(b);
//释放流资源
fs.close();
}
}
3.字节输入流
public class ByteInputputStreamDemo2 {
public static void main(String[] args) throws IOException {
//1.创建对象
FileInputStream fis = new FileInputStream("D:\\a.txt");
//2.读取数据
int b1 = fis.read();
System.out.println((char)b1);
//3.释放资源
fis.close();
}
}
4.一次读一个字节数组时的覆盖问题
5.字符集问题
utf-8:一个中文3个字节
GBK中一个中文2个字节
6.乱码问题解决
7.字符流
- 以字符为单位去读取数据
8.字符输入流
9.字符输出流
书写细节
8.字符输入流原理
9.缓冲区的细节
public class CharReaderStreamDemo4 {
public static void main(String[] args) throws IOException {
FileReader fr = new FileReader("D:\\b.txt");
fr.read();//会把文件中的数据放到缓冲区当中
//清空文件
FileWriter fw = new FileWriter("D:\\b.txt");
fw.close();
fr.close();
}
}
如果我再次使用fr进行读取,会读取到数据吗?如果能读取到,请问读取的数据是什么?
public class CharReaderStreamDemo4 {
public static void main(String[] args) throws IOException {
FileReader fr = new FileReader("D:\\b.txt");
fr.read();//会把文件中的数据放到缓冲区当中
//清空文件
FileWriter fw = new FileWriter("D:\\b.txt");
//请问,如果我再次使用fr进行读取,会读取到数据吗?
int ch;
while ((ch = fr.read()) != -1) {
System.out.println((char) ch);
}//再次读取
fw.close();
fr.close();
}
}
正确答案:但是只能读取缓冲区中的数据,文件中剩余的数据无法再次读取
10.字符输出流的原理
11.flush和close的区别
12.字节流与字符流的区别
相同点
都可以完成对数据的读取和写入操作
本质上都是在以字节为单位在读取
不同点
组成不同
1、字节流的组成:字节流是由字节组成的。
2、字符流的组成:字符流是由字符组成的。
缓冲区的不同
操作对象的不同
1、字节流 :字节读写, 字节流(ASCII)处理二进制文件。
可以传输音频,视频,图片,文本等,传输数据的基本单位为字节。
InputStream OutputStream
2、字符流:快读写 ,字符流(Unicode)处理文本文件。
只能传输纯文本, 传输数据的基本单位为字符 。
FileWriter FileReader 一个字符等于2个字节
两者的处理不同
字节流与字符流主要的区别是他们的的处理方式
字节流是最基本的,采用ASCII编码,所有的InputStream和OutputStream的子类都是,主要用在处理二进制数据,它是按字节来处理的
但实际中很多的数据是文本,又提出了字符流的概念,采用Unicode编码.它是按虚拟机的encode来处理,也就是要进行字符集的转化
这两个之间通过 InputStreamReader,OutputStreamWriter来关联,实际上是通过byte[]和String来关联
字节流与字符流的应用场景
13.应用场景
13.1文件的拷贝
public class Test01 {
public static void main(String[] args) throws IOException {
//拷贝一个文件夹,考虑子文件夹
//1.创建对象表示数据源
File src = new File("D:\\aaa\\src");
//2.创建对象表示目的地
File dest = new File("D:\\aaa\\dest");
//3.调用方法开始拷贝
copydir(src,dest);
}
/*
* 作用:拷贝文件夹
* 参数一:数据源
* 参数二:目的地
*
* */
private static void copydir(File src, File dest) throws IOException {
dest.mkdirs();
//递归
//1.进入数据源
File[] files = src.listFiles();
//2.遍历数组
for (File file : files) {
if(file.isFile()){
//3.判断文件,拷贝
FileInputStream fis = new FileInputStream(file);
FileOutputStream fos = new FileOutputStream(new File(dest,file.getName()));
byte[] bytes = new byte[1024];
int len;
while((len = fis.read(bytes)) != -1){
fos.write(bytes,0,len);
}
fos.close();
fis.close();
}else {
//4.判断文件夹,递归
copydir(file, new File(dest,file.getName()));
}
}
}
}
13.2文件加密
public class Test02 {
public static void main(String[] args) throws IOException {
/**
为了保证文件的安全性,就需要对原始文件进行加密存储,再使用的时候再对其进行解密处理。
加密原理:
对原始文件中的每一个字节数据进行更改,然后将更改以后的数据存储到新的文件中。
解密原理:
读取加密之后的文件,按照加密的规则反向操作,变成原始文件。
*/
/*
* ^ : 异或
* 两边表达式相同:false
* System.out.println(true ^ true);//false
* System.out.println(false ^ false);//false
* 两边表达式不同:true
* System.out.println(false ^ true);//true
* System.out.println(true ^ false);//true
*/
System.out.println(true ^ true);//false
System.out.println(false ^ false);//false
System.out.println(false ^ true);//true
System.out.println(true ^ false);//true
/**
*
* 表达式还可以是数字:System.out.println(100 ^ 10);//110-二进制对位计算后得到的二进制转换成十进制就是110
* 100的二进制:1100100
* 10的二进制: 1010
* 0:false
* 1:true
* 01100100
* 00001010
* -------
* 01101110---十进制--->110
*-------------------------------------------
* 100的二进制:1100100
* 10的二进制: 1010
* 0:false
* 1:true
* 01101110
* 00001010
* -------
* 01100100---十进制--->100
*
* 结论:可以利用^完成加密、解密
* 如把100当做原始内容,那么经过 ^ 10后得到的110就是加密后的内容;
* 反之把110当做解密内容,那么经过 ^ 10后得到的100就是原始的内容;
*/
System.out.println(100 ^ 10);//110
System.out.println(110 ^ 10);//100
//加密处理
/*encryptionAndReduction("D://我想吃鱼了.png","D://ency.png");*/
//解密处理
encryptionAndReduction("D://ency.png","D://我想吃鱼了.png");
}
public static void encryptionAndReduction(String src, String dest) throws IOException {
//1.创建对象关联的原始文件
FileInputStream fis = new FileInputStream(src);
//2.创建对象关联的加密文件
FileOutputStream fos = new FileOutputStream(dest);
//3.加密处理
int b;
while ((b = fis.read()) != -1) {
fos.write(b ^ 2);
}
//4.释放资源
fos.close();
fis.close();
}
}
13.3 修改文件中的数据
13.3.1 常规实现
public class Test03 {
public static void main(String[] args) throws IOException {
/**
文本文件中有以下的数据:
2-1-9-4-7-8
将文件中的数据进行排序,变成以下的数据:
1-2-4-7-8-9
*/
//1.读取数据
FileReader fr = new FileReader("D:\\a.txt");
StringBuilder sb = new StringBuilder();
int ch;
while((ch = fr.read()) != -1){
sb.append((char)ch);
}
fr.close();
System.out.println(sb);
//2.排序
String str = sb.toString();
String[] arrStr = str.split("-");//2-1-9-4-7-8
ArrayList<Integer> list = new ArrayList<>();
for (String s : arrStr) {
int i = Integer.parseInt(s);
list.add(i);
}
Collections.sort(list);
System.out.println(list);
//3.写出
FileWriter fw = new FileWriter("D:\\a.txt");
for (int i = 0; i < list.size(); i++) {
if(i == list.size() - 1){
fw.write(list.get(i) + "");
}else{
fw.write(list.get(i) + "-");
}
}
fw.close();
}
}
13.3.2stream流实现
public class Test04 {
public static void main(String[] args) throws IOException {
/**
文本文件中有以下的数据:
2-1-9-4-7-8
将文件中的数据进行排序,变成以下的数据:
1-2-4-7-8-9
细节1:
文件中的数据不要换行
细节2:
bom头
*/
//1.读取数据
FileReader fr = new FileReader("D:\\a.txt");
StringBuilder sb = new StringBuilder();
int ch;
while((ch = fr.read()) != -1){
sb.append((char)ch);
}
fr.close();
System.out.println(sb);
//2.排序
Integer[] arr = Arrays.stream(sb.toString()
.split("-"))
.map(Integer::parseInt)
.sorted()
.toArray(Integer[]::new);
//3.写出
FileWriter fw = new FileWriter("D:\\a.txt");
String s = Arrays.toString(arr).replace(", ","-");
String result = s.substring(1, s.length() - 1);
fw.write(result);
fw.close();
}
}
高级流
1.缓冲流
1.1字符缓冲流
- 字符缓冲输入流:BufferedReader
- 字符缓冲输出流:BufferedWriter
- 当读取数据时,数据按块读入缓冲区,其后的读操作则直接访问缓冲区
- 当使用BufferedInputStream读取字节文件时,BufferedInputStream会一次性从文件中读取8192个(8Kb),存在缓冲区中,直到缓冲区装满了,才重新从文件中读取下一个8192个字节数组。
- 向流中写入字节时,不会直接写到文件,先写到缓冲区中直到缓冲区写满,
BufferedOutputStream才会把缓冲区中的数据一次性写到文件里。使用方法
flush()可以强制将缓冲区的内容全部写入输出流。
1.2字节缓冲流
- 字节缓冲输入流:BufferedInputStream
- 字节缓冲输出流:和 BufferedOutputStream
为了提高数据读写的速度,Java API提供了带缓冲功能的流类,在使用这些流类时,会创建一个内部缓冲区数组,缺省使用8192个字节(8Kb)的缓冲区。
- 当读取数据时,数据按块读入缓冲区,其后的读操作则直接访问缓冲区
- 当使用BufferedInputStream读取字节文件时,BufferedInputStream会一次性从文件中读取8192个(8Kb),存在缓冲区中,直到缓冲区装满了,才重新从文件中读取下一个8192个字节数组。
- 向流中写入字节时,不会直接写到文件,先写到缓冲区中直到缓冲区写满,
BufferedOutputStream才会把缓冲区中的数据一次性写到文件里。使用方法
flush()可以强制将缓冲区的内容全部写入输出流。
1.3缓冲流原理
2.转换流
BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("D:\\a.txt")));
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("D:\\a.txt"),"GBK");
3.数据流
作用:
可以让(写入的)输入的数据以该数据对应的数据类型字节大小保存在文件中,读取数据的时候再以对应的数据类型进行读取。
3.1数据输出流
/**
*
* 需求:
* 以字节的形式写入数据,要求,数据是以字节的大小进行保存的。用DataOutputStream(数据输出流)完成
* 123 file.txt 123
* 123 file.txt int所占字节大小保存的
*/
public class Test01 {
public static void main(String args[]) throws Exception{
//构建数据输入流,并指定路径
DataOutputStream dos = new DataOutputStream(new FileOutputStream(new File("D://file.txt")));
//调用writeInt()方法,写入Int类型数据
dos.writeInt(123);
//调用writeInt()方法,写入Int类型数据
dos.writeInt(123);
//调用writeLong方法,写入Long类型数据
dos.writeLong(123);
//用writeFloat方法,写入Float类型数据
dos.writeFloat(2.5f);
//调用writeDouble方法,写入Double类型数据
dos.writeDouble(1.9999999999999);
//调用writeUTF方法,写入字符串类型数据
dos.writeUTF("你好世界");
}
}
3.2 数据输入流
/**
* 需求:
* 采用数据输入流按照数据类型去读取数据(DataInputStream)
*/
public class Test02 {
public static void main(String arg[]) throws Exception {
//构建数据输入流对象,并指定路径
DataInputStream dis = new DataInputStream(new FileInputStream(new File("D://file.txt")));
//调用readInt()方法,读取Int类型数据
int value1 = dis.readInt();
//调用readInt()方法,读取Int类型数据
int value2 = dis.readInt();
//调用readLong()方法,读取Long类型数据
long value3 = dis.readLong();
//调用readFloat()方法,读取Float类型数据
float value4 = dis.readFloat();
//调用readDouble()方法,读取Double类型数据
double value5 = dis.readDouble();
//调用readUTF()方法,读取字符串类型数据
String value6 = dis.readUTF();
System.out.println("value1:" + value1);
System.out.println("value2:" + value2);
System.out.println("value3:" + value3);
System.out.println("value4:" + value4);
System.out.println("value5:" + value5);
//System.out.println("value6:"+value6);
dis.close();
/**
* 注意:
* 采用数据输入流去读取数据时,读取数据的顺序一定要和,输入的顺序一致,否则产生异常
*/
}
}
4.序列化流(对象操作流)
4.1序列化流(对象操作输出流)
- ObjectOutputStream
- writeObject()
* 需求:
* 采用对象流,完成对象数据的读取,(对象输入流)
* 1.构建对象字节输入流对象,并且指定路径
* 2.调用方法,读取对象数据
*
弊端:
*如果不知道要写入多少个对象数据该怎么呢?(也不知道要读取多少个数据) 写入和读取不一致了
* 优化此程序:
* 优化的背景:
* 读取数据的顺序必须和写入数据的顺序一致
* 解决方案:
* 1.化零为整,以整体的形式去写对象数据
* 2.把要写的对象数据,存储到集合中,再把整体把集合中的数据通过对象流写出去
* 3.读取的时候也是集合的形式进行读取
*
*/
public class Test03 {
public static void main(String args[]) throws Exception, IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(new File("D://file.txt")));
List<Person> list = new ArrayList<Person>();
list.add(new Person("刘备", 48, new Date()));
list.add(new Person("关羽", 49, new Date()));
list.add(new Person("张飞", 50, new Date()));
oos.writeObject(list);
oos.close();
}
}
4.2 反序列化流(对象操作输入流)
需求:
* 采用对象流,完成对象数据的读取,(对象输入流)
* 1.构建对象字节输入流对象,并且指定路径
* 2.调用方法,读取对象数据
*
* 优化此程序:
* 优化的背景:
* 读取数据的顺序必须和写入数据的顺序一致
* 解决方案:
* 3.读取的时候也是集合的形式进行读取
*
*/
public class Test04 {
public static void main(String args[]) throws Exception, IOException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File("D://file.txt")));
List<Person> list = (List<Person>)ois.readObject();
for(Person p: list){
System.out.println(p);
}
/**
* 1.某个属性不想要被序列化呢?该怎么办呢?比如说age
* 2.再自定义一个类类型,Pet 作为Person内部的属性,问此类Person是否需要序列化
*/
}
}
- Serializable
序列化标识接口,无须重写任何方法。只有实现此标识接口,对象数据才能被读取,这也是大多数常用类,实现此接口的原因。
- Transient
修饰不需要被序列化的属性
4.3 序列化和反序列化的应用
* 单独声明一个类,用于封装序列化代码(通过流的方式实现对象的克隆)
* 核心:通过流将对象写入到缓冲区,然后再利用流对象读取出来。
*/
public class DeepCopy {
//Object obj 代表克隆时所须要的对象
public static Object copyDeep(Object obj) {
//声明所需要的局部对象
Object o = null;
ObjectOutputStream oos = null;
ObjectInputStream ois = null;
try {
//完成对象写的操作
ByteArrayOutputStream baos = new ByteArrayOutputStream();
oos = new ObjectOutputStream(baos);
oos.writeObject(obj);
//完成对象读的操作
byte[] b = baos.toByteArray();
ois = new ObjectInputStream(new ByteArrayInputStream(b));
o = ois.readObject();
oos.close();
} catch (Exception e) {
e.printStackTrace();
}
return o;
}
}
public class Test07 {
public static void main(String args[]) throws CloneNotSupportedException {
//实例化对象,得到一个宠物,
Pet pet = new Pet("雪豹");
//实例化对象,得到一个人
Person p_1 = new Person("李达康", 55, pet);
System.out.println("对象p_1情况");
System.out.println("p_1:" + p_1);
System.out.println("对象p_1情况");
//调用序列化的方法,实现克隆
Person p_2 = (Person) DeepCopy.copyDeep(p_1);
//通过p_2对p_1这个人的宠物的名字进行修改,检测p_1是否会受到影响
p_2.getPet().setName("花斑虎");
System.out.println("p_1:" + p_1);
}
}
/**
* 程序运行结果分析:
* <p>
* 对象p_1情况
* p_1:Person [name=李达康, age=55, pet=Pet [name=雪豹]]
* 对象p_1情况
* p_1:Person [name=李达康, age=55, pet=Pet [name=雪豹]]
* <p>
* <p>
* 通过程序的执行结果大家可以发现,p_1宠物的名字原来是雪豹,在克隆出来的p_2基础之上把宠物的名字修改成花斑虎,发现最终p_1的宠物的名字依然是雪豹,所以说我们最终就可以得出一个结论,现在通过序列化的方式可以直接克隆含有引用类型的类,是可以实现克隆的。
*/