当前位置: 首页 > news >正文

第八章 IO流

IO流的分类 

  • 什么是IO流?

        IO流指的是:程序中数据的流动。数据可以从内存(临时存储文件的空间)流动到硬盘(持久化设备的一种),也可以从硬盘流动到内存。
        Java中IO流最基本的作用是:完成文件的读(输入流,硬盘->内存)和写(输出流,内存->硬盘)。

  • IO流的分类?

        根据数据流向分为:输入和输出是相对于内存而言的,
        ① 输入流:从硬盘到内存。(输入又叫做读:read)
        ② 输出流:从内存到硬盘。(输出又叫做写:write)

  • 根据读写数据形式分为:

        ① 字节流:一次读取一个字节。适合读取非文本数据。例如图片、声音、视频等文件。(当然字节流是万能的。什么都可以读和写。)
        ②字符流:一次读取一个字符。只适合读取普通文本。不适合读取二进制文件。因为字符流统一使用Unicode编码,可以避免出现编码混乱的问题。

        注意:Java的所有IO流中凡是以Stream结尾的都是字节流。凡是以Reader和Writer结尾的都是字符流。

  • 根据流在10操作中的作用和实现方式来分类:

        节点流:节点流负责数据源和数据目的地的连接,是IO中最基本的组成部分。
        处理流:处理流对节点流进行装饰/包装,提供更多高级处理操作,方便用户进行数据处理。

节点流关闭的时候会自动刷新,包装流是需要手动刷新的。 

流名称是否需要刷新节点流/处理流典型用法
FileInputStream节点流直接读取文件中的原始字节。
FileOutputStream节点流直接写入字节到文件。
BufferedInputStream处理流包装其他输入流,提供缓冲读取。
BufferedOutputStream处理流包装其他输出流,缓冲写入并需手动刷新。
ByteArrayInputStream节点流从内存字节数组读取数据。
ByteArrayOutputStream节点流将数据写入内存字节数组。
DataInputStream处理流读取基本数据类型(如 intdouble)。
ObjectOutputStream处理流序列化对象到流(需调用 flush())。
GZIPInputStream处理流解压 GZIP 格式的压缩数据。
GZIPOutputStream处理流压缩数据为 GZIP 格式(需手动刷新)。
ZipInputStream处理流读取 ZIP 文件中的条目。
ZipOutputStream处理流将文件写入 ZIP 格式压缩包。
PrintStream处理流输出格式化的字节数据(如 System.out)。
流名称是否需要刷新节点流/处理流典型用法
FileReader节点流读取文本文件(默认使用系统编码)。
FileWriter节点流写入文本文件(默认使用系统编码)。
BufferedReader处理流逐行读取文本文件。
BufferedWriter处理流高效写入文本并支持换行符。
PrintWriter处理流输出格式化的字符数据(如 HTML 生成)。
InputStreamReader处理流将字节流按指定编码转换为字符流。
OutputStreamWriter处理流将字符流按指定编码转换为字节流。

字节流

FileInputStream

        文件字节输入流。负责读。任何文件都能读。但还是建议读二进制文件。例如:图片,声音,视频等。可以读普通文本的。只不过一次读取一个    字节   。容易出现乱码问题。

FileInputStream(String name) 
# 通过文件路径构建一个文件字节输入流对象

常用方法

// int read(); 调用一次read()方法则读取一个字节,返回读到的字节本身。如果读不到任何数据则返回 -1in = new FileInputStream("D:/Java/Java学习/Java2025/chapter08/src/file01.txt");int read = in.read();System.out.println("第一次读到的字节:" + read);     // a读出的字符是97read = in.read();System.out.println("第二次读到的字节:" + read);// 改进while循环
int readByte = 0;
while((readByte = in.read()) != -1) {System.out.print(readByte);System.out.println((char)readByte);
}
// 文件内容是abcdef
fis = new FileInputStream("D:\\Java\\Java学习\\Java2025\\chapter08\\src\\file01.txt");// int read(byte[] b); 一次最多可以读到 b.length个字节(只要文件内容足够多)。
// 返回值是读取到的字节数量。如果这一次没有读取到任何数据,则返回 -1// 提前准备一个Byte[]数组(一次最多读取四个字节)
byte[] bytes = new byte[4];// 将Byte数组转换成字符串    此时byte数组里面已经存放了abcd
int readCount = fis.read(bytes);
System.out.println("第一次读取到的字节数量:" + readCount);     // 4 abcdString str = new String(bytes, 0, readCount);
System.out.println(str);readCount = fis.read(bytes);
System.out.println("第二次读取到的字节数量:" + readCount);     // 2 
String str1 = new String(bytes, 0, readCount);
System.out.println(str1);                        // efreadCount = fis.read(bytes);
System.out.println("第三次读取到的字节数量:" + readCount);     // -1// 遍历
int readCount = 0;
while((readCount = fis.read(bytes)) != -1) {System.out.print(new String(bytes, 0, readCount));
}
// int read(byte[] b, int off, int len); 一次读取len个字节。将读到的数据从byte数组的off位置开始放。fis = new FileInputStream("D:\\Java\\Java学习\\Java2025\\chapter08\\src\\file01.txt");
byte[] bytes = new byte[10];int readCount = fis.read(bytes, 2, 5);System.out.println("读取到了多少个字节:" + readCount);for (byte b : bytes) {System.out.print(b + " ");System.out.println((char)b);
}// 跳过n个字节
int readByte = fis.read();
System.out.println(readByte);     // 97// 跳过两个
fis.skip(2);readByte = fis.read();
System.out.println(readByte);     // 100// 还剩多少个字节没读
int readCount1 = fis.available();
System.out.println(readCount1);     // 2

FileOutputStream

// FileOutputStream(String name)
// 创建一个文件字节输出流对象,这个流在第一次使用的时候,最开始会将原文件内容全部清空,然后写入。// FileOutputStream(String name, boolean append)
// 创建一个文件字节输出流对象,当append是true的时候,不会清空原文件的内容,在原文件的末尾追加写入。
// 创建一个文件字节输出流对象,当append是false的时候,会清空原文件的内容,然后写入。// void close();
// void flush();
// void write(int b); 写一个字节
// void write(byte[] b); 将整个byte字节数组写出
// void write(byte[] b, int off, int len) 将byte字节数组的一部分写出。public class FileOutputStreamTest01 {public static void main(String[] args) {// 创建文件字节输出流对象FileOutputStream out = null;try {
//            out = new FileOutputStream("D:\\Java\\Java学习\\Java2025\\chapter08\\src\\file02.txt", true);// 当原文件中有内容时,后面两个运行结果一样out = new FileOutputStream("D:\\Java\\Java学习\\Java2025\\chapter08\\src\\file02.txt");
//            out = new FileOutputStream("D:\\Java\\Java学习\\Java2025\\chapter08\\src\\file02.txt", false);byte[] bytes = {97, 98, 99, 100};out.write(bytes);out.write(bytes, 0, 2);byte[] bs = "动力节点".getBytes();out.write(bs);// 开始写      同一个流只在第一次写的时候会清空out.write(97);      // aout.write(98);      // about.write(99);      // abcout.write(100);    // 刷新out.flush();        // abcdab动力节点abcd} catch (FileNotFoundException e) {throw new RuntimeException(e);} catch (IOException e) {throw new RuntimeException(e);} finally {try {out.close();} catch (IOException e) {throw new RuntimeException(e);}}}
}

try-with-resources   资源自动关闭

文件复制

try(FileInputStream fis = new FileInputStream("D:\\Java\\JavaMe\\Java2025\\chapter08\\src\\file01.txt");FileOutputStream out = new FileOutputStream("D:\\Java\\JavaMe\\Java2025\\chapter08\\src\\file03.txt")) {byte[] bytes = new byte[1024];int readCount = 0;while ((readCount = fis.read(bytes)) != -1) {out.write(bytes, 0, readCount);}
} catch (FileNotFoundException e) {throw new RuntimeException(e);
} catch (IOException e) {throw new RuntimeException(e);
}

字符流

FileReader(包装流)

字符输入流,一次至少读取一个字符;


字符流(包装流)需要关闭;

try(FileReader reader = new FileReader("D:\\Java\\JavaMe\\Java2025\\chapter08\\src\\file02.txt")){// 开始读char[] buf = new char[1];int readCount = 0;while ((readCount = reader.read(buf)) != -1) {System.out.print(new String(buf,0,readCount));}} catch (FileNotFoundException e) {throw new RuntimeException(e);
} catch (IOException e) {throw new RuntimeException(e);
}

解码默认UTF-8

// 创建FileReader的时候,没有指定字符集,默认采用UTF-8的字符集进行解码。
FileReader reader = new FileReader("E:\\powernode\\02-JavaSE\\code\\file2.txt");// 开始读
int readCount = 0;
char[] chars = new char[1024];
while((readCount = reader.read(chars)) != -1){System.out.print(new String(chars, 0, readCount));
}reader.close();

FileWriter

try (FileWriter fis = new FileWriter("D:\\Java\\JavaMe\\Java2025\\chapter08\\src\\file03.txt")){fis.write("hello");fis.write("张三李四", 2, 1);    // hello李fis.write("\n");fis.write("张三李四王五".toCharArray());      // 张三李四王五fis.write("张三李四王五".toCharArray(), 0, 2);    // 张三fis.append("1");fis.write("\n");fis.write("2");fis.flush();
} catch (IOException e) {throw new RuntimeException(e);
}

编码默认UTF-8

// 创建文件字符输出流,   后面的true表示以追加的方式在文件后面 再 进行追加
FileWriter writer = new FileWriter("E:\\powernode\\02-JavaSE\\code\\file3.txt", true);// 开始写
writer.write("动力节点"); // 这里的“动力节点”采用的是UTF-8的编码方式// 刷新
writer.flush();// 关闭
writer.close();

文件的复制

try(FileReader fis = new FileReader("D:\\Java\\JavaMe\\Java2025\\chapter08\\src\\file03.txt");FileWriter fos = new FileWriter("D:\\Java\\JavaMe\\Java2025\\chapter08\\src\\file04.txt")) {char[] buf = new char[1024];int readCount = 0;while ((readCount = fis.read(buf)) != -1) {fos.write(buf, 0, readCount);}
} catch (FileNotFoundException e) {throw new RuntimeException(e);
} catch (IOException e) {throw new RuntimeException(e);
}

解决乱码问题

解码(转换流、字符流InputStreamReader)

// InputStreamReader时,可以指定解码的字符集。用来解决读过程中的乱码问题
// InputStreamReader是一个字符流。是一个转换流,是一个输入和解码的过程// InputStreamReader常用的构造方法:
// InputStreamReader(InputStream in) 采用平台默认的字符集进行解码。
// InputStreamReader(InputStream in, String charsetName) 采用指定的字符集进行解码。// FileReader实际上是InputStreamReader的子类。// FileReader也是一个  包装流  ,不是节点流(InputStream就是节点流)。 // 包装流包括这个节点流
// 创建一个转换流对象(输入流)
// 节点流
//FileInputStream in = new FileInputStream("");
// 包装流
//InputStreamReader isr = new InputStreamReader(in);//InputStreamReader isr = new InputStreamReader(new FileInputStream("E:\\powernode\\02-JavaSE\\code\\file3.txt"), "GBK");// 以上代码太长了。在IO流的继承体系结构中,IO流又给InputStreamReader提供了一个子类:FileReader
// 代码可以这样写了:
//FileReader isr = new FileReader("E:\\powernode\\02-JavaSE\\code\\file3.txt", Charset.defaultCharset());
FileReader isr = new FileReader("E:\\powernode\\02-JavaSE\\code\\file3.txt", Charset.forName("GBK"));// 开始读
int readCount = 0;
char[] chars = new char[1024];
while((readCount = isr.read(chars)) != -1){System.out.print(new String(chars, 0, readCount));
}
// 关闭流
isr.close();

编码(转换流、字符流OutputStreamWriter)

// FileWriter是OutputStreamWriter的子类。
// 创建转换流对象OutputStreamWriter
// 以下代码采用的是UTF-8的字符集进行编码。(采用平台默认的字符集)
// 注意:以下代码中输出流以覆盖的方式输出/写。
//OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("E:\\powernode\\02-JavaSE\\code\\file4.txt"));//通过OutputStreamWriter的第二个参数可强制指定编码
//OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("E:\\powernode\\02-JavaSE\\code\\file4.txt"), "GBK");//追加写入且使用GBK编码,适合日志文件等需要持续记录的场景
//OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("E:\\powernode\\02-JavaSE\\code\\file4.txt", true), "GBK");/*OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("E:\\powernode\\02-JavaSE\\code\\file4.txt", true));*//*OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("E:\\powernode\\02-JavaSE\\code\\file4.txt", true), "GBK");*/FileWriter osw = new FileWriter("E:\\powernode\\02-JavaSE\\code\\file4.txt", Charset.forName("UTF-8"), true);// 开始写
osw.write("来动力节点学Java");osw.flush();
osw.close();

缓冲流

BufferedInputStream(包装流)

// java.io.BufferedInputStream
// 在内存当中存在一个大的byte数组看作一个缓冲区,这个byte数组是不需要维护的,
// 由BufferedInputStream和BufferedOutputStream自动维护
// 主要功能是减少内存与硬盘之间的交互次数// java.io.BufferedInputStream的用法和FileInputStream用法相同
// 不同点是:
//     FileInputStream是节点流。
//     BufferedInputStream是缓冲流(包装流/处理流)。这个流的效率高。自带缓冲区。并且自己维护这个缓冲区。
//     读大文件的时候建议采用这个缓冲流来读取。//     BufferedInputStream对 FileInputStream 进行了功能增强。增加了一个缓冲区的功能//     创建一个BufferedInputStream对象:BufferedInputStream(InputStream in)
public static void main(String[] args) {BufferedInputStream bis = null;try {
//            // 创建节点流
//            FileInputStream fis = new FileInputStream("/chapter08/file04.txt");
//
//            // 创建包装流
//            bis = new BufferedInputStream(fis);// 组合起来写bis = new BufferedInputStream(new FileInputStream("chapter08/src/file05.txt"));// 读和FileInputStream用法完全相同byte[] buf = new byte[1024];int readCount = 0;while ((readCount = bis.read(buf)) != -1) {System.out.println(new String(buf, 0, readCount));}} catch (Exception e) {e.printStackTrace();} finally {// 包装流以及节点流,只需要关闭最外层的包装流即可,节点流不需要手动关闭if (bis != null) {try {bis.close();} catch (IOException e) {e.printStackTrace();}}}}

BufferedOutputStream

try {bos = new BufferedOutputStream(new FileOutputStream("chapter08/src/file05.txt"));// BufferedOutputStream的write()方法只能接受字节数组或单个字节。若直接传入字符串,需通过getBytes()将其转换为字节数组// 若字符串包含中文或其他多字节字符,必须通过getBytes()指定编码//  bos.write("中文".getBytes(StandardCharsets.UTF_8));  // 显式指定编码bos.write("张三李四王麻子".getBytes());//          若数据已经是byte[]或单个字节(int类型),可直接写入byte[] data = {97, 98, 99};bos.write(data);bos.write(10);      // int, double等直接写入数值会丢失精度(仅取最低8位)
//  bos.write(zhangsan.getBytes());// 手动刷新bos.flush();
}

缓冲流的复制

try(BufferedInputStream bis = new BufferedInputStream(new FileInputStream("chapter08/src/file05.txt"));BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("chapter08/src/file06.txt"))){// 一边读一边写byte[] bytes = new byte[1024];int readCount = 0;while((readCount = bis.read(bytes)) != -1){bos.write(bytes, 0, readCount);}// 手动刷新bos.flush();
}

BufferedReader / BufferedWriter

bw = new BufferedWriter(new FileWriter("chapter08/src/file07.txt"));bw.write("hello");
bw.write(97);
bw.write("张三李四王麻子");
bw.flush();BufferedReader br = new BufferedReader(new FileReader("chapter08/src/file06.txt"));// 开始读(br.readLine()方法每次读取一行,如果读取不到任何数据,则返回null)
String s = null;
while((s = br.readLine()) != null) {System.out.println(s);
}br.close();

数据流(数据字节流)

DataOutputStream 数据字节输出流 

// java.io.DataOutputStream:数据流(数据字节输出流)// 作用:将java程序中的数据直接写入到文件,写到文件中就是二进制。// DataOutputStream写的效率很高,原因是:写的过程不需要转码。// DataOutputStream写到文件中的数据,只能由DataInputStream来读取。
DataOutputStream dos = new DataOutputStream(new FileOutputStream("data.txt"));// 准备数据
byte b = -127;
short s = 32767;
int i = 2147483647;
long l = 1111111111L;
float f = 3.0F;
double d = 3.14;
boolean flag = false;
char c = '国';
String str = "动力节点";// 开始写
dos.writeByte(b);
dos.writeShort(s);
dos.writeInt(i);
dos.writeLong(l);
dos.writeFloat(f);
dos.writeDouble(d);
dos.writeBoolean(flag);
dos.writeChar(c);
dos.writeUTF(str);dos.flush();
dos.close();

DataInputStream 数据字节输入流

// java.io.DataInputStream:数据流(数据字节输入流)// 作用:专门用来读取使用DataOutputStream流写入的文件。// 注意:读取的顺序要和写入的顺序一致。(要不然无法恢复原样。)// 创建数据字节输入流对象
DataInputStream dis = new DataInputStream(new FileInputStream("data.txt"));//        System.out.println(dis.readBoolean());        // 此时读到的是第一个二进制位// 开始读
//        byte b = dis.readByte();
//        short s = dis.readShort();
//        int i = dis.readInt();
//        long l = dis.readLong();
//        float f = dis.readFloat();
//        double d = dis.readDouble();
//        boolean flag = dis.readBoolean();
//        char c = dis.readChar();
//        String str = dis.readUTF();
//
//        System.out.println(b);
//        System.out.println(s);
//        System.out.println(i);
//        System.out.println(l);
//        System.out.println(f);
//        System.out.println(d);
//        System.out.println(flag);
//        System.out.println(c);
//        System.out.println(str);// 关闭流
dis.close();FileInputStream fis = new FileInputStream("data.txt");System.out.println(fis.read());     // 129    无符号值
System.out.println(fis.read());     // 127
System.out.println(fis.read());     // 255
System.out.println(fis.read());     // 127fis.close();

对象流

ObjectOutputStream    (序列化)

// java.io.ObjectOutputStream:对象流(对象字节输出流)// 1. 它的作用是完成对象的序列化过程。// 2. 它可以将JVM当中的Java对象序列化到文件中/网络中。// 3. 序列化:将Java对象转换为字节序列的过程。(字节序列可以在网络中传输。)// 4. 序列化:serial// 创建“对象字节输出流”对象
// 包装流
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("object"));// 准备一个Java对象
Date nowTime = new Date();
System.out.println(nowTime);// 序列化 serial
oos.writeObject(nowTime);// 刷新
oos.flush();// 关闭
oos.close();

序列化对象如果是多个对象的话,一般会序列化一个集合。

Date date1 = new Date();
Date date2 = new Date();
Date date3 = new Date();
Date date4 = new Date();
Date date5 = new Date();
Date date6 = new Date();List<Date> list = new ArrayList<>();list.add(date1);
list.add(date2);
list.add(date3);
list.add(date4);
list.add(date5);
list.add(date6);// 序列化
ObjectOutputStream dos = new ObjectOutputStream(new FileOutputStream("dates"));
dos.writeObject(list);// 反序列化
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("dates"));List<Date> dates = (List<Date>)ois.readObject();for(Date date : dates){System.out.println(date);
}dos.flush();
dos.close();

序列化自定义对象

  1. 凡是参与 序列化和反序列化 的对象必须实现 java.io.Serializable 可序列化的接口。
  2. 这个接口是一个标志接口,没有任何方法。只是起到一个标记的作用。

  3. 当java程序中类实现了Serializable接口,编译器会自动给该类添加一个“序列化版本号”:序列化版本号:serialVersionUID

  4. 序列化版本号的作用?

    在Java语言中是如何区分class版本的?

    首先通过类的名字,然后再通过序列化版本号进行区分的。

  5. 为了保证序列化的安全,只有同一个class才能进行序列化和反序列化。在java中是如何保证同一个class的?

    类名 + 序列化版本号:serialVersionUID

Student对象
public class Student implements Serializable {// 建议:不是必须的。// 如果你确定这个类确实还是以前的那个类。类本身是合法的。没有问题。// 建议将序列化版本号写死!@Serialprivate static final long serialVersionUID = -7005027670916214239L;// 随着java版本的更改,源代码类中同样有着固定的序列化版本号        Alt+回车可以随机生成序列号private String name;private transient int age;      // transient关键字修饰的属性不会参与序列化。反序列化为0private String addr;public String getAddr() {return addr;}public void setAddr(String addr) {this.addr = addr;}@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +", age=" + age +'}';}public Student() {}public Student(String name, int age) {this.name = name;this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}
}
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("student"));Student stu = new Student("zhangsan", 20);oos.writeObject(stu);// 其实ObjectOutputStream中也有这些方法,和DataOutputStream中的方法一样。
oos.writeInt(100);
oos.writeBoolean(false);
oos.writeUTF("张三");oos.flush();oos.close();

ObjectInputStream   反序列化

专门完成反序列化的。(将字节序列转换成JVM当中的java对象。)

// 包装流
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("object"));// 读
Object o = ois.readObject();System.out.println(o);// 关闭
ois.close();

打印流PrintStream

专业的负责打印的流,(字节形式)。

PrintStream不需要手动刷新,自动刷新。

// 创建一个打印流对象
// 构造方法:PrintStream(OutputStream out)
// 构造方法:PrintStream(String fileName)
PrintStream ps = new PrintStream("log1");// 没有这样的构造方法。
//PrintStream ps2 = new PrintStream(new FileWriter(""));//PrintStream ps2 = new PrintStream(new FileOutputStream("log1"));      // 这个支持// 打印流可以打印各种数据类型数据。
ps.print(100);
ps.println(false);
ps.println("abc");
ps.println('T');
ps.println(3.14);
ps.println("hell world");
ps.println(200);ps.println("\"hello world!\"");String name = "张三";
double score = 95.5;ps.printf("姓名:%s,考试成绩:%.2f", name, score);      // %c表示字符   %d表示整数// 关闭
ps.close();

PrintWriter(字符形式)

需要手动刷新

/**
PrintWriter比PrintStream多一个构造方法:PrintStream构造方法:PrintStream(OutputStream)PrintWriter构造方法:PrintWriter(OutputStream)PrintWriter(Writer)
*/
// 创建字符打印流
//PrintWriter pw = new PrintWriter(new FileOutputStream("log2"));PrintWriter pw = new PrintWriter(new FileWriter("log2"), true);     // 支持自动刷新// 打印
pw.println("world hello!!!");
pw.println("zhangsan hello!!!");// 刷新
//pw.flush();// 关闭
pw.close();

标准输入流

/*** 标准输入流:System.in*      1. 标准输入流怎么获取?*          System.in       获取到的InputStream就是一个标准输入流*      2. 标准输入流是从哪个数据源读取数据的?*          控制台。*      3. 普通输入流是从哪个数据源读取数据的?*          文件或者网络或者其他.....*      4. 标准输入流是一个全局的输入流,不需要  手动关闭  。JVM退出的时候,JVM会负责关闭这个流。*          标准输入流是用来接收用户在控制台上的输入的;  普通输入流是用来获得文件或网络中的数据*/
public class SystemInTest {public static void main(String[] args) throws Exception{// 获取标准输入流对象。(直接通过系统类System中的in属性来获取标准输入流对象。)InputStream in = System.in;// 开始读byte[] bytes = new byte[1024];int readCount = in.read(bytes);for (int i = 0; i < readCount; i++) {System.out.println(bytes[i]);       // 回车键还有个 10}}
}

修改数据源

// 改变数据源的。不让其从控制台读数据。也可以让其从文件中或网络中读取数据// 修改标准输入流的数据源。System.setIn(new FileInputStream("log2"));// 获取标准输入流
InputStream in = System.in;

从键盘接受用户的输入

public class SystemInTest03 {public static void main(String[] args) throws Exception{// 创建BufferedReader对象// InputStreamReader是转换流(包装流)// System.in是节点流BufferedReader br = new BufferedReader(new InputStreamReader(System.in));/*InputStream in = System.in;Reader reader = new InputStreamReader(in);BufferedReader br = new BufferedReader(reader);*/String s = null;while((s = br.readLine()) != null){if("exit".equals(s)){break;}System.out.println("您输入了:" + s);}/*Scanner scanner = new Scanner(System.in);String name = scanner.next();System.out.println("您的姓名是:" + name);*/}
}

File类

// java.io.File// File和IO流没有继承关系,父类是Object,通过File不能完成文件的读和写// File可能是一个文件,也可能是一个目录
public class FileTest01 {public static void main(String[] args) throws Exception{// 构造一个File对象File file = new File("e:/file");// 调用File对象的相关方法System.out.println(file.exists() ? "存在" : "不存在");// 如果不存在则以新文件的形式创建if(!file.exists()){// 以新的文件的形式创建出来file.createNewFile();}// 如果不存在则以目录的形式新建if(!file.exists()){file.mkdir();}// 构造一个File对象File file2 = new File("e:/a/b/c/d");// 如果不存在则以目录的形式新建if(!file2.exists()){file2.mkdirs();}}
}
public class FileTest02 {public static void main(String[] args) {// 构造一个File对象File file1 = new File("E:\\新建文本文档.txt");// 判断文件是否存在,如果存在则删除if(file1.exists()){file1.delete();}File file2 = new File("E:\\a\\b\\c\\d");if(file2.exists()){file2.delete();         // 删除目录}// 创建File对象File file3 = new File("log");       // 此时用的是相对路径// 获取绝对路径System.out.println("log文件的绝对路径:" + file3.getAbsolutePath());// 获取名字System.out.println("文件名:" + file3.getName());// 创建File对象File file4 = new File("E:\\a\\b\\c");// 获取父路径System.out.println("父路径:" + file4.getParent());  // E:\a\b// 判断该路径是否为绝对路径System.out.println(file4.isAbsolute()? "是绝对路径" : "不是绝对路径");     // 是// 判断某个File是目录还是文件。File file5 = new File("E:\\file");System.out.println(file5.isDirectory() ? "是目录" : "是文件");System.out.println(file5.isFile() ? "是文件" : "是目录");// 判断该文件是否是隐藏文件System.out.println(file1.isHidden() ? "隐藏文件" : "非隐藏文件");// 获取文件的最后修改时间点long l = file1.lastModified();Date time = new Date(l);SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");String str = sdf.format(time);System.out.println("文件最后修改时间点:" + str);// 获取文件的大小:总字节数System.out.println(file1.getName() + "文件的总字节数:" + file1.length() + "字节");                           // abcdef是六个字节// 重命名      会将file1文件移动到当前项目下,并重命名为file2File file2 = new File("file2");file1.renameTo(file2);}
}

常用方法

File类的常用方法:File[] listFiles();

public class FileTest04 {public static void main(String[] args) {File file = new File("D:\\Java\\JavaMe\\Java2025\\chapter08\\src");// 获取所有的子文件,包括子目录。  但不会获取子目录中的文件File[] files = file.listFiles();// 遍历数组for(File f : files){System.out.println(f.getName());}System.out.println("=====================================");File file1 = new File("D:\\Java\\JavaMe\\Java2025\\chapter08\\src");File[] files1 = file1.listFiles(new FilenameFilter() {@Overridepublic boolean accept(File dir, String name) {/*if (name.endsWith(".mdj")) {return true;}return false;*/return name.endsWith(".txt");}});for(File f : files1){System.out.println(f.getName());}}
}

目录的递归拷贝

public static void main(String[] args) {// 拷贝源File src = new File("D:\\Java\\Java学习\\Java2025\\chapter08\\src\\"); //// 拷贝目标File dest = new File("D:\\a\\b\\c"); // 桌面:\a\b\c\powernode\02-JavaSE\code\chapter01\A.java// 开始拷贝copy(src, dest);
}

Copy函数

private static void copy(File src, File dest) {if(src.isFile()){// 是文件的时候要拷贝。try(FileInputStream in = new FileInputStream(src);// .substring(2) 指的是从第三个字符位置开始FileOutputStream out = new FileOutputStream(dest.getAbsoluteFile() + src.getAbsolutePath().substring(2))){// 开始拷贝byte[] bytes = new byte[1024 * 1024];int readCount = 0;while((readCount = in.read(bytes)) != -1){out.write(bytes, 0, readCount);}out.flush();}catch(IOException e){e.printStackTrace();}return;}// 假设src是一个目录// 程序能够执行到此处一定是一个目录// 创建目录File newDir = new File(dest.getAbsolutePath() + src.getAbsolutePath().substring(2));if(!newDir.exists()){newDir.mkdirs();}File[] files = src.listFiles();for (File file : files){//System.out.println(file.getAbsolutePath());copy(file, dest);}
}

压缩流

文件压缩GZIPOutputStream(xxx.gz)

// 创建文件字节输入流(读某个文件,这个文件将来就是被压缩的。)
FileInputStream in = new FileInputStream("chapter08/file09.txt");// 创建一个GZIP压缩流对象
GZIPOutputStream gzip = new GZIPOutputStream(new FileOutputStream("chapter08/file09.txt.gz"));// 开始压缩(一边读一边读)
byte[] bytes = new byte[1024];
int readCount = 0;
while((readCount = in.read(bytes)) != -1){gzip.write(bytes, 0, readCount);
}// 非常重要的代码需要调用
// 刷新并且最终生成压缩文件。
gzip.finish();// 关闭流
in.close();
gzip.close();

解压缩GZIPInputStream(xxx.gz)

// 创建GZIP解压缩流对象
GZIPInputStream gzip = new GZIPInputStream(new FileInputStream("chapter08/file09.txt.gz"));// 创建文件字节输出流
FileOutputStream out = new FileOutputStream("chapter08/file09.txt");// 一边读一边写
byte[] bytes = new byte[1024];
int readCount = 0;
while((readCount = gzip.read(bytes)) != -1){out.write(bytes, 0, readCount);
}// 关闭流
gzip.close();
// 节点流关闭的时候会自动刷新,包装流是需要手动刷新的。(补充的知识点。)
out.close();

属性配置文件

使用Properties集合类 + IO流来读取属性配置文件。

将属性配置文件中的配置信息加载到内存中。

(属性配置文件中配置信息为key=value, key不能重复,重复会对key的value进行覆盖)

// 创建输入流对象
//FileReader reader = new FileReader("chapter08/src/db.properties");// getContextClassLoader():获取当前线程的类加载器
// getResource("db.properties"):从类路径根目录(通常是 src 或 resources 目录)查找文件
// getPath():返回文件的系统路径
String path = Thread.currentThread().getContextClassLoader().getResource("db.properties").getPath();  // 这个地址是从类的根目录开始
FileReader reader = new FileReader(path);// 创建一个Map集合(属性类对象)
Properties pro = new Properties();// 加载:将jdbc.properties文件中的配置信息加载到Properties对象中。
pro.load(reader);// 获取所有key
Enumeration<?> names = pro.propertyNames();    // 返回所有键的枚举
while (names.hasMoreElements()) {String name = (String)names.nextElement();String value = pro.getProperty(name);        // 根据键获取值System.out.println(name + "=" + value);
}// 通过key来获取value
String driver = pro.getProperty("driver");
String url = pro.getProperty("url");
String user = pro.getProperty("user");
String password = pro.getProperty("password");System.out.println(driver);
System.out.println(url);
System.out.println(user);
System.out.println(password);// 关闭输入流
reader.close();

绑定配置文件

使用JDK中提供的资源绑定器来绑定属性配置文件。

// 获取资源绑定器对象
// 使用这个工具要求文件也必须是一个属性配置文件。xxx.properties
// 无需处理文件路径和流,ResourceBundle 会自动处理此编码的读取,避免中文等非ASCII字符乱码
// ResourceBundle 默认不支持UTF-8,需通过工具转义字符
ResourceBundle bundle = ResourceBundle.getBundle("com.powernode.javase.io.jdbc");   // 将其当作一个类
//ResourceBundle bundle = ResourceBundle.getBundle("com/powernode/javase/io/jdbc");// 这个获取的是类的根路径下的jdbc.properties文件。   src里面第一层
//ResourceBundle bundle = ResourceBundle.getBundle("jdbc");
// 这个代码的意思是从类的根路径下找db.properties文件。
//ResourceBundle bundle = ResourceBundle.getBundle("db");// 以下两行都是错误的:资源找不到。
//ResourceBundle bundle = ResourceBundle.getBundle("com.powernode.javase.io.db.properties");
//ResourceBundle bundle = ResourceBundle.getBundle("com/powernode/javase/io/db.properties");// 通过key获取value
String driver = bundle.getString("driver");
String url = bundle.getString("url");
String user = bundle.getString("user");
String password = bundle.getString("password");System.out.println(driver);
System.out.println(url);
System.out.println(user);
System.out.println(password);

字节数组流

向内存中的字节数组写数据。

// ByteArrayOutputStream的基本用法。
ByteArrayOutputStream baos = new ByteArrayOutputStream(); //节点流// 开始写
baos.write(1);
baos.write(2);
baos.write(3);// 怎么获取内存中的哪个byte[]数组呢?
byte[] byteArray = baos.toByteArray();
for (byte b : byteArray){System.out.println(b);
}

装饰器设计模式

装饰器设计模式: 包装流和节点流是可以随意组合的。

ObjectOutputStream(包装流)和ByteArrayOutputStream(节点流)进行组合。

ByteArrayOutputStream和ByteArrayInputStream都是内存操作流(节点流),不需要打开和关闭文件等操作。能够方便读写字节数组、图像数据等内存中的数据。程序和内存中的字节数组之间的写入和读取。

// 节点流
ByteArrayOutputStream baos = new ByteArrayOutputStream();
// 包装流
ObjectOutputStream oos = new ObjectOutputStream(baos);// 开始写
oos.writeInt(100);
oos.writeBoolean(false);
oos.writeDouble(3.14);
oos.writeUTF("动力节点");
oos.writeObject(new Date());// 使用了包装流就需要手动刷新一下。
// 但是真正的流是ByteArrayOutputStream所以不用关闭
oos.flush();// 获取内存中的大byte数组
byte[] byteArray = baos.toByteArray();
for(byte b : byteArray){System.out.println(b);
}
// 使用ByteArrayInputStream将上面这个byte数组恢复。
// 读的过程,读内存中的大byte数组。
// 节点流
ByteArrayInputStream bais = new ByteArrayInputStream(byteArray);
// 包装流
ObjectInputStream ois = new ObjectInputStream(bais);// 开始读
System.out.println(ois.readInt());
System.out.println(ois.readBoolean());
System.out.println(ois.readDouble());
System.out.println(ois.readUTF());
System.out.println(ois.readObject());

装饰者设计模式详解

// 接口
public interface Flyable {void fly();
}// 被装饰者
public class Cat implements Flyable{@Overridepublic void fly() {System.out.println("Cat fly!");}
}public class Bird implements Flyable{@Overridepublic void fly() {System.out.println("Bird fly!");}
}

 装饰者

/*** 所有的装饰者应该有一个共同的父类。这个父类通常是一个抽象类。* 所有装饰者的头领。*/
public abstract class FlyableDecorator implements Flyable{private Flyable flyable;        // 实现抽象类,并且装饰抽象类// FlyableDecorator是一个装饰者,在其的构造方法中传入一个被装饰者public FlyableDecorator(Flyable flyable) {this.flyable = flyable;}@Overridepublic void fly() {flyable.fly();}
}
// 如果实现Flyable接口,那么就必须实现其中的方法
public class LogDecorator extends FlyableDecorator{public LogDecorator(Flyable flyable) {super(flyable);}@Overridepublic void fly() {Date now = new Date();SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");System.out.println(sdf.format(now) + ": 起飞前的准备");super.fly();now = new Date();System.out.println(sdf.format(now) + ": 安全降落");}
}
public class TimerDecorator extends FlyableDecorator{public TimerDecorator(Flyable flyable){super(flyable);}@Overridepublic void fly() {// 这里可以添加代码(前增强)long begin = System.currentTimeMillis();super.fly();// 这里可以添加代码(后增强)long end = System.currentTimeMillis();System.out.println("耗时"+(end - begin)+"毫秒");}
}

功能测试

/*** 0. 装饰器设计模式的主要目标:在松耦合的前提下,能够完成功能的扩展。* 1. 在装饰器设计模式中有两个非常重要的角色:装饰者,被装饰者。* 2. 装饰器设计模式当中要求:装饰者 与 被装饰者 应实现同一个接口/同一些接口,继承同一个抽象类....* 3. 为什么装饰者 与 被装饰者 要实现同一个接口呢?*      因为实现了同一个接口之后,对于客户端程序来说,使用装饰者的时候就向在使用被装饰者一样。* 4. 装饰者含有被装饰者的引用。(A has a B。尽量使用has a【耦合度低一些】。不要使用is a。)* */
//Flyable flyable1 = new Cat();
//Flyable flyable1 = new FlyableDecorator(new Cat());Flyable flyable1 = new TimerDecorator(new Cat());
//Flyable flyable1 = new LogDecorator(new Cat());// BufferedReader装饰者,new FileReader("")就是被装饰者
//BufferedReader br = new BufferedReader(new FileReader(""));
flyable1.fly();//Flyable flyable2 = new Bird();
//Flyable flyable2 = new TimerDecorator(new Bird());
Flyable flyable2 = new LogDecorator(new Bird());
flyable2.fly();

深克隆

/*** 之前的深克隆是重写clone()方法* 使用ByteArrayOutputStream和ByteArrayInputStream直接复制的对象就是一个深克隆。* 将要克隆的Java对象写到内存中的字节数组中,再从内存中的字节数组中读取对象,读取到的就是一个深克隆** 除了这个方式之外,对象的拷贝方式:*  1. 调用Object的clone方法,默认是浅克隆,需要深克隆的话,就需要重写clone方法。*  2. 可以通过序列化和反序列化完成对象的克隆。*/
// 准备对象
Address addr = new Address("北京", "朝阳");
User user = new User("zhangsan", 20, addr);// 将Java对象写到一个byte数组中。
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);oos.writeObject(user);oos.flush();// 从byte数组中读取数据恢复java对象
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);// 这就是经过深拷贝之后的新对象
// 深克隆, user并不受影响
User user2 = (User) ois.readObject();user2.getAddr().setCity("南京");System.out.println(user);
System.out.println(user2);

相关文章:

  • 深圳举办2025年全国儿童预防接种日主题宣传活动 全生命周期健康守护再升级
  • Compose笔记(十九)--NestedScroll
  • 基于javaweb的SSM投票管理系统设计与实现(源码+文档+部署讲解)
  • 优化 Nginx 配置主域名重定向与 Mongrel 规则迁移
  • 网络攻防第一~四集
  • asammdf 库的高级功能:优化数据处理和分析
  • Android学习总结之协程对比优缺点(协程一)
  • TP4056 电池管理芯片介绍及电路应用
  • 1.1.1 用于排序规则的IComparable接口使用介绍
  • 直线模组精度测试的标准是什么?
  • 前端面试之吊打面试官 HTML篇
  • 关闭网桥的STP,解决RHEL10上qemu使用u-boot加载uImage自动加载失败的问题
  • RNN——循环神经网络
  • 基于YOLO与PySide6的道路缺陷检测系统(源码)
  • 数据库MySQL学习——day5(总结与复习实践)
  • AAAI2016论文 UCO: A Unified Cybersecurity Ontology
  • i18n-ai-translate开源程序,可以使用DeepSeek等模型将您的 i18nJSON翻译成任何语言
  • PyTorch作为深度学习框架在建筑行业的应用
  • pymongo功能整理与基础操作类
  • 力扣面试150题--合并两个有序链表和随机链表的复制
  • 106岁东江纵队老战士、四川省侨联名誉主席邱林逝世
  • 我国对国家发展规划专门立法
  • 湖南小伙“朱雀玄武敕令”提交申请改名为“朱咸宁”
  • 可实时追踪血液中单个细胞的穿戴医疗设备问世
  • 天津外国语大学原校长修刚突发疾病去世,享年68岁
  • “梅花奖”快闪走入上海张园,朱洁静在石库门前起舞