22-04-23 西安 javaSE(14)File类、文件IO流、缓冲流、转换流...

File类

File对象

Java的标准库java.io提供了File对象来操作文件和目录包括新建、删除、重命名文件和目录,但是File不能访问文件内容本身,需要借助IO流完成。

构造器:File(String pathname) //参数可以是相对路径和绝对路径

  • 绝对路径是一个完整的路径,Windows 系统中,绝对路径通常盘符开始。
D:\workspace\mybatis-plus
绝对路径描述了一个文件或目录在文件系统中的确切位置
  • 相对路径是一个不完整的路径:系统总是根据用户的工作路径来解释相对路径,由系统属性"user.dir"指定
//获得的是“工作目录”
String property = System.getProperty("user.dir");
System.out.println(property);//E:\workspace\jxc

路径分隔符

Windows平台使用\作为路径分隔符,如D:\workspace\mybatis-plus

在Java中反斜线(\)表示转义字符,所以需要用\\表示一个\作为路径分隔符。或者直接使用(/)也可以,java支持将斜线(/)当成与平台无关的路径分隔符

File file1=new File("D:\\atguigu\\PDF课件"); //   用\\起到转义作用

路径Path

File对象有3种形式表示的路径

  • getPath(),返回File对象调用构造方法传入的路径
  • getAbsolutePath(),返回File对象绝对路径
  • getCanonicalPath,它和绝对路径类似,但是返回的是规范路径
    public static void main(String[] args) throws IOException {
        File f = new File("..");
        System.out.println(f.getPath());//..
        System.out.println(f.getAbsolutePath());//D:\workSpace\mybatis-plus\..
        System.out.println(f.getCanonicalPath());//D:\workSpace
    }


File对象常用方法

  1. getAbsolutePath();//返回此 File 的绝对路径。
  2. getPath();//获取File对象的路径
  3. getName();//返回文件名或目录名
  4. length();//获取文件的长度 ,以字节为单位,File 对象表示目录的时候,返回值并无意义
  5. isDirectory()//判断File对象是否为目录
  6. isFile();//判断File对象是否为文件
  7. exists();/判断文件或目录是否存在。
  8. File[]  listFiles();//获取指定file对象下的所有目录和文件
  9. renameTo() 重命名
  10. mkdir() 创建目录,只能创建一级目录
  11. mkdirs() 创建多级目录如果父目录不存在,则会一并创建,可以多级目录。
  12. createNewFile() 文件不存在,创建一个新的空文件并返回 true,文件存在,不创建文件并返回false
  13. delete() 删除文件或者目录(只能删除空的目录
  14. separator 代替文件或文件夹路径的斜线或反斜线,防跨平台出现错误
  15. static FIle createTempFile()创建临时文件
  16. void deleteOnExit 在java虚拟机退出时,删除File对象所对应的文件和目录

判断文件的权限

  • boolean canRead():是否可读
  • boolean canWrite():是否可写
  • boolean canExecute():是否可执行
  • boolean isHidden():判断当前FIle类对象对应的文件是不是一个隐藏文件
  • boolean isAbsolute(); 判断当前File类对象保存的路径是不是绝对路径
  • long lastModified():获取最后一次的修改时间,毫秒值

list()遍历文件

  1. list() 返回该 File 目录中的所有文件或者文件夹的名字数组   (返回值String 类型数组)
  2. listFiles() 返回该 File 目录中的所有文件或者文件夹组成的File数组  (返回File[] 类型数组) 

递归遍历文件目录

    @Test
    public void test1() {
        File file = new File("C:\\Users\\lenovo\\Pictures");//  用\\起到转义作用
        print(file);
    }

    public static void print(File path){
        //使用递归遍历多级文件夹
        File[] files = path.listFiles();
        for (int i = 0; i < files.length; i++) {
            File file=files[i];
            if(file.isFile()){
                System.out.println("文件--"+file);
            }else {
                System.out.println("目录--"+file);
                print(file);
            }
        }
    }

需求:遍历文件输出时按照修改日期排序

  1.File数组情况

Arrays.sort(files, new Comparator<File>(){
    public int compare(File f1, File f2)
    {
        return Long.valueOf(f1.lastModified()).compareTo(f2.lastModified());
    } });

  2.File的List集合情况

List<File> files= new ArrayList<File>(); 
//按照修改日期 降序排序
List<File> fileList = files.stream().sorted(Comparator.comparing(File::lastModified).reversed()).collect(Collectors.toList());

文件过滤

通过FilenameFilter列出只符合条件的文件。

如下:我要输出指定目录下所有的dll文件

File file = new File("E:\\vmware");
//指定文件过滤器遍历
File[] files = file.listFiles(new FilenameFilter() {
    @Override
    public boolean accept(File dir, String name) {
        return name.endsWith(".dll");
    }
});
System.out.println(Arrays.toString(files));

accept方法返回true,则会列出该子目录或文件


删除目录或文件

1.File类的delete()

删除成功返回true,删除失败返回false。但是不知道是

因为文件夹不存在导致失败还是文件夹不为空导致失败

//File类的delete()
File file = new File("C:\\Users\\Administrator\\Desktop\\test");
boolean result = file.delete();

2.Files.delete()

//Files.delete()
try {
    Files.delete(Paths.get("C:\\Users\\Administrator\\Desktop\\test"));
} catch (IOException e) {
    e.printStackTrace();
}

此时就会抛出异常,我们就知道是因为文件夹不为空导致删除失败

那怎么删除带子文件的文件夹?


RandomAccessFile

RandomAccessFile 构造

RandomAccessFile 虽然属于java.io包,但它不是InputStream或者OutputStream的子类。
优势
  1. RandomAccessFile 同时支持文件的读和写
  2. 支持任意访问。即RandomAccessFile 允许跳转到文件的任何位置,从那里开始读取或写入
局限
只能读写文件、不能读写其他IO结点
RandomAccessFile(File file, String mode) :使用给定的文件对象和访问模式创建一个新的RandomAccessFile 实例。

RandomAccessFile(String name, String mode) :使用给定的文件名和访问模式创建一个新的RandomAccessFile 实例

RandomAccessFile 模式

RandomAccessFile共有4种模式:"r", "rw", "rws"和"rwd"。

"r"    以只读方式打开。调用结果对象的任何 write 方法都将导致抛出 IOException。 

"rw"   以读写模式打开文件。如果文件不存在,它将被创建。

"rws"  打开以便读取和写入。相对于 "rw","rws" 还要求对“文件的内容”或“元数据”的每个更新都同步写入到基础存储设备。 

"rwd"  打开以便读取和写入,相对于 "rw","rwd" 还要求对“文件的内容”的每个更新都同步写入到基础存储设备。 

RandomAccessFile 函数列表 

RandomAccessFile(File file, String mode)
RandomAccessFile(String fileName, String mode)
void  close()
synchronized final FileChannel  getChannel()
final FileDescriptor  getFD()
long  getFilePointer()//返回文件指针的当前位置
long  length() //返回此文件的长度
int  read(byte[] buffer, int off, int len)//从该文件中读取字节数据并将其存储到指定的字节数组中,从偏移量 off 开始,最多读取 len 个字节。
int  read(byte[] buffer)//从该文件中读取字节数据并将其存储到指定的字节数组中
int  read()//从该文件中读取一个字节数据
final boolean  readBoolean()
final byte  readByte()
final char  readChar()
final double  readDouble()
final float  readFloat()
final void  readFully(byte[] dst)
final void  readFully(byte[] dst, int offset, int byteCount)
final int  readInt()
final String  readLine()//从该文件中读取一行文本
final long  readLong()
final short  readShort()
final String  readUTF()
final int  readUnsignedByte()
final int  readUnsignedShort()
void  seek(long pos)//将文件指针设置到文件中的 pos 位置
void  setLength(long newLength)
int  skipBytes(int count)
void  write(int oneByte) //将指定的字节写入该文件
void  write(byte[] buffer, int off, int len) //将指定字节数组的部分字节写入该文件,从偏移量off 开始,写入 len 个字节
void  write(byte[] buffer)//将指定的字节数组的所有字节写入该文件
final void  writeBoolean(boolean val)
final void  writeByte(int val)
final void  writeBytes(String str)
final void  writeChar(int val)
final void  writeChars(String str)
final void  writeDouble(double val)
final void  writeFloat(float val)
final void  writeInt(int val)
final void  writeLong(long val)
final void  writeShort(int val)
final void  writeUTF(String str)

RandomAccessFile记录指针

RandomAccessFile类中包含记录指针,用以标识当前读写处的位置(文件记录指针开始位于文件头(也就是0处),当读/写了n个字节后,文件记录指针将会向后移动n个字节。),

RandomAccessFile可以自由移动该记录指针

RandomAccessFile包含了如下两个方法来操作文件记录指针。
        ➢ long getFilePointer():返回文件记录指针的当前位置。
        ➢ void seek(long pos):将文件记录指针定位到pos位置。


RandomAccessFile使用举例

文件内容追加

public static void main(String[] args) throws IOException {
    RandomAccessFile accessFile = null;
    File file = null;
    try {
        file = new File("C:\\Users\\lenovo\\Desktop\\小程序.txt");
        //以读写的方式打开一个RandomAccessFile对象
        accessFile = new RandomAccessFile(file, "rw");
        //将记录指针移动到该文件的最后
        accessFile.seek(accessFile.length());
        //向文件末尾追加内容
        byte[] bytes = "\r\n追加内容:我无敌,你随意".getBytes();
        accessFile.write(bytes);
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        accessFile.close();
    }
}

RandomAccessFile的一个重要使用场景就是网络请求中的多线程下载及断点续传 


大文件分片上传


IO流

1、输入流&输出流

输入、输出都是从内存的角度来划分的

输入流:(外部设备->程序),
输出流:(程序->外部设备)

  • 输入流只能读取数据,而不能向其写入数据
  • 输出流只能向其写入数据,而不能从中读取数据

2、字节流&字符流

字节流可以处理一切文件(图像、视频、音频...),而字符流只能处理纯文本文件

字节流:输入输出以字节(byte)为单位

字节(byte)是计算机中用来表示存储容量的一个计量单位,通常情况下,一个字节有 8 位(bit)

字符流:输入输出以字符(char)为单位

字符(char)可以是计算机中使用的字母、数字、和符号

规定每个“字符”分别用一个字节还是多个字节存储,用哪些字节来存储,这个规定就叫做“编码”

常用字符编码所占字节数?utf8 :英文占 1 字节,中文占 3 字节,unicode:任何字符都占 2 个字节,gbk:英文占 1 字节,中文占 2 字节


3、节点流&处理流

节点流:使用节点流进行输入输出时、程序直接连接到实际的数据源

处理流:对一个已经存在的流进行封装,通过封装后的流实现数据读写功能

处理流是一种典型的装饰器模式。

只要使用相同的处理流,程序就可以采用完全相同的输入输出代码来访问不同的数据源。

4、流的关闭

所有的流都实现了: java.io.closeable 接口,都是可关闭的,都有 close()方法。流毕竟是一个管道,这个是内存和硬盘之间的通道,用完之后一定要关闭,不然会耗费(占用)很多资源

public interface Closeable extends AutoCloseable {
    public void close() throws IOException;
}

文件IO字节流

1、FileOutputStream节点流

文件字节输出流 FileOutputStream:针对文件以字节为单位进行输出操作的工具类。

FileOutputStream类构造器  
    FileOutputStream(File file) 
    FileOutputStream(File file, boolean append) 
    FileOutputStream(String name) //指定一个相对路径,往哪个里输出。
    FileOutputStream(String name, boolean append) //append为true则开启文件续写

FileOutputStream类方法
void write(int b)  //以单字节输出fos.Write(97);
void write(byte[] b)  

以单个字节数组为单位输出  fos.Write("尚硅谷".getBytes())

void write(byte[] b, int off, int len)  

以指定范围的字节数组为单位进行输出,off是起始索引,len是长度
//或者说
将数组 b 中的从 off 位置开始,长度为 len 的字节写入

void close()  : 关闭流,释放资源

void flush() : 强制刷新,将缓冲区的数据写入
WEB服务器 通过输出流向客户端响应了一个300字节的信息,但是,这时的输出流有一个1024字节的缓冲区。所以,输出流就一直等着WEB服务器继续向客户端响应信 息,当WEB服务器的响应信息把输出流中的缓冲区填满时,这时,输出流才向WEB客户端响应消息。
flush()方法可以强迫输出流(或缓冲的流)发送数据,即使此时缓冲区还没有填满,以此来打破这种死锁的状态。

2、FileInputStream节点流

文件字节输入流  FileInputStream :针对文件以字节为单位进行输入(读取)操作的工具类

获取类路径

test.class  test是类名

URL resource = test.class.getClassLoader().getResource("student.xml");
File file = new File(resource.getFile());
System.out.println(file.getPath());

FileInputStream构造器:

FileInputStream(File file) 
FileInputStream(String name) 

FileInputStream方法
public int read()

返回读到的数据字节;如果已到达文件末尾,则返回 -1

public int read(byte[] b)

返回读入缓冲区的字节总数,如果因为已经到达文件末尾而没有更多的数据,则返回 -1。 

public void close() 关闭流,释放资源

int available() :返回可读的字节数
-----------------------------------------------------
FileInputStream fis = new FileInputStream("D:\\workspace\\mybatis-plus\\springboot-project\\target\\classes\\student.xml");
int available = fis.available();
System.out.printf("可读字节数:%d", available);
fis.close();

read()深究

FileInputStream单个字节地正确的读文件,应该这个样子写

//返回读到的数据字节;如果已到达文件末尾,则返回 -1
public int read()

返回值之所以是int,是因为字节数据可以直接转为int类型

public class ExceptionDemo {
    public static void main(String[] args) throws IOException {
        FileInputStream fis = new FileInputStream("a.txt");
        //假设a.txt文件里只有abc三个字母,以单个字节读入
        int len;
        while((len=fis.read())!=-1){
            //这里len是读取到的单个字节
            System.out.println(len);
        }
        //关闭资源
        fis.close();
    }
}


FileInputStream以字节数组byte[]的方式正确的读取文件

//从输入流中最多读取b.length个字节,将读到的字节放入到字节数组b中,并返回实际读取的字节数
int read(byte[] b);

fis.read()之后,byte数组的内容都在变化,把读的数据放到这个byte数组。

public class ExceptionDemo {
    public static void main(String[] args) throws IOException {
        print();
    }
    public static void print() throws IOException {
//      创建文件字节输入流对象
        FileInputStream fis=new FileInputStream("a.txt");
//      来个小推车 abc
        byte[] bytes=new byte[2];
        int len;
        while((len=fis.read(bytes))!=-1){
            System.out.println("len="+len);//len是实际读取到的字节数
            System.out.println("读取到的内容="+new String(bytes,0,len));
            System.out.println("变换的byte数组:"+ Arrays.toString(bytes));
            System.out.println("============");
        }
    }
}


3、使用举例:文件复制

//以字节数组方式复制图片
public class ExceptionDemo {
    public static void main(String[] args) throws IOException {
        print();
    }
    public static void print() throws IOException {
        FileInputStream fis = new FileInputStream("girl.jpg");
        FileOutputStream fos = new FileOutputStream("girl_copy.jpg");
/*        byte数组大小
        1、文件的平均单位根据文件进行制定
        2、没有指定,约定俗称8192,8KB*/
        byte[] bytes = new byte[8192];
        int len;
        while((len=fis.read(bytes))!=-1){
            fos.write(bytes,0,len);
        }
//      关闭资源的原则,先开后关,后开先关
        fos.close();
        fis.close();
    }
}

4、字节流转换为字符流

转换输入流 InputStreamReader:是字节流通向字符流的桥梁,使用指定的 charset 读取字节并将其解码为字符

构造器
InputStreamReader(InputStream in) 
InputStreamReader(InputStream in, String charsetName)//以指定编码格式读


文件IO字符流

1、字符流

不管是文件读写还是网络发送接收,信息的最小存储单元都是字节。 那为什么 I/O 流操作要分为字节流操作和字符流操作呢?

假如我们不知道编码方式就很容易出现乱码的问题,所以I/O流就干净利索的提供了一个直接操作字符的接口,方便我们平时对字符进行流操作。

使用字节流读取含有中文的文本文件时,会先将中文字符拆分为字节,进行读写操作,再将拆分后的字节进行重新组合。

字符流读写的时候,不会将字符拆分,直接以字符为单位(不管是中文还是英文,都是一个字符)

  • 文件字节流:用于读写音频、视频、图片,pdf属于图片类。
  • 文件字符流:用于读写文本文件

2、FileReader

文件字符输入流 FileReader:针对文件以字符为单位进行输入操作的工具类

构造器

FileReader(File file) 
FileReader(String fileName) 

方法:
 

public void close()
public int read()//读取单个字符
public int read(char[] cbuf)//

1、字符流以单个字符读取

public class ExceptionDemo {
    public static void main(String[] args) throws IOException {
        FileReader fr=new FileReader("a.txt");
//      以单个字符读取
        int len;
        while((len=fr.read())!=-1){
            char c=(char) len;
            System.out.println(c);
        }
        fr.close();
    }
}

2、字符流以单个字符数组读取

public class ExceptionDemo {
    public static void main(String[] args) throws IOException {
        FileReader fr = new FileReader("a.txt");
//      以单个字符数组读取
        char[] chars = new char[8192];
        int len;
        while ((len = fr.read(chars)) != -1) {
            System.out.println(len);//6
            System.out.println(new String(chars, 0, len));//XYM小羽毛
        }
        fr.close();
    }
}

 


3、FileWriter

FileWriter 文件字符输出流:针对文件以字符为单位进行输出操作的工具类

构造器:

FileWriter(File file) 
FileWriter(File file, boolean append) 
FileWriter(String fileName)
FileWriter(String fileName, boolean append) 

方法:

public void close();//关闭此流之前调用flush()
public void flush();

public void write(int c);//写单个字符
public void write(char cbuf);//写单个字符数组
public void write(char cbuf,int off,int len);//写单个字符数组,指定范围
public void write(String str);//写入字符串,等价于 write(str, 0, str.length())
public void write(String str,int off,int len);
append(CharSequence csq) //将指定的字符序列附加到指定的 Writer 对象并返回该 Writer 对象。
append(char c) //将指定的字符附加到指定的 Writer 对象并返回该 Writer 对象。

增加了 off 参数(偏移量)和 len 参数(要读取的最大字符数)

flush :刷新缓冲区,流对象可以继续使用。
close : 先刷新缓冲区,然后通知系统释放资源。流对象不可以再被使用了

1、以单个字符,字符数组,字符串输出到文件

public class ExceptionDemo {
    public static void main(String[] args) throws IOException {
        FileWriter fw=new FileWriter ("a.txt");
//      以单个字符输出
        fw.write(97);
        fw.write(27801);//写汉字沙
//      以字符数组
        fw.write("abc".toCharArray());
//      指定范围字符数组
        fw.write("defgk".toCharArray(),0,3);

        fw.write("\r\n");//回车换行
//      以字符串方式
        fw.write("abc");
//      以指定范围字符串方式
        fw.write("defgk",0,3);
        //fw.flush();把内存缓冲区的东西写到文本文件中
//      做了两步操作,(1)刷新(2)关闭
        fw.close();
    }
}


4、字符流转换为字节流

转换输出流 OutputStreamWriter:是字符流通向字节流的桥梁,可使用指定的 charset 将要写入流中的字符编码成字节

构造器

OutputStreamWriter(OutputStream out) 
OutputStreamWriter(OutputStream out, String charsetName)//以指定编码格式写


5、转换流-解决乱码

指定以哪种方式读取,以哪种方式写入
场景一:用户上传utf-8编码文件。【开发环境utf-8】,下载utf-8的文件。
场景二:用户上传gbk编码文件。【开发环境utf-8】,下载utf-8的文件。
场景三:用户上传utf-8编码文件。【开发环境utf-8】,下载gbk的文件。
场景四:用户上传gbk编码文件。【开发环境utf-8】,下载gbk的文件。

1、乱码问题

public class ExceptionDemo {
    public static void main(String[] args) throws IOException {
        //FileWriter.txt这个文件是GBK编码的
        FileReader fr = new FileReader("FileWriter.txt");
        char[] chars=new char[8192];
        int len;
        while((len=fr.read(chars))!=-1){
            System.out.println(new String(chars,0,len));//此时会输出乱码
        }
    }
}

2、解决乱码问题

public class ExceptionDemo {
    public static void main(String[] args) throws IOException {
        InputStreamReader isr = new InputStreamReader(new FileInputStream("FileWriter.txt"),"GBK");
        char[] chars=new char[8192];
        int len;
        while((len=isr.read(chars))!=-1){
            System.out.println(new String(chars,0,len));//此时会输出乱码
        }
    }
}


缓冲流

开了缓冲外挂,可以做高效的读写操作。
缓冲流之所以高效是因为底层封装了一个长度为8192的字节数组

1、BufferedInputStream

缓冲字节输入节流 BufferedInputStream:针对另外一个字节输入流添加高效输入操作的工具类

构造器:
public BufferedInputStream(InputStream in)
方法:
public void close();
public int read()//
public int read(byte[] b)//


2、BufferedOutputStream

缓冲字节输出节流 BufferedOutputStream:针对另外一个字节输除流添加高效输出操作的工具类

构造器:
public BufferedOutputStream(OutputStream out)
方法:
public void close();
public void write(int b);//
public void write(byte[] b,int off,int len);//


3、BufferedReader

缓冲字符输入流 BufferedReader

构造器
public BufferedReader(Reader in)  //接收一个Reader类的实例
特有方法

String readLine()读取一个文本行,读取一行数据

BufferedReader读取文件

public class ExceptionDemo {
    public static void main(String[] args) throws IOException {
//      创建缓冲字符输入流
        BufferedReader br=new BufferedReader(new FileReader("a.txt"));
        String len;
        while((len=br.readLine())!=null){
            System.out.println(len);//len是读取的一行文字
        }
        br.close();
    }
}

BufferedReader读取键盘输入

public static void main(String args[]) throws IOException {
    //System.in本身表示的是InputStream(字节流)
    //InputStreamReader转换流
    BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
    for (; ; ) {
        String line = in.readLine();
        if (line == null) {
            break;
        }
        System.out.println(line);
    }
}

行的终止符号:通过下列字符之一即可认为某行已终止:换行(‘\n’)、(‘\r’)或回车后直接跟着换行。


4、BufferedWriter

缓冲字符输出流 BufferedWriter

构造器
public BufferedWriter(Writer out)
特有方法
String newLine()//写入一个分隔符

将会自动根据操作系统的不同,选择\r\n或者是\r或者是\n。

使用缓冲字符输出流可以换行打印

public class ExceptionDemo {
    public static void main(String[] args) throws IOException {
//      使用匿名对象创建缓冲字符输出流
        BufferedWriter bw= new BufferedWriter(new FileWriter("a.txt"));
//      进行输出操作
        bw.write("尚");
//      "\r\n"是windows的回车换行
//      bw.write("\r\n");
        bw.newLine();//标准的换行
        bw.write("尚");
        bw.close();
    }
}


内存流

可以用于在内存中读写数据,比如将数据存储在字节数组中进行压缩、加密、序列化等操作。它的优点是不需要创建临时文件,可以提高程序的效率
如:原型模式的深拷贝时使用
  • ByteArrayOutputStream 对象,用于写入数据到内存缓冲区中
  • ByteArrayInputStream 对象,用于从字节数组中读取数据
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User implements Serializable, Cloneable {
    private Integer userId;
    private String name;
    private String password;
    private Address address;//引用类型
 
    //深拷贝 - 通过对象的序列化实现 (推荐)
    @Override
    protected Object clone() throws CloneNotSupportedException {
        try{
            //创建一个 32 字节 ( 默认大小 ) 的缓冲区
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(bos);
            oos.writeObject(this);
 
            ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(bis);
 
            User clone = (User)ois.readObject();
            return clone;
 
        }catch (Exception e){
            e.printStackTrace();
            return null;
        }
    }
}


对象流

Serializable 接口

类通过实现 java.io.Serializable 接口以启用其序列化功能。未实现此接口的类将无法使其任何状态序列化或反序列

序列化:把对象转换为字节序列的过程称为对象的序列化。
反序列化:把字节序列恢复为对象的过程称为对象的反序列化。

在Java中,对象的序列化与反序列化被广泛应用到RMI(远程方法调用)及网络传输中。

具有相同的序列化id才能序列化和反序列化成功,默认是 1L

//类通过实现 java.io.Serializable 接口以启用其序列化功能
private static final long serialVersionUID = 1L;

Java的序列化机制是通过判断类的serialVersionUID来验证版本一致性的。

在进行反序列化时,JVM会把传来的字节流中的serialVersionUID与本地相应实体类的serialVersionUID进行比较,如果相同就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异常,即是InvalidCastException

注意:

  1. 操作的对象的类要实现序列化接口 java.io.Serializable 
  2. 进行多个对象的序列化操作【用集合】,针对集合对象进行序列化和反序列化操作。
  3. 在序列化和反序列化中, 操作对象所在的类文件不能做任何更改。
  4. transient 瞬时的 被Transient修饰的变量,在反序列化时无法正确序列化,其值会被置为初始化值。int类型变量会被置为0,引用类型变量会被置为null
  5. 如果父类实现了Serializable,子类不需要重复实现Serializable接口也可以被序列化。
  6. 如果子类实现了Serializable而父类没有实现Serializable接口的情况。虽然java里面创建子类对象前会先创建父类对象。但由于父类没有实现Serializable接口,所以父类的自有变量的值不会被序列化。而是初始化值,int类型变量会被置为0,引用类型变量会被置为null。

ObjectOutputStream

ObjectOutputStream  对象输出流:针对跨项目使用对象实现序列化操作的工具类

构造器:
public ObjectOutputStream(OutputStream out) 

方法:
writeObject(Object obj)  可以将参数为 obj 的对象写出(即保存其状态),要恢复的话则用输入流。


ObjectInputStream

类通过实现 java.io.Serializable 接口以启用其序列化功能。

ObjectInputStream 对象输入流 :针对跨项目使用对象实现反序列化操作的工具类

  1. 对于不想进行序列化的变量,使用 transient 关键字修饰。
  2. transient 只能修饰变量,不能修饰类和方法。

构造器:
public ObjectInputStream (InputStream in)
方法:
public final Object readObject();//读取对象

序列化与反序列化演示

public class ExceptionDemo {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
         method01();
         method02();
    }
    public static void method01() throws IOException {
        //序列化
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("a.txt"));
        Account account = new Account("001",922113212);// Account要实现序列化接口,启动序列化功能。
        oos.writeObject(account);
        oos.close();
    }
    public static void method02() throws IOException, ClassNotFoundException {
        //反序列化
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("a.txt"));
        Account account = (Account)ois.readObject();
        System.out.println(account);
        ois.close();
    }

 控制台打印:


标准流

1、何为标准?

键盘(标准输入)、控制台(标准输出)

标准的输入流与输出流都属于System类的静态成员。

in代表的是已经创建好的可以直接从键盘读取的InputStream类型的流对象;

public static final InputStream in
//使用标准输入流,即从键盘直接输入
InputStream is = System.in;

out代表的是已经创建好的可以直接写入数据到控制台的PrintStream类型的流对象

public static final PrintStream out

2、标准输入流 System.in   

标准输入流:从标准输入设备(键盘)流向程序的数据

第1种获取键盘录入的方法

方式一:
public class ExceptionDemo {
    public static void main(String[] args) {
        System.out.println("输入一个字符串");
        Scanner sc = new Scanner(System.in);
        String s = sc.next();
        System.out.println(s);
        sc.close();
    }
}

键盘录入方式一运行测试: 不尽人意啊!

第2种获取键盘录入的方法(优化版):通过 BufferedReader

方式二:
public class ExceptionDemo {
    public static void main(String[] args) throws IOException {
        System.out.println("输入一个字符串");
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        String s = br.readLine();
        System.out.println(s);
        br.close();
    }
}


3、标准输出流 System.out  

PrintStream类 ( 打印流)  :针对文本文件进行快速打印。

构造器:
PrintStream(String fileName) 
方法:
public void print()

public void println()

设置系统的打印流流向,输出到print.txt     应用场景:日志

System.out 就是 PrintStream 类型的,只不过它的流向是系统规定的,打印在控制台上。
标准输出流设置为指向文件又设置回来的方法
ByteArrayOutputStream baos = new ByteArrayOutputStream();
PrintStream ps = new PrintStream(baos);
PrintStream stdout = System.out;
System.setOut(ps);

代码结束时,

System.setOut(stdout);

4、标准的改变

不过,既然 是流对象,我们就可以玩一个"小把戏" ,改变它的流向。
  • 改变标准的输入:System.setIn();
  • 改变标准的输出:System.setOut();
public class ExceptionDemo {
    public static void main(String[] args) throws IOException{
        PrintStream ps=new PrintStream("print.txt");
//      重置标准输出流  设置系统的打印流流向,输出到print.txt
        System.setOut(ps);
        System.out.println("hello");
        System.out.println("xiaoyumao");
        ps.println("world");//输出到print.txt
        ps.close();
    }
}

控制台一行没输入:

在print.txt中:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值