【1】文件 目录
文件:
内存中存放的数据在计算机关机后就会消失,要长久保存数据,就要使用硬盘,光盘,U盘等设备,为了便于数据的管理和检索,引入了"文件"的概念,操作系统以文件为单位管理磁盘中的数据
从数据存储的角度说,所有的文件本质都是一样的,都是由一个个字节组成的,归根到底都是0,1比特串。
文件夹
n多个文件,如果不分类放在一起,用户使用起来显然非常不方便,因此又引入了树形目录(也叫文件夹)的机制,可以把文件放在不同的文件夹中。
【2】如何在java程序中操纵 文件/ 目录 ?
java程序,最典型的特点,面向对象,java程序最擅长的就是操作对象,盘符上的文件/目录,将它的各种信息进行封装,封装为一个对象。
这个对象 --> File类
即 盘符上的文件 --> 封装为对象 --> 对象属于File类的对象 --> 有了这个对象程序就可以直接操纵这个对象,通过这个对象获取文件的各种信息,还可以对文件进行创建,删除
public class Test01 {
public static void main(String[] args) throws IOException {
// 将文件封装为一个File类的对象
//File file = new File("d:\test.txt");
//File file1 = new File("d:/test.txt");
// File.separator属性帮我们获取当前操作系统的路径拼接符号
File file3 = new File("d:" + File.separator + "test.txt");
// canRead canWrite 只读 只写
file3.canRead();
file3.canWrite();
// 文件的名字
file3.getName();
// 上级目录
file3.getParent();
// 是否是一个目录
file3.isDirectory();
// 是否是一个文件
file3.isFile();
// 是否隐藏
file3.isHidden();
// 文件大小
file3.length();
// 是否存在
file3.exists();
// 删除
file3.delete();
// 创建一个新文件
file3.createNewFile();
}
}
public class Test02 {
public static void main(String[] args) throws IOException {
File f1 = new File("d://test.txt");
File f2 = new File("d://aa//test.txt");
System.out.println(f1.equals(f2)); // false
//跟路径相关
File f3 = new File("E:\ExerciseJava\day1.txt");
System.out.println(f3.getAbsoluteFile());
System.out.println(f3.getAbsolutePath());
System.out.println(f3.toString());
System.out.println("---------------------------------");
File f5 = new File("demo.txt");
if(!f5.exists()){
f5.createNewFile();
}
// 绝对路径 F:projectLearningJavademo.txt
System.out.println(f5.getAbsolutePath());
System.out.println(f5.getAbsoluteFile());
// 相对路径 demo.txt
System.out.println(f5.getPath());
// toString效果 和 相对路径相同
System.out.println(f5.toString());
System.out.println("---------------------------------");
File f6 = new File("a/b/c/demo.txt");
System.out.println(f6.getAbsolutePath());
System.out.println(f6.getPath());
}
}
对目录进行操作
public class Test03 {
public static void main(String[] args) {
// 将目录封装为File类的对象
File file = new File("F:\project\LearningJava");
// 跟目录相关的方法
File f1 = new File("D:\a");
// 创建单层目录
//f1.mkdir();
File f2 = new File("E:\ExerciseJava\a\b\c");
// 创建多层目录
f2.mkdirs();
// 删除:如果只是删除目录的话,只删除一层,前提是这层目录是空的
f2.delete();
// 查看 目录下所有目录和文件的名称
String[] list = file.list();
for (String s : list) {
System.out.println(s);
}
// listFiles() 返回File对象的数组 作用更加广泛
File[] files = file.listFiles();
for (File file1 : files) {
System.out.println(file1);
}
}
}
File类:封装文件/目录的各种信息,对目录/文件进行操作,但是我们不可以获取到文件/目录中的内容
形象理解:IO流 当作一根“管”
字符流
案例
java代码实现文件复制
功能分解1:文件 --> 程序: FileReader
一个字符一个字符的读取
public class CopyFileDemo {
public static void main(String[] args) throws IOException {
// 文件 --> 内存
File file = new File("E:\ExerciseJava\CopyTxtDemo.txt");
// 创建一个FileReader的流的对象
FileReader fr = new FileReader(file);
// 读取动作
// 方式1:
// 方式2:
int n1;
while ((n1 = fr.read()) != -1) {
System.out.println(n1);
}
// 关闭流
// 流 数据库 网络资源 靠JVM本身没有办法帮我们关闭,所以此时必须程序员手动关闭
fr.close();
}
}
功能分解2:程序 --> 文件 : FileWriter
public class CopyFileDemo2 {
public static void main(String[] args) throws IOException {
String str = "abcdef你好老师";
// 目标文件
File f = new File("E:\ExerciseJava\DemoTest.txt");
// FileWriter
// FileWriter fw = new FileWriter(f,false); 原文件覆盖
// FileWriter fw1 = new FileWriter(f,true); 原文件追加
FileWriter fw = new FileWriter(f);
// 输出到文件 目标文件不存在,自动创建目标文件 ,如果存在 会覆盖这个文件
// 利用缓冲数组进行输出
char[] chars = str.toCharArray();
fw.write(chars);
// 关闭流
fw.close();
}
}
实现文件复制 读取 --> 输出
public class CopyFileDemo {
public static void main(String[] args) throws IOException {
File inputFile = new File("E:\ExerciseJava\CopyTxtDemo.txt");
File outPutFile = new File("E:\ExerciseJava\CopyTxtDemoOutput.txt");
FileReader fr = new FileReader(inputFile);
FileWriter fw = new FileWriter(outPutFile);
// 方式一: 一个字符一个字符
// 方式二: 缓冲读取
// 方式三:
char[] chars = new char[5];
int len = fr.read(chars);
while (len != -1){
// 将缓冲数组有效长度写出
String s = new String(chars, 0, len);
fw.write(s);
len = fr.read(chars);
}
// 关闭流
fw.close();
fr.close();
}
}
ps:
文本文件:.txt .java .c .cpp --> 建议使用字符流操作
非文本文件: .jpg .mp3 .mp4 .doc .ppt --> 建议使用字节流操作
利用try-catch finally 处理异常方式
public class CopyFileDemo {
public static void main(String[] args) {
File inputFile = new File("E:\ExerciseJava\CopyTxtDemo.txt");
File outPutFile = new File("E:\ExerciseJava\CopyTxtDemoOutput.txt");
FileReader fr = null;
FileWriter fw = null;
try{
fr = new FileReader(inputFile);
fw = new FileWriter(outPutFile);
char[] chars = new char[5];
int len = fr.read(chars);
while (len != -1){
// 将缓冲数组有效长度写出
String s = new String(chars, 0, len);
fw.write(s);
len = fr.read(chars);
}
}catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
try{
if(fw != null){// 防止空指针异常
fw.close();
}
}catch (IOException e){
e.printStackTrace();
}
try{
if(fr != null){// 防止空指针异常
fr.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
FileInputStream
字节流
public class Test01 {
public static void main(String[] args) throws IOException {
// 字节流
File file = new File("E:\ExerciseJava\CopyTxtDemo.txt");
FileInputStream fis = new FileInputStream(file);
int read = fis.read();
while(read != -1){
System.out.println(read);
read = fis.read();
}
fis.close();
}
}
【2】利用字节流读取非文本文件 (以图片为例)
一个字节一个字节读取 效率低
public class Test01 {
public static void main(String[] args) throws IOException {
// 字节流
File file = new File("E:\ExerciseJava\aa.png");
FileInputStream fis = new FileInputStream(file);
// 定义一个计数器 用来记录读入的字节的个数
int count = 0;
int read = fis.read();
while (read != -1) {
System.out.println(read);
read = fis.read();
count++;
}
System.out.println("读入字节数量" + count);
fis.close();
}
}
【2】利用缓冲数组 效率高
public class Test02 {
public static void main(String[] args) throws IOException {
// 字节流
File file = new File("E:\ExerciseJava\aa.png");
FileInputStream fis = new FileInputStream(file);
// 利用缓冲数组
byte[] b = new byte[1024*2];
int len = fis.read(b);
while(len != -1){
//System.out.println(len);
for (int i = 0; i < len; i++) {
System.out.println(b[i]);
}
len = fis.read(b);
}
fis.close();
}
}
【3】非文本文件复制:读入一个字节写出一个字节
```java
public class Test02 {
public static void main(String[] args) throws IOException {
// 字节流
File file = new File("E:\ExerciseJava\aa.png");
File file1 = new File("E:\ExerciseJava\aa2.png");
FileInputStream fis = new FileInputStream(file);
FileOutputStream fos = new FileOutputStream(file1);
// 利用缓冲数组
int len = fis.read();
while(len != -1){
fos.write(len);
len = fis.read();
}
fos.close();
fis.close();
}
}
【4】非文本文件复制: 缓冲读取写入
public class Test02 {
public static void main(String[] args) throws IOException {
// 字节流
File file = new File("E:\ExerciseJava\aa.png");
File file1 = new File("E:\ExerciseJava\aa3.png");
FileInputStream fis = new FileInputStream(file);
FileOutputStream fos = new FileOutputStream(file1);
// 利用缓冲数组
byte[] b = new byte[1024*8];
int len = fis.read(b);
while(len != -1){
fos.write(b,0,len); // ps: 重点 : 提取有效长度
len = fis.read(b);
}
fos.close();
fis.close();
}
}
BufferedInputStream & BufferedOutputStream
缓冲字节流
【1】一个字符一个字符读
与硬盘交互次数多
【2】利用缓冲字节数组:
降低与硬盘交互次数
【3】利用缓冲区
单纯利用FileInputStream 与 FileOutputStream 是无法完成的
优化: 引入 BufferedInputStream 与 BufferOutputStream --> 处理流
public class Test06 {
public static void main(String[] args) throws IOException {
// 字节流
File file = new File("E:\ExerciseJava\aa.png");
File file1 = new File("E:\ExerciseJava\aa3.png");
FileInputStream fis = new FileInputStream(file);
FileOutputStream fos = new FileOutputStream(file1);
BufferedInputStream bis = new BufferedInputStream(fis);
BufferedOutputStream bos = new BufferedOutputStream(fos);
byte[] b = new byte[1024*6];
int len = bis.read(b);
while (len != -1){
bos.write(b,0,len);
// 底层已经帮我们做了刷新缓冲区操作
len = bis.read(b);
}
// 如果处理流包裹着节点流,那么其实只需要关闭高级流(处理流)
bos.close();
bis.close();
fos.close();
fis.close();
}
}
BufferedReader && BufferedWriter
public class Test07 {
public static void main(String[] args) throws IOException {
File file = new File("E:\ExerciseJava\CopyTxtDemo.txt");
File file1 = new File("E:\ExerciseJava\CopyTxtDemo11.txt");
FileReader fr = new FileReader(file);
FileWriter fw = new FileWriter(file1);
BufferedReader br = new BufferedReader(fr);
BufferedWriter bw = new BufferedWriter(fw);
// 一个字符一个字符读
// 利用缓冲数组
char[] chars = new char[30];
int len = br.read(chars);
while(len != -1){
bw.write(chars,0,len);
len = br.read(chars);
}
// 方式三: 读取String
String str = br.readLine();
while(str != null){ // 每次读取文本文件中一行,然后返回对应字符串
bw.write(str);
// 在文本文件中,应该在写出一个换行
bw.newLine();// 新起一行
str = br.readLine();
}
bw.close();
br.close();
}
}
InputStreamReader && OutputStreamWriter
【1】转换流: 作用:将字节流和字符类进行转换
InputStreamReader 与 OutputStreamWriter 属于 字符流
InputStreamReader: 将字节输入流 --> 转换为字符的输入
OutputStreamWriter:将字节输出流 --> 转换为字符的输出
public class Test08 {
public static void main(String[] args) throws IOException {
File file = new File("E:\ExerciseJava\CopyTxtDemo.txt");
File file1 = new File("E:\ExerciseJava\CopyTxtDemo12.txt");
FileInputStream fis = new FileInputStream(file);
FileOutputStream fos = new FileOutputStream(file1);
// 将字节转换为字符的时候,需要指定一个编码,与文件本身的编码格式统一
// 不指定编码格式,就会获取程序本身的编码 --> utf-8
InputStreamReader isr = new InputStreamReader(fis,"utf-8");
OutputStreamWriter osw = new OutputStreamWriter(fos,"utf-8");
char[] chars = new char[20];
int len = isr.read(chars);
while (len != -1){
osw.write(chars,0,len);
len = isr.read(chars);
}
osw.close();
isr.close();
fos.close();
fis.close();
}
}
System类对IO流的支持
System类
System.in 获取标准输入流 --> 默认从键盘输入
public class Test01 {
public static void main(String[] args) throws IOException {
//InputStream in = System.in;
//int n = in.read(); // read()方法等待键盘的录入,所以这个方法是一个阻塞方法
//System.out.println(n);
// 以上案例:从键盘录入一个int类型的数据:
// 从上面的代码证明,键盘录入实际上是:System.in
// 形象理解:从键盘上输入的东西,顺着这个System.in这个流,流向了程序内存
// Scanner作用:扫描器:起扫描作用的,扫键盘通过流 流出来的数据
// Scanner sc = new Scanner(System.in)
// 既然Scanner 的其他应用:
Scanner sc = new Scanner(new FileInputStream(new File("E:\ExerciseJava\CopyTxtDemo.txt")));
while(sc.hasNext()){
System.out.println(sc.next()); //abc你好
}
}
}
System.out 获取标准输出流 --> 默认输入到显示器
public class Test02 {
public static void main(String[] args) {
PrintStream out = System.out;
out.print("nihao1");// 直接在控制台写出,不换行
out.print("nihao2");
out.print("nihao3");
out.println("nihao4");
out.println("nihao5");
out.println("nihao6");
System.out.println("Hello.World");
}
}
案例: 键盘录入内容输出到文件中
思路:
public class Test03 {
public static void main(String[] args) throws IOException {
InputStream in = System.in;
// 字节流 --> 字符流
InputStreamReader isr = new InputStreamReader(in);
// 缓冲流
BufferedReader br = new BufferedReader(isr);
File file = new File("E:\ExerciseJava\out.txt");
FileWriter fw = new FileWriter(file);
BufferedWriter bw = new BufferedWriter(fw);
String s = br.readLine();
while(!s.equals("exit")){
bw.write(s);
bw.newLine();// 换行操作
s = br.readLine();
}
bw.close();
br.close();
}
}
DataInputStream && DataOutputStream
【1】数据流:用来操作基本数据类型和字符串的
DataInputStream:将文件中存储的基本数据类型和字符串 写入内存的变量中
DataOutputStream:将内存中的基本数据类型和字符串的变量 写出 文件中
DataOutputStream 例子
public class Test04 {
public static void main(String[] args) throws IOException {
// DataOutputStream:
File f = new File("");
//FileOutputStream fos = new FileOutputStream(f);
//DataOutputStream dos = new DataOutputStream(fos);
DataOutputStream dos = new DataOutputStream(new FileOutputStream(new File("E:\ExerciseJava\D1.txt")));
dos.writeUTF("你好");
dos.writeBoolean(false);
dos.writeInt(1);
dos.writeShort(1);
dos.close();
}
}
DataInputStream 例子
public class Test05 {
public static void main(String[] args) throws IOException {
DataInputStream dis = new DataInputStream(new FileInputStream(new File("E:\ExerciseJava\D1.txt")));
System.out.println(dis.readUTF());
System.out.println(dis.readBoolean());
System.out.println(dis.readInt());
System.out.println(dis.readShort());
dis.close();
}
}
ps: 写出的类型一定要跟读入的类型 匹配 顺序一致
【1】对象流: 用于存储和读取基本数据类型数据或对象的处理流
它的强大之处就是可以把JAVA中的对象写入到数据源中,也能把对象从数据源还原回来
【2】序列化和反序列化
ObjectOutputStream类:把内存中的java对象转换成平台无关的二进制数据,从而允许把这种二进制数据持久地保存在磁盘上,或通过网络将这种二进制数据传输到另一个网络节点。 --> 序列化
用ObjectInputStream类: 当其他程序获取了这种二进制数据,就可以恢复成原来的java对象。 --> 反序列化
将字符串对象写入到文件中去 序列化
public class Test01 {
public static void main(String[] args) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(new File("E:\ExerciseJava\outObject.txt")));
oos.writeObject("你好");
oos.close();
}
}
查看文件:
将文件中的对象读入内存 反序列化
public class Test02 {
public static void main(String[] args) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File("E:\ExerciseJava\outObject.txt")));
Object o = ois.readObject();
String str = (String) o;
System.out.println(str);
ois.close();
}
}
查看控制台
操作自定义类的序列化与反序列化
自定义Person类
public class Person {
private int age;
private String name;
public Person(int age, String name) {
this.age = age;
this.name = name;
}
public Person() {
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Person{" +
"age=" + age +
", name='" + name + ''' +
'}';
}
}
public class Test01 {
public static void main(String[] args) throws IOException {
Person p = new Person(19,"lili");
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(new File("E:\ExerciseJava\outPersonObject.txt")));
oos.writeObject(p);
oos.close();
}
}
运行时出现异常: 原因 :未进行序列化
想要序列化的对象对应的类必须要实现serializable接口
Serializable接口内部,什么都没有,这种接口叫,标识接口,起标识作用
public class Person implements Serializable {
private static final long serialVersionUID = 1223L;
private int age;
private String name;
public Person(int age, String name) {
this.age = age;
this.name = name;
}
public Person() {
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Person{" +
"age=" + age +
", name='" + name + ''' +
'}';
}
}
序列化对象到文件
public class Test01 {
public static void main(String[] args) throws IOException {
Person p = new Person(19,"lili");
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(new File("E:\ExerciseJava\outPersonObject.txt")));
oos.writeObject(p);
oos.close();
}
}
从文件将序列化文件读入内存
public class Test02 {
public static void main(String[] args) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File("E:\ExerciseJava\outPersonObject.txt")));
Person person = (Person) ois.readObject();
System.out.println(person);
ois.close();
}
}
【3】serialVersionUID
凡是实现Serializable接口(标识接口)的类都有一个表示序列化版本标识符的静态变量
1.private static final long serialVersionUID;
2.serialVersionUID用来表明类的不同版本间的兼容性。简言之,其目的是以序列化对象进行版本控制,有关各版本反序列化时是否兼容
3.如果类没有显示定义这个静态变量,它的值是java运行时环境根据类的内部细节自动生成的。若类的实例变量做了修改,serialVersionUID可能发生变化,故建议显式声明
4.简单来说,Java的序列化机制是通过在运行时判断类的serialVersionUID来验证版本一致性的。在进行反序列化时,JVM会把传来的字节流中的serialVersionUID与本地相应实体类的serialVersionUID进行比较,如果相同就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异常。(InvalidCastException)
Idea中配置序列化版本号
【4】序列化的细节
1.被序列化的类的内部属性都是可以被序列化的(基本数据类型,都是可序列化的)
2.static , transient修饰的属性,不可被序列化



