Java IO 流
1. File
1.1 File 类概述和构造方法
File:它是文件和目录路径名的抽象表示
- 文件和目录是可以通过File封装成对象的
- 对于File而言,其封装的并不是一个真正存在的文件,仅仅是一个路径名而已。它可以是存在的,也可以是不存在的。将来是要通过具体的操作把这个路径的内容转换为具体存在的。
方法名 | 说明 |
---|---|
File(String pathname) | 通过将给定的路径名字符串转换为抽象路径名来创建新的File实例 |
File(String parent,String child) | 从父路径名字符串和子路径名字符串创建新的File实例 |
File(File parent , String child) | 从父抽象路径名和子路径名字符串创建新的File实例 |
public class test {
public static void main(String[] args) {
// File(String pathname) | 通过将给定的路径名字符串转换为抽象路径名来创建新的File实例
File f1 = new File("D:\\JavaCode\\iofile.txt");//路径不存在,但是不报错,说明是转为抽象路径名
System.out.println(f1);//输出:D:\JavaCode\iofile,说明重写了toStirng方法
// File(String parent,String child) | 从父路径名字符串和子路径名字符串创建新的File实例
File f2= new File("D:\\JavaCode","iofile.txt");
System.out.println(f2);//输出:D:\JavaCode\iofile
// File(File parent , String child) | 从父抽象路径名和子路径名字符串创建新的File实例
File f3 = new File("D:\\JavaCode");
File f4 = new File(f3,"iofile.txt");
System.out.println(f4);//输出D:\JavaCode\iofile:
}
}
1.2 File类创建功能
方法名 | 说明 |
---|---|
public boolean createNewFile( ) | 当具有该名称的文件不存在时,创建一个由该抽象路径名命名的新空文件 |
public boolean mkdir( ) | 创建由此抽象路径名命名的目录 |
public boolean mkdirs( ) | 创建由此抽象路径名命名的目录,包括任何必需但不存在的父目录(创建父目录和子目录,多级目录的创建) |
/*
public boolean createNewFile( ) | 当具有该名称的文件不存在时,创建一个由该抽象路径名命名的新空文件
如果文件不存在就创建文件,并返回true;如果文件存在就false
public boolean mkdir( ) | 创建由此抽象路径名命名的目录
如果目录不存在就创建目录,并返回true;如果文件存在就false
public boolean mkdirs( ) | 创建由此抽象路径名命名的目录,包括任何必需但不存在的父目录(创建父目录和子目录,多级目录的创建)
如果目录不存在就创建目录,并返回true;如果文件存在就false
*/
public class test {
public static void main(String[] args) throws IOException {
//需求1:我要在D:\\JavaCode目录下创建iofile.txt文件
File f1 = new File("D:\\JavaCode\\iofile.txt");
System.out.println(f1.createNewFile());//这里需要抛出一个ioexception异常
//输出:true,并且在对应目录下创建了一个文件iofile.txt
//需求2:我要在D:\\JavaCode目录下创建一个目录JavaSE
File f2 = new File("D:\\JavaCode\\JAVASE");
System.out.println(f2.mkdir());//在当前目录下创建一个新目录
//输出:true,并且在对应目录下创建了一个目录JAVASE
//需求3:我要在D:\\JavaCode目录下创建一个多级目录JavaWeb\HTML
File f3 = new File("D:\\JavaCode\\JavaWeb\\HTML");
// System.out.println(f3.mkdir());//false,多级目录从创建不能用mkdir,要用mkdirs
System.out.println(f3.mkdirs());//创建多级目录
//输出:true,并且在JavaCode下创建了JavaWEB,又在JavaWeb下创建了HTML目录
//需求4:我要在D:\\JavaCode目录下创建一个文件iofilese.txt
File f4 = new File("D:\\JavaCode\\iofilse.txt");
System.out.println(f4.mkdir());//这里是创建了一个叫iofilese.txt名字的目录,而不是文件
//具体创建的是目录还是文件,要看我们调用的方法是什么,而不是看我们写的抽象路径名是什么
//如果目录和文件重名,也不能创建成功
System.out.println(f4.createNewFile());//输出:false
}
}
1.3 File类的判断和获取功能
方法名 | 说明 |
---|---|
pubilc boolean isDirectory( ) | 测试此抽象路径名表示的FIle是否为目录 |
public boolean isFile( ) | 测试此抽象路径名表示的FIle是否为文件 |
public boolean exists( ) | 测试此抽象路径名表示的File是否存在 |
public String getAbsolutePath( ) | 返回此抽象路径名的绝对路径名字符串 |
public String getPath( ) | 将此抽象路径名转换为路径名字符串 |
public String getName( ) | 返回由此抽象路径名表示的文件或目录的名称 |
public String[ ] list( ) | 返回此抽象路径名表示的目录中的文件和目录的名称字符串数组 |
public File[ ] listFiles( ) | 返回此抽象路径名表示的目录中的文件和目录的File对象数组 |
import java.awt.image.AreaAveragingScaleFilter;
import java.io.File;
import java.io.IOException;
import java.util.*;
public class test {
public static void main(String[] args) throws IOException {
File f = new File("D:\\JavaCode\\iofile.txt");
System.out.println(f.createNewFile());
// pubilc boolean isDirectory( ) | 测试此抽象路径名表示的FIle是否为目录
// public boolean isFile( ) | 测试此抽象路径名表示的FIle是否为文件
// public boolean exists( ) | 测试此抽象路径名表示的File是否存在
System.out.println(f.isDirectory());//false
System.out.println(f.isFile());//true
System.out.println(f.exists());//true
// public String getAbsolutePath( ) | 返回此抽象路径名的绝对路径名字符串
// public String getPath( ) | 将此抽象路径名转换为路径名字符串
// public String getName( ) | 返回由此抽象路径名表示的文件或目录的名称
System.out.println(f.getAbsolutePath());//D:\JavaCode\iofile.txt
System.out.println(f.getPath());//D:\JavaCode\iofile.txt,一般返回两个路径名称,如果是D://JAVA//JavaSe//JavaCode//iofile.txt就返回JavaCode\\iofile.txt
System.out.println(f.getName());//iofile.txt
// public String[ ] list( ) | 返回此抽象路径名表示的目录中的文件和目录的名称字符串数组
File f2= new File("D:\\JavaCode");
String[] strarray = f2.list();
for (String s : strarray) {
System.out.println(s);
}
/*
输出:
.idea
iofile.txt
Javalearn
*/
// public File[ ] listFiles( ) | 返回此抽象路径名表示的目录中的文件和目录的File对象数组
File[] file = f2.listFiles();
for (File fi : file) {
System.out.println(fi);
/*
D:\JavaCode\.idea
D:\JavaCode\iofile.txt
D:\JavaCode\Javalearn
*/
System.out.println(fi.getName());
/*
.idea
iofile.txt
Javalearn
*/
if(fi.isFile()){
System.out.println(fi.getName());
}
/*
iofile.txt
*/
}
}
}
1.4 File类删除功能
方法名 | 说明 |
---|---|
public boolean delete( ) | 删除由此抽象路径名表示的文件或目录 |
绝对路径和相对路径的区别:
- 绝对路径:完整的路径名,不需要任何其他信息就可以定位它所表示的文件。例如:E:\itcast\java.txt
- 相对路径:必须使用取自其他路径名的信息进行解释。例如:myFile\java.txt
删除目录时的注意事项;
- 如果一个目录中有内容目录,文件),不能直接删除.应该先删除目录中的内容,最后才能删除目录
public class test {
public static void main(String[] args) throws IOException {
//需求1:在当前模块目录下创建java.txt文件
File f1 =new File("javalearn\\java.txt");
System.out.println(f1.createNewFile());//在当前项目的javalearn目录下创建了一个java.txt文件
//需求2:删除在当前模块目录下的java.txt文件
System.out.println(f1.delete());//true
//需求3:在当前模块目录下创建javase目录
File f2 = new File("javalearn\\javase");
System.out.println(f2.mkdir());//true
//需求4:删除在当前模块目录下的javase目录
System.out.println(f2.delete());//true
//需求5:在当前模块目录下创建JAVAYYDS目录,再在在当前目录下创建javayyds.txt文件
File f3 = new File("javalearn\\JAVAYYDS");
System.out.println(f3.mkdir());//true
File f4 = new File("javalearn\\JAVAYYDS\\javayyds");//如果上级目录JAVAYYDS不存在就会报错
System.out.println(f4.createNewFile());//true
//需求6:删除当前目录下的JAVAYYDS目录
//删除目录时,如果该目录下面由子文件,或者子目录,就必须先删除子文件或子目录,否则无法删除该目录
System.out.println(f4.delete());//true
System.out.println(f3.delete());//true
}
}
1.5 递归遍历目录
需求:给定一个路径(”D:\JavaCode“),请通过递归完成遍历该目录下的所有内容,并把所有文件的绝对路径输出在控制台
思路:
- 根据给定的路径创建一个File对象
- 定义一个方法,用于获取给定目录下的所有内容,参数为第1步创建的File对象
- 获取给定的File目录下的所有文件或者目录的File数组
- 遍历该File数组,得到每一个File对象
- 判断该File对象是否是目录
- 是:递归调用
- 不是:获取绝对路径输出在控制台
- 调用方法
public class test {
public static void main(String[] args) throws IOException {
//1. 根据给定的路径创建一个File对象
File srcfile = new File("D:\\JavaCode");
//6. 调用方法
getAllFilePath(srcfile);
}
//2. 定义一个方法,用于获取给定目录下的所有内容,参数为第1步创建的File对象
public static void getAllFilePath(File srcfile){
//3. 获取给定的File目录下的所有文件或者目录的File数组
File[] files = srcfile.listFiles();
if(files!=null){
//4. 遍历该File数组,得到每一个File对象
for (File file : files) {
//5. 判断该File对象是否是目录
if(file.isDirectory()){
//1. 是:递归调用
getAllFilePath(file);
}else{
//2. 不是:获取绝对路径输出在控制台
System.out.println(file.getAbsolutePath());
}
}
}
}
}
2. 字节流
2.1 IO流概述和分类
IO流概述:
- IO:输入/输出(Input/Output)
- 流:是一种抽象概念,是对数据传输的总称。也就是说数据在设备间的传输称为流,流的本质是数据传输
- IO流就是用来处理设备间数据传输问题的
- 常见的应用:文件复制,文件上传,文件下载
IO流分类:
- 按照数据的流向
- 输入流:读数据
- 输出流:写数据
- 按照数据类型来分
- 字节流
- 字节输入流;字节输出流
- 字符流
- 字符输入流;字符输出流
- 字节流
一般来说,我们说IO流分类是按照数据类型来分的
那么这两种流都在什么情况下使用呢?
- 如果数据通过Window自带的记事本软件打开,我们还可以读懂里面的内容,就使用字符流,否则使用字节流。如果你不知道该使用哪种类型的流,那使用字节流,字节流是万能的
2.2 字节流写数据
字节流抽象基类
- InputStream:这个抽象类是表示字节输入流的所有类的超类
- OutputStream:这个抽象类是表示字节输出流的所有类的超类
- 子类名特点:子类名称都是以其父类名作为子类名的后缀
FileOutputStream:文件输出流用于将数据写入File
- FileOutputStream(String name):创建文件输出流以指定的名称写入文件
- FileOutputStream(File file):创建文件输出流以写入由指定的File对象表示的文件
使用字节输出流写数据的步骤:
- 创建字节输出流对象(调用系统功能创建文件,创建字节输出流对象,让字节输出流对象指向文件)
- 调用字节输出流对象的写数据方法
- 释放资源(关闭此文件输出流并释放与此相关联的任何系统资源)
public class test {
public static void main(String[] args) throws IOException {
FileOutputStream fileout = new FileOutputStream("test.txt");
// File f1 = new File("text.txt");
// FileOutputStream fs=new FileOutputStream(f1);
/*
这里自动做了三件事情:
a.调用系统功能自动创建文件
b.创建字节输出流对象
c.让字节输出流对象指向创建好的文件
*/
//void write(int b);将指定的字节写入此文件输出流
fileout.write(98);//写入b
fileout.write(57);//写入9
fileout.write(55);//写入7
//最后一定要释放资源
//void close():关闭此文件输出流,释放与此相关联的任何系统资源
fileout.close();
}
}
2.3 字节流写数据的3种方式
方法名 | 说明 |
---|---|
void write(int b) | 将指定的字节写入此文件输出流,一次写一个字节数据 |
void write(byte[ ] b) | 将b.length字节从指定的字节数组写入此文件输出流,一次写一个字节数组的数据 |
void write(byte[ ] b ,int off , int len) | 将len字节从指定的字节数组开始,从偏移量off(索引位置)开始写入此文件输出流,一次写一个字节数组的部分数据(即从b的字节数组中下标为off的地方到off+len的这段字节写入) |
public class test {
public static void main(String[] args) throws IOException {
FileOutputStream fileout = new FileOutputStream("test.txt");
// void write(int b)
fileout.write(97);
fileout.write(98);
fileout.write(99);
//写入了abc
// void write(byte[ ] b)写一个字节数组的全部数据
byte[] bys={101,102,103,104};
fileout.write(bys);
//写入了efgh
//这里可以用String类下的byte[] getBytes()方法:返回一个字符串对应的一个字节数组
byte[] bys2 = "i love you yy".getBytes();
fileout.write(bys2);
//写入了i love you yy
// void write(byte[ ] b ,int off , int len)写一个字节数组的部分数据
fileout.write(bys2,0,bys2.length);//写入:i love you yy
fileout.write(bys2,2,4);//从下标为2的地方,连续取三个字节,[2,4]
//写入:love
//释放资源
fileout.close();
}
}
2.4 字节流写数据的两个小问题
1.字节流写数据如何实现换行?
不同系统对于换行符的识别不一样,但IDEA里面都可以识别
windows:\r\n
linux:\n
mac:\r
2.字节流写数据如何实现追加写入?
- public FileOutputStream (String name,boolean append)
- 创建文件输出流以指定的名称写入文件, 如果第二个append参数为true,则字节将写入文件的末尾而不是开头
public class test {
public static void main(String[] args) throws IOException {
FileOutputStream fileout = new FileOutputStream("test.txt",true);
for(int i=0;i<10;i++){
fileout.write("hello".getBytes());
//换行
fileout.write("\r\n".getBytes());
}
//追加后,在原来10个hello基础上,在加上10个hello,就变成20个hello
}
}
2.5 字节流写数据加异常处理
finally:在异常处理时提供finally块来执行所有清除操作。比如说IO流中的释放资源
特点:被finally控制的语句一定会执行,除法JVM退出
try{
可能出现异常的代码
}catch(异常类名 变量名){
异常处理的代码
}finally{
执行所有清除操作
}
public class test {
public static void main(String[] args) throws IOException {
FileOutputStream fileout =null;
try{
fileout = new FileOutputStream("F:\\java\\my.txt");//FileNotFoundException
fileout.write(98);
}catch (IOException e){
e.printStackTrace();
}finally {//执行清除资源的操作
if(fileout!=null){
try{
fileout.close();
}catch (IOException e){
e.printStackTrace();
}
}
}
}
}
2.6 字节流读数据(一次读一个字节)
需求:把文件test.txt中的内容读取出来在控制台输出
FileInputStream:从文件系统中的文件获取输入字节
- FileInputStream(String name):通过打开与实际文件的连接来创建一个FileInputStream,该文件由文件系统中的路径名name命名
使用字节输入流读数据的步骤:
- 创建字节输入流对象
- 调用字节输入流对象的读数据方法
- 释放资源
public class test {
public static void main(String[] args) throws IOException {
// 1. 创建字节输入流对象FileInputStream
FileInputStream fis = new FileInputStream("test.txt");
// 2. 调用字节输入流对象的读数据方法
//用 int read():从该输入流读取一个字节的数据,并且返回它的字节值,如果字节读完了,就会返回-1
/* int by=fis.read();
while(by!=-1){
System.out.print((char)by);//将字节数值强制转换为字符
by=fis.read();
}*/
//优化代码
int by;
while((by=fis.read())!=-1){
System.out.print((char)by);
}
/*
fis.read() 读数据
by=fis.read() 赋值给by
by!=-1 判断是否为-1
*/
// 3. 释放资源
fis.close();
}
}
2.7 字节流复制文本文件
需求:把“D:\JavaCode\test.txt”复制到模块目录下的“test.txt”
分析:
- 复制文本文件,其实就把文本文件的内容从一个文件中读取出来(数据源),然后写入到另一个文件中(目的地)
- 数据源:D:\JavaCode\test.txt-----读数据-----InpubtStream—FileInputStreaem
- 目的地:destop\test.txt----写数据-----OutputStream----FileOutputStream
思路:
- 根据数据源创建字节输入流对象
- 根据目的地创建字节输出流对象
- 读写数据,复制文本文件(一次读取一个字节,一次写入一个字节)
- 释放资源
public class test {
public static void main(String[] args) throws IOException {
// 1. 根据数据源创建字节输入流对象
FileInputStream pis = new FileInputStream("D:\\JavaCode\\test.txt");
// 2. 根据目的地创建字节输出流对象
FileOutputStream pos = new FileOutputStream("C:\\Users\\lu'hai'peng\\Desktop\\test.txt");
// 3. 读写数据,复制文本文件(一次读取一个字节,一次写入一个字节)
int by;
while((by=pis.read())!=-1){
pos.write(by);
}
// 4. 释放资源
pis.close();
pos.close();
}
}
2.8 字节流读取数据(一次读一个字节数组数据)
需求:把文件test.txt中的内容读取出来在控制台输出
使用字节输入流读取数据的步骤:
- 创建字节输入流对象
- 调用字节输入流对象的读数据方法
- 释放资源
public class test {
public static void main(String[] args) throws IOException {
FileInputStream fis =new FileInputStream("D:\\JavaCode\\test.txt");
//注意,读数据时,\r\n也会当数据读入,
byte[] bys = new byte[1024];//这里最好是1024的整数倍
int len;
while((len=fis.read(bys))!=-1){//把fis里面的内容写入bys中
System.out.println(new String(bys,0,len));//将内容转换从字符串读出,输出bys中0到len长度的字节转换后的字符
}
}
}
2.9 复制图片
需求:把“D:\JavaCode\mm.jpg”复制到模块下的mm.jpg
思路:
- 根据数据源创建字节输入对象
- 根据目的地创建字节输出对象
- 读写数据,复制图片(一次读取一个字节数组,一次写入一个字节数组)
- 释放资源
public class test {
public static void main(String[] args) throws IOException {
// 1. 根据数据源创建字节输入对象
FileInputStream fis = new FileInputStream("D:\\JavaCode\\mm.jpg");
// 2. 根据目的地创建字节输出对象
FileOutputStream fos = new FileOutputStream("C:\\Users\\lu'hai'peng\\Desktop\\mm.jpg");
// 3. 读写数据,复制图片(一次读取一个字节数组,一次写入一个字节数组)
byte[] bys = new byte[1024];
int len;
while((len=fis.read(bys))!=-1){
fos.write(bys,0,len);
}
// 4. 释放资源
fis.close();
fos.close();
}
}
2.10 字节缓冲流
字节缓冲流:
- BufferOutputStream:该类实现缓冲输出流。通过设置这样的输出流,应用程序可以向底层输出流写入字节,而不必为写入的每个字节导致底层系统的调用
- BufferInputStream:创建BufferInputStream将创建一个内部缓冲区数组。当从流中读取或跳过字节时,内部缓冲区将根据需要从所包含的输入流中重新填充,一次很多字节
构造方法:
- 字节缓冲输出流:BufferedOutputStream(OutputStream out)
- 字节缓冲输入流:BufferedInputStream(InputStream in)
为什么构造方法需要的是字节流,而不是具体的文件或者路径呢?
- 字节缓冲流仅仅提供缓冲区,而真正的读写数据还得依靠基本的字节流对象进行操作
public class test {
public static void main(String[] args) throws IOException {
// 字节缓冲输出流:BufferedOutputStream(OutputStream out)
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("D:\\JavaCode\\test.txt"));
bos.write("hello\r\n".getBytes());
bos.write("world\r\n".getBytes());
// 字节缓冲输入流:BufferedInputStream(InputStream in)
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("D:\\JavaCode\\test.txt"));
//一次读一个字节
int by;
while((by=bis.read())!=-1){
System.out.println(by);
}
//一次读一个字节数组
byte[] bys = new byte[1024];
int len;
while((len=bis.read(bys))!=-1){
System.out.println(new String(bys,0,len));//将bys转换为String再输出
}
bos.close();;
bis.close();
}
}
2.11 复制视频
需求:把“D:\JavaCode\test.avi”复制到模块目录下的“test.avi”
思路:
- 根据数据源创建字节输入流对象
- 根据目的地创建字节输出流对象
- 读写数据,复制视频
- 释放资源
四中方式实现复制视频,并且记录每种方式的时间:
- 基本字节流一次读写一个字节 (64565毫秒)
- 基本字节流一次读写一个字节数组(107毫秒)
- 字节缓冲流一次读写一个字节(405毫秒)
- 字节缓冲流一次读写一个字节数组(60毫秒)
public class test {
public static void main(String[] args) throws IOException {
long start = System.currentTimeMillis();
// 1. 基本字节流一次读写一个字节 (64565毫秒)
method1();
// 2. 基本字节流一次读写一个字节数组(107毫秒)
method2();
// 3. 字节缓冲流一次读写一个字节(405毫秒)
method3();
// 4. 字节缓冲流一次读写一个字节数组(60毫秒)
method4();
long end = System.currentTimeMillis();
System.out.println("耗时:" + (end - start));
}
//1. 基本字节流一次读写一个字节 (64565毫秒)
public static void method1() throws IOException {
FileInputStream fis = new FileInputStream("D:\\JavaCode\\test.avi");
FileOutputStream fos = new FileOutputStream("test.avi");
int by;
while ((by = fis.read()) != -1) {
fos.write(by);
}
fis.close();
fos.close();
}
// 2. 基本字节流一次读写一个字节数组(107毫秒)
public static void method2() throws IOException {
FileInputStream fis = new FileInputStream("D:\\JavaCode\\test.avi");
FileOutputStream fos = new FileOutputStream("test.avi");
byte[] bys = new byte[1024];
int len;
while ((len = fis.read(bys)) != -1) {
fos.write(bys, 0, len);
}
fis.close();
fos.close();
}
// 3. 字节缓冲流一次读写一个字节(405毫秒)
public static void method3() throws IOException {
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("D:\\JavaCode\\test.avi"));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("test.avi"));
int by;
while ((by = bis.read()) != -1) {
bos.write(by);
}
bis.close();
bos.close();
}
// 4. 字节缓冲流一次读写一个字节数组(60毫秒)
public static void method4() throws IOException {
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("D:\\JavaCode\\test.avi"));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("test.avi"));
byte[] bys = new byte[1024];
int len;
while ((len = bis.read(bys)) != -1) {
bos.write(bys, 0, len);
}
bis.close();
bos.close();
}
}
3. 字符流
3.1 为什么会出现字符流
由于字节流操作中文不是特别方便,所以java就提供字符流
- 字符流=字节流+编码表
用字节流复制文本文件时,文本文件也会有中文,但是没有问题,原因时最终底层操作会自动进行字节拼接成中文,如何识别是中文的呢?
- 汉字才存储的时候,无论选择哪种编码存储,第一个字节都是负数
一个汉字存储:
如果是GBK编码,占用2个字节
如果是UTF-8编码,占用3个字节
3.2 编码表
- ASCII:多用于英文
- GB2312:简码中文码表
- GBK:最常用的中文码表(重点掌握)
- GB18030:最新的中文码表
Unicode字符集:
- 表达任意语言的字符而设计,有UTF-8,UTF-16,UTF-32
- 最常用UTF-8
小结:采用何种规则编码,就要采用对应规则解码,否则就会出现乱码
3.3 字符串中的编码解码问题
编码:
- byte[] getBytes( ):使用平台的默认字符集将该String编码为一系列字节,将结果存储到新的字节数组中
- byte[] getBytes(String charsetName):使用指定的字符集将该String编码为一系列字节,将结果存储到新的字节数组中
解码:
- String(byte[] bytes):通过使用平台的默认字符集解码指定的字节数组来构造新的String
- String(byte[] bytes , String charsetName):通过指定的字符集解码指定的字节数组来构造新的String
public class test {
public static void main(String[] args) throws IOException {
String s ="中国";
//编码:
// - byte[] getBytes( ):使用平台的默认字符集将该String编码为一系列字节,将结果存储到新的字节数组中
byte[] bys1 = s.getBytes();
System.out.println(Arrays.toString(bys1));//[-28, -72, -83, -27, -101, -67]三个编码表示一个汉字UTF-8
// - byte[] getBytes(String charsetName):使用指定的字符集将该String编码为一系列字节,将结果存储到新的字节数组中
byte[] bys2 = s.getBytes("UTF-8");
System.out.println(Arrays.toString(bys2));//[-28, -72, -83, -27, -101, -67]
byte[] bys3 = s.getBytes("GBK");
System.out.println(Arrays.toString(bys3));//[-42, -48, -71, -6],两个编码表示一个汉字
//解码:
// - String(byte[] bytes):通过使用平台的默认字符集解码指定的字节数组来构造新的String
System.out.println(new String(bys1));//中国
// - String(byte[] bytes , String charsetName):通过指定的字符集解码指定的字节数组来构造新的String
System.out.println(new String(bys1,"UTF-8"));//中国
System.out.println(new String(bys1,"GBK"));//涓浗
System.out.println(new String(bys3,"GBK"));//中国
}
}
3.4 字符流中的编码和解码问题
字符流抽象基类
- Reader:字符输入流的抽象类
- Writer:字符输出流的抽象类
字符流中和编码解码问题相关的两个类:
- InputStreamReader:是字节流到字符流的桥梁
它读取字节,并使用指定的编码将其解码为字符
它使用的字符集可以由名称指定,也可以被明确指定,或者可以接收平台的默认字符集
- OutputStreamWriter:是字符流到字节流的桥梁
是从字符流到字节流的桥梁,使用指定的编码将写入的字符编码为字节
它使用的字符集可以由名称指定,也可以被明确指定,或者可以接收平台的默认字符集
public class test {
public static void main(String[] args) throws IOException {
// OutputStreamWriter(OutputStream out):默认字符编码的OutputStreamWriter
OutputStreamWriter os1 = new OutputStreamWriter(new FileOutputStream("test.txt"));
os1.write("中国");
os1.close();
// OutputStreamWriter(OutputStream out,String charsetName):指定字符编码的OutputStreamWriter
OutputStreamWriter os2 = new OutputStreamWriter(new FileOutputStream("test.txt"),"UTF-8");
OutputStreamWriter os3 = new OutputStreamWriter(new FileOutputStream("test.txt"),"GBK");
os2.write("中国yyds");//正常输出
os3.write("中国yyds");//乱码
os2.close();
os3.close();
// InputStreamReader(InputStream in):默认字符编码的InputStreamReader
InputStreamReader ir1 = new InputStreamReader(new FileInputStream("test.txt"));
//一个字符一个字符发读
int ch;
while((ch=ir1.read())!=-1){
System.out.print((char)ch);
}
//以字符数组的形式读
int len;
char[] chs=new char[1024];
while((len=ir1.read(chs))!=-1){
System.out.print(chs.toString());
}
// InputStreamReader(InputStream in,String charsetName):指定字符编码的InputStreamReader
InputStreamReader ir2 = new InputStreamReader(new FileInputStream("test.txt"),"GBK");
//一个字符一个字符发读
int ch2;
while((ch2=ir2.read())!=-1){
System.out.print((char)ch2);
}
//以字符数组的形式读
int len2;
char[] chs2=new char[1024];
while((len2=ir1.read(chs2))!=-1){
System.out.print(chs2.toString());
}
}
}
3.5 字符流写数据的5种方式
方法名 | 说明 |
---|---|
void write(int c) | 写一个字符 |
void write(char[ ] cbuf) | 写入一个字符数组 |
void write(char[ ] cbuf,int off,int len) | 写入字符数组的一部分 |
void write(String str) | 写一个字符串 |
void write(String str,int off,int len) | 写一个字符串的一部分 |
方法名 | 说明 |
---|---|
flush( ) | 刷新流,还可以继续写数据 |
close( ) | 关闭流,释放资源,但是在关闭之前会先刷新流。一旦关闭,就不能再写数据 |
public class test {
public static void main(String[] args) throws IOException {
// OutputStreamWriter(OutputStream out):默认字符编码的OutputStreamWriter
OutputStreamWriter os1 = new OutputStreamWriter(new FileOutputStream("test.txt"));
//void write(char c)
os1.write(97);//执行到这一步是不会在文件里显示内容的,该内容现在只是在缓存区
//void flush():刷新流,用于文本中的内容显示出来
os1.flush();
// void write(char[ ] cbuf) | 写入一个字符数组
char[] chs= {'a','b','c','d'};
os1.write(chs);
// void write(char[ ] cbuf,int off,int len) | 写入字符数组的一部分
os1.write(chs,0,chs.length);
os1.write(chs,0,2);//从第1个开始,连续写2个
// void write(String str) | 写一个字符串
String s="abcdef";
os1.write(s);
// void write(String str,int off,int len) | 写一个字符串的一部分
os1.write(s,0,s.length());
os1.write(s,0,2);
os1.close();//不执行flush,直接写close,是先刷新,在关闭流。流关闭之后就不能在操作os来写数据了
}
}
3.6 字符流读数据的2种方式
方法名 | 说明 |
---|---|
int read( ) | 一次读一个字符数据 |
int read(char[ ] cbuf) | 一次读一个字符数组数据 |
public class test {
public static void main(String[] args) throws IOException {
InputStreamReader isr=new InputStreamReader(new FileInputStream("test.txt"));//还可以读.java文件
//int read( ) 一次读一个字符数据
int ch;
while((ch=isr.read())!=-1){
System.out.print((char)ch);
}
//int read(char[ ] cbuf) 一次读一个字符数组数据
int len;
char[] chs= new char[1024];
while((len=isr.read(chs))!=-1){
System.out.print(new String(chs,0,len));
}
isr.close();
}
}
案例 :复制Java文件
需求:把模块目录下的“LeecodeSolution.java”复制到模块目录下的"Copy.java"
思路:
- 根据数据源创建字符输入流对象
- 根据目的地创建字符输出流对象
- 读写数据,复制文件
- 释放资源
public class test {
public static void main(String[] args) throws IOException {
// 1. 根据数据源创建字符输入流对象
InputStreamReader isr = new InputStreamReader(new FileInputStream("Javalearn\\Solution.java"));
// 2. 根据目的地创建字符输出流对象
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("Javalearn\\Copy.java"));
// 3. 读写数据,复制文件
//一个字节读
int ch;
while((ch=isr.read())!=-1){
osw.write(ch);
}
//一个字符数组读
int len;
char[] chs = new char[1024];
while((len=isr.read(chs))!=-1){
osw.write(new String(chs,0,len));
}
// 4. 释放资源
isr.close();
osw.close();
}
}
复制Java文件改进版
分析:
- 转换流的名字较长,而我们常见的操作都是按照本地默认编码实现的,所以,为了简化书写,转换流提供了对应的子类
- FileReader:用于读取字符文件的边界类:FileReader(String fileName)
- FileWriter:用于写入字符文件的便捷类:FileWriter(String fileName)
- 数据源和目的地分析
- 数据源:Solution.java--------读数据-----Reader—ImputStreamReader-------FileReader
- 目的地:Copy.java-----------写数据-------Writer------OutputStreamWriter------FileWriter
思路:
- 根据数据源创建字符输入流对象
- 根据目的地创建字符输出流对象
- 读写数据,复制文件
- 释放资源
public class test {
public static void main(String[] args) throws IOException {
// 1. 根据数据源创建字符输入流对象
FileReader fr = new FileReader("Javalearn\\Solution.java");
// 2. 根据目的地创建字符输出流对象
FileWriter fw = new FileWriter("Javalearn\\Copy.java");
// 3. 读写数据,复制文件
//一个字节读
int ch;
while((ch=fr.read())!=-1){
fw.write(ch);
}
//一个字符数组读
int len;
char[] chs = new char[1024];
while((len=fr.read(chs))!=-1){
fw.write(new String(chs,0,len));
}
// 4. 释放资源
fr.close();
fw.close();
}
}
3.7 字符缓冲流
字符缓冲流:
- **BufferedWriter:**将文本写入字符输出流,缓冲字符,以提供单个字符,数组和字符串的高效写入,可以指定缓存区大小,或者可以接收默认大小。默认值足够大,可用于大多数用途。
- **BufferedReader:**从字符输入流读取文本,缓冲字符,以提供字符,数组和行的高效读取,可以指定缓存区大小,或者可以使用默认大小,默认值足够大,可以用于大多数用途。
构造方法:
- BufferedWriter(Writer out)
- BufferedReader(Reader in)
public class test {
public static void main(String[] args) throws IOException {
// BufferedWriter(Writer out)
BufferedWriter bw = new BufferedWriter(new FileWriter("test.txt"));
bw.write("hello\r\n");
bw.write("java");
// BufferedReader(Reader in)
BufferedReader br = new BufferedReader(new FileReader("test.txt"));
int ch ;
while((ch=br.read())!=-1){
System.out.print((char)ch);
}
int len;
char[] chs = new char[1024];
while((len=br.read(chs))!=-1){
System.out.print(new String(chs,0,len));
}
}
}
案例 :复制Java文件(字符缓冲流版本)
需求:把模块目录下的“LeecodeSolution.java”复制到模块目录下的"Copy.java"
思路:
- 根据数据源创建字符输入流对象
- 根据目的地创建字符输出流对象
- 读写数据,复制文件
- 释放资源
public class test {
public static void main(String[] args) throws IOException {
// 1. 根据数据源创建字符输入流对象
BufferedReader fr = new BufferedReader(new FileReader("Javalearn\\Solution.java"));
// 2. 根据目的地创建字符输出流对象
BufferedWriter fw = new BufferedWriter(new FileWriter("Javalearn\\Copy.java"));
// 3. 读写数据,复制文件
//一个字节读
int ch;
while((ch=fr.read())!=-1){
fw.write(ch);
}
//一个字符数组读
int len;
char[] chs = new char[1024];
while((len=fr.read(chs))!=-1){
fw.write(new String(chs,0,len));
}
// 4. 释放资源
fr.close();
fw.close();
}
}
3.8 字符缓冲流特有功能
BufferedWriter:
- void newLine():写一行行分隔符,行分隔符字符串由系统属性定义,(windows下是\r\n)
BufferedReader:
- public String readLine():读一行文字。结果包含行的字符串,不包括任何行终止字符(换行符),如果流的结尾已经到达,则为null
public class test {
public static void main(String[] args) throws IOException {
// 2. 根据目的地创建字符输出流对象
BufferedWriter fw = new BufferedWriter(new FileWriter("Javalearn\\Solution.java"));
for(int i=0;i<3;i++){
fw.write("hello");
// void newLine():写一行行分隔符,行分隔符字符串由系统属性定义,(windows下是\r\n)
fw.newLine();//换行
}
// 1. 根据数据源创建字符输入流对象
BufferedReader fr = new BufferedReader(new FileReader("Javalearn\\Copy.java"));
String s;
while((s=fr.readLine())!=null){
// public String readLine():读一行文字。结果包含行的字符串,**不包括任何行终止字符(换行符)**,如果流的结尾已经到达,则为null
System.out.println(s);//这里需要手动换行
}
// 4. 释放资源
fr.close();
fw.close();
}
}
改进版本(最常用方式)(使用字符流的特有功能实现)
public class test {
public static void main(String[] args) throws IOException {
// 2. 根据目的地创建字符输出流对象
BufferedWriter fw = new BufferedWriter(new FileWriter("Javalearn\\Solution.java"));
// 1. 根据数据源创建字符输入流对象
BufferedReader fr = new BufferedReader(new FileReader("Javalearn\\Copy.java"));
String s;
while((s=fr.readLine())!=null){//这里只读内容,不读换行符
fw.write(s);
fw.newLine();
fw.flush();
}
// 4. 释放资源
fr.close();
fw.close();
}
}
3.9 IO流小结
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hcm16LY2-1648472879387)(C:\Users\lu’hai’peng\AppData\Roaming\Typora\typora-user-images\image-20220324204855201.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LKBfB9Og-1648472879390)(C:\Users\lu’hai’peng\AppData\Roaming\Typora\typora-user-images\image-20220324204923188.png)]
案例:复制单级文件夹(里面只有文件,没有其他文件夹)
需求:把“E:\itcast”这个文件夹复制到模块目录下
思路:
- 创建数据源目录File对象,路径是E:\itcast
- 获取数据源目录File对象的名称(itcast)
- 创建目的地目录File对象,路径名是模块名+itcast组成(mycahrStream\itcast)
- 判断目的地目录对应的File是否存在,如果不存在,就创建
- 获取数据源目录下所有文件的File数组
- 遍历File数组,得到每一个File对象,该File对象,其实就是数据源文件(数据源文件:E:\itcast\mn.jpg)
- 获取数据源文件File对象的名称mn.jpg
- 创建目的地文件File对象,路径名是目的地目录+mn.jpg组成
- 复制文件:由于文件不仅仅是文本文件,还有图片,视频,所以采用字节流复制文件
public class test {
public static void main(String[] args)throws IOException {
// 1. 创建数据源目录File对象,路径是E:\\itcast
File srcFolder = new File("E:\\itcasr");
// 2. 获取数据源目录File对象的名称(itcast)
String srcFolderName = srcFolder.getName();
// 3. 创建目的地目录File对象,路径名是模块名+itcast组成(mycahrStream\\itcast)
File destFolder = new File("myCharStream",srcFolderName);
// 4. 判断目的地目录对应的File是否存在,如果不存在,就创建
if(!destFolder.exists()){
destFolder.mkdir();
}
// 5. 获取数据源目录下所有文件的File数组
File[] listfiles=srcFolder.listFiles();
// 6. 遍历File数组,得到每一个File对象,该File对象,其实就是数据源文件(数据源文件:E:\\itcast\\mn.jpg)
for(File srcFile:listfiles){
// 7. 获取数据源文件File对象的名称mn.jpg
// 8. 创建目的地文件File对象,路径名是目的地目录+mn.jpg组成
String srcFileName = srcFile.getName();
File desFile= new File(destFolder,srcFileName);
//复制文件
copyFile(srcFile,desFile);
}
}
// 9. 复制文件:由于文件不仅仅是文本文件,还有图片,视频,所以采用字节流复制文件
private static void copyFile(File srcFile,File destFile) throws IOException {
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(srcFile));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(destFile));
byte[] bys = new byte[1024];
int len;
while((len=bis.read(bys))!=-1){
bos.write(bys,0,len);
}
bis.close();
bos.close();
}
}
案例:复制多级文件夹(文件夹里面还有文件夹)(递归)
需求: 把“E:\iscast"复制到F盘下面
思路:
- 创建数据源File对象,路径是E:\itcast
- 创建目的地File对象,路径是F:\
- 写方法实现文件夹的复制,参数为数据源File对象和目的地File对象
- 判断数据源File是否是目录
- 是:
- 在目的地 下创建和数据源File名称一样的目录
- 获取数据源File下所有文件或者目录的File数组
- 遍历该File数组,得到每一个File对象
- 把该File作为数据源File对象,调用递归复制文件夹的方法
- 不是:说明是文件,直接复制,用字节流
- 是:
public class test {
public static void main(String[] args)throws IOException {
// 1. 创建数据源File对象,路径是E:\\itcast
File srcFile = new File("E:\\itcast");
// 2. 创建目的地File对象,路径是F:\\
File destFile = new File("F:\\");
// 3. 写方法实现文件夹的复制,参数为数据源File对象和目的地File对象
copyFolder(srcFile,destFile);
}
//复制文件夹
private static void copyFolder(File srcFile,File destFile)throws IOException{
// 4. 判断数据源File是否是目录
if(srcFile.isDirectory()){
// 1. 在目的地 下创建和数据源File名称一样的目录
String srcFileName = srcFile.getName();
File newFolder = new File(destFile,srcFileName);
if(!newFolder.exists()){
newFolder.mkdir();
}
// 2. 获取数据源File下所有文件或者目录的File数组
File[] fileArray= srcFile.listFiles();
// 3. 遍历该File数组,得到每一个File对象
for(File file:fileArray){
// 4. 把该File作为数据源File对象,调用递归复制文件夹的方法
copyFolder(file,newFolder);
}
}else{// 2. 不是:说明是文件,直接复制,用字节流
File newFile = new File(destFile,srcFile.getName());
copyFile(srcFile,destFile);
}
}
// 字节流复制文件
private static void copyFile(File srcFile,File destFile) throws IOException {
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(srcFile));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(destFile));
byte[] bys = new byte[1024];
int len;
while((len=bis.read(bys))!=-1){
bos.write(bys,0,len);
}
bis.close();
bos.close();
}
}
3.10 复制文件的异常处理
- 用try…catch…finally的做法
public class copy{
private static void method2(){
FileReader fr=null;
FileWriter fw=null;
try{
fr = new FileReader("fr.txt");
fw = new FileWriter("fw.txt");
char[] chs = new char[1024];
int len;
while((len=fr.read())!=-1){
fw.write(chs,0,len);
}
}catch (IOException e){
e.printStackTrace();
}finally{
if(fw!=null){
try{
fw.close();
}catch(IOException e){
e.printStackTrace();
}
}
if(fr!=null){
try{
fr.close();
}catch(IOException e){
e.printStackTrace();
}
}
}
}
}
- JDK7改进方案:
try (定义流对象){
}catch(异常类名 变量名){
}该方法自动释放资源
public class copy{
private static void method2(){
try(FileReader fr = new FileReader("fr.txt");
FileWriter fw = new FileWriter("fw.txt");){
char[] chs = new char[1024];
int len;
while((len=fr.read())!=-1){
fw.write(chs,0,len);
}
}catch(IOException e){
e.printStackTrace();
}
}
}
- JDK9改进方案:
定义输出流对象
定义输出流对象
**try ** (输入流对象,输出流对象){
}catch(异常类名 变量名){
异常代码
}自动释放资源
public class copy{
private static void method2(){
FileReader fr = new FileReader("fr.txt");
FileWriter fw = new FileWriter("fw.txt");
try(fr,fw){
char[] chs = new char[1024];
int len;
while((len=fr.read())!=-1){
fw.write(chs,0,len);
}
}catch(IOException e){
e.printStackTrace();
}
}
}
4. 特殊操作流
4.1 标准输入输出流
System类中有两个静态成员变量:
- public static final InputStream in:标准输入流。通常该流对应于键盘输入或者由主机环境或用户指定的另一个输入源
- public static final OutputStream out:标准输出流。通常该流对应于显示输出或由主机环境或用户指定的另一个输出目标
4.1.1 标准输入流
自己实现键盘录入数据:
- BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
写起来太麻烦了,Java就提供了一个类实现键盘录入
- Scanner sc = new Scanner(System.in);
public class test {
public static void main(String[] args) throws IOException {
/*// public static final InputStream in:标准输入流。通常该流对应于键盘输入或者由主机环境或用户指定的另一个输入源
InputStream is = System.in;
//字节流,但是不能正常输出中文
int by;
while((by=is.read())!=-1){
System.out.println((char)by);
}
//如何把字节流转换为字符流?用转换流
InputStreamReader isr = new InputStreamReader(is);
//使用字符流能不能实现一次读取一行数据呢?可以
//但是一次读取一行的数据的方法是字符缓冲输入流的特有方法
BufferedReader br = new BufferedReader(isr);*/
//可以将上面的代码变成优化为一句
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
System.out.println("请输入一个字符串:");
String line = br.readLine();
System.out.println("你输入的是"+line);
System.out.println("请输入一个整数:");
int i = Integer.parseInt(br.readLine());
System.out.println("你输入的整数是:"+i);
//自己实现键盘录入数据太麻烦了,所以Java就提供了一个类供我们使用
Scanner sc = new Scanner(System.in);
}
}
4.1.2 标准输出流
输出语言的本本质:是一个标准输出流:
- PrintStream ps = System.out;
- PrintStream类有的方法,System.out都可以使用
4.2 打印流
打印流的分类:
- 字节打印流:PrintStream
- 字符打印流:PrintWriter
打印流的特点:
- 只负责输出数据,不负责读取数据
- 有自己的特有方法
4.2.1 字节打印流
- PrintStream(String fileName):使用指定的文件名创建新的打印流
- 使用继承父类的方法写数据(write),查看的时候会转码;使用自己的特有方法写数据(print/println),查看的数据原样输出
public class test {
public static void main(String[] args)throws IOException {
//PrintStream(String fileName):使用指定的文件名创建新的打印流
PrintStream ps = new PrintStream("Javalearn\\test.txt");
//写数据
//字节输出流有的方法
ps.write(97);//这里显示的是a(ASCII码的形式)
ps.println(97);//这里显示的是97
ps.print(98);//这里显示的是98
//释放资源
ps.close();
}
}
4.2.2 字符打印流
字符打印流PrintWriter的构造方法:
方法名 | 说明 |
---|---|
PrintWriter(String fileName) | 使用指定的文件名创建一个新的PrintWriter,而不需要自动执行刷新 |
PrintWriter(Writer out, boolean autoFlush) | 创建一个新的PrintWriter ( out :字符输出流 ) -autoFlush:一个布尔值,如果为真,则println,printf,或者format方法将刷新输出缓存区 |
public class test {
public static void main(String[] args)throws IOException {
//PrintWriter(String fileName),使用指定的文件名创建一个新的PrintWriter,而不需要自动执行刷新
PrintWriter pw = new PrintWriter("Javalean\\test.txt");
pw.write(98);
pw.write("\r\n");//换行符
pw.flush();
pw.write(99);
pw.write("\r\n");
pw.flush();
pw.println("hello");
/*
pw.write();
pw.write("\r\n");//换行符
*/
pw.flush();
//PrintWriter(Writer out, boolean autoFlush)创建一个新的PrintWriter( out :字符输出流 )-autoFlush:一个布尔值,如果为真,则println,printf,或者format方法将刷新输出缓存区
PrintWriter pp = new PrintWriter(new FileWriter("Javalean\\test.txt"),true);
pp.println("hello");//自动刷新
pp.close();
}
}
案例:复制Java文件(打印流改进版)
需求:把模块目录下的PrintStreamDemo.java复制到模块目录下的Copy.java
思路:
- 根据数据源创建字符输入流对象
- 根据目的地创建字符输出流对象
- 读写数据,复制文件
- 释放资源
public class test {
public static void main(String[] args)throws IOException {
/*// 1. 根据数据源创建字符输入流对象
BufferedReader br = new BufferedReader(new FileReader("Javalean\\PrintStreamDemo.java"));
// 2. 根据目的地创建字符输出流对象
BufferedWriter bw = new BufferedWriter(new FileWriter("Javalean\\Copy.java"));
// 3. 读写数据,复制文件
String line;
while((line=br.readLine())!=null){
bw.write(line);
bw.newLine();
bw.flush();
}
// 4. 释放资源
bw.close();
br.close();*/
//用打印流来做
// 1. 根据数据源创建字符输入流对象
BufferedReader br = new BufferedReader(new FileReader("Javalean\\PrintStreamDemo.java"));
// 2. 根据目的地创建字符输出流对象
PrintWriter pw = new PrintWriter(new FileWriter("Javalean\\Copy.java"),true);
// 3. 读写数据,复制文件
String line;
while((line=br.readLine())!=null){
pw.println(line);
}
// 4. 释放资源
br.close();
pw.close();
}
}
4.3 对象序列化流和反序列化流
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-G2vfTjYZ-1648472879392)(C:\Users\lu’hai’peng\AppData\Roaming\Typora\typora-user-images\image-20220327194548585.png)]
4.3.1 对象序列化流
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RdXzxrJN-1648472879393)(C:\Users\lu’hai’peng\AppData\Roaming\Typora\typora-user-images\image-20220327194759806.png)]
构造方法:
- ObjectOutputStream(OutputStream out):创建一个写入指定的OutputStream的ObjectOutputStream
序列化对象的方法:
- void writeObject(Object obj): 将指定的对象写入ObjectOutputStream
注意:
- 一个对象要想被序列化,该对象所属的类必须必须实现Serializable接口
- Serializable是一个标记接口,实现该接口,不需要重写任何方法
public class Student implements Serializable {
int age;
String name;
public Student() {
}
public Student(int age, String name) {
this.age = age;
this.name = name;
}
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;
}
}
public class test {
//NotSerializableException:抛出一个实例需要一个Serializable接口,序列化运行时或实例的类可能会抛出此异常
//类的序列化有实现java.io.Serializable接口的类启用,不实现此接口的类将不会使用任何状态序列化或反序列化
public static void main(String[] args)throws IOException {
// ObjectOutputStream(OutputStream out):创建一个写入指定的OutputStream的ObjectOutputStream
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("test.txt"));
// void writeObject(Object obj): 将指定的对象写入ObjectOutputStream
Student stu = new Student(20, "lll");
oos.writeObject(stu);
//释放资源
oos.close();
}
//序列化时一堆看不懂的乱码�� sr com.operator.test01.Student9�!�� I ageL namet Ljava/lang/String;xp t lll
//需要反序列化将其转换为我们可以看懂的内容
}
4.3.2 对象的反序列化流
对象反序列化流:ObjectInputStream
- ObjectInputStream反序列化先前使用ObjectOutputStream编写的原始数据和对象
构造方法:
- ObjectInputStream(InputStream in):创建从指定的InputStream读取的ObjectInputStream
反序列化对象的方法:
- Object readObject( ):从ObjectInputStream读取一个对象
public class Student implements Serializable {
int age;
String name;
public Student() {
}
public Student(int age, String name) {
this.age = age;
this.name = name;
}
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;
}
}
public class test {
public static void main(String[] args) throws IOException, ClassNotFoundException {
// ObjectInputStream反序列化先前使用ObjectOutputStream编写的原始数据和对象
// ObjectInputStream(InputStream in):创建从指定的InputStream读取的ObjectInputStream
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("test.txt"));
// Object readObject( ):从ObjectInputStream读取一个对象
Object obj = ois.readObject();
Student s = (Student) obj;
System.out.println(s.getAge()+" "+s.getName());//读取出来20 lll
ois.close();
}
}
4.4 serialVersionUID和transient
public class Student implements Serializable {
private static final long serialVersionUID=42L;
private int age;
private transient String name;
public Student() {
}
public Student(int age, String name) {
this.age = age;
this.name = name;
}
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;
}
//如果对象被序列化后,再重写toString方法,可能会出错,因为重新写后serialVersionUID会变,需要自己定义一个不变的UID才能解决问题
@Override
public String toString() {
return "Student{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
}
public class test {
public static void main(String[] args) throws IOException, ClassNotFoundException {
write();
read();
}
private static void read() throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("test.txt"));
Object obj = ois.readObject();
Student s =(Student) obj;
System.out.println(s.getName()+" "+s.getAge());
ois.close();
}
private static void write() throws IOException{
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("test.txt"));
Student s = new Student(20,"lll");
oos.writeObject(s);
oos.close();
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Du907XfS-1648472879395)(C:\Users\lu’hai’peng\AppData\Roaming\Typora\typora-user-images\image-20220327213357882.png)]
4.5 Properties
Properties概述:
- 是一个Map体系的集合类
- Properties可以保存到流中或从流中加载
练习:Properties作为Map集合的使用
public class test {
public static void main(String[] args) {
Properties prop = new Properties();//Properties不是泛型,不能指定类型,所以传入Object
prop.put("ithema","lalala");
prop.put("20202","asdas");
prop.put("1413141","swwwww");
Set<Object> keyset = prop.keySet();
for (Object key : keyset) {
Object value=prop.get(key);
System.out.println(key+" "+value);
}
}
}
4.5.1 Properties作为集合的特有方法
方法名 | 说明 |
---|---|
Object setProperty(String key,String value) | 设置集合的键和值,都是String类型,底层调用Hashtable方法来put |
String getProperty(String key) | 使用此属性列表中指定的键搜索属性 |
Set stringPropertiesNames() | 从该属性列表中返回一个不可修改的键集,其中键及其对应的值都是字符串 |
public class test {
public static void main(String[] args) {
//创建集合对象
Properties prop = new Properties();
//Object setProperty(String key,String value) 设置集合的键和值,都是String类型,底层调用Hashtable方法来put
prop.setProperty("001","lili");
prop.setProperty("002","wawa");
prop.setProperty("003","pwpw");
// String getProperty(String key) 使用此属性列表中指定的键搜索属性
System.out.println(prop.getProperty("001"));//lili
System.out.println(prop.getProperty("002"));//wawa
System.out.println(prop.getProperty("003"));//pwpw
System.out.println(prop.getProperty("004"));//null
// Set<String> stringPropertiesNames() 从该属性列表中返回一个不可修改的键集,其中键及其对应的值都是字符串
Set<String> strings=prop.stringPropertyNames();
for (String string : strings) {
String value = prop.getProperty(string);
System.out.println(string +" "+value);
/*
输出:
003 pwpw
002 wawa
001 lili
*/
}
}
}
4.5.2 Properties 和IO流结合的方法
方法名 | 说明 |
---|---|
void load(InputStream inStream) | 从输入字节流读取属性列表(键和元素对) |
void load(Reader reader) | 从输入字符流读取属性列表 |
void store(OutputStream out,String comments) | 将此属性列表(键和元素)写入此Properties表中,以适合于使用load(InputStream)方法的格式写入输出字节流 |
void stroe(Writer writer,String comments) | 将此属性列表(键和元素对)写入此Properties表中,以适合使用load(Reader)方法的格式写入输出字符流 |
//void load(InputStream inStream) 从输入字节流读取属性列表(键和元素对)
//void load(Reader reader) 从输入字符流读取属性列表
//void store(OutputStream out,String comments) 将此属性列表(键和元素)写入此Properties表中,以适合于使用load(InputStream)方法的格式写入输出字节流
//void stroe(Writer writer,String comments) 将此属性列表(键和元素对)写入此Properties表中,以适合使用load(Reader)方法的格式写入输出字符流
public class test {
public static void main(String[] args) throws IOException {
//把集合中的数据保存到文件中
myStore();
//把文件中的数据加载到集合
myLoad();
}
private static void myLoad() throws IOException {
Properties prop = new Properties();
//void load(Reader reader) 从输入字符流读取属性列表
FileReader fr = new FileReader("test.txt");
prop.load(fr);
fr.close();
System.out.println(prop);
}
private static void myStore() throws IOException {
Properties prop = new Properties();
prop.setProperty("001","lll");
prop.setProperty("002","www");
prop.setProperty("003","ooo");
//void stroe(Writer writer,String comments) 将此属性列表(键和元素对)写入此Properties表中,以适合使用load(Reader)方法的格式写入输出字符流
FileWriter fw = new FileWriter("test.txt");
prop.store(fw,null);
fw.close();
}
}
案例
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Gtr0WMq6-1648472879397)(C:\Users\lu’hai’peng\AppData\Roaming\Typora\typora-user-images\image-20220328210512940.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LwsXa4tH-1648472879398)(C:\Users\lu’hai’peng\AppData\Roaming\Typora\typora-user-images\image-20220328210421226.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HwGIHEoI-1648472879400)(C:\Users\lu’hai’peng\AppData\Roaming\Typora\typora-user-images\image-20220328210444488.png)]