Java SE--IO流 [复制链接]
发表于 2026-2-8 21:19:34 | 显示全部楼层 |阅读模式
一.File范例

如果我们想在步调中利用大概形貌一个文件夹或文件,可以使用File范例
File范例在java.io包下
File可以新建,删除,移动,修改,重定名文件夹,也可以对文件大概文件夹的属性举行访问;但是不能对文件的内容举行访问(读和写);
常用的构造器:
File(String pathname)
File(String parent , String child) 
File(File  parent  , String child)
须要注意的是,构造器只负责吸收步调员传入的文件路径,并不会判定这个文件路径是否真实存在
目次的分隔符可以使用  File.separator来获取;
  1. File file = new File("D:"+File.separator+"dir1");
  2.         boolean result = file.exists();
  3.         if(result){
  4.             System.out.println("所描述的文件或者文件夹存在");
  5.         }else{
  6.             System.out.println("所描述的文件或者文件夹不存在");
  7.         }
复制代码
我们起首可以形貌一个文件,然后使用file.exists();方法来判定该文件是否存在,上述代码使用的是第一种构造器,直接传入路径
  1. /**
  2.          * 调用构造器File(String parent String child)
  3.          */
  4.         File file1 = new File("D:/chengxu","ch2.R");
  5.         System.out.println(file1.exists());
  6.         /**
  7.          * 调用构造器File(File parent String child)
  8.          */
  9.         File parent = new File("D:/chengxu");
  10.         File file2 = new File(parent,"ch2.R");
  11.         System.out.println(file2.exists());
复制代码
根据上述代码,我们可以使用别的两种构造器;第一种是传入父路径和子路径;第二种是传入File范例的父文件和字路径;

查察文件属性的相干方法
  1.         File file = new File("D:/code/ch2.R");
  2.         System.out.println("文件名称:"+file.getName());
  3.         System.out.println("绝对路径:"+file.getAbsolutePath());
  4.         System.out.println("该文件的父路径:"+file.getParent());
  5.         System.out.println("路径:"+file.getPath());
  6.         System.out.println("是否是一个文件:"+file.isFile());
  7.         System.out.println("是否是一个文件夹:"+file.isDirectory());
  8.         System.out.println("该文件是否是一个隐藏文件:"+file.isHidden());
  9.         System.out.println("该文件是一个可读文件:"+file.canRead());
  10.         System.out.println("该文件是一个可写文件:"+file.canWrite());//可写一定是可读文件
  11.         System.out.println("该文件是一个可执行的文件:"+file.canExecute());
  12.         //可执行一定是可读文件
  13.         System.out.println("最后一次修改时间:"+file.lastModified());
  14.         System.out.println("文件的大小:"+file.length());
复制代码
上述代码中我们可以查察文件的名称,并查察文件的绝对路径,文件的父路径,文件的路径

可以判定该文件是否是一个文件,或判定该文件是否是一个文件夹,可以判定该文件是否埋伏,可读,可写或可实行;

末了还可以得到该文件末了一次修改的时间以及文件的巨细;

文件检索,查询相干方法

我们可以使用两种方式;一个是返回字符串范例的数组,一个是返回File范例的数组;
  1. File file = new File("D:/code");
  2.         String[] lists = file.list();
  3.         for (String list : lists) {
  4.             System.out.println(list);
  5.         }
复制代码
起首形貌一个文件,并检索该文件,使用String范例的数组来吸收,并对该数组举行遍历,打印效果;
  1. File[] files = file.listFiles();
  2.         for (File file1 : files) {
  3.             System.out.println(file1.getName()+"   "+file1.getAbsolutePath());
  4.         }
复制代码
之后我们使用File范例的数组来检索该文件,并对其举行遍历,由于File范例的对象可以调用File范例的方法,因此我们可以得到文件名和绝对路径;

过滤文件
在对文件举行利用的时间,我们经常会须要过滤文件,过滤器对象可以实现这个功能,这是一个内部类须要重写内里的accept方法,编写过滤逻辑,该方法有两个形参,dir指须要利用的文件夹;name指文件夹中的每一个文件或文件夹;
  1. FilenameFilter filter = new FilenameFilter() {
  2.             //重写accept方法:编写过滤逻辑,name指的就是文件夹里的每一个文件或文件夹
  3.             //dir就是要操作的那个文件列表,即文件夹
  4.             public boolean accept(File dir, String name) {
  5.                 return !name.endsWith(".R");
  6.             }
  7.         };
复制代码
如许一个过滤器对象就创建好了;之后我们就可以对文件举行遍历;传入过滤器对象
  1. String[] list = file.list(filter);
  2.         for (String list1 : list) {
  3.             System.out.println(list1);
  4.         }
  5.         File[] files1 = file.listFiles(filter);
  6.         System.out.println(Arrays.toString(files1));
复制代码
然后打印出符合要求的文件即可

File的新建,删除

File的新建可以使用mkdir()方法,重要用于新建目次;
  1. File dir = new File("D:/dir1");
  2.         if (!dir.exists()) {
  3.             //创建文件命令  文件名.mkdir
  4.             dir.mkdir();
  5.         }
复制代码
如果要新建文件,可以使用createNewFile( );
  1. File file1  = new File("D:/dir1/file1.txt");
  2.         if (!file1.exists()) {
  3.             file1.createNewFile();
  4.         }
复制代码
如许文件夹和文件就创建好了

文件夹的多层级创建:
使用mkdir( )下令只能创建一个文件夹,不能多层级创建文件夹,如果想多层级创建,则须要使用mkdirs( )下令
  1. File file = new File("D:/dir11/dir22/dir33");
  2.         if(!file.exists()){
  3.             file.mkdirs();//mkdir方法只能创建出一个空文件夹
  4.         }
复制代码

文件的重定名:
使用reName( )方法来对文件重定名,方法的形参是文件的文件的路径,在路径中修改文件名
  1. File file1 = new File("D:/dir11/dir22/dir33");
  2.         if(file1.exists()){
  3.             file1.renameTo(new File("D:/dir11/dir22/dir3333"));
  4.         }
复制代码
如许就可以把file1的dir33修改为dir3333;

文件的移动:
我们可以使用reName()方法来实现文件的移动利用;
  1. /**
  2.          * 使用renameTo(File file)方法来达到移动文件的作用
  3.          */
  4.         File file2 = new File("D:/dir11/dir22/dir3333/file1.txt");
  5.         File file3 = new File("D:/dir11/dir22/file1.txt");
  6.         if(file2.exists()){
  7.             file2.renameTo(file3);
  8.         }
复制代码

使用原文件去调用reName,传入新文件的地点即可,没有修改文件的名字,只是修改了文件的路径,就可以到达移动的目的;
文件的删除利用:
delete( )方法可以实现文件的删除利用;须要注意的是,delete只能删除空文件夹大概文件,不能多层级删除,如果想删除非空文件夹,须要先把内里全部的子文件夹和文件全部删除,才气删除该文件夹;
  1. File file = new File("D:/dir11/dir22/file1.txt");
  2.         if (file.exists()) {
  3.             boolean f = file.delete();
  4.             System.out.println(f);
  5.         }
复制代码
我们可以通过上述的代码删除在dir22目次中的file1.txt文件;


delete方法可以删除某一个文件;只须要传入文件的路径对象即可;
假云云时我们想删除dir11,属于删除非空文件夹,使用delete方法无法删除
  1. /**
  2.          * 测试: 删除D:/dir11
  3.          */
  4.         File file1 = new File("D:/dir11");
  5.         if (file1.exists()) {
  6.             boolean f1 = file1.delete();
  7.             System.out.println(f1);
  8.         }
复制代码

返回效果是false,分析删除利用失败了;那么此时我们就须要使用删除的递归利用,多条理删除
  1. public static void deleteAll(File file) {
  2.         if(!file.exists()){
  3.             throw new RuntimeException("要删除的文件或文件夹不存在");
  4.         }
  5.            File[] files = file.listFiles();
  6.            for (File f : files) {
  7.                if(f.isDirectory()){
  8.                    deleteAll(f);
  9.                }
  10.                f.delete();
  11.            }
  12.            file.delete();
  13.     }
复制代码
在方法体中,起首判定须要删除的文件或文件夹是否存在,可以抛一个运行时非常来提示调用者,之后我们对文件夹遍历,如果子文件照旧文件夹,就重新进入循环,直到不是文件夹为止,然后就可以调用delete方法举行删除,须要注意,最外层的文件夹也须要使用删除利用举行删除;

如许就可以把非空文件夹删除了;
二.IO流--字节流

InputStream是字节输入流的顶级父类,是抽象类。界说了根本的读取方法。OutputStream是字节输出流的顶级父类,也是抽象类,界说了根本的写出方法。
FileInputStream--文件输入流

FileInputStream是InputStream抽象类的子范例,用于毗连文件和步调,常用的构造器
FileInputStream(String  pathname)
FileInputStream ( File  file)
  1.             FileInputStream fis = null;
  2.             fis = new FileInputStream("D:/file1.txt");
  3.             //读取一个字节
  4.             int ch = fis.read();
  5.             System.out.println((char)ch);
  6.             //读取后面的10个字节
  7.             for (int i = 0; i < 10; i++) {
  8.                  ch = fis.read();//每读取一次,都会与磁盘交互一次,次数越多,性能越差
  9.                 System.out.println((char)ch);
  10.             }
  11.             //提前创建一个10个长度的byte数组
  12.             byte[] bs = new byte[10];
  13.             //将数据读到数组中了
  14.             int length = -1;
  15.             while ((length = fis.read(bs)) != -1) {//大大减少了与磁盘的交互次数
  16.                 String str = new String(bs,0,length);
  17.                 System.out.println(str);
  18.             }
复制代码
我们起首创建一个文件输入流的对象fis;赋值为null,并调用构造器,创建新的对象,
读取一个字节可以使用方法read( ); 须要注意,我们使用的吸收变量是int范例,以是在打印的时间须要强转;
如果我们想读取十个字节的话,可以使用for循环来举行读取,但是每次调用read( )方法都会与磁盘举行一次交互,如果读取很多数据的话,会导致性能变差,所
以我们可以提前创建一个数组,将数据先读取到数组中,之后每当数组满了就一次性读取到步调中,如许可以大大淘汰与磁盘的交互次数,从而进步性能
根据上述代码,我们创建了一个长度为10的数组,并创建一个循环,让字节读入数组,当数组的长度不是-1时,就可以让数组转换为字符串情势输出。

由于在输入流中大概会报错,以是我们须要使用try-catch布局来运行步调
  1. try {
  2.             fis = new FileInputStream("D:/file1.txt");
  3.             //读取一个字节
  4.             int ch = fis.read();
  5.             System.out.println((char)ch);
  6.             //读取后面的10个字节
  7.             for (int i = 0; i < 10; i++) {
  8.                  ch = fis.read();//每读取一次,都会与磁盘交互一次,次数越多,性能越差
  9.                 System.out.println((char)ch);
  10.             }
  11.             //提前创建一个10个长度的byte数组
  12.             byte[] bs = new byte[10];
  13.             //将数据读到数组中了
  14.             int length = -1;
  15.             while ((length = fis.read(bs)) != -1) {//大大减少了与磁盘的交互次数
  16.                 String str = new String(bs,0,length);
  17.                 System.out.println(str);
  18.             }
  19.         } catch (FileNotFoundException e) {
  20.             throw new RuntimeException(e);
  21.         }catch (IOException e) {
  22.             throw new RuntimeException(e);
  23.         }finally {
  24.             try {
  25.                 fis.close();
  26.             } catch (IOException e) {
  27.                 throw new RuntimeException(e);
  28.             }
  29.         }
复制代码
在catch布局中我们可以抛出多种非常,有文件找不到非常,另有IO非常;我们在使用IO流时,在使用完必须要对其举行关闭;在关闭时也有大概出现非常,以是我们须要对fis.close( )也添加一个try-catch布局
FileOutputStream--文件输出流

FileOutputStream是OutputStream的子范例;是一个低级流,用于毗连步调和文件常用的构造器:
FileOutputStream(File  file)
FileOutputStream(String  pathname)
别的,还可以为构造器对象的形参添加追加效果,在形参中加上 boolean append即可;追加利用可以在每次运行步调时,都在步调的末了重新运行代码,不会覆盖掉原来的内容
须要注意的是,上述的构造器会将文件中原有的内容覆盖掉;
输出流不会资助我们创建不存在的文件夹,但是可以资助我们创建一个文件;
  1. FileOutputStream fos =null;
  2.         try{
  3. //             fos = new FileOutputStream("D:/file1.txt");
  4.             //使用追加模式的构造器
  5.              fos = new FileOutputStream("D:/file1.txt",true);
  6.              //写一个A
  7.             fos.write(65);
  8.             //将B到Z写到文件中
  9. //            for (int i = 66; i < 91; i++) {
  10. //                fos.write(i);
  11. //            }
  12.             for (int i = 'B'; i <='Z' ; i++) {
  13.                 fos.write(i);//注意:每写一次,都会与磁盘进行交互一次。次数越多,性能越差
  14.             }
  15.             //将hello world写入到文件中
  16.             byte[] bytes = "hello world".getBytes("UTF-8");
  17. //            fos.write(bytes);
  18.             fos.write(bytes,0,7);
复制代码
起首我们须要创建文件输出流对象;并赋值为null;之后在try-catch模块中,调用构造器来创建一个新的对象,并添加追加利用,并在文件中写一个A;
须要使用fos.write( )方法;传入的参数是int范例,但是会主动转换成字符型数据;以是我们传入65,编译器会主动转换成为' A ';
之后我们可以使用for循环;把B-Z全部传入文件,每次在调用write方法时,都会与磁盘交互一次,性能会变差;为了办理这种标题,我们可以使用字节数组来调用write方法;
起首创建一个byte[ ]范例的数组bytes,直接赋值一个字符串;并调用getBytes方法,传入须要转换的编码集;然后使用输出流对象fos.wtrte( );形参有三个,一个是字符数组名,第二个是开始索引,第三个是输出的长度;
在写完try模块的内容后,我们依然要增补catch模块和finall模块;

由于构造器加上了追加利用,运行了两次步调,文件中就会出现两次A-Z hello w;分析添加追加利用之后,文件本来的内容不会被覆盖;
BufferedInputStream--字节缓冲输入流

BufferedInputStream是一个高级流,内部维护了一块缓冲区,默认8KB,在读取文件的数据时,会尽大概的读取缓冲区巨细的字节;在使用read( )方法来从缓冲区获取数据时;如果全部读取完,就会继续从磁盘上读取数据储存到缓冲区;
常用的构造器:
BufferedInputStream(InputStream is)
BufferedInputStream(InputStream is , int size)
形参须要传入一个低级流;FileInputStream
  1. BufferedInputStream bis = null;
  2. //        FileInputStream fis = null;
  3.         try{
  4.             bis = new BufferedInputStream(
  5.                     new FileInputStream("D:/file1.txt"));
  6.             //读取一个字节
  7.             int ch = bis.read();
  8.             System.out.println((char)ch);
  9.             //一次性读取多个字节
  10.             byte[] bytes = new byte[10];
  11.             int len = -1;
  12.             while((len=bis.read(bytes))!=-1){
  13.                 System.out.println(new String(bytes,0,len));
  14.             }
复制代码
我们仍然是先创建一个字节缓冲输入流对象;并赋值为null;之后在try模块中调用构造器,须要注意的时,由于高级流的构造器的形参是低级流,以是我们还须要调用低级流的构造器,传入须要读取的文件如果须要读取一个字节的话,就可以直接调用read()方法,并在打印输出语句时强转为char范例;,如果一次性读取多个字节,可以创建一个byte数组,并界说一个int范例的变量len,之后添加一个while循环;当数组中有新添加的元素时,就须要把数组的元素打印出来;
还须要再添加catch模块和finally模块,用来抛出非常和关闭流,再关闭流时,先关闭高级流,再关闭低级流
  1. }catch(IOException e){
  2.             e.printStackTrace();
  3.         }finally {
  4.             try {
  5.                 bis.close();
  6.             } catch (IOException e) {
  7.                 throw new RuntimeException(e);
  8.             }
  9.         }
复制代码
BufferedOutputStream--字节缓冲输出流

BufferedOutputStream内部维护了一个字节数组作为缓冲区:默认值是8KB;
当我们在写数据时,不是直接写在磁盘上,而是先写到缓冲区内,当缓冲区满了之后再一次性写在磁盘上,从而淘汰了与磁盘的交互次数,进步了性能;
当我们在末了一次写在缓冲区时,大概内容没有写满,此时就须要调用flush( )方法来欺凌写出BufferedOutputStream构造器:
BufferedOutputStream(OutputStream os)
BufferedOutputStream(OutputStream os , int size )
我们可以自己界说缓存区的巨细;通过size来设置;
  1. BufferedOutputStream bos = null;
  2.         try{
  3.             bos = new BufferedOutputStream(
  4.                     new FileOutputStream("D:/file2.txt", true));
  5.             //将A写入磁盘
  6.             bos.write('A');
  7.             //将"helloworld"写入缓冲区
  8.             byte[] bytes = "helloworld".getBytes("UTF-8");
  9.             bos.write(bytes);
复制代码
起首界说一个字节缓冲输出流对象;并赋值为null;在try模块中调用构造器创建新的对象在构造器中调用文件输出流的构造器,传入文件的地点,并添加追加利用;
写入磁盘使用write方法即可,写入缓冲区时,须要先创建一个数组,并用字符串赋值,之后调用getBytes方法传入编码集;

之后不要忘记添加catch模块和finally模块;
DataOutputStream--数据输出流

DataOutputStream是OutputStream的子范例;扩展了一些功能,可以直接写出根本数据范例的方法;该流的构造器:
DataOutputStream(OutputStream os)
  1. try( DataOutputStream dos =
  2.                      new DataOutputStream(
  3.                      new FileOutputStream("D:/file3.txt"))){
  4.             dos.writeInt(10);//4个字节
  5.             dos.writeDouble(3.14);//8个字节
  6.             dos.writeBoolean(true);//1个字节
  7.             dos.writeUTF("你是最棒的");//15个字节  注意:每写一次utf的字符串,都会使用两个字节来记录字符串的长度
  8.             dos.writeUTF("你是最棒的");//15个字节
  9.             dos.flush();
  10.         }catch (IOException e){
  11.             e.printStackTrace();
  12.         }
复制代码
该流在try模块中可以直接使用小括号的情势来创建流的对象,如许利用会在流不使用时主动关闭流,不须要再写finally模块专门关闭流了;在创建数据流的对象时,传入的形参是文件输出流;可以直接调用文件输出流的构造器,传入一个文件的地点;
在输出内容时,可以调用writeInt方法,可以直接输出int范例的数字,也可以使用浮点数,布尔范例的值,须要注意的是:在我们调用writeUTF方法时,传入的字符串每个字符占3个字节,而且每调用一次该方法,就会使用额外的两个字节来纪录字符串的长度,每个int范例的值的数字占两个字节,浮点数也是两个字节,布尔范例的值占一个字节;之后我们可以调用flush方法将缓冲区的内容欺凌输出
DataInputStream--数据输入流

DataInputStream是InputStream的子范例;扩展了一些功能,可以直接读取根本数据范例和String范例的数据;该流的构造器:
DataInputStream( InputStream is)
其使用方法与DataOutputStream类似,只是把write方法变化成read方法;而且read方法是须要返回值的;记得添加,然后我们就可以把变量打印出来了;
  1. try (DataInputStream dos =
  2.                 new DataInputStream(
  3.                         new FileInputStream("D:/file3.txt")
  4.                 )
  5.             ){
  6.             int i = dos.readInt();
  7.             double v = dos.readDouble();
  8.             boolean b = dos.readBoolean();
  9.             String s = dos.readUTF();
  10.             System.out.println(i);
  11.             System.out.println(v);
  12.             System.out.println(b);
  13.             System.out.println(s);
  14.         }catch (IOException e) {
  15.             e.printStackTrace();
  16.         }
复制代码
我们可以直接在try模块的花括号前添加小括号;将创建流对象的过程写在小括号里调用数据输入流的构造器,形参传入一个文件输出流的构造器,形参传入须要写入的文件地点,尤其须要注意在写小括号的时间,不要落下;否则报错
在调用方法的时间须要添加返回值;


ObjectOutputStream--对象输出流

对象序列化:我们须要将内存中的对象转换为字节数组;这个过程称为序列化。序列化的目的:存储或传输
反序列化:字节数组转换为对象;这个过程称为反序列化,反序列化的目的:规复对象的状态,以便在步调中使用。
序列化的构造器:     ObjectOutputStream(OutputStream os)
在我们自界说的类中;如果想要使对象序列化,就必须要实现  Serializable接口,该接口内部什么也没有,只是一个规范
serailVersionUID: 序列化的版本号:
在举行序列化时,体系会默认给该类一个版本号。当反序列化时,体系会查抄该类的版本号与对象的版本号是否划一,如果不一样,反序列化将会失败,报出非常:版本号不兼容
注意:体系提供的版本号在差异时间大概差异
为了办理上述的标题,我们须要自己界说一个版本号,如许序列化和反序列化用的就都是同一个版本号,不会出现不兼容的征象
  1. class Student implements Comparable<Student> , Serializable {
  2.     public static final long serialVersionUID = 2100000000;
  3.     private String name;
  4.     private int age;
  5.     private char gender;
  6.     public Student(String name, int age, char gender) {
  7.         this.name = name;
  8.         this.age = age;
  9.         this.gender = gender;
  10.     }
  11.     public Student() {}
  12.     public String getName() {
  13.         return name;
  14.     }
  15.     public void setName(String name) {
  16.         this.name = name;
  17.     }
  18.     public int getAge() {
  19.         return age;
  20.     }
  21.     public void setAge(int age) {
  22.         this.age = age;
  23.     }
  24.     public char getGender() {
  25.         return gender;
  26.     }
  27.     public void setGender(char gender) {
  28.         this.gender = gender;
  29.     }
  30.     @Override
  31.     public boolean equals(Object o) {
  32.         if (this == o) return true;
  33.         if (o == null || getClass() != o.getClass()) return false;
  34.         Student student = (Student) o;
  35.         return age == student.age && gender == student.gender && Objects.equals(name, student.name);
  36.     }
  37.     @Override
  38.     public int hashCode() {
  39.         return Objects.hash(name, age, gender);
  40.     }
  41.     @Override
  42.     public String toString() {
  43.         return "Student{" +
  44.                 "name='" + name + '\'' +
  45.                 ", age=" + age +
  46.                 ", gender=" + gender +
  47.                 '}';
  48.     }
  49.     @Override
  50.     public int compareTo(Student o) {
  51.         return this.age - o.age;
  52.     }
  53. }
复制代码
我们创建了一个Student类,实现了comparable接口和Serializable接口;之后创建了静态常量
serailVersionUID: 序列化的版本号:2100000000,如许版本号就固定了,不会出现序列化和反序列化过程不兼容的标题。创建了三个成员变量:name , age , gender 然后分别提供了全参构造器和无参构造器;并提供了getter和setter方法,又重写了equals和hashCode方法,toString方法,并重写了compareTO方法
  1. public static void main(String[] args) {
  2.         //创建一个学生
  3.         Student s1 = new Student("小明",20,'男');
  4.         Student s2 = new Student("小明",20,'男');
  5.         //使用对象输出流将学生写出到文件中
  6.         ObjectOutputStream oos = null;
  7.         try{
  8.             oos = new ObjectOutputStream(
  9.                     //流的构造器的相对路径相对的是Project(项目)文件夹  ./表示的是项目文件夹
  10.                     new BufferedOutputStream(new FileOutputStream("./student.s",true),4096));
  11.             //写出对象
  12.             oos.writeObject(s1);
  13.             oos.writeObject(s2);
  14.         }catch(IOException e){
  15.             e.printStackTrace();
  16.         }finally {
  17.             try {
  18.                 oos.close();
  19.             } catch (IOException e) {
  20.                 throw new RuntimeException(e);
  21.             }
  22.         }
  23.     }
复制代码
在上述方法中,我们创建了两个弟子对象;并使用对象输出流将弟子输出到文件中,起首使用对象输出流的构造器为其赋值,在构造器中传入字节缓冲流的构造器,在构造器中传入文件输出流的构造器,在构造器中传入文件的相对路径;并添加追加利用,在字节缓冲流的构造器中添加字节缓冲区的巨细;之后我们可以调用writeObject方法来写出对象,须要添加catch模块和finally模块来关闭流。

可以发现,创建出来的文件是在project下的,以是说相对路径指的就是当前项目的路径;
ObjectInputStream--对象输入流

反序列化的构造器:     ObjectInputStream(InputStream is)
知识扩展:
在序列化时,步调每实行一次都会创建一个两个字节长度的流头,如果文件输出流是追加模式,则会出现多个流头,以是当文件中如果有多个流头时,大概会将除了第一个流头之外,别的的流头会被当成文件内容而剖析,会报非常:非法范例的代码
  1. ObjectInputStream ois = null;
  2.         try{
  3.             ois = new ObjectInputStream(new BufferedInputStream(new FileInputStream("./student.s"),4096));
  4.             Object obj = ois.readObject();
  5.             System.out.println(obj);
  6.             Object o = ois.readObject();
  7.             System.out.println(o);
  8.         }catch (IOException | ClassNotFoundException e){
  9.             e.printStackTrace();
  10.         }finally {
  11.             try {
  12.                 ois.close();
  13.             } catch (IOException e) {
  14.                 throw new RuntimeException(e);
  15.             }
  16.         }
复制代码
在上述代码中,我们先创建了一个对象输入流对象ois;然后调用对象输入流构造器举行赋值,传入字节缓冲流构造器,并提供字节缓冲区的巨细为4096个字节,传入文件输入流构造器,传入文件的地点,之后就可以调用readObject方法了;来打印输出效果;

我们还可以做一个这个实例演示:
  1. class Teacher implements Serializable , Comparable<Teacher>{
  2.     public static final int serialVersionUID = 1;
  3.     private String name;
  4.     private int age;
  5.     private String address;
  6.     private transient String remark;
  7.     public Teacher(String name, int age, String address, String remark) {
  8.         this.name = name;
  9.         this.age = age;
  10.         this.address = address;
  11.         this.remark = remark;
  12.     }
  13.     public Teacher() {}
  14.     public String getName() {
  15.         return name;
  16.     }
  17.     public void setName(String name) {
  18.         this.name = name;
  19.     }
  20.     public int getAge() {
  21.         return age;
  22.     }
  23.     public void setAge(int age) {
  24.         this.age = age;
  25.     }
  26.     public String getAddress() {
  27.         return address;
  28.     }
  29.     public void setAddress(String address) {
  30.         this.address = address;
  31.     }
  32.     public String getRemark() {
  33.         return remark;
  34.     }
  35.     public void setRemark(String remark) {
  36.         this.remark = remark;
  37.     }
  38.     @Override
  39.     public boolean equals(Object o) {
  40.         if (this == o) return true;
  41.         if (o == null || getClass() != o.getClass()) return false;
  42.         Teacher teacher = (Teacher) o;
  43.         return age == teacher.age && Objects.equals(name, teacher.name) && Objects.equals(address, teacher.address) && Objects.equals(remark, teacher.remark);
  44.     }
  45.     @Override
  46.     public int hashCode() {
  47.         return Objects.hash(name, age, address, remark);
  48.     }
  49.     @Override
  50.     public String toString() {
  51.         return "Teacher{" +
  52.                 "name='" + name + '\'' +
  53.                 ", age=" + age +
  54.                 ", address='" + address + '\'' +
  55.                 ", remark='" + remark + '\'' +
  56.                 '}';
  57.     }
  58.     @Override
  59.     public int compareTo(Teacher o) {
  60.         return 0;
  61.     }
  62. }
复制代码
在上述代码中,我们仍然是创建了一个Teacher类,添加成员变量name , age , address , remark , 须要注意的是,我们可以在成员变量的范例之前添加一个关键字transient来修饰该成员变量;
在我们序列化时,该成员变量的信息不须要生存或转达,也就是不告急的意思;那么此时我们就可以使用transient关键字来举行修饰;
我们正常添加无参构造器和全参构造器;并添加getter和setter方法,重写equals方法,hashCode方法,toString方法,以及在实现comparable接口时须要重写的compareTo方法。
  1. public static  void serialObject(Object obj){
  2.         ObjectOutputStream oos = null;
  3.         try {
  4.             oos = new ObjectOutputStream(
  5.                     new ObjectOutputStream(
  6.                             new FileOutputStream("object.txt")));
  7.             oos.writeObject(obj);
  8.         }catch (IOException e){
  9.             e.printStackTrace();
  10.         }finally{
  11.             try {
  12.                 oos.close();
  13.             } catch (IOException e) {
  14.                 throw new RuntimeException(e);
  15.             }
  16.         }
复制代码
在上述的代码中,我们把序列化对象的过程封装在了方法中,起首创建了对象输出流的对象oos并赋值为null;在try模块中对oos变量调用构造器来举行赋值,传入的形参是字节缓冲输出流的构造器,传入的形参是文件输出流的构造器,传入的形参是文件地点
之后我们可以使用对象调用writeObject方法来写出对象。
  1. /**
  2.      * 反序列化对象
  3.      */
  4.     public static Teacher deSerialObject() {
  5.         ObjectInputStream ois = null;
  6.         try {
  7.             ois = new ObjectInputStream(
  8.                     new ObjectInputStream(
  9.                             new FileInputStream("object.txt")));
  10.             Object o = ois.readObject();
  11.             if(o instanceof Teacher){
  12.                 return (Teacher)o;
  13.             }
  14.         }catch (IOException | ClassNotFoundException e){
  15.             e.printStackTrace();
  16.         }finally{
  17.             try {
  18.                 ois.close();
  19.             } catch (IOException e) {
  20.                 throw new RuntimeException(e);
  21.             }
  22.         }
  23.         return  null;
复制代码
我们也可以把反序列化的过程封装在一个方法里;起首在方法体中创建一个对象输入流的对象ois,并赋值为null,在try模块里为ois调用构造器举行赋值,传入的参数是字节缓冲输入流的构造器;传入的形参是文件输入流的构造器;传入文件的地点,之后可以调用readObject方法举行读取内容,该方法是有返回值的,须要返回Teacher范例的变量或表达式,我们须要判定读取到的数据是不是Teacher范例的对象,并对Object范例的对象举行强转之后才气返回;
  1. //创建一个Teacher对象
  2.         Teacher teacher = new Teacher("张老师",25,"China","..");
  3.         //序列化Teacher对象
  4.         serialObject(teacher);
  5.         //反序列化Teacher对象
  6.         Teacher teacher1 = deSerialObject();
  7.         System.out.println(teacher1);
复制代码
在上述代码中,我们在main方法里实例化了一个Teacher范例的对象teacher,并调用构造器对其举行赋值;之后我们可以调用序列化和反序列化方法对其举行利用,末了打印出担当的效果;

此时我们就会发现remark中的信息并没有被转达过来,由于我们在成员变量前面添加了transient关键字。
三.IO流--字符流

Writer--字符输出流

Writer是字符输出流的抽象父类,誊写的单元是以一个char为单元;底层仍然是一个字节流;
根本的字符流,我们称之为转换流,由于涉及到字符与字节之间的转换时所使用的编码集,在编写代码时默认使用的编码集是UTF-8,每个字母都是一个字节,汉字是三个字节;由于Writer是抽象类,我们都使用其子类OutputWriter来创建对象;构造器:
OutputWriter(OutputStream os )
OutputWriter(OutputStream os , String charsetName): 传入指定的字符集
  1. OutputStreamWriter osw = null;
  2.         try {
  3. //            osw = new OutputStreamWriter(new FileOutputStream("./test07/char.txt"));
  4.             osw = new OutputStreamWriter(
  5.                     //常用的字符集 UTF-8  GBK  GB2312  UNICODE
  6.                     // ISO-8859-1:HTTP通信时使用
  7.                     new FileOutputStream("./test07/char.txt"),"GBK");
  8.             //写一个字符出去
  9.             osw.write('中');
  10.             //写一个字符数组出去
  11.             char[] ch = {'国','欢','迎','你'};
  12.             osw.write(ch);
  13.             //将ch中的后三个在写一次
  14.             osw.write(ch,ch.length-3,3);
  15.             //将一个字符串写出去
  16.             osw.write("奥运健儿加油");
  17.             //如果有缓存区,强制重刷一下(写出)
  18.             osw.flush();
复制代码
我们照旧在模块外创建一个字符输出流对象 osw ,并赋值为null;
之后在try模块中,调用构造器对其举行赋值,传入的形参是文件输出流的构造器,传入的形参是文件的地点,别的,还可以添加编码集,必须添加在字符输出流构造器中;编码集用字符串范例;
在我们写出字符时,使用对象来调用write方法即可,传入的形参是字符范例;该方法的重载情势可以传入一个字符数组,还可以传入字符数组的一部门,确定开始传输的索引,和传输的长度即可;

Reader--字符输入流

Reader是字符输入流的抽象父类,我们一样平常使用其子类InputReader来实例化对象;
常用的构造器:
InputReader(InputStresm is )
InputReader(InputStream is , String  charsetName)
  1. InputStreamReader isr = null;
  2.         try {
  3.             isr = new InputStreamReader(
  4.                     new FileInputStream("./test07/char.txt"),"GBK");
  5.             //取出一个元素
  6.             char i = (char)isr.read();
  7.             System.out.println(i);
  8.             //取出10个元素
  9.             //继续读取10个字符
  10.             char[] chs = new char[10];
  11.             int len  = 0;
  12.             while ((len = isr.read(chs)) != -1) {
  13.                 System.out.println("chs:"+ new String(chs,0,len));
  14.                 System.out.println("len: "+len);
  15.             }
复制代码
在上述代码中,我们先创建了一个字符输入流对象isr,并赋值为null;
在try模块中我们调用构造器对其举行赋值,传入的参数是文件输入流的构造器,传入的参数是文件的路径,而且可以在字符输入流的构造器中传入和输出流类似的字符编码集;
之后可以通过对象来调用read方法,举行读取文件的内容,可以一次读一个,也可以使用字符数组一次读10个字符,须要声明一个纪录长度的变量len并对其初始化;使用while循环来控制打印的数组长度
PrintWriter--字符缓冲输出流

字符缓冲输出流一样平常使用PrintWriter这个子类,而不是BufferedWriter;
由于PrintWriter这个子类的构造器比力丰富
PrintWriter(File file ):直接传入一个File 范例的对象;
PrintWriter(Stirng  pathname ) :直接传入文件的路径
PrintWriter(OutputStream os ): 传入字节流对象
PrintWriter(Writer wirter ): 传入Writer范例的对象;
别的,另有两种重载方法,在反面两种构造器中添加true,表现autoFulsh,主动欺凌从缓冲区输出
  1. try(PrintWriter pw = new PrintWriter(
  2.                 new OutputStreamWriter(
  3.                         new FileOutputStream("test07/char.txt"), "GBK"),true)){
  4.             //写出一个字符数组
  5.             char[] chars = "你好中国".toCharArray();
  6.             pw.write(chars);
  7.             //调用print方法,写出去"床前明月光"
  8.             pw.print("床前明月光"); //print不带ln的方法,没有自动行刷新的方法
  9. //            pw.flush();
  10.             //调用println的方法,写出“疑是地上霜”
  11.             //当缓冲流构造器中指定了行刷新功能,也就是设计为true,此时ln的方法才会在行末添加换行符,并强制从缓存里写出
  12.             pw.println("疑是地上霜");
  13.             pw.println("举头望明月");
复制代码
起首使用小括号在try模块反面创建PrintWriter对象pw,使用构造器对其举行赋值,传入的参数是字符输出流的构造器,传入的参数是字节输出流的构造器,传入的参数是文件的地点,在字符输出流的构造器中添加字符编码集,并在字符缓冲输出流中添加true,表现可以主动重刷缓存区;
如果print方法不带有ln,则不具有主动行革新的功能,当输入疑是地上霜后,字符串的末了才会带有换行符,输入举头望明月才会换行。

BufferedReader--字符缓冲输入流

内部也维护了一个缓存区,尽大概一次性读满然后存入缓存区;
常用的方法有readLine()
构造器: BufferedReader(Reader reader )
  1. try(BufferedReader br= new BufferedReader(
  2.                 new InputStreamReader(new FileInputStream("test07/char.txt"),"GBK"))){
  3.             char ch = (char)br.read();
  4.             System.out.println("ch:"+ch);
  5.             //调用读取一行的方法
  6.             String s = null;
  7.             while((s=br.readLine())!=null){
  8.                 System.out.println("line:"+s);
  9.             }
  10.         }catch(Exception e){
  11.             e.printStackTrace();
  12.         }
复制代码
照旧使用小括号的方式来创建BufferedReader对象br,调用构造器,传入的参数是字符输入流的构造器,传入的参数是字节输入流的构造器;传入的参数是文件的地点,还须要再字符输入流的构造器中传入字符编码集GBK;
我们可以使用对象来调用read( )的方法来读取第一个字符,须要注意该方法的返回值是int范例,我们想获取字符则须要强转;
如果想获取一行的化,就须要先声明一个字符串,赋值为空,之后通过while循环,只要把输入流对象调用readline方法后的效果赋给s,s不是null的话,就可以打印。

FileWriter--文件输出流

相当于字符输出流和字节文件输出流合在一起的功能;但是不可以或许指定字符集
FileWriter(File file )
FileWriter(File file ,boolean append)
FileWriter(String pathname )
FileWriter(String pathname , boolean append )
我们可以直接输出字符而且可以举行追加利用;缺点就是不能指定字符集
  1. try(FileWriter fw = new FileWriter(new File("./test07/char.txt"),true)){
  2.             //将“中国好男儿”写出去
  3.             fw.write("中国好男儿");
  4.             fw.write("华夏好儿女");
  5.             //append():追加方法,追加到文件的后面
  6.             fw.append("北京上海");
  7.         }catch (Exception e){
  8.             e.printStackTrace();
复制代码
我们在小括号中创建一个FileWriter对象fw并使用构造器对其赋值,传入参数是File范例的构造器,传入文件的地点,并在FileWriter构造器中添加true,表现可以举行追加利用;使用对象名调用write方法可以写出内容,使用对象名调用append方法可以在内容反面追加新的内容,我们把步调运行双方,文件内容中会出现两遍写入的内容;

FileReader--文件输入流

相当于字符输入流和字节文件输入流合起来的作用;但是不能指定字符集;
常用构造器:
FileReader(File file )
FileReader(String pathname )
  1. try(FileReader fr = new FileReader("./test07/char.txt")){
  2.             char  i = (char)fr.read();
  3.             System.out.println(i);
  4.             int len = -1;
  5.             char[] chars = new char[10];
  6.             while((len=fr.read(chars))!=-1){
  7.                 String a = new String(chars, 0, len);
  8.                 System.out.println(a);
  9.             }
  10.         }
  11.         catch (Exception e){
  12.             e.printStackTrace();
  13.         }
复制代码
先调用构造器来创建对象,传入文件的地点即可,在获取第一个字符时,可以选择在声明变量时强转,也可以在打印时强转;之后如果想一次获取10个字符,可以界说一个char数组,声明一个int范例的变量,调用对象的read方法,将文件中的字符读入数组中,只要数组的长度不是-1,就将数组中的元素赋值给字符串,长度是数组中新添加进的变量的长度,之后打印字符串即可;

四.体系类--System

体系类中封装了一些本地方法和静态的属性;
1.静态属性:
  -PrintStream  out :  标准输出流,默认目的地是控制台;
  -PrintStream  err :  标准错误输出流,默认目的地是控制台;
  -PrintStream  in  :  标准输入流,默认数据源是控制台;
我们在使用System.out的时间,相当于创建了一个标准输出流对象,调用println方法,即调用的是PrintStream的方法,将效果打印到控制台;
我们可以对标准输入流和标准输出流的数据源和目的地举行修改;
我们先测试System.out
起首创建一个静态的无返回值的方法,先将默认的目的地生存起来,以免我们无法修改归去
  1. //测试System.out
  2.     public static void testSystemOut() throws Exception {
  3.         System.out.println("Hello World");
  4.         /**
  5.          * 修改out这个流的目的地
  6.          * 1.将默认的目的地临时保存起来
  7.          */
  8.         PrintStream ps = System.out;
  9.         //使用IO流,来重新定位
  10.         PrintStream pw = new PrintStream("./test07/char.txt");
  11.         //将上述的流对象赋值给System.out属性
  12.         System.setOut(pw);
  13.         //调用out的println方法,写出数据
  14.         System.out.println("你好世界,我是。。。。。。");
  15.         //改回来
  16.         System.setOut(ps);
  17.         System.out.println("我回来了");
  18.     }
复制代码
之后我们可以使用IO流,来重新定位System.Out的目的地,创建一个新的对象pw,通过new调用构造器把新的文件地点赋给对象pw,之后将流对象的文件地点赋给System.out属性,之后就可以调用方法,打印一个字符串,如果想要修改返来,只须要将之前储存的默认目的地对象通过set来给System.out属性赋值归去就可以了;
之后我们可以在main方法中举行测试;直接调用方法名就可以;


对于修改System.in;方法大抵也是云云;
  1. //测试System.in
  2.     public static void testSystemIn() throws Exception {
  3.         //将System.in 临时保存到一个变量中
  4.         InputStream in = System.in;
  5.         //修改数据源
  6.         FileInputStream fis = new FileInputStream("./test07/char.txt");
  7.         //将数据源从控制台改为指定文件
  8.         System.setIn(fis);
  9.         //使用扫描类
  10.         Scanner sc = new Scanner(System.in); //扫描类会从文件中读取数据
  11.         //与迭代器类似,先问是否有下一行
  12.         while(sc.hasNextLine()) {
  13.             //取下一行数据
  14.             String line = sc.nextLine();
  15.             System.out.println(line);
  16.         }
  17.     }
复制代码
我们须要先界说一个输入流对象来储存System.in的数据源;之后可以对数据源举行修改,使用文件输入流创建一个对象,填入我们想修改的数据泉源,然后调用set方法将数据源从控制台改为指定的哪个文件;接下来创建一个扫描器对象sc来从文件中扫描数据;之后对文件的内容举行遍历,与迭代器类似,先扣问是否另有下一行,然后界说一个字符串范例的变量来吸收并将该字符串举行打印;

就会将之前录入的文件内容打印在控制台上;
五.拷贝利用的实现

我们可以使用IO流来实现对文件的拷贝工作;在下面的案例中,我们分别使用字节流和字符流两种方式来拷贝纯文本文件和视频文件;
与之前的案例差异的是,我们须要在一个方法中同时封装输入流和输出流,我们可以先处理处罚字节流的拷贝方法;
  1. public static void copyFile1(File fromFile, File toFile) throws IOException {
  2.         try(FileInputStream fis =
  3.                     new FileInputStream(fromFile);
  4.             FileOutputStream fos =
  5.                     new FileOutputStream(toFile)){
  6.             byte[] buffer = new byte[1024];
  7.             int len ;
  8.             while((len = fis.read(buffer))!=-1){
  9.                 fos.write(buffer, 0 ,len);
  10.             }
  11.         }catch (Exception e){
  12.             e.printStackTrace();
  13.         }
  14.     }
复制代码
起首在try模块的小括号中完成创建输入流和输出流,调用FileInput的构造器,传入我们须要的文件路径,输入流对象创建完毕,之后调用FileOutputStream的构造器,传入我们须要拷贝到的文件路径,输出流对象创建完毕;我们须要创建一个字节数组用于储存传入和传出的字节长度,然后界说一个int范例的变量len,作为长度,在while循环布局中,循环条件是字节数组中是否有新添加进的元素,在循环体中调用输出流对象的write方法来输出数组中的元素,之后就可以在main方法中调用该方法。传入的形参是两个File范例的对象;
  1. /**
  2.          * 测试1,调用copyFile1方法拷贝一个纯文本文件
  3.          */
  4.         File f1 = new File("./Exercise/111/a.txt");
  5.         File f2 = new File("./Exercise/abc/a.txt");
  6.         copyFile1(f1,f2);
  7.         /**
  8.          * 测试2,调用copyFile1方法拷贝一个视频文件
  9.          */
  10.         File f3 = new File("./Exercise/111/1.mp4");
  11.         File f4 = new File("./Exercise/abc/1.mp4");
  12.         copyFile1(f3,f4);
复制代码
至此使用字节流拷贝文件的案例就完毕了

使用字符流拷贝的方式与上述根本划一;
  1. public static void copyFile2(String fromPath, String toPath) throws Exception {
  2.         try(FileReader fr =
  3.                     new FileReader(fromPath);
  4.             FileWriter fw =
  5.                     new FileWriter(toPath)){
  6.             char[] buffer = new char[1024];
  7.             int len = -1 ;
  8.             while((len =fr.read(buffer))!=-1){
  9.                 fw.write(buffer,0,len);
  10.             }
  11.         }catch(Exception e){
  12.             e.printStackTrace();
  13.         }
  14.     }
复制代码
在创建流的对象和字符数组与上述的案例有些许差异,须要注意。
  1. /**
  2.          * 测试3,调用copyFile2方法拷贝一个纯文本文件
  3.          */
  4.          copyFile2("./Exercise/111/a.txt","./Exercise/def/a.txt");
  5.         /**
  6.          * 测试4,调用copyFile2方法拷贝一个视频文件
  7.          */
  8.         copyFile2("./Exercise/111/1.mp4","./Exercise/def/1.mp4");
复制代码



免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!qidao123.com:ToB企服之家,中国第一个企服评测及软件市场,开放入驻,技术点评得现金

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
回复

使用道具 举报

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表