
Java基础系列文章
Java基础(一):语言概述 | Java基础(二):原码、反码、补码及进制之间的运算 | Java基础(三):数据类型与进制 | Java基础(四):逻辑运算符和位运算符 |
|---|---|---|---|
Java基础(五):流程控制语句 | Java基础(六):数组 | Java基础(七):面向对象编程 | Java基础(八):封装、继承、多态性 |
Java基础(九):Object 类的使用 | Java基础(十):关键字static、代码块、关键字final | Java基础(十一):抽象类、接口、内部类 | Java基础(十二):枚举类 |
Java基础(十三):注解(Annotation) | Java基础(十四):包装类 | Java基础(十五):异常处理 | Java基础(十六):String的常用API |
Java基础(十七):日期时间API | Java基础(十八):java比较器、系统相关类、数学相关类 | Java基础(十九):集合框架 | Java基础(二十):泛型 |
Java基础(二十一):集合源码 | Java基础(二十二):File类与IO流 | Java基础(二十三):反射机制 | Java基础(二十四):网络编程 |
Java基础(二十五):Lambda表达式、方法引用、构造器引用 | Java基础(二十六):Java8 Stream流及Optional类 |
java.io包下可能存在的一个文件或者文件夹不能访问文件内容本身 public File(String pathname) :以pathname为路径(绝对路径和相对路径)创建File对象public class FileObjectTest {
public static void main(String[] args) {
// 文件路径名
String pathname = "D:\\aaa\\bbb.txt";
File file1 = new File(pathname);
// 通过父路径和子路径字符串
String parent = "D:\\aaa";
String child = "bbb.txt";
File file2 = new File(parent, child);
// 通过父级File对象和子路径字符串
File parentDir = new File("D:\\aaa");
String childFile = "bbb.txt";
File file3 = new File(parentDir, childFile);
}
}绝对路径和相对路径
System.getProperty("user.dir")获取 单元测试方法文件中的相对路径,是相对于"当前module"main(包括springboot项目)方法文件中的相对路径,是相对于"当前工程"
resources目录路径URL url = this.getClass().getClassLoader().getResource("");
String filePath = url.getFile();特别注意:
路径分隔符使用\,而Java程序中的\表示转义字符 \\/当成平台无关的路径分隔符File.separator常量值表示new File(“D:”+File.separator + “info.txt”);public String getName() :获取名称public String getPath() :获取路径public String getAbsolutePath():获取绝对路径public long length() :获取文件的大小(字节数)public long lastModified() :获取最后一次的修改时间,毫秒值
相对路径
@Test
public void test1() {
File file1 = new File("hello.txt");
System.out.println("文件名称: " + file1.getName());
System.out.println("文件路径: " + file1.getPath());
System.out.println("绝对路径: " + file1.getAbsolutePath());
System.out.println("文件大小: " + file1.length());
System.out.println("最后修改时间: " + file1.lastModified());
}输出结果:
文件名称: hello.txt
文件路径: hello.txt
绝对路径: /Users/xuchang/Documents/javaCode/study/code-collection/small-projects/hello.txt
文件大小: 11
最后修改时间: 1710768104275绝对路径
@Test
public void test2() {
File file1 = new File("/Users/xuchang/Documents/javaCode/study/code-collection/small-projects/hello.txt");
System.out.println("文件名称: " + file1.getName());
System.out.println("文件路径: " + file1.getPath());
System.out.println("绝对路径: " + file1.getAbsolutePath());
System.out.println("文件大小: " + file1.length());
System.out.println("最后修改时间: " + file1.lastModified());
}输出结果:
文件名称: hello.txt
文件路径: /Users/xuchang/Documents/javaCode/study/code-collection/small-projects/hello.txt
绝对路径: /Users/xuchang/Documents/javaCode/study/code-collection/small-projects/hello.txt
文件大小: 11
最后修改时间: 1710768104275public String[] list() :返回一个String数组,表示该File目录中的所有子文件或目录public File[] listFiles() :返回一个File数组,表示该File目录中的所有的子文件或目录@Test
public void test3() {
//public String[] list()
File file1 = new File("/Users/xuchang/Documents/txt文件");
String[] fileArr = file1.list();
for (String s : fileArr) {
System.out.println(s);
}
System.out.println();
//public File[] listFiles()
File[] files = file1.listFiles();
for (File f : files) {
System.out.println(f);
}
}输出结果:
环境.txt
.DS_Store
我的文件
/Users/xuchang/Documents/txt文件/环境.txt
/Users/xuchang/Documents/txt文件/.DS_Store
/Users/xuchang/Documents/txt文件/我的文件public boolean renameTo(File dest):把文件重命名为指定的文件路径 即:剪切并修改文件名称@Test
public void test4() {
// 资源对象-hello.txt必须存在
File file1 = new File("hello.txt");
// 目标对象-相对路径下io文件夹必须存在,abc.txt必须不存在
File file2 = new File("io/abc.txt");
boolean renameSuccess = file1.renameTo(file2);
System.out.println(renameSuccess ? "重命名成功" : "重命名失败");
}public boolean exists() :文件或目录是否实际存在public boolean isDirectory() :是否为目录(不存在返回false)public boolean isFile() :是否为文件(不存在返回false)public boolean createNewFile() :创建文件。若文件存在,则不创建,返回falsepublic boolean mkdir() :创建文件目录 不创建了,返回falsepublic boolean mkdirs() :创建文件目录 一并创建public boolean delete() :删除文件或者文件夹 目录必须为空才能删除
流(stream)” 的方式进行,可以看做是一种数据的流动
Input/Output的缩写, I/O技术是非常实用的技术,用于处理设备之间的数据传输。如读/写文件,网络通讯等输入input:读取外部数据(磁盘、光盘等存储设备的数据)到程序(内存)中输出output:将程序(内存)数据输出到磁盘、光盘等存储设备中
其他设备上读取到内存中的流 内存 中写出到其他设备上的流 字节为单位,读写数据的流 字符为单位,读写数据的流 

小结:图解

(抽象基类) | 输入流 | 输出流 |
|---|---|---|
字节流 | InputStream | OutputStream |
字符流 | Reader | Writer |

常用的节点流:
常用处理流:
java.io.Reader抽象类是表示用于读取字符流的所有类的父类,可以读取字符信息到内存中。它定义了字符输入流的基本共性功能方法
public int read(): 从输入流读取一个字符 public int read(char[] cbuf): 从输入流中读取一些字符,并将它们存储到字符数组 cbuf中 public int read(char[] cbuf,int off,int len):从输入流中读取一些字符,并将它们存储到字符数组 cbuf中,从cbuf[off]开始的位置存储 public void close() :关闭此流并释放与此流相关联的任何系统资源注意:当完成流的操作时,必须调用close()方法,释放系统资源,否则会造成内存泄漏
java.io.Writer 抽象类是表示用于写出字符流的所有类的超类,将指定的字符信息写出到目的地。它定义了字节输出流的基本共性功能方法
public void write(int c) :写出单个字符public void write(char[] cbuf) :写出字符数组public void write(char[] cbuf, int off, int len) :写出字符数组的某一部分。off:数组的开始索引;len:写出的字符个数public void write(String str) :写出字符串public void flush() :刷新该流的缓冲public void close() :关闭此流java.io.FileReader 类用于读取字符文件,构造时使用系统默认的字符编码和默认字节缓冲区
FileReader(File file): 创建一个新的 FileReader ,给定要读取的File对象FileReader(String fileName): 创建一个新的 FileReader ,给定要读取的文件的名称举例:
@Test
public void test1() throws IOException {
//1. 创建File类的对象,对应着物理磁盘上的某个文件
File file = new File("hello.txt");
//2. 创建FileReader流对象,将File类的对象作为参数传递到FileReader的构造器中
FileReader fr = new FileReader(file);
//3. 通过相关流的方法,读取文件中的数据
// int data = fr.read(); //每调用一次读取一个字符
// while (data != -1) {
// System.out.print((char) data);
// data = fr.read();
// }
int data;
while ((data = fr.read()) != -1) {
System.out.print((char) data);
}
//4. 关闭相关的流资源,避免出现内存泄漏
fr.close();
}@Test
public void test2() {
FileReader fr = null;
try {
//1. 创建File类的对象,对应着物理磁盘上的某个文件
File file = new File("hello.txt");
//2. 创建FileReader流对象,将File类的对象作为参数传递到FileReader的构造器中
fr = new FileReader(file);
//3. 通过相关流的方法,读取文件中的数据
int data;
while ((data = fr.read()) != -1) {
System.out.println((char) data);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//4. 关闭相关的流资源,避免出现内存泄漏
try {
if (fr != null)
fr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}@Test
public void test3() {
FileReader fr = null;
try {
//1. 创建File类的对象,对应着物理磁盘上的某个文件
File file = new File("hello.txt");
//2. 创建FileReader流对象,将File类的对象作为参数传递到FileReader的构造器中
fr = new FileReader(file);
//3. 通过相关流的方法,读取文件中的数据
char[] cbuf = new char[5];
/*
* read(char[] cbuf) : 每次将文件中的数据读入到cbuf数组中,并返回读入到数组中的
* 字符的个数。
* */
int len; //记录每次读入的字符的个数
while ((len = fr.read(cbuf)) != -1) {
//处理char[]数组即可
//错误:
// for(int i = 0;i < cbuf.length;i++){
// System.out.print(cbuf[i]);
// }
//错误:
// String str = new String(cbuf);
// System.out.print(str);
//正确:
// for(int i = 0;i < len;i++){
// System.out.print(cbuf[i]);
// }
//正确:
String str = new String(cbuf, 0, len);
System.out.print(str);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//4. 关闭相关的流资源,避免出现内存泄漏
try {
if (fr != null)
fr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}java.io.FileWriter 类用于写出字符到文件,构造时使用系统默认的字符编码和默认字节缓冲区
FileWriter(File file): 创建一个新的 FileWriter,给定要读取的File对象FileWriter(String fileName): 创建一个新的 FileWriter,给定要读取的文件的名称FileWriter(File file,boolean append): 创建一个新的 FileWriter,指明是否在现有文件末尾追加内容举例:
@Test
public void test01()throws IOException {
// 使用文件名称创建流对象
FileWriter fw = new FileWriter(new File("fw.txt"));
// 写出数据
fw.write(97); // 写出第1个字符
fw.write('b'); // 写出第2个字符
fw.write('C'); // 写出第3个字符
fw.write(30000); // 写出第4个字符,中文编码表中30000对应一个汉字。
// 字符串转换为字节数组
char[] chars = "尚硅谷".toCharArray();
// 写出字符数组
fw.write(chars); // 尚硅谷
// 写出从索引1开始,2个字符。
fw.write(chars,1,2); // 硅谷
// 写出字符串
fw.write("尚硅谷");
//关闭资源
fw.close();
}@Test
public void test02(){
FileWriter fw = null;
try {
//1. 创建File的对象
File file = new File("personinfo.txt");
//2. 创建FileWriter的对象,将File对象作为参数传递到FileWriter的构造器中
//如果输出的文件已存在,则会对现有的文件进行覆盖
fw = new FileWriter(file);
// fw = new FileWriter(file,false);
//如果输出的文件已存在,则会在现有的文件末尾写入数据
// fw = new FileWriter(file,true);
//3. 调用相关的方法,实现数据的写出操作
//write(String str) / write(char[] cbuf)
fw.write("I love you,");
fw.write("you love him.");
fw.write("so sad".toCharArray());
} catch (IOException e) {
e.printStackTrace();
} finally {
//4. 关闭资源,避免内存泄漏
try {
if (fw != null)
fw.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}小结
必须在物理磁盘上存在,否则执行就会报FileNotFoundException 可以不存在的 覆盖已有的文件追加写出内容flush() 方法了flush() :刷新缓冲区,流对象可以继续使用close() :先刷新缓冲区,然后通知系统释放资源。流对象不可以再被使用了注意:即便是flush()方法写出了数据,操作的最后还是要调用close方法,释放系统资源
举例:
public class FWWriteFlush {
//注意:应该使用try-catch-finally处理异常。这里出于方便阅读代码,使用了throws的方式
@Test
public void test() throws IOException {
// 使用文件名称创建流对象
FileWriter fw = new FileWriter("fw.txt");
// 写出数据,通过flush
fw.write('刷'); // 写出第1个字符
fw.flush();
fw.write('新'); // 继续写出第2个字符,写出成功
fw.flush();
// 写出数据,通过close
fw.write('关'); // 写出第1个字符
fw.close();
fw.write('闭'); // 继续写出第2个字符,【报错】java.io.IOException: Stream closed
fw.close();
}
}如果我们读取或写出的数据是非文本文件,则Reader、Writer就无能为力了,必须使用字节流
java.io.InputStream 抽象类是表示字节输入流的所有类的超类,可以读取字节信息到内存中。它定义了字节输入流的基本共性功能方法
public int read(): 从输入流读取一个字节。返回读取的字节值。虽然读取了一个字节,但是会自动提升为int类型 public int read(byte[] b): 从输入流中读取一些字节数,并将它们存储到字节数组 b中 public int read(byte[] b,int off,int len):从输入流中读取一些字节数,并将它们存储到字节数组 b中 public void close() :关闭此输入流并释放与此流相关联的任何系统资源说明:close()方法,当完成流的操作时,必须调用此方法,释放系统资源
java.io.OutputStream 抽象类是表示字节输出流的所有类的超类,将指定的字节信息写出到目的地。它定义了字节输出流的基本共性功能方法
public void write(int b) :将指定的字节输出流。虽然参数为int类型四个字节,但是只会保留一个字节的信息写出public void write(byte[] b):将 b.length字节从指定的字节数组写入此输出流public void write(byte[] b, int off, int len) :从指定的字节数组写入 len字节,从偏移量 off开始输出到此输出流public void flush() :刷新此输出流并强制任何缓冲的输出字节被写出public void close() :关闭此输出流并释放与此流相关联的任何系统资源java.io.FileInputStream 类是文件输入流,从文件中读取字节
FileInputStream(File file): 通过打开与实际文件的连接来创建一个 FileInputStream ,该文件由文件系统中的 File对象 file命名FileInputStream(String name): 通过打开与实际文件的连接来创建一个 FileInputStream ,该文件由文件系统中的路径名 name命名举例:
@Test
public void test() throws IOException {
// 使用文件名称创建流对象
FileInputStream fis = new FileInputStream("read.txt");
// 读取数据,返回一个字节
int read = fis.read();
System.out.println((char) read);
read = fis.read();
System.out.println((char) read);
read = fis.read();
System.out.println((char) read);
read = fis.read();
System.out.println((char) read);
read = fis.read();
System.out.println((char) read);
// 读取到末尾,返回-1
read = fis.read();
System.out.println(read);
// 关闭资源
fis.close();
/*
文件内容:abcde
输出结果:
a
b
c
d
e
-1
*/
}@Test
public void test02()throws IOException{
// 使用文件名称创建流对象
FileInputStream fis = new FileInputStream("read.txt");
// 定义变量,保存数据
int b;
// 循环读取
while ((b = fis.read())!=-1) {
System.out.println((char)b);
}
// 关闭资源
fis.close();
}@Test
public void test04()throws IOException{
// 使用文件名称创建流对象.
FileInputStream fis = new FileInputStream("read.txt"); // 文件中为abcde
// 定义变量,作为有效个数
int len;
// 定义字节数组,作为装字节数据的容器
byte[] b = new byte[2];
// 循环读取
while (( len= fis.read(b))!=-1) {
// 每次读取后,把数组的有效字节部分,变成字符串打印
System.out.println(new String(b,0,len));// len 每次读取的有效字节个数
}
// 关闭资源
fis.close();
/*
输出结果:
ab
cd
e
*/
}java.io.FileOutputStream 类是文件输出流,用于将数据写出到文件
public FileOutputStream(File file):创建文件输出流,写出由指定的 File对象表示的文件public FileOutputStream(String name): 创建文件输出流,指定的名称为写出文件public FileOutputStream(File file, boolean append): 创建文件输出流,指明是否在现有文件末尾追加内容举例:
@Test
public void test01() throws IOException {
// 使用文件名称创建流对象
FileOutputStream fos = new FileOutputStream("fos.txt");
// 写出数据
fos.write(97); // 写出第1个字节
fos.write(98); // 写出第2个字节
fos.write(99); // 写出第3个字节
// 关闭资源
fos.close();
/* 输出结果:abc*/
}@Test
public void test02()throws IOException {
// 使用文件名称创建流对象
FileOutputStream fos = new FileOutputStream("fos.txt");
// 字符串转换为字节数组
byte[] b = "abcde".getBytes();
// 写出从索引2开始,2个字节。索引2是c,两个字节,也就是cd。
fos.write(b,2,2);
// 关闭资源
fos.close();
}//这段程序如果多运行几次,每次都会在原来文件末尾追加abcde
@Test
public void test03()throws IOException {
// 使用文件名称创建流对象
FileOutputStream fos = new FileOutputStream("fos.txt",true);
// 字符串转换为字节数组
byte[] b = "abcde".getBytes();
fos.write(b);
// 关闭资源
fos.close();
}@Test
public void test05() {
FileInputStream fis = null;
FileOutputStream fos = null;
try {
//1. 造文件-造流
//复制图片:成功
// fis = new FileInputStream(new File("pony.jpg"));
// fos = new FileOutputStream(new File("pony_copy1.jpg"));
//复制文本文件:成功
fis = new FileInputStream(new File("hello.txt"));
fos = new FileOutputStream(new File("hello1.txt"));
//2. 复制操作(读、写)
byte[] buffer = new byte[1024];
int len;//每次读入到buffer中字节的个数
while ((len = fis.read(buffer)) != -1) {
fos.write(buffer, 0, len);
// String str = new String(buffer,0,len);
// System.out.print(str);
}
System.out.println("复制成功");
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
//3. 关闭资源
try {
if (fos != null)
fos.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
try {
if (fis != null)
fis.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}为了提高数据读写的速度,Java API提供了带缓冲功能的流类:缓冲流BufferedInputStream,BufferedOutputStreamBufferedReader,BufferedWriter8192个字节(8Kb)的缓冲区),通过缓冲区读写,减少系统IO次数,从而提高读写的效率

public BufferedInputStream(InputStream in) :创建一个 新的字节型的缓冲输入流public BufferedOutputStream(OutputStream out): 创建一个新的字节型的缓冲输出流public BufferedReader(Reader in) :创建一个 新的字符型的缓冲输入流public BufferedWriter(Writer out): 创建一个新的字符型的缓冲输出流查询API,缓冲流读写方法与基本的流是一致的,我们通过复制大文件(375MB),测试它的效率
public void copyFileWithFileStream(String srcPath,String destPath){
FileInputStream fis = null;
FileOutputStream fos = null;
try {
//1. 造文件-造流
fis = new FileInputStream(new File(srcPath));
fos = new FileOutputStream(new File(destPath));
//2. 复制操作(读、写)
byte[] buffer = new byte[100];
int len;//每次读入到buffer中字节的个数
while ((len = fis.read(buffer)) != -1) {
fos.write(buffer, 0, len);
}
System.out.println("复制成功");
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
//3. 关闭资源
try {
if (fos != null)
fos.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
try {
if (fis != null)
fis.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}public void copyFileWithBufferedStream(String srcPath,String destPath){
FileInputStream fis = null;
FileOutputStream fos = null;
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
try {
//1. 造文件
File srcFile = new File(srcPath);
File destFile = new File(destPath);
//2. 造流
fis = new FileInputStream(srcFile);
fos = new FileOutputStream(destFile);
bis = new BufferedInputStream(fis);
bos = new BufferedOutputStream(fos);
//3. 读写操作
int len;
byte[] buffer = new byte[100];
while ((len = bis.read(buffer)) != -1) {
bos.write(buffer, 0, len);
}
System.out.println("复制成功");
} catch (IOException e) {
e.printStackTrace();
} finally {
//4. 关闭资源(如果有多个流,我们需要先关闭外面的流,再关闭内部的流)
try {
if (bos != null)
bos.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
try {
if (bis != null)
bis.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}public String readLine(): 读一行文字public void newLine(): 写一行行分隔符,由系统属性定义符号public class BufferedIOLine {
@Test
public void testReadLine()throws IOException {
// 创建流对象
BufferedReader br = new BufferedReader(new FileReader("in.txt"));
// 定义字符串,保存读取的一行文字
String line;
// 循环读取,读取到最后返回null
while ((line = br.readLine())!=null) {
System.out.println(line);
}
// 释放资源
br.close();
}
@Test
public void testNewLine()throws IOException{
// 创建流对象
BufferedWriter bw = new BufferedWriter(new FileWriter("out.txt"));
// 写出数据
bw.write("尚");
// 写出换行
bw.newLine();
bw.write("硅");
bw.newLine();
bw.write("谷");
bw.newLine();
// 释放资源
bw.close();
}
}注意:
1、涉及到嵌套的多个流时,如果都显式关闭的话,需要先关闭外层的流,再关闭内层的流
2、其实在开发中,只需要关闭最外层的流即可,因为在关闭外层流时,内层的流也会被关闭
情况1:
FileReader 读取项目中的文本文件public class Problem {
public static void main(String[] args) throws IOException {
FileReader fileReader = new FileReader("E:\\File_GBK.txt");
int data;
while ((data = fileReader.read()) != -1) {
System.out.print((char)data);
}
fileReader.close();
}
}
输出结果:
���引入情况2:
作用:转换流是字节与字符间的桥梁!

具体来说:

java.io.InputStreamReader,是Reader的子类,是从字节流到字符流的桥梁指定的字符集将其解码为字符InputStreamReader(InputStream in): 创建一个使用默认字符集的字符流InputStreamReader(InputStream in, String charsetName): 创建一个指定字符集的字符流举例:
public class InputStreamReaderDemo {
public static void main(String[] args) throws IOException {
// 定义文件路径,文件为gbk编码
String fileName = "E:\\file_gbk.txt";
//方式1:
// 创建流对象,默认UTF8编码
InputStreamReader isr1 = new InputStreamReader(new FileInputStream(fileName));
// 定义变量,保存字符
int charData;
// 使用默认编码字符流读取,乱码
while ((charData = isr1.read()) != -1) {
System.out.print((char)charData); // ��Һ�
}
isr1.close();
//方式2:
// 创建流对象,指定GBK编码
InputStreamReader isr2 = new InputStreamReader(new FileInputStream(fileName) , "GBK");
// 使用指定编码字符流读取,正常解析
while ((charData = isr2.read()) != -1) {
System.out.print((char)charData);// 大家好
}
isr2.close();
}
}java.io.OutputStreamWriter ,是Writer的子类,是从字符流到字节流的桥梁指定的字符集将字符编码为字节OutputStreamWriter(OutputStream in): 创建一个使用默认字符集的字符流OutputStreamWriter(OutputStream in,String charsetName): 创建一个指定字符集的字符流举例:
public class OutputStreamWriterDemo {
public static void main(String[] args) throws IOException {
// 定义文件路径
String FileName = "E:\\out_utf8.txt";
// 创建流对象,默认UTF8编码
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(FileName));
// 写出数据
osw.write("你好"); // 保存为6个字节
osw.close();
// 定义文件路径
String FileName2 = "E:\\out_gbk.txt";
// 创建流对象,指定GBK编码
OutputStreamWriter osw2 = new OutputStreamWriter(new
FileOutputStream(FileName2),"GBK");
// 写出数据
osw2.write("你好");// 保存为4个字节
osw2.close();
}
}如果需要将内存中定义的变量(包括基本数据类型或引用数据类型)保存在文件中,那怎么办呢?
int age = 300;
char gender = '男';
int energy = 5000;
double price = 75.5;
boolean relive = true;
String name = "巫师";
Student stu = new Student("张三",23,89);Java提供了数据流和对象流来处理这些类型的数据:
byte readByte() short readShort()
int readInt() long readLong()
float readFloat() double readDouble()
char readChar() boolean readBoolean()
String readUTF() void readFully(byte[] b)基本数据类型和对象写入字节输出流中。通过在流中使用文件可以实现Java各种基本数据类型的数据以及对象的持久存储基本数据类型的数据和对象进行读入操作,保存在内存中public ObjectOutputStream(OutputStream out) : 创建一个指定的ObjectOutputStreampublic void writeObject(Object obj):写出一个obj对象public ObjectInputStream(InputStream in) : 创建一个指定的ObjectInputStream举例:
public class ReadWriteDataOfAnyType {
@Test
public void save() throws IOException {
String name = "巫师";
int age = 300;
char gender = '男';
int energy = 5000;
double price = 75.5;
boolean relive = true;
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("game.dat"));
oos.writeUTF(name);
oos.writeInt(age);
oos.writeChar(gender);
oos.writeInt(energy);
oos.writeDouble(price);
oos.writeBoolean(relive);
oos.close();
}
@Test
public void reload()throws IOException{
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("game.dat"));
String name = ois.readUTF();
int age = ois.readInt();
char gender = ois.readChar();
int energy = ois.readInt();
double price = ois.readDouble();
boolean relive = ois.readBoolean();
System.out.println(name+"," + age + "," + gender + "," + energy + "," + price + "," + relive);
ois.close();
}
}1、何为对象序列化机制?
对象序列化机制允许把内存中的Java对象转换成平台无关的二进制流 
对象的类型和对象中存储的属性等信息持久保存了一个对象的信息反序列化对象的数据、对象的类型和对象中存储的数据信息,都可以用来在内存中创建对象public final void writeObject (Object obj) : 将指定的对象写出public final Object readObject () : 读取一个对象
对象所属的类及其属性是可序列化的java.io.Serializable 接口Serializable 是一个标记接口,不实现此接口的类将不会使任何状态序列化或反序列化,会抛出NotSerializableExceptionSerializable 接口transient 关键字修饰静态(static)变量的值不会序列化。因为静态变量的值不属于某个对象举例:
@Data
public class Employee implements Serializable {
//static final long serialVersionUID = 23234234234L;
public static String company; //static修饰的类变量,不会被序列化
public String name;
public String address;
public transient int age; // transient瞬态修饰成员,不会被序列化
}public class ReadWriteObject {
@Test
public void save() throws IOException {
Employee.setCompany("尚硅谷");
Employee e = new Employee("小谷姐姐", "宏福苑", 23);
// 创建序列化流对象
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("employee.dat"));
// 写出对象
oos.writeObject(e);
// 释放资源
oos.close();
System.out.println("Serialized data is saved"); // 姓名,地址被序列化,年龄没有被序列化。
}
@Test
public void reload() throws IOException, ClassNotFoundException {
// 创建反序列化流
FileInputStream fis = new FileInputStream("employee.dat");
ObjectInputStream ois = new ObjectInputStream(fis);
// 读取一个对象
Employee e = (Employee) ois.readObject();
// 释放资源
ois.close();
fis.close();
System.out.println(e);
}
}文本文件:

idea转为字符的文件内容:

问题1:
对于JVM可以反序列化对象,它必须是能够找到class文件的类。如果找不到该类的class文件,则抛出一个 ClassNotFoundException 异常
问题2:
当JVM反序列化对象时,能找到class文件,但是class文件在序列化对象之后发生了修改,那么反序列化操作也会失败,抛出一个InvalidClassException异常
解决办法:
Serializable 接口给需要序列化的类,提供了一个序列版本号:serialVersionUIDstatic final long serialVersionUID = 234242343243L; //它的值由程序员随意指定即可不同版本间的兼容性 自动生成的 可能发生变化。因此,建议显式声明public class Employee implements Serializable {
private static final long serialVersionUID = 1324234L; //增加serialVersionUID
//其它结构:略
}序列化对象写出
// 你会看到通过对象的Class创建了ObjectStreamClass
// ObjectStreamClass会保存类相关的信息,包括获取serialVersionUID
// 之后也会调用ObjectStreamClass的write方法进行写入
ObjectStreamClass desc = ObjectStreamClass.lookup(cl, true);
...
// 此处代码判断对象的类型是否可以被序列化
// Class
if (obj instanceof Class) {
writeClass((Class) obj, unshared);
}
// ObjectStreamClass
else if (obj instanceof ObjectStreamClass) {
writeClassDesc((ObjectStreamClass) obj, unshared);
}
// String
else if (obj instanceof String) {
writeString((String) obj, unshared);
}
// 数组
else if (cl.isArray()) {
writeArray(obj, desc, unshared);
}
// 枚举
else if (obj instanceof Enum) {
writeEnum((Enum<?>) obj, desc, unshared);
}
// Serializable实现序列化接口
else if (obj instanceof Serializable) {
writeOrdinaryObject(obj, desc, unshared);
}
// 其他情况都会抛出异常
else {
if (extendedDebugInfo) {
throw new NotSerializableException(
cl.getName() + "\n" + debugInfoStack.toString());
} else {
throw new NotSerializableException(cl.getName());
}
}// 写入ObjectOutputStream,此处用到了serialVersionUID
void writeNonProxy(ObjectOutputStream out) throws IOException {
// 写入类的名称
out.writeUTF(name);
// 写入SerialVersionUID
out.writeLong(getSerialVersionUID());
// 写入其他标记,这里就直接省略了
...
// 开始写入属性
out.writeShort(fields.length);
for (int i = 0; i < fields.length; i++) {
ObjectStreamField f = fields[i];
out.writeByte(f.getTypeCode());
out.writeUTF(f.getName());
if (!f.isPrimitive()) {
out.writeTypeString(f.getTypeString());
}
}
}反序列化对象读取
private Object readOrdinaryObject(boolean unshared)
throws IOException
{
if (bin.readByte() != TC_OBJECT) {
throw new InternalError();
}
// 读取序列化中的ObjectStreamClass
ObjectStreamClass desc = readClassDesc(false);
...
// 通过反射,创建对象
Object obj;
try {
obj = desc.isInstantiable() ? desc.newInstance() : null;
} catch (Exception ex) {
...
}
// 判断是否实现了Externalizable接口
if (desc.isExternalizable()) {
readExternalData((Externalizable) obj, desc);
}
// 我们并没有实现Externalizable接口,会进入到else判断
else {
// 读取序列化的数据
readSerialData(obj, desc);
}
// 判断是否自定义了readObject方法,如果有会调用,如果没有直接返回obj
// 此处省略
...
return obj;
}if (model.serializable == osc.serializable && !cl.isArray() && suid != osc.getSerialVersionUID()) {
throw new InvalidClassException(osc.name,
"local class incompatible: " +
"stream classdesc serialVersionUID = " + suid +
", local class serialVersionUID = " +
osc.getSerialVersionUID());
}private void defaultReadFields(Object obj, ObjectStreamClass desc)
throws IOException
{
// 异常检查等操作
...
// 开始遍历类的属性列表
ObjectStreamField[] fields = desc.getFields(false);
Object[] objVals = new Object[desc.getNumObjFields()];
int numPrimFields = fields.length - objVals.length;
for (int i = 0; i < objVals.length; i++) {
ObjectStreamField f = fields[numPrimFields + i];
// 注意此处会回到最开始的反序列化位置,完成属性值的反序列化
objVals[i] = readObject0(f.isUnshared());
if (f.getField() != null) {
handles.markDependency(objHandle, passHandle);
}
}
// 通过反射进行赋值
if (obj != null) {
desc.setObjFieldValues(obj, objVals);
}
passHandle = objHandle;
}键盘,输出设备是:显示器重定向:通过System类的setIn,setOut方法对默认设备进行改变 举例:
public class OtherStreamTest {
public static void main(String[] args) {
System.out.println("请输入信息(退出输入e或exit):");
// 把"标准"输入流(键盘输入)这个字节流包装成字符流,再包装成缓冲流
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String s = null;
try {
// 读取用户输入的一行数据 --> 阻塞程序
while ((s = br.readLine()) != null) {
if ("e".equalsIgnoreCase(s) || "exit".equalsIgnoreCase(s)) {
System.out.println("安全退出!!");
break;
}
// 将读取到的整行字符串转成大写输出
System.out.println("-->:" + s.toUpperCase());
System.out.println("继续输入信息");
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (br != null) {
br.close(); // 关闭过滤流时,会自动关闭它包装的底层节点流
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}输出结果:
请输入信息(退出输入e或exit):
abc
-->:ABC
继续输入信息
1aA
-->:1AA
继续输入信息
exit
安全退出!!
进程已结束,退出代码0拓展:
public final static InputStream in = null;
public final static PrintStream out = null;
public final static PrintStream err = null;奇怪的是:
final声明的常量,表示在Java的语法体系中它们的值是不能修改的,而这三个常量对象的值是由C/C++等系统函数进行初始化和修改值的,所以它们故意没有用大写,也有set方法
举例:
public class TestScanner {
@Test
public void test01() throws IOException {
Scanner input = new Scanner(System.in);
PrintStream ps = new PrintStream("1.txt");
while(true){
System.out.print("请输入一个单词:");
String str = input.nextLine();
if("stop".equals(str)){
break;
}
ps.println(str);
}
input.close();
ps.close();
}
@Test
public void test2() throws IOException {
Scanner input = new Scanner(new FileInputStream("1.txt"));
while(input.hasNextLine()){
String str = input.nextLine();
System.out.println(str);
}
input.close();
}
}commonsIO,大大简化了IO开发- 静态方法:IOUtils.copy(InputStream in,OutputStream out)传递字节流,实现文件复制。
- 静态方法:IOUtils.closeQuietly(任意流对象)悄悄的释放资源,自动处理close()方法抛出的异常。举例:
public class Test01 {
public static void main(String[] args)throws Exception {
//- 静态方法:IOUtils.copy(InputStream in,OutputStream out)传递字节流,实现文件复制。
IOUtils.copy(new FileInputStream("E:\\Idea\\io\\1.jpg"),new FileOutputStream("E:\\Idea\\io\\file\\柳岩.jpg"));
//- 静态方法:IOUtils.closeQuietly(任意流对象)悄悄的释放资源,自动处理close()方法抛出的异常。
/* FileWriter fw = null;
try {
fw = new FileWriter("day21\\io\\writer.txt");
fw.write("hahah");
} catch (IOException e) {
e.printStackTrace();
}finally {
IOUtils.closeQuietly(fw);
}*/
}
}- 静态方法:void copyDirectoryToDirectory(File src,File dest):整个目录的复制,自动进行递归遍历
参数:
src:要复制的文件夹路径
dest:要将文件夹粘贴到哪里去
- 静态方法:void writeStringToFile(File file,String content):将内容content写入到file中
- 静态方法:String readFileToString(File file):读取文件内容,并返回一个String
- 静态方法:void copyFile(File srcFile,File destFile):文件复制举例:
public class Test02 {
public static void main(String[] args) {
try {
//- 静态方法:void copyDirectoryToDirectory(File src,File dest);
FileUtils.copyDirectoryToDirectory(new File("E:\\Idea\\io\\aa"),new File("E:\\Idea\\io\\file"));
//- 静态方法:writeStringToFile(File file,String str)
FileUtils.writeStringToFile(new File("day21\\io\\commons.txt"),"柳岩你好");
//- 静态方法:String readFileToString(File file)
String s = FileUtils.readFileToString(new File("day21\\io\\commons.txt"));
System.out.println(s);
//- 静态方法:void copyFile(File srcFile,File destFile)
FileUtils.copyFile(new File("io\\yangm.png"),new File("io\\yangm2.png"));
System.out.println("复制成功");
} catch (IOException e) {
e.printStackTrace();
}
}
}