Java文件读写和(反)序列化

Java文件读写

文件系统及Java文件基本操作

文件概述

• 文件系统是由OS(操作系统)管理的
• 文件系统和Java进程是平行的,是两套系统
• 文件系统是由文件夹和文件递归组合而成
• 文件目录分隔符
Linux/Unix 用/隔开
Windows用\隔开,涉及到转义,在程序中需用/或\\代替
• 文件包括文件里面的内容和文件基本属性
• 文件基本属性:名称、大小、扩展名、修改时间等

Java 文件类File

• java.io.File是文件和目录的重要类(JDK6及以前是唯一)
–目录也使用File类进行表示
• File类与OS无关,但会受到OS的权限限制
想要在Java程序中表示一个真实存在的文件或目录,那么必须有一个File对象,但是Java程序中的一个File对象,可能没有一个真实存在的文件或目录。
• File对象可以作为参数传递给流的构造器

File常用构造器

构造器 介绍
public File(String pathname) 以pathname为路径创建File对象,可以是绝对路径或者相对路径,如果pathname是相对路径,则默认的当前路径在系统属性user.dir中存储。(绝对路径:是一个固定的路径,从盘符开始,相对路径:是相对于某个位置开始)
public File(String parent,String child) 以parent为父路径,child为子路径创建File对象。
public File(File parent,String child) 根据一个父File对象和子文件路径创建File对象

File类常用方法

–createNewFile,delete,exists, getAbsolutePath, getName,
getParent,getPath, isDirectory, isFile, length, listFiles, mkdir, mkdirs
• 注意:File不涉及到具体的文件内容,只涉及属性

File类的获取功能

方法 介绍
public String getAbsolutePath() 获取绝对路径
public String getPath() 获取路径
public String getName() 获取名称
public String getParent() 获取上层文件目录路径。若无,返回null
public long length() 获取文件长度(即:字节数)。不能获取目录的长度。
public long lastModified() 获取最后一次的修改时间,毫秒值
public String[] list() 获取指定目录下的所有文件或者文件目录的名称数组
public File[] listFiles() 获取指定目录下的所有文件或者文件目录的File数组

File类的重命名功能

方法 介绍
public boolean renameTo(File dest) 把文件重命名为指定的文件路径

File类的判断功能

方法 介绍
public boolean isDirectory() 判断是否是文件目录
public boolean isFile() 判断是否是文件
public boolean exists() 判断是否存在
public boolean canRead() : 判断是否可读
public boolean canWrite() : 判断是否可写
public boolean isHidden() : 判断是否隐藏

File类的创建功能

方法 介绍
public boolean createNewFile() 创建文件。若文件存在,则不创建,返回false
public boolean mkdir() 创建文件目录。如果此文件目录存在,就不创建了。如果此文件目录的上层目录不存在,也不创建。
public boolean mkdirs() 创建文件目录。如果上层文件目录不存在,一并创建

注意事项:如果你创建文件或者文件目录没有写盘符路径,那么,默认在项目路径下。
File类的删除功能

方法 介绍
public boolean delete() 删除文件或者文件夹

删除注意事项:
Java中的删除不走回收站
要删除一个文件目录,请注意该文件目录内不能包含文件或者文件目录
Java文件读写和(反)序列化_第1张图片
Java文件读写和(反)序列化_第2张图片
Java文件读写和(反)序列化_第3张图片

Java文件读写和(反)序列化_第4张图片

Java IO 包

• 文件系统和Java是两套系统
• Java读写文件,只能以(数据)流的形式进行读写
• java.io包中
–节点类:直接对文件进行读写
–包装类
• 转化类:字节/字符/数据类型的转化类
• 装饰类:装饰节点类

• 字节:byte, 8bit, 最基础的存储单位
• 字符:a, 10000, 我,の
• 数据类型: 3, 5.25,abcdef
• 文件是以字节保存,因此程序将变量保存到文件需要转化

• 节点类: 直接操作文件类
InputStream, OutputStream(字节)
FileInputStream,FileOutputStream

Reader,Writer(字符)
FileReader, FileWriter

• 转换类:字符到字节之间的转化
–InputStreamReader:文件读取时字节,转化为Java能理解的字符
–OutputStreamWriter:Java将字符转化为字节输入到文件中

• 装饰类:装饰节点类
–DataInputStream,DataOutputStream: 封装数据流
–BufferedInputStream,BufferOutputStream:缓存字节流
–BufferedReader, BufferedWriter:缓存字符流

• 总结

–Java文件处理类都在java.io包中
–处理类分为:节点类、包装类(转化类、装饰类)

Java IO

1.流的概念

  • 在程序中所有的数据都是以流的方式进行传输或保存的,程序需要数据的时候要使用输入流读取数据, 而当程序需要将一些数据保存起来的时候,就要使用输出流完成。

  • 往内存里写,是输入流。 输入流是指从外设流入计算机的数据流 内存往文件里写,是输出流。 输出流是指从计算机流向外设的数据流

  • 在 java 使用流的机制进行数据的传送,从文件到内存是输入流,从内存到文件是 输出流,输入流可以通过 read 读取,输出流以write 或 print 写入,对于流可以是分 为高层流和低层流,低层以一个字节或字符为单位进行处理,高层流以一批数据为单位 进行处理。

  • java的流类提供了结构化方法,如,底层流和高层过滤流。而高层流不是从输入设备读取,而是从其他流读取。同样高层输出流也不是写入输出设备,而是写入其他流

流的区别

  • 字节流
    -Java 语言中提供了很多用于操作字节流的类和接口,都集中在 java.io
    包中,其中InputStream、OutputStream两个类是字节流处理的的基类;InputStream 类是用来处理输入流的,而OutputStream 用来处理输出流。 在这两个基类的基础上,java.io包还衍生出来很多子类供用户使用。比如FileInputStream、FileOutputStream 类,等等。

  • 字符流
    字符流是在 JDK1.1 里面引进的,是为了处理字符而提出来的;字符流常常用于读取文本类型的数据或字符串流的操作。字符流的处理和字节流差不多,API 基本上完全一样,只是计量单位不同;字符流一次可以读取一个字符(1char = 2byte = 16bit);其专门用来操作字符、字符数组或字符串。
    java.io 包中也提供了很多用于操作字符流的类和接口,其中 Reader、Writer两个类是字符流处理的的基类。java.io 包还衍生出来很多子类供用户使用,比如 FileReader、 FileWriter 类,等等。在针对字符、字符串、文本文件进行输入与输出的操作时,应该首选字符流处理。
    注意:常用的 GBK 字符集中,英文是占用 1 个字节,中文是 2 个字节;UTF-8 字符集,英文是 1 个字节,中文是 3 个字节;Unicode 字符集,英文中文都是 2 个字节;

  • 字符流处理的单元为2个字节的Unicode字符,分别操作字符、字符数组或字符串,而字节流处理单元为1个字节,操作字节和字节数组。所以字符流是由Java虚拟机将字节转化为2个字节的Unicode字符为单位的字符而成的,所以它对多国语言支持性比较好!如果是音频文件、图片、歌曲( ),就用字节流好点,如果是关系到中文(文本)的( ),用字符流好点

  • 所有文件的储存是都是字节(byte)的储存,在磁盘上保留的并不是文件的字符而是先把字符编码成字节,再储存这些字节到磁盘。在读取文件(特别是文本文件)时,也是一个字节一个字节地读取以形成字节序列

  • 字节流可用于任何类型的对象,包括二进制对象,而字符流只能处理字符或者字符串;
    2.字节流提供了处理任何类型的IO操作的功能,但它不能直接处理Unicode字符,而字符流就可以

  • 字节流是最基本的,所有的InputStrem和OutputStream的子类都是,主要用在处理二进制数据,它是按字节来处理的但实际中很多的数据是文本,又提出了字符流的概念,它是按虚拟机的encode来处理,也就是要进行字符集的转化 这两个之间通过InputStreamReader,OutputStreamWriter来关联,实际上是通过byte[]和String来关联在实际开发中出现的汉字问题实际上都是在字符流和字节流之间转化不统一而造成的

字节流:InputStream、OutputStream

处理字节流的抽象类
InputStream 是字节输入流的所有类的超类,一般我们使用它的子类,如FileInputStream等.
OutputStream是字节输出流的所有类的超类,一般我们使用它的子类,如FileOutputStream等.

InputStream

  • int read()
    从输入流中读取数据的下一个字节。返回0 到255 范围内的int 字节值。如果因为已经到达流末尾而没有可用的字节,则返回值-1。

  • int read(byte[] b)
    从此输入流中将最多 b.length 个字节的数据读入一个 byte 数组中。如果因为已经到达流末尾而没有可用的字节,则返回值 -1。否则以整数形式返回实际读取的字节数。
    -int read(byte[] b, int off,int len)
    将输入流中最多 len 个数据字节读入 byte 数组。尝试读取 len 个字节,但读取的字节也可能小于该值。以整数形式返回实际读取的字节数。如果因为流位于文件末尾而没有可用的字节,则返回值-1。

  • public void close() throws IOException
    关闭此输入流并释放与该流关联的所有系统资源。

OutputStream

  • void write(int b)
    将指定的字节写入此输出流。write 的常规协定是:向输出流写入一个字节。要写入的字节是参数b 的八个低位。b 的24 个高位将被忽略。即写入0~255范围的。

  • void write(byte[] b)
    将b.length 个字节从指定的byte 数组写入此输出流。write(b) 的常规协定是:应该与调用write(b, 0, b.length) 的效果完全相同。

  • void write(byte[] b,int off,int len)
    将指定byte 数组中从偏移量off 开始的len 个字节写入此输出流。

  • public void flush()throws IOException
    刷新此输出流并强制写出所有缓冲的输出字节,调用此方法指示应将这些字节立即写入它们预期的目标。

  • public void close() throws IOException
    关闭此输出流并释放与该流关联的所有系统资源。

字符流:Reader Writer

Reader

  • int read()
    读取单个字符。作为整数读取的字符,范围在 0 到 65535 之间 (0x00-0xffff)(2个字节的Unicode码),如果已到达流的末尾,则返回-1

  • int read(char[] cbuf)
    将字符读入数组。如果已到达流的末尾,则返回 -1。否则返回本次读取的字符数。

  • int read(char[] cbuf,int off,int len)
    将字符读入数组的某一部分。存到数组cbuf中,从off处开始存储,最多读len个字符。如果已到达流的末尾,则返回-1。否则返回本次读取的字符数。

  • public void close() throws IOException
    关闭此输入流并释放与该流关联的所有系统资源。

Writer

  • void write(int c)
    写入单个字符。要写入的字符包含在给定整数值的 16 个低位中,16 高位被忽略。 即写入0 到65535 之间的Unicode码。

  • void write(char[] cbuf)
    写入字符数组。

  • void write(char[] cbuf,int off,int len)
    写入字符数组的某一部分。从off开始,写入len个字符

  • void write(String str)
    写入字符串。

  • void write(String str,int off,int len)
    写入字符串的某一部分。

  • void flush()
    刷新该流的缓冲,则立即将它们写入预期目标。

  • public void close() throws IOException
    关闭此输出流并释放与该流关联的所有系统资源。

一般使用 FileReader,FileWriter

缓冲流

  • 为了提高数据读写的速度,Java
    API提供了带缓冲功能的流类,在使用这些流类时,会创建一个内部缓冲区数组,缺省使用8192个字节(8Kb)的缓冲区。

在这里插入图片描述

  • 缓冲流要“套接”在相应的节点流之上,根据数据操作单位可以把缓冲流分为:
    BufferedInputStream 和 BufferedOutputStream
    BufferedReader 和 BufferedWriter

  • 当读取数据时,数据按块读入缓冲区,其后的读操作则直接访问缓冲区

  • 当使用BufferedInputStream读取字节文件时,BufferedInputStream会一次性从文件中读取8192个(8Kb),存在缓冲区中,直到缓冲区装满了,才重新从文件中读取下一个8192个字节数组。

  • 向流中写入字节时,不会直接写到文件,先写到缓冲区中直到缓冲区写满, BufferedOutputStream才会把缓冲区中的数据一次性写到文件里。使用方法flush()可以强制将缓冲区的内容全部写入输出流

  • 关闭流的顺序和打开流的顺序相反。只要关闭最外层流即可,关闭最外层流也会相应关闭内层节点流

  • flush()方法的使用:手动将buffer中内容写入文件

  • 如果是带缓冲区的流对象的close()方法,不但会关闭流,还会在关闭流之前刷新缓冲区,关闭后不能再写出

Java文件读写和(反)序列化_第5张图片

转换流:InputStreamReader OutputStreamWriter

  • 字节流中的数据都是字符时,转成字符流操作更高效。
  • 很多时候我们使用转换流来处理文件乱码问题。实现编码和解码的功能。

InputStreamReader
InputStreamReader 是字节流通向字符流的桥梁,它将字节流转换为字符流.
(InputStreamReader:将InputStream转换为Reader

  • 需要和InputStream“套接”。
  • 构造器
    public InputStreamReader(InputStream in)
    public InputSreamReader(InputStream in,String charsetName)
    如: Reader isr = new InputStreamReader(System.in,”gbk”);

OutputStreamWriter
OutputStreamWriter是字符流通向字节流的桥梁,它将字符流转换为字节流.
( OutputStreamWriter:将Writer转换为OutputStream

  • 需要和OutputStream“套接”。
  • 构造器
    public OutputStreamWriter(OutputStream out)
    public OutputSreamWriter(OutputStream out,String charsetName)

Java文件读写和(反)序列化_第6张图片

BufferedReader BufferedWriter(处理流之字符缓冲流)

• 是对一个已存在的流的连接和封装,处理流又称为过滤流(Filter)
• 不直接连接到数据源或目的地,而是“连接”在已存在的流(节点流或处理流)之上,通过对数据的处理为程序提供更为强大的读写功能。
• 如缓冲处理流 BufferedReader
• 处理流的构造方法总是要带一个其他的流对象作参数

BufferedReader 由Reader类扩展而来,提供通用的缓冲方式文本读取,readLine读取一个文本行,从字符输入流中读取文本,缓冲各个字符,从而提供字符、数组和行的高效读取。
BufferedWriter 由Writer 类扩展而来,提供通用的缓冲方式文本写入, newLine使用平台自己的行分隔符,将文本写入字符输出流,缓冲各个字符,从而提供单个字符、数组和字符串的高效写入。

InputStream能从來源处读取一個一個byte,所以它是最低级的,

•为了使用方便,经常将System.in用各种处理流进行封装处理,如:BufferedReader br = new BufferedReader(new InputStreamReader( System.in ) );
br.readLine();

标准输入、输出流

  • System.in和System.out分别代表了系统标准的输入和输出设备

  • 默认输入设备是:键盘,输出设备是:显示器

  • System.in的类型是InputStream

  • System.out的类型是PrintStream,其是OutputStream的子类FilterOutputStream 的子类

  • 重定向:通过System类的setIn,setOut方法对默认设备进行改变。
    public static void setIn(InputStream in)
    public static void setOut(PrintStream out)

数据流

  • 为了方便地操作Java语言的基本数据类型和String的数据,可以使用数据流。

  • 数据流有两个类:(用于读取和写出基本数据类型、String类的数据)
    DataInputStream 和 DataOutputStream
    分别“套接”在 InputStream 和 OutputStream 子类的流上

  • DataInputStream中的方法
    boolean readBoolean()
    char readChar()
    double readDouble()
    long readLong()
    String readUTF()
    byte readByte()
    float readFloat()
    short readShort()
    int readInt()

  • DataOutputStream中的方法
    将上述的方法的read改为相应的write即可。

总结

  1. 一个字符等于两个字节

  2. 实际上字节流在操作时本身不会用到缓冲区(内存),是文件本身直接操作的, 而字符流在操作时使用了缓冲区,通过缓冲区再操作文件

  3. 字符流处理的单元为2个字节的Unicode字符,分别操作字符、字符数组或字符串, 而字节流处理单元为1个字节,操作字节和字节数组。

  4. 在字节流中输出数据主要是使用OutputStream完成,输入使的是InputStream,
    在字符流中输出主要是使用Writer类完成,输入流主要使用Reader类完成。(这四个都是抽象类)

  5. 在所有的硬盘上保存文件或进行传输的时候都是以字节的方法进行的,包括图片也是按字节完成,
    而字符是只有在内存中才会形成的,所以使用字节的操作是最多的。

  6. 如果要java程序实现一个拷贝功能,应该选用字节流进行操作(可能拷贝的是图片),并且采用边读边写的方式(节省内存)。

  7. 为什么要把FileInputStream流转换成InputStream流,FIleInputStream是InputStream流的子类,在能用到父类的地方都可以被子类替换

  8. 对于流可以是分为高层流和低层流,低层以一个字节或字符为单位进行处理,高 层流以一批数据为单位进行处理。

2.文本文件的读写

文件类型

• 文件类型
–一般文本文件(若干行字符构成的文件),如txt等
–一般二进制文件,如数据文件dat
–带特殊格式的文本文件,如xml等
–带特殊格式二进制文件,如doc,ppt等
• 文件是数据的一个容器(口袋)
• 文件可以存放大量的数据
• 文件很大,注定Java只能以流形式依次处理

输入流和输出流的区别

• 从Java角度理解
–输出:数据从Java到文件中,写操作
–输入:数据从文件到Java中,读操作
• 文本文件读写
–输出文本字符到文件中
–从文件中读取文本字符串

先记住:
FieoutputStream节点类,负责写字节;
OutputStreamWriter转化类,负责字符到字节转化;
BufferedWriter装饰类,负责写字符到缓存区。
三者构建关系:BufferedWriter (OutputStreamlriter (File0utputStream)))。

文件读写步骤
导入相关的包
1创建文件File类对象
2创建文件读写输入输出流对象
3进行读写操作
4关闭输入输出流
5整个过程需要异常处理

例如:

1·用FileInputStream读文本文件

导入相关的包
import java.io.IOException;
import java.io.InputStream;
import java.io.FileInputStream;
构造一个文件输入流对象
File f = new File(“text.txt”);
InputStream fis = new FileInputStream(f);

InputStream fis = new FileInputStream(“text.txt”);
利用文件输入流类的方法读取文本文件的数据
fis.available(); //可读取的字节数
fis.read(); //读取文件的数据
关闭文件输入流对象
fis.close();

public static void main (String[] args) {
	try{
	        int size;
	        InputStream fis = new FileInputStream("text.txt");
	        System.out.println("可读取的字节数: " +
		 (size = fis.available()));
	        char[] text = new char[200] ;
	        for (int count = 0; count < size; count++) {
	            text[count] = ((char) fis.read());
	            System.out.print(text[count]);
	        }
	        fis.close();			
	}catch(IOException ex){
	        System.out.println (ex.getMessage());
	}
} 
@Test
public void testFileInputStream() {
    FileInputStream fis = null;
    try {
        //1. 造文件
        File file = new File("hello.txt");

        //2.造流
        fis = new FileInputStream(file);

        //3.读数据
        byte[] buffer = new byte[5];
        int len;//记录每次读取的字节的个数
        while((len = fis.read(buffer)) != -1){

            String str = new String(buffer,0,len);
            System.out.print(str);

        }
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        if(fis != null){
            //4.关闭资源
            try {
                fis.close();
            } catch (IOException e) {
                e.printStackTrace();
            }

        }
    }

}

2·用FileOutputStream写文本文件

  • 在写入一个文件时,如果使用构造器FileOutputStream(file),则目录下有同名文件将被覆盖。

  • 如果使用构造器FileOutputStream(file,true),则目录下的同名文件不会被覆盖,在文件内容末尾追加内容。

导入相关的包
import java.io.IOException;
import java.io.OutputStream;
import java.io.FileOutputStream;
构造一个文件输出流对象
File f = new File(“text.txt”);
OutputStream fos = new FileOutputStream(f);

OutputStream fos = new FileOutputStream(“text.txt”);
利用文件输出流的方法写文本文件
String str =“好好学习Java”;
byte[] words = str.getBytes();
fos.write(words, 0, words.length);
关闭文件输出流
fos.close();

public static void main(String[] args) { 
       try {                 
            String str ="好好学习Java";
            byte[] words  = str.getBytes();
            OutputStream fos = new FileOutputStream("Text.txt");
            fos.write(words, 0, words.length);
            System.out.println("Text文件已更新!");
            fos.close();
       } catch (IOException obj) {
                 System.out.println("创建文件时出错!");
       }
  } 

结合案例

import java.io.*;

public class FileStreamDemo {
	public static void main(String[] args) {
		try{
			File f1=new File("img.jpg");
			//创建输入流
			InputStream fis=new FileInputStream(f1);
			
			//输出流
			File f2=new File("img3.jpg");
			OutputStream fos=new FileOutputStream(f2);
			
			int temp;
			
			while((temp=fis.read())!=-1){
				fos.write(temp); //写入到第二个文件,二进制文件复制
			}
			//关闭
			fis.close();
			fos.close();
			System.out.println("文件复制成功!!!");
		}catch(FileNotFoundException ex){
			ex.printStackTrace();
		}catch(IOException ex){
			ex.printStackTrace();
		}
	}
}

3·用BufferedReader读文本文件

引入相关的类
import java.io.FileReader;
import java.io.BufferedReader;
import java.io.IOException;
构造一个BufferedReader对象
FileReader fr=new FileReader(“mytest.txt”);
BufferedReader br=new BufferedReader(fr);
BufferedReader的read方法读取文本文件的数据
BufferedReader的readLine()方法可以读取一行数据,返回字符串
关闭相关的流对象
br.close();
fr.close();

–FileInputStream, InputStreamWriter, BufferedReader

例如

FileReader fr=new FileReader("mytest.txt"); 

        /**创建一个BufferedReader 对象.*/
        BufferedReader br=new BufferedReader(fr); 
        /**读取一行数据.*/ 
        String line=br.readLine();
        
        while(line!=null){ 
            System.out.println(line);
            line=br.readLine(); 
        }
         /**流的关闭.*/ 
        br.close();
        fr.close(); 

4·用BufferedWriter写文本文件

引入相关的类
import java.io.FileWriter ;
import java.io.BufferedWriter ;
import java.io.IOException;
构造一个BufferedWriter对象
FileWriter fw=new FileWriter(“mytest.txt”);
BufferedWriter bw=new BufferedWriter(fw);
利用BufferedWriter的方法写文本文件
例如:bw.write (“hello”);
相关流对象的清空和关闭
bw.flush();
fw.close();

       /**创建一个FileWriter 对象*/
       FileWriter fw=new FileWriter("mytest.txt"); 
       /**创建一个BufferedWriter 对象*/
       BufferedWriter bw=new BufferedWriter(fw); 
       bw.write("大家好!"); 
       bw.write("我正在学习BufferedWriter。"); 
       bw.newLine(); 
       bw.write("请多多指教!"); 
       bw.newLine();
       bw.write("email: [email protected]"); 
       bw.flush();
       fw.close();

结合案例:

import java.io.*;

public class BufferedDemo {

	public static void main(String[] args) {
		try{
			File f1=new File("aaa.txt");
			//创建低层输入流
			FileReader fr=new FileReader(f1);
			//创建高层输入流
			BufferedReader br=new BufferedReader(fr);
			
			//创建输出流
			File f2=new File("ccc.txt");
			FileWriter fw=new FileWriter(f2,true);//true表示在文件末尾追加
			BufferedWriter bw=new BufferedWriter(fw);
			
			//输入、输出
			String line;
			while((line=br.readLine())!=null){//readLine方法是读取文件中的一行,不包括换行符
				System.out.println(line); //输出一行文字,不包括换行符
				//写入ccc.txt文件
				bw.write(line);//向文件写入字符串
				//在文件中换行
				bw.newLine();
			}
			//关闭
			br.close();
			fr.close();
			bw.close();
			fw.close();
			
		}catch(FileNotFoundException ex){
			ex.printStackTrace();
		}catch(IOException ex){
			ex.printStackTrace();
		}
	}
}

5.用DataInputStream读二进制文件

引入相关的类
import java.io.FileInputStream;
import java.io.DataInputStream;
构造一个数据输入流对象
FileInputStream fis = new FileInputStream(“HelloWorld.class”);
DataInputStream dis = new DataInputStream(fis);
利用数据输入流类的方法读取二进制文件的数据
dis.readInt(); // 读取出来的是整数
dis.readByte(); // 读取出来的数据是Byte类型
数据输入流的关闭
dis.close (); //关闭数据输入流
fis.close();

6.用DataOutputStream写二进制文件

引入相关的类
import java.io. FileOutputStream;
import java.io. DataOutputStream;
构造一个数据输出流对象
FileOutputStream fos = new FileOutputStream(“temp.class”);
DataOutputStream dos = new DataOutputStream(fos);
利用文件输出流类的方法写二进制文件
dos.writeByte(1); //把数据写入二进制文件
数据输出流的关闭
dos.close();
fos.close();

结合案例

import java.io.*;
public class DataStreamDemo {

	public static void main(String[] args) {
		try{
			File f1=new File("img.jpg");
			//创建输入流
			FileInputStream fis=new FileInputStream(f1);
			DataInputStream dis=new DataInputStream(fis);
			
			//输出流
			File f2=new File("img2.jpg");
			FileOutputStream fos=new FileOutputStream(f2);
			DataOutputStream dos=new DataOutputStream(fos);
			
			int temp;
			//循环复制图片文件
			while((temp=dis.read())!=-1){
				dos.write(temp); //写入到第二个文件,二进制文件复制
			}
			//关闭
			dis.close();
			fis.close();
			dos.close();
			fos.close();
			System.out.println("文件复制成功!!!");
		}catch(FileNotFoundException ex){
			ex.printStackTrace();
		}catch(IOException ex){
			ex.printStackTrace();
		}
	}
}

7.格式化输出流PrintWriter

PrintWriter提供了print( )、println( )、format( )等一系列方法,来格式化输出字符流。
写字符串:
println(“将信息写入文件”);
write(“将信息写到文件中\r\n”);
根据ASCII码写字符
write(65);
带格式输出:
printf("%f",3.14159263423);

import java.io.*;

public class PrintWriterDemo {

	public static void main(String[] args) {
		try{
			PrintWriter pw=new PrintWriter("myFile.txt");
			//写入文件
			pw.print("欢迎学习Java"); //不换行
			pw.println("软件开发很具有技术性的工作");//换行
			pw.write(65); //不换行
			pw.write("Programming\r\n"); //\r\n表示换行
			pw.printf("%.2f\r\n",3.1415926);
			pw.printf("%d\r\n",100);
			pw.close();
		}catch(IOException ex){
			ex.printStackTrace();
		}
	}

}

3. Zip文件读写

Java zip 包

• 压缩包:zip, rar, gz, ……
• Java zip 包支持Zip和Gzip包的压缩和解压
• zip文件操作类: java.util.zip包中
–java.io.InputStream, java.io.OutputStream的子类
–ZipInputStream, ZipOutputSream 压缩文件输入/输出流
–ZipEntry 压缩项

压缩

• 单个/多个压缩
–打开输出zip文件
–添加一个ZipEntry
–打开一个输入文件,读数据,向ZipEntry写数据,关闭输入文件
–重复以上两个步骤,写入多个文件到zip文件中
–关闭zip文件

代码(1) SingleFileZip.java
Java文件读写和(反)序列化_第7张图片
代码(2) MultipleFileZip.java
//文件夹压缩

import java.io.File ;
import java.io.FileInputStream ;
import java.io.InputStream ;
import java.util.zip.ZipEntry ;
import java.util.zip.ZipOutputStream ;
import java.io.FileOutputStream ;
public class MultipleFileZip{
	public static void main(String args[]) throws Exception{	// 所有异常抛出
		File file = new File("c:/temp/multiple") ;	// 定义要压缩的文件夹
		File zipFile = new File("c:/temp/multiple2.zip") ;	// 定义压缩文件名称
		
		InputStream input = null ;	// 定义文件输入流
		ZipOutputStream zipOut = null ;	// 声明压缩流对象
		zipOut = new ZipOutputStream(new FileOutputStream(zipFile)) ;
		zipOut.setComment("multiple file zip") ;	// 设置注释
		
		//开始压缩
		int temp = 0 ;
		if(file.isDirectory()){	// 判断是否是文件夹
			File lists[] = file.listFiles() ;	// 列出全部子文件
			for(int i=0;i<lists.length;i++){
				input = new FileInputStream(lists[i]) ;	// 定义文件的输入流
				zipOut.putNextEntry(new ZipEntry(file.getName()
					+File.separator+lists[i].getName())) ;	// 设置ZipEntry对象
				System.out.println("正在压缩" + lists[i].getName());
				while((temp=input.read())!=-1){	// 读取内容
					zipOut.write(temp) ;	// 压缩输出
				}
				input.close() ;	// 关闭输入流
			}
		}
		zipOut.close() ;	// 关闭输出流
		System.out.println("multiple file zip done.");
	}
}

解压

• 单个/多个解压
–打开输入的zip文件
–获取下一个ZipEntry
–新建一个目标文件,从ZipEntry读取数据,向目标文件写入数据,
关闭目标文件
–重复以上两个步骤,从zip包中读取数据到多个目标文件
–关闭zip文件

代码(4) SingleFileUnzip.java
Java文件读写和(反)序列化_第8张图片
代码(5) MultipleFileUnzip

import java.io.File ;  
import java.io.OutputStream ;  
import java.io.InputStream ;  
import java.util.zip.ZipEntry ;  
import java.util.zip.ZipFile ;  
import java.util.zip.ZipInputStream ;  
import java.io.FileInputStream ;  
import java.io.FileOutputStream ;  
public class MultipleFileUnzip{  
    public static void main(String args[]) throws Exception{    
    	//待解压的zip文件,需要在zip文件上构建输入流,读取数据到Java中
        File file = new File("c:/temp/multiple.zip") ;   // 定义压缩文件名称  
        File outFile = null ;   // 输出文件的时候要有文件夹的操作  
        ZipFile zipFile = new ZipFile(file) ;   // 实例化ZipFile对象  
        ZipInputStream zipInput = null ;    // 定义压缩输入流  
        
        //定义解压的文件名
        OutputStream out = null ;   // 定义输出流,用于输出每一个实体内容  
        InputStream input = null ;  // 定义输入流,读取每一个ZipEntry  
        ZipEntry entry = null ; // 每一个压缩实体  
        zipInput = new ZipInputStream(new FileInputStream(file)) ;  // 实例化ZIpInputStream 
        
        //遍历压缩包中的文件
        while((entry = zipInput.getNextEntry())!=null){ // 得到一个压缩实体  
        	System.out.println("解压缩" + entry.getName() + "文件") ;  
            outFile = new File("c:/temp/" + entry.getName()) ;   // 定义输出的文件路径  
            if(!outFile.getParentFile().exists()){  // 如果输出文件夹不存在 
                outFile.getParentFile().mkdirs() ;   
                // 创建文件夹 ,如果这里的有多级文件夹不存在,请使用mkdirs()
                // 如果只是单纯的一级文件夹,使用mkdir()就好了
            }  
            if(!outFile.exists()){  // 判断输出文件是否存在
            	if(entry.isDirectory())
            	{
            		outFile.mkdirs();
            		System.out.println("create directory...");
            	}
            	else
            	{
            		outFile.createNewFile() ;   // 创建文件
            		System.out.println("create file...");
            	}                  
            }  
            if(!entry.isDirectory())
            {
            	input = zipFile.getInputStream(entry) ; // 得到每一个实体的输入流  
                out = new FileOutputStream(outFile) ;   // 实例化文件输出流  
                int temp = 0 ;  
                while((temp=input.read())!=-1){  
                    out.write(temp) ;  
                }  
                input.close() ;     // 关闭输入流  
                out.close() ;   // 关闭输出流  
            }
            
        }  
        input.close() ;  
    }  
}
总结

• Java支持Zip和Gzip文件解压缩
• 重点在Entry和输入输出流向, 无需关注压缩算法

总结:

字节流到字符流的转换
InputStreamReader:是Reader的子类,将一个字节流的输入对象变为字符流的输入对象。
OutputStreamWriter:是Writer的子类,将一个字符流的输出对象变为字节流输出对象。

为什么抛异常

  1. 因为IO通常涉及到文件,网络相关的操作,很容易在读写过程中出现异常,比如网络突然中断,文件突然被另一个进程删除了,所以涉及IO的很多操作会需要捕获异常。另外IO还涉及到占用资源,所以放在try里面的资源可以方便finally的时候关闭以释放资源。
  2. 因为会抛出checked异常。必须进行显示的捕获。
  3. 从代码的角度,因为Iostream会抛出异常。 所以调用方需要处理异常

子类异常写父类异常前面

string类
创建之后不会再做修改和变动

stringbuff类
创建后允许再做更改和变化

底层流关闭了,上层流没有关闭,还会进行文件的读写操作。只不过此时写不到文件里面。而是写到缓冲里面。但是关闭了上层流,就等于关闭了缓冲流,底层流没有缓冲流给提供读写操作的内容。无法进行读写。自然就关了

FileOutputStream fos = new FileOutputStream(file)与FileOutputStream fos = new FileOutputStream(file,true)的区别在于FileOutputStream(file)会覆盖之前写入的内容,FileOutputStream(file,true)是追加,不会覆盖之前写入的内容。

FileInputStream(String name) 用某个文件名 name 创建一个字节输入流对象
FileInputStream(File file) 用 File 对象创建一个字节输入流对象

为什么?

int temp
while((temp=fis.read())!=-1){
				fos.write(temp); //写入到第二个文件,二进制文件复制
			}
String line;
			while((line=br.readLine())!=null){//readLine方法是读取文件中的一行,不包括换行符
				System.out.println(line); //输出一行文字,不包括换行符
				//写入ccc.txt文件
				bw.write(line);//向文件写入字符串
				//在文件中换行
				bw.newLine();
			}
  • InputStream是字节输入流的抽象基类 ,InputStream作为基类,给它的基类定义了几个通用的函数: read(byte[]
    b):从流中读取b的长度个字节的数据存储到b中,返回结果是读取的字节个数(当再次读时,如果返回-1说明到了结尾,没有了数据)
  • FileInputStream主要用来操作文件输入流,它除了可以使用基类定义的函数外,它还实现了基类的read()函数(无参的): read():从流中读取1个字节的数据,返回结果是一个int,(如果编码是以一个字节一个字符的,可以尝试转成char,用来查看数据)。
  • InputStream inputstream = new FileInputStream(“fileName”); Reader类的read()方法返回类型为int :作为整数读取的字符(占两个字节共16位),范围在 0 到 65535 之间
    (0x00-0xffff),如果已到达流的末尾,则返回 -1
    inputStream的read()虽然也返回int,但由于此类是面向字节流的,一个字节占8个位,所以返回 0 到 255 范围内的
    int 字节值。如果因为已经到达流末尾而没有可用的字节,则返回值
    -1。因此对于不能用0-255来表示的值就得用字符流来读取!比如说汉字.
  • BufferedReader的是缓冲流string类型

其他流(网络那那章会用到)
数据输出流允许应用程序以与机器无关方式将基本数据类型写到底层输出流.
下面的构造方法用来创建数据输出流对象.
DataOutputStream out = new DataOutputStream(OutputStream out);

getinputstream 返回此套接字的输入流
getoutputstream 返回此套接字的输出百流

对象序列化和反序列化

概念

(1)Java序列化就是指把Java对象转换为字节序列的过程
(2)Java反序列化就是指把字节序列恢复为Java对象的过程
将对象的状态数据以字节流的形式进行处理,即在文件中长久保存这个对象的状态称为序列化(Serialization)
在需要时从文件中获取该对象的信息以重新获得一个完全的对象,称为反序列化( Deserialization)

规则

一个类需要满足如下的要求,方可序列化

  • 1.需要实现接口:Serializable

  • 2.当前类提供一个全局常量:serialVersionUID

  • 3.除了当前类需要实现Serializable接口之外,还必须保证其内部所有属性也必须是可序列化的。(默认情况下,基本数据类型可序列化)

  • 补充:ObjectOutputStream和ObjectInputStream不能序列化static和transient修饰的成员变量

序列化实现步骤

1导入java.io包
2需要序列化的对象所属的类必须实现Serializable接口
3构造FileOutputStream对象
4构造ObjectOutputStream对象
5使用ObjectOutputStream对象的writeObject()方法进行序列化
6关闭ObjectOutputStream对象
7关闭FileOutputStream对象
8对序列化全程捕获IOException异常

反序列化实现步骤

1导入java.io包
2需要反序列化的对象所属的类必须实现Serializable接口
3构造FileInputStream对象
4构造ObjectInputStream对象
5使用ObjectInputStream对象的readObject()方法进行反序列化
6关闭ObjectInputStream对象
7关闭FileInputStream对象
8对反序列化全程捕获ClassNotFoundException和IOException异常

serialVersionUID的作用

序列化时使用一个serialVersionUID 的版本号,序列化操作的时候系统会把当前类的serialVersionUID写入到序列化文件中,当反序列化时系统会去检测文件中的serialVersionUID,判断它是否与当前类的serialVersionUID一致,如果一致就说明序列化类的版本与当前类版本是一样的,可以反序列化成功,否则失败。
Student.java

import java.io.*;

public class Student implements Serializable{
	final static long serialVersionUID=1L; //L表示是一个长整形
	String name;
	int age;
	
	public Student(){
		
	}
	
	public Student(String name,int age){
		this.name=name;
		this.age=age;
	}
	
	public void printInfo(){
		System.out.println(name+","+age);
	}
}
import java.io.*;

public class SerialDemo {

	public static void main(String[] args) {
		try{
			//******************序列化**********************//
			File file=new File("student.dat"); //二进制文件
			//构造序列化输出流对象
			FileOutputStream fos=new FileOutputStream(file);
			ObjectOutputStream oos=new ObjectOutputStream(fos);
			
			Student s1=new Student("李红",23);
			s1.printInfo();
			System.out.println("s1 UID:"+s1.serialVersionUID);
			//序列化
			oos.writeObject(s1); //将学生对象一次性写入文件
			
			//关闭
			oos.close();
			fos.close();
			
			//******************反序列化**********************//
			System.out.println("********************反序列化后************************");
			//创建反序列化的输入流
			FileInputStream fis=new FileInputStream(file);
			ObjectInputStream ois=new ObjectInputStream(fis);
			//反序列化读取数据
			Student s2=(Student)ois.readObject(); //返回Object的对象,要进行强制性转换
			s2.printInfo();//输出反序列化回复的学生对象的信息
			System.out.println("s2 UID:"+s2.serialVersionUID);
			ois.close();
			fis.close();
			
		}catch(ClassNotFoundException ex){
			ex.printStackTrace();
		}catch(IOException ex){
			ex.printStackTrace();
		}
	}

}

Extenalizable接口-1

Extenalizable是定制化序列化接口
如果你想定制自己的序列化机制的话,它就派上用场了,它通过你自己编码的方式完成对象的编解码
实现该接口的类需要:
无参构造方法
重写readExtenal和writeExtenal方法

ExternalDemo:

import java.io.*;
//实现定制序列化接口
class UserInfo implements Externalizable {
    public String userName;
    public String userPass;
    public int userAge;

    public UserInfo( ) {

    }
    public UserInfo(String username, String userpass, int userage) {
        this.userName = username;
        this.userPass = userpass;
        this.userAge = userage;
    }
    //重写接口里的方法
    public void writeExternal(ObjectOutput out) throws IOException {
    	//自定义的序列化
        out.writeObject(userName);
        out.writeObject(userPass);
        out.writeObject(userAge);
    }
    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException{
        this.userName = (String) in.readObject();
        this.userPass = (String) in.readObject();
        this.userAge = (Integer) in.readObject();
    }
    public void printInfo(){
    	System.out.println(userName+","+userPass+","+userAge);
    }
}


public class ExternalDemo {
	//自定义的序列化
	public static void serialize(String fileName){
		try{
			//创建序列化输出流
			FileOutputStream fos=new FileOutputStream(fileName);
            ObjectOutputStream oos=new ObjectOutputStream(fos);
            UserInfo ui=new UserInfo("王浩","888888",20);
            oos.writeObject(ui);//自动调用UserInfo里的 writeExternal方法
            oos.close();
            fos.close();
		}catch(IOException ex){//--8
            ex.printStackTrace();
        }
	}
	//自定义的反序列化
	public static void deserialize(String fileName){
		try{
			//创建反序列化输入流
			FileInputStream fis=new FileInputStream(fileName);
            ObjectInputStream ois=new ObjectInputStream(fis);
            UserInfo ui=(UserInfo)ois.readObject();//自动调用UserInfo里的 readExternal方法
            ui.printInfo();
            ois.close();
            fis.close();
		}catch(ClassNotFoundException ex){//--8
            ex.printStackTrace();
        }catch(IOException ex){//--8
            ex.printStackTrace();
        }
	}
	public static void main(String[] args) {
		//调用自定义方法进行序列化
		serialize("userInfo.dat");
		System.out.println("序列化成功");
		//调用自定义的反序列化方法进行反序列化
		deserialize("userInfo.dat");
		System.out.println("反序列化成功");
	}
}

序列化和反序列化使用规则

  1. 并不是任何引用对象都可以序列化,只有实现了Serializable接口的类的对象才可以。
    不仅对象可以序列化和反序列化,对象的数组或集合也可以序列化和反序列化。
    如果一个父类实现了Serializable接口,其所有子类均实现Serializable接口。
    static的属性和方法是不可以序列化的,因为static的属性和成员与对象无关,而序列化和反序列化均针对“对象”而言。
  2. 对于不希望被序列化的非static属性(实例变量),可以在该属性声明时使用transient关键字进行标明如果一个序列化子类的父类是非序列化的,那么该子类的自己的属性均被正确的序列化,但是从父类继承的属性将回到其默认的初始值而不被序列化。

你可能感兴趣的:(javase基础,java,编程语言)