14 IO

01 file类

file类概述

java.io.File类 文件和目录路径名的抽象表示形式。 java把电脑中的文件和文件夹(目录)封装为了一个File类,我们可以使用File类对文件和文件夹进行操作

我们可以使用File类的方法 创建一个文件/文件夹 删除文件/文件夹获取文件/文件夹 判断文件/文件夹是否存在 对文件夹进行遍历 获取文件的大小 File类是一个与系统无关的类,任何的操作系统都可以使用这个类中的方法

重点:记住这三个单词 file:文件 directory:文件夹/目录 path:路径

file类静态变量

 
 
 
 
 
 
 
package file;
import java.io.File;
public class Demo01 {
    public static void main(String[] args) {
        /*
        static String pathSeparator 与系统有关的路径分隔符,为了方便,它被表示为一个字符串。
        static char pathseparatorchar 与系统有关的路径分隔符。
        static String separator 与系统有关的默认名称分隔符,为了方便,它被表示为一个字符串。
        static char separatorchar 与系统有关的默认名称分隔符。
        操作路径:路径不能写死了
        C:\develop\a\a.txt windows
        C:/develop/a/a.txt Linux
        "C:"+File.separator+"develop"+File.separator+"a"+File.separator+"a.txt"
         */
        String pathSeparator = File.pathSeparator;
        System.out.println(pathSeparator);//路径分隔符  Windows:分号;   linux: 冒号 :
        String separator = File.separator;
        System.out.println(separator);//文件名称分隔符  Windows:反斜杠\   linux: 正斜杠 /
    }
}
 

file类的构造方法

第一种

 
 
 
 
 
 
 
package file.Demo01;
import java.io.File;
/*
路径:
    绝对路径:是一个完整的路径
        以盘符(C:,D:)开始的路径
        c:\\a.txt
        C:\\Users\itcast\\IdeaProjects\\shungyuan\\123.txt D:1I demol lb.txt
    相对路径:是一个简化的路径
        相对指的是相对于当前项目的根目录(C:\\Users\itcast\\IdeaProjects\\shungyuan)
        如果使用当前项目的根目录,路径可以简化书写
        C:\\Users\itcast\\IdeaProjects\\shungyuan\\123.txt-->简化为:123.txt(可以省略项目的根目录)
   注意:
        1.路径是不区分大小写
        2.路径中的文件名称分隔符windows使用反斜杠,反斜杠是转义字符,两个反斜杠代表一个普通的反斜杠
 */
public class Demo02 {
    public static void main(String[] args) {
        /*
            File类的构造方法
         */
        show01();
    }
    /*
    File(String pathname)
        通过将给定路径名字符串转换为抽象路径名来创建一个新File实例。
        参数:
            String pathname:字符串的路径名称
            路径可以是以文件结尾,也可以是以文件夹结尾
            路径可以是相对路径,也可以是绝对路径
            路径可以是存在,也可以是不存在
            创建File对象,只是把字符串路径封装为File对象,不考虑路径的真假情况
     */
    private static void show01(){
        File f1 = new File("C:\\t430\\java\\code\\heima\\src\\a.txt");
        System.out.println(f1);//重写了object类里的toString方法  C:\t430\java\code\heima\src\a.txt
        File f2 = new File("C:\\t430\\java\\code\\heima\\src");
        System.out.println(f2);
    
        File f3 = new File("b.txt");
        System.out.println(f3);
    }
}
 

第二种

 
 
 
 
 
 
 
package file.Demo01;
import java.io.File;
public class Demo02 {
    public static void main(String[] args) {
        /*
            File类的构造方法
         */
        show02("c:\\","a.txt");
    }
    /*
    File(String parent,String child)
    根据parent路径名字符串和child路径名字符串创建一个新File实例。
    参数:把路径分成了两部分
        String parent:父路径
        String child:子路径
    好处:父路径和子路径,可以单独书写,使用起来非常灵活;父路径和子路径都可以变化
     */
    private static void show02(String parent,String child){
        File file = new File(parent,child);
        System.out.println(file);
    }
}
 

第三种

 
 
 
 
 
 
 
package file.Demo01;
import java.io.File;
public class Demo03 {
    public static void main(String[] args) {
        /*
            File类的构造方法
         */
        show03();
    }
    /*
File(File parent,String child)根据 parent 抽象路径名和 child 路径名字符串创建一个新File实例。
参数:把路径分成了两部分
    File parent:父路径
    string child:子路径
好处:
    父路径和子路径,可以单独书写,使用起来非常灵活;父路径和子路径都可以变化
    父路径是File类型,可以使用File的方法对路径进行一些操作,再使用路径创建对象
     */
    private static void show03(){
        File parent = new File("c:\\");
        File file = new File(parent,"hello.java");
        System.out.println(file);
    }
}
 

File类获取功能的方法

 
 
 
 
 
 
 
package file.Demo01;
import java.io.File;
/*
File类获取功能的方法
public String getAbsolutePath():返回此File的绝对路径名字符串。
public string getPath():将此File转换为路径名字符串。
public String getName():返回由此File表示的文件或目录的名称。
public long Length():返回由此File表示的文件的长度。
 */
public class Demo02 {
    public static void main(String[] args) {
        show01();
        show02();
        show03();
        show04();
    }
    /*
    public string getAbsolutePath():返回此File的绝对路径名字符串。
    获取的构造方法中传递的路径
    无论路径是绝对的还是相对的,getAbsolutePath方法返回的都是绝对路径
     */
    /*
    public Long Length():返回由此File表示的文件的长度。
    获取的是构造方法指定的文件的大小,以字节为单位
    注意:文件夹是没有大小概念的,不能获取文件夹的大小
        如果构造方法中给出的路径不存在,那么Length方法返回0
     */
    private static void show04(){
        File f1 = new File("C:\\Users\\CHPang\\Pictures\\Screenshots\\1.jpg");
        long l1 = f1.length();
        System.out.println(l1);
    }
    /*
    public String getName():返回由此File表示的文件或目录的名称。
    获取的就是构造方法传递路径的结尾部分(文件/文件夹)
     */
    public static void show03(){
        File f1 = new File("C:\\t430\\java\\code\\heima\\a.txt");
        String name1 = f1.getName();
        System.out.println(name1);
        File f2 = new File("C:\\t430\\java\\code\\heima");
        String name2 = f2.getName();
        System.out.println(name2);
    }
    /*
    public String getPath():将此File转换为路径名字符串。
    获取的构造方法中传递的路径
     */
    public static void show02(){
        File f1 = new File("C:\\t430\\java\\code\\heima\\a.txt");
        File f2 = new File("a.txt");
        String path1 = f1.getPath();
        String path2 = f2.getPath();
        System.out.println(path1);
        System.out.println(path2);
    }
    private static void show01(){
        File f1 = new File("C:\\t430\\java\\code\\heima\\a.txt");
        String absolutePath1 = f1.getAbsolutePath();
        System.out.println(absolutePath1);
        File f2 = new File("a.txt");
        String absolutePath2 = f2.getAbsolutePath();
        System.out.println(absolutePath2);
    }
}
 

File类判断功能

 
 
 
 
 
 
 
package file.Demo01;
import java.io.File;
/*
File判断功能的方法
public boolean exists():此File表示的文件或目录是否实际存在。
public boolean isDirectory():此File表示的是否为目录。
public boolean isFile():此File表示的是否为文件。
 */
public class Demo03 {
    public static void main(String[] args) {
        show01();
        show02();
    }
    /*
    public boolean exists():此File表示的文件或目录是否实际存在。
    用于判断构造方法中的路径是否存在
    存在:true不存在:false
     */
    private static void show01(){
        File f1 = new File("C:\\t430\\java\\code\\heima");
        File f2 = new File("C:\\t430\\java\\code\\hei");
        File f3 = new File("C:src");//相对路径
        File f4 = new File("C:1.txt");//相对路径
        System.out.println(f1.exists());
        System.out.println(f2.exists());
        System.out.println(f3.exists());
        System.out.println(f4.exists());
    }
    /*
    public boolean isDirectory():此File表示的是否为目录。
        用于判断构造方法中给定的路径是否以文件夹结尾
        是:true
        否:false
    public boolean isFile():此File表示的是否为文件。
        用于判断构造方法中给定的路径是否以文件结尾
        是:true否:false
    注意:
        电脑的硬盘中只有文件/文件夹,两个方法是互斥
        这两个方法使用前提,路径必须是存在的,否则都返回false
     */
    public static void show02(){
        File f1 = new File("C:\\t430\\java\\code\\hei");
        //不存在,没必要获取
        if(f1.exists()){
            System.out.println(f1.isDirectory());
            System.out.println(f1.isFile());
        }
        File f2 = new File("C:\\\\t430\\\\java\\\\code\\\\heima");
        if (f2.exists()){
            System.out.println(f2.isFile());
            System.out.println(f2.isDirectory());
        }
    }
}
 

File类创建删除功能

 
 
 
 
 
 
 
package file.Demo01;
import java.io.File;
import java.io.IOException;
/*
File类创建删除功能的方法
    public boolean createNewFile():当且仅当具有该名称的文件尚不存在时,创建一个新的空文件。
    public boolean delete():删除由此File表示的文件或目录。
    public boolean mkdir():创建由此File表示的目录。
    public boolean mkdirs():创建由此File表示的目录,包括任何必需但不存在的父目录。
 */
public class Demo04 {
    public static void main(String[] args) throws IOException {
        show01();//创建文件
        show02();//创建文件夹
        show03();//删除文件夹
    }
    /*
    public boolean createNewFile():当且仅当具有该名称的文件尚不存在时,创建一个新的空文件。
    创建文件的路径和名称在构造方法中给出(构造方法的参数)
    返回值:布尔值
        true:文件不存在,创建文件,返回true
        false:文件存在,不会创建,返回false
    注意:
        1.此方法只能创建文件,不能创建文件夹
        2.创建文件的路径必须存在,否则会抛出异常
    public boolean createNewFile()throws IOException
    createNewFile声明抛出了IOException,我们调用这个方法,就必须的处理这个异常,要么throws,要么trycatch
     */
    private static void show01() throws IOException {
        File f1 = new File("C:\\t430\\java\\anything\\1.txt");
        boolean b1 = f1.createNewFile();
        System.out.println(b1);
        File f2 = new File("C:\\t430\\java\\anything\\2.txt");
        System.out.println(f2.createNewFile());
    }
    /*
    public boolean mkdir():创建单级文件夹
    public boolean mkdirs():创建多级文件夹
    创建文件夹的路径和名称在构造方法中给出(构造方法的参数)
    返回值:布尔值
        true:文件夹不存在,创建文件夹,返回true
        false:文件夹存在,不会创建,返回false;构造方法中给出的路径不存在返回false
    注意:
        1.此方法只能创建文件,不能创建文件夹
        2.创建文件的路径必须存在,否则会抛出异常
     */
    private static void show02(){
        File f1 = new File("C:\\t430\\java\\anything\\aaa");
        boolean b1 = f1.mkdir();
        System.out.println("b1:"+b1);
        File f2 = new File("C:\\t430\\java\\anything\\111\\222");
        f2.mkdirs();
    }
    /*
    public boolean delete():删除由此File表示的文件或目录。
    此方法,可以删除构造方法路径中给出的文件/文件夹
    返回值:布尔值
        true:文件/文件夹删除成功,返回true
        false:文件夹中有内容,不会删除返回false;构造方法中路径不存在false
    注意:delete方法是直接在硬盘删除文件/文件夹,不走回收站,删除要谨慎
     */
    public static void show03(){
        File f1 = new File("C:\\t430\\java\\anything\\1.txt");
        boolean b1 = f1.delete();
        System.out.println(b1);
    }
}
 

file类遍历目录

 
 
 
 
 
 
 
package file.Demo01;
import java.io.File;
/*
File类遍历(文件夹)目录功能
    public String[]list():返回一个String数组,表示该File目录中的所有子文件或目录。
    public File[]listFiles():返回一个File数组,表示该File目录中的所有的子文件或目录。
注意:
    list方法和ListFiles方法遍历的是构造方法中给出的目录
    如果构造方法中给出的目录的路径不存在,会抛出空指针异常
    如果构造方法中给出的路径不是一个目录,也会抛出空指针异常
 */
public class Demo05 {
    public static void main(String[] args) {
        show01();
    }
    /*
    public String[]list():返回一个String数组,表示该File目录中的所有子文件或目录。
    遍历构造方法中给出的目录,会获取目录中所有文件/文件夹的名称,把获取到的多个名称存储到一个String类型的数组中
     */
    private static void show01(){
        File file = new File("C:\\t430\\java\\anything");
        String[] arr = file.list();
        for (String filename : arr) {
            System.out.println(filename);
        }
    }
}
 

02 递归

概念

 
 
 
 
 
 
 
package file.digui;
/*
递归:方法自己调用自己
递归的分类:
    递归分为两种,直接递归和间接递归。
    直接递归称为方法自身调用自己。
    间接递归可以A方法调用B方法,B方法调用c方法,c方法调用A方法。
注意事项:
    递归一定要有条件限定,保证递归能够停止下来,否则会发生栈内存溢出。
    在递归中虽然有限定条件,但是递归次数不能太多。否则也会发生栈内存溢出。
    构造方法,禁止递归
递归的使用前提:
    当调用方法的时候,方法的主体不变,每次调用方法的参数不同,可以使用递归
 */
public class Demo01 {
    public static void main(String[] args) {
//        a();
        b(1);
    }
    /*
    在递归中虽然有限定条件,但是递归次数不能太多。否则也会发生栈内存溢出。
     */
    private static void b(int i) {
        System.out.println(i);
        if (i==10000){
            return;//结束方法
        }
        b(++i);
    }
    /*
    递归一定要有条件限定,保证递归能够停止下来,否则会发生栈内存溢出。
    Exception in thread"main"java.Lang.StackoverflowError
     */
//    private static void a() {
//        System.out.println("a方法");
//        a();
//    }
}
 

练习 : 计算1-n的和

package file.digui;

public class Demo02 {
public static void main(String[] args) {
int s = sum(100);
System.out.println(s);
}

/*
定义方法,使用递归算和
递归结束的条件,获取1的时候结束
目的:获取到下一个被加的数字
*/

public static int sum(int n){
if (n==1){
return 1;
}
return n + sum(n-1);
}
}

递归打印多级目录

 
 
 
 
 
 
 
package file.digui;
import java.io.File;
public class Demo03 {
    public static void main(String[] args) {
        File file = new File("C:\\t430\\java\\anything");
        getAllFile(file);
    }
    private static void getAllFile(File dir) {
        System.out.println(dir);//打印被遍历的文件夹
        File[] files = dir.listFiles();
        for (File f:files){
            //对遍历得到的File对象f进行判断,判断是否是文件夹
            if (f.isDirectory()){
                //f是一个文件夹,则继续遍历这个文件夹
                //我们发现getALLFile方法就是传递文件夹,遍历文件夹的方法
                //所以直接调用getAllFile方法即可:递归(自己调用自己)
                getAllFile(f);
            }else {
                System.out.println(f);
            }
        }
    }
}
 

综合案例文件搜索

 
 
 
 
 
 
 
package file.digui;
import java.io.File;
import java.util.Locale;
//只要.jpg结尾的文件
public class Demo04 {
    public static void main(String[] args) {
        File file = new File("C:\\t430\\java\\anything");
        getAllFile(file);
    }
    private static void getAllFile(File dir) {
//        System.out.println(dir);//打印被遍历的文件夹
        File[] files = dir.listFiles();
        for (File f:files){
            //对遍历得到的File对象f进行判断,判断是否是文件夹
            if (f.isDirectory()){
                //f是一个文件夹,则继续遍历这个文件夹
                //我们发现getALLFile方法就是传递文件夹,遍历文件夹的方法
                //所以直接调用getAllFile方法即可:递归(自己调用自己)
                getAllFile(f);
            }else {
                //只要.jpg结尾的文件
                //把File对象f转换为字符串对象
//                String name = f.getName();
//                String path = f.getPath();
                String s = f.toString();
                //把字符串改为小写
                s = s.toLowerCase();
                //调用String类中的方法endswith,判断是不是以。java结尾
                boolean b = s.endsWith(".jpg");
                //如果是java结尾输出
                if (b){
                    System.out.println(f);
                }
            }
        }
    }
}
 

对上一个例子进行简化

 
 
 
 
 
 
 
package file.digui;
import java.io.File;
import java.util.Locale;
public class Demo05 {
    public static void main(String[] args) {
        File file = new File("C:\\t430\\java\\anything");
        getAllFile(file);
    }
    private static void getAllFile(File dir) {
//        System.out.println(dir);//打印被遍历的文件夹
        File[] files = dir.listFiles();
        for (File f:files){
            if (f.isDirectory()){
                getAllFile(f);
            }else {
                if (f.getName().toLowerCase().endsWith(".jpg")){
                    System.out.println(f);
                }
            }
        }
    }
}
 

03 过滤器

 

 

 

 

 

package file.过滤器;

import java.io.File;
import java.io.FileFilter;

//创建过滤器FileFilter的实现类,重写过滤方法accept,定义过滤规则
public class FileFilterImpl implements FileFilter {
@Override
public boolean accept(File pathname) {
//如果pathname是一个文件夹,返回true,继续遍历这个文件夹
if (pathname.isDirectory()){
return true;
}
return pathname.getName().toLowerCase().endsWith(".jpg");
}
}
 
 
 
 
 
 
 
package file.过滤器;
import java.io.File;
/*
我们可以便用过滤器来实现
在File类中有两个和ListFiles重载的方法,方法的参数传递的就是过滤器
File[]listFiles(FileFilter filter)
java.io.FileFilter接口:用于抽象路径名(File对象)的过滤器。
    作用:用来过滤文件(File对象)
    抽象方法:用来过滤文件的方法
        boolean accept(File pathname)测试指定抽象路径名是否应该包含在某个路径名列表中。
        参数:
            File pathname:使用ListFiles方法遍历目录,得到的每一个文件对象
File[]listFiles(FilenameFilter filter)
java.io.FilenameFilter接口:实现此接口的类实例可用于过滤器文件名。
    作用:用于过滤文件名称
    抽象方法:用来过滤文件的方法
        boolean accept(File dir,String name)测试指定文件是否应该包含在某一文件列表中。
        参数:
            File dir:构造方法中传递的被遍历的目录
            String name:使用ListFiles方法遍历目录,获取的每一个文件/文件夹的名称
注意:两个过滤器接口是没有实现类的,需要我们自己写实现类,重写过滤的方法accept,在方法中自己定义过滤的规则
 */
public class Demo01 {
    public static void main(String[] args) {
        File file = new File("C:\\t430\\java\\anything");
        getAllFile(file);
    }
    private static void getAllFile(File dir) {
        File[] files = dir.listFiles(new FileFilterImpl());//传递了一个过滤器
        for (File f:files){
            //对遍历得到的File对象f进行判断,判断是否是文件夹
            if (f.isDirectory()){
                //f是一个文件夹,则继续遍历这个文件夹
                //我们发现getALLFile方法就是传递文件夹,遍历文件夹的方法
                //所以直接调用getAllFile方法即可:递归(自己调用自己)
                getAllFile(f);
            }else {
                System.out.println(f);
            }
        }
    }
}
 

使用内部类实现上述问题

 
 
 
 
 
 
 
package file.过滤器;
import java.io.File;
import java.io.FileFilter;
public class Demo02 {
    public static void main(String[] args) {
        File file = new File("C:\\t430\\java\\anything");
        getAllFile(file);
    }
    private static void getAllFile(File dir) {
        //传递过滤器对象 使用匿名内部类
        File[] files = dir.listFiles(new FileFilter() {
            @Override
            public boolean accept(File pathname) {
                return pathname.isDirectory() || pathname.getName().toLowerCase().endsWith(".jpg");
            }
        });
        for (File f:files){
            //对遍历得到的File对象f进行判断,判断是否是文件夹
            if (f.isDirectory()){
                //f是一个文件夹,则继续遍历这个文件夹
                //我们发现getALLFile方法就是传递文件夹,遍历文件夹的方法
                //所以直接调用getAllFile方法即可:递归(自己调用自己)
                getAllFile(f);
            }else {
                System.out.println(f);
            }
        }
    }
}
 

另一种内部类实现方法

 
 
 
 
 
 
 
package file.过滤器;
import java.io.File;
import java.io.FileFilter;
import java.io.FilenameFilter;
public class Demo02 {
    public static void main(String[] args) {
        File file = new File("C:\\t430\\java\\anything");
        getAllFile(file);
    }
    private static void getAllFile(File dir) {
        File[] files = dir.listFiles(new FilenameFilter() {
            @Override
            public boolean accept(File dir, String name) {
                return new File(dir,name).isDirectory() || name.toLowerCase().endsWith(".jpg");
            }
        });
        for (File f:files){
            //对遍历得到的File对象f进行判断,判断是否是文件夹
            if (f.isDirectory()){
                //f是一个文件夹,则继续遍历这个文件夹
                //我们发现getALLFile方法就是传递文件夹,遍历文件夹的方法
                //所以直接调用getAllFile方法即可:递归(自己调用自己)
                getAllFile(f);
            }else {
                System.out.println(f);
            }
        }
    }
}
 

注意:这两个简化方法都可以用lamda表达式进行优化


04 IO字节流

一切皆为字节

字节输出并写入数据到文件

 
 
 
 
 
 
 
package file.字节流;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
/*
java.io.Outputstream:字节输出流
    此抽象类是表示输出字节流的所有类的超类。
定义了一些子类共性的成员方法:
    public void close():关闭此输出流并释放与此流相关联的任何系统资源。
    public void flush():刷新此输出流并强制任何缓冲的输出字节被写出。
    public void write(byte[]b):将b.Length字节从指定的字节数组写入此输出流。
    public void write(byte[]b,int off,int Len):从指定的字节数组写入Len字节,从偏移量 off开始输出到此输出流。
    public abstract void write(int b):将指定的字节输出流。
java.io.FileOutputstream extends OutputStream
FileOutputStream:文件字节输出流
作用:把内存中的数据写入到硬盘的文件中
构造方法:
    Fileoutputstream(String name)创建一个向具有指定名称的文件中写入数据的输出文件流。
    FileOutputStream(File file)创建一个向指定File 对象表示的文件中写入数据的文件输出流。
    参数:写入数据的目的
        string name:目的地是一个文件的路径
        File file:目的地是一个文件
    构造方法的作用:
        1.创建一个FileOutputStream对象
        2.会根据构造方法中传递的文件/文件路径,创建一个空的文件
        3.会把FileOutputStream对象指向创建好的文件
写入数据的原理:
    java程序-->JVM(java虚拟机)-->OS(操作系统)-->OS调用写数据的方法-->把数据写入到文件中
字节输出流的使用步骤(重点):
    1.创建一个FileOutputStream对象,构造方法中传递写入数据的目的地
    2.调用FileOutputStream对象中的方法write,把数据写入到文件中
    3.释放资源(流使用会占用一定的内存,使用完毕要把内存清空,提供程序的效率)
 */
public class Demo01 {
    public static void main(String[] args) throws IOException {
        // 1.创建一个FileOutputStream对象,构造方法中传递写入数据的目的地
        FileOutputStream fos = new FileOutputStream("C:\\t430\\java\\anything\\a.txt");
        //2.调用FileOutputStream对象中的方法write,把数据写入到文件中
        //    public abstract void write(int b):将指定的字节输出流。
        fos.write(97);
        // 3.释放资源(流使用会占用一定的内存,使用完毕要把内存清空,提供程序的效率)
        fos.close();
    }
}
 

写多个字节

package file.字节流;

import javax.imageio.IIOException;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.Array;
import java.util.Arrays;

/*
public void write(byte[]b):将b.Length字节从指定的字节数组写入此输出流。
public void write(byte[]b,int off,int Len):从指定的字节数组写入Len字节,从偏移量 off开始输出到此输出流。
*/
public class Demo02 {
public static void main(String[] args) throws IOException {
//创建FileOutputStream对象,构造方法中绑定要写入数据的目的地,
FileOutputStream fos = new FileOutputStream(new File("C:\\t430\\java\\anything\\b.txt"));
//调用FileOutputStream对象中的方法write,把数据写入到文件中
//在文件中显示100,写个字节
fos.write(49);
fos.write(48);
fos.write(48);

/*
public void write(byte[]b):将b.Length字节从指定的字节数组写入此输出流。
一次写多个字节:
如果写的第一个字节是正数(0-127),那么显示的时候会查询ASCII表
如果写的第一个字节是负数,那第一个字节会和第二个字节,两个字节组成一个中文显示,查询系统默认码表(GBK)
*/
byte[] bytes = {65,66,67,68,69};
//byte[] bytes = {-65,-66,-67,68,69};比较特殊
fos.write(bytes);

/*
public void write(byte[]b,int off,int len):把字节数组的一部分写入到文件中
int off:数组的开始索引
int Len:写几个字节
*/
fos.write(bytes,1,2);

/*
写入字符的方法:可以使用String类中的方法把字符串,转换为字节数组
byte[]getBytes()把字符串转换为字节数组
*/
byte[] bytes1 = "你好".getBytes();
System.out.println(Arrays.toString(bytes1)); //[-28, -67, -96, -27, -91, -67]
fos.write(bytes1);

//释放资源
fos.close();
}
}

输出字节流的续写和换行写

 
 
 
 
 
 
 
package file.字节流;
import java.io.FileOutputStream;
import java.io.IOException;
/*
追加写/续写:使用两个参数的构造方法
    FileOutputStream(String name,boolean append)创建一个向具有指定name的文件中写入数据的输出文件流。
    FileOutputStream(File file,boolean append)创建一个向指定File 对象表示的文件中写入数据的文件输出流。
参数:
    String name,File file:S入数据的目的地
    boolean append:追加写开关
        true:创建对象不会覆盖源文件,继续在文件的末尾追加写数据
        false:创建一个 新文件,覆盖源文件
写换行:写换行符号
    windows:Irln
    Linux:/n
    mac:/r
 */
public class Demo03 {
    public static void main(String[] args) throws IOException {
        FileOutputStream fos = new FileOutputStream("C:\\t430\\java\\anything\\c.txt",true);
        for (int i = 0; i < 10; i++) {
            fos.write("你好".getBytes());
            fos.write("\r\n".getBytes());
        }
        fos.close();
    }
}
 

字节输入流_InputStream类&Fil,来读取字节数据

 
 
 
 
 
 
 
package file.字节流;
import java.io.FileInputStream;
import java.io.IOException;
/*
java.io.InputStream:字节输入流
此抽象类是表示字节输入流的所有类的超类。
定义了所有子类共性的方法:
    int read()从输入流中读取数据的下一个字节。
    int read(byte[]b)从输入流中读取一定数量的字节,并将其存储在缓冲区数组b中。
    void close()关闭此输入流并释放与该流关联的所有系统资源。
java.io.FileInputStream extends InputStream
FileInputStream:文件字节输入流
作用:把硬盘文件中的数据,读取到内存中使用
构造方法:
    FileInputStream(String name)
    FileInputStream(File file)
    参数:读取文件的数据源
        String name:文件的路径
        File file:文件
    构造方法的作用:
        1.会创建一个FileInputStream对象
        2.会把FileInputStream对象指定构造方法中要读取的文件
字节输入流的使用步骤(重点):
    1.创建FileInputStream对象,构造方法中绑定要读取的数据源
    2.使用FileInputStream对象中的方法read,读取文件
    3.释放资源
 */
public class Demo04 {
    public static void main(String[] args) throws IOException {
        FileInputStream fis = new FileInputStream("C:\\t430\\java\\anything\\a.txt");
//        int len = fis.read();
//        System.out.println(len);
//
//        int len1 = fis.read();
//        System.out.println(len1);
//        int len2 = fis.read();
//        System.out.println(len2);
//        int len3 = fis.read();
//        System.out.println(len3);
/*
发现以上读取文件是一个重复的过程,
所以可以使用循环优化
不知道文件中有多少字节,使用while循环while循环结束条件,读取到-1的时候结束 
 */
        int len = 0;//记录读取到的字节
        while ((len = fis.read())!=-1){
            System.out.println(len);
            System.out.println((char)len);
        }
        fis.close();
    }
}
 

字节输入流一次读取多个字节

package file.字节流;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Arrays;

/*
字节输入流一次读取多个字节的方法:
int read(byte[]b)从输入流中读取一定数量的字节,并将其存储在缓冲区数组b中。
明确两件事情:
1.方法的参数byte[]的作用?
2.方法的返回值int是什么?
String类的构造方法
string(byte[]bytes):把字节数组转换为字符串
string(byte[]bytes,int offset,int length)把字节数组的一部分转换为字符串 offset:数组的开始索引Length:转换的字节个数
*/
public class Demo05 {
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream("C:\\t430\\java\\anything\\b.txt");
byte[] bytes = new byte[2];
// int len = fis.read(bytes);
// System.out.println(len);//2
// //System.out.println(Arrays.toString(bytes));//[65,66]
// System.out.println(new String(bytes));
//
// len = fis.read(bytes);
// System.out.println(len);//2
// System.out.println(new String(bytes));
//
// len = fis.read(bytes);
// System.out.println(len);//2
// System.out.println(new String(bytes));
//循环
byte[] bytes1 = new byte[1024];//存储读取道德多个字节
int len = 0;
while ((len = fis.read(bytes1))!=-1){
System.out.println(new String(bytes1));//一i那位长度1024,剩余的就都是空格
System.out.println(new String(bytes1,0,len));//方法二,结果正常
}

//释放资源
fis.close();
}
}

一次一个字节与一次多个字节的原理image-20220317232209443

 

 

 

 

练习: 文件复制

 

 
 
package file.字节流;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
/*
文件复制练习:一读一写
明确:
    数据源:c:111.jpg
    数据的目的地:d:111.jpg
文件复制的步骤:
    1.创建一个字节输入流对象,构造方法中绑定要读取的数据源
    2.创建一个字节输出流对象,构造方法中绑定要写入的目的地
    3.使用字节输入流对象中的方法read读取文件
    4.使用字节输出流中的方法write,把读取到的字节写入到目的地的文件中
    5·释放资源
 */
public class Demo06 {
    public static void main(String[] args) throws IOException {
        //1.创建一个字节输入流对象,构造方法中绑定要读取的数据源
        FileInputStream fis = new FileInputStream("C:\\t430\\java\\anything\\a.txt");
        //2.创建一个字节输出流对象,构造方法中绑定要写入的目的地
        FileOutputStream fos = new FileOutputStream("C:\\t430\\java\\anything1\\a.txt");
        // 3.使用字节输入流对象中的方法read读取文件
//        int len = 0;
//        while ((len = fis.read())!=-1){
//            // 4.使用字节输出流中的方法write,把读取到的字节写入到目的地的文件中
//            fos.write(len);
//        }
        //一次读取一个字节太慢了,使用数组比较快
        byte[] bytes = new byte[1024];
        //3.使用字节输入流对象中的read读取文件
        int len = 0;
        while ((len = fis.read(bytes))!=-1) {
            // 4.使用字节输出流中的方法write,把读取到的字节写入到目的地的文件中
            fos.write(bytes,0,len);
        }
        //5·释放资源  先关写的,后关读的。因为写完了肯定读完了
        fos.close();
        fis.close();
    }
}
 

 

使用字节流读取中文

使用字节流读取中文文件 1个中文 GBK:占用两个字节 UTF-8:占用3个字节


05字符流

字符输入流

 
 
 
 
 
 
 
package file.字符流;
import java.io.FileReader;
import java.io.IOException;
/*
java.io.Reader:字符输入流,是字符输入流的最顶层的父类,定义了一些共性的成员方法,是一个抽象类
共性的成员方法:
    int read()读取单个字符并返回。
    int read(char[]cbuf)一次读取多个字符,将字符读入数组。
    void close()关闭该流并释放与之关联的所有资源。
java.io.FileReader extends InputStreamReader extends Reader
FileReader:文件字符输入流
作用:把硬盘文件中的数据以字符的方式读取到内存中
构造方法:
    FileReader(String fileName)
    FileReader(File file)
    参数:读取文件的数据源
        String fileName:文件的径
        File file:一个文件
    FileReader构造方法的作用:
        1.创建一个FileReader对象
        2.会把FileReader对象指向要读取的文件
字符输入流的使用步骤:
    1.创建FiLeReader对象,构造方法中绑定要读取的数据源
    2.使用FileReader对象中的方法read读取文件3,释放资源
 */
public class Demo01 {
    public static void main(String[] args) throws IOException {
        //  1.创建FiLeReader对象,构造方法中绑定要读取的数据源
        FileReader fr = new FileReader("C:\\t430\\java\\anything\\b.txt");
        //  2.使用FileReader对象中的方法read读取文件3,释放资源
//        int len = 0;
//        while ((len = fr.read())!=-1){
//            System.out.print((char)len);
//        }
        // int read(char[]cbuf)一次读取多个字符,将字符读入数组
        char[] cs = new char[1024];//存储读取到的多个字符
        int len = 0;//记录的是每次读取的字符个数
        while ((len = fr.read(cs))!=-1){
            /*
                string类的构造方法
                string(char[]value)把字符数组转换为字符串
                String(char[]value,int offset,int count)把字符数组的一部分转换为字符串 offset数组的开始索引count转换的个数
             */
            System.out.println(new String(cs,0,len));
        }
        fr.close();
    }
}
 

字符输出流

 
 
 
 
 
 
 
package file.字符流;
import java.io.FileWriter;
import java.io.IOException;
/*
java.io.Writer:字符输出流,是所有字符输出流的最顶层的父类,是一个抽象类
共性的成员方法:
    -void write(int c)写入单个字符。
    -void write(char[]cbuf)写入字符数组。
    -abstract void write(char[]cbuf,int off,int Len)写入字符数组的某一部分,off数组的开始索引,Len写的字符个数。
    -void write(String str)写入字符。
    -void write(String str,int off,int Len)写入字符串的某一部分,off字符串的开始索引,Len写的字符个数。
    -void flush()刷新该流的缓冲。
    -void close()关闭此流,但要先刷新它。
java.io.Filewriter extends OutputStreamwriter extends writer
Filewriter:文件字符输出流
作用:把内存中字符数据写入到文件中
构造方法:
    Filewriter(File file)根据给定的File 对象构造一个Filewriter对象。
    Filewriter(String fileName)根据给定的文件名构造一个Filewriter对象。
    参数:写入数据的目的地
        string fileName:文件的路径
        File file:是一个文件
    构造方法的作用:
        1.会创建一个FiLewriter对象
        2.会根据构造方法中传递的文件/文件的路径,创建文件
        3.会把Filewriter对象指向创建好的文件
字符输出流的使用步骤(重点):
1.创建Filewriter对象,构造方法中绑定要写入数据的目的地,
2.使用Filewriter中的方法write,把数据写入到内存缓冲区中(字符转换为字节的过程)
3.使用Filewriter中的方法flush,把内存缓冲区中的数据,刷新到文件中
4.释放资源(会先把内存缓冲区中的数据刷新到文件中)
 */
public class Demo02 {
    public static void main(String[] args) throws IOException {
        //1.创建Filewriter对象,构造方法中绑定要写入数据的目的地,
        FileWriter fw = new FileWriter("C:\\t430\\java\\anything\\d.txt");
        //2.使用Filewriter中的方法write,把数据写入到内存缓冲区中(字符转换为字节的过程)
        //void write(int c) 写入单个字符
        fw.write(97);
        //3.使用Filewriter中的方法flush,把内存缓冲区中的数据,刷新到文件中
        fw.flush();
        fw.close();
    }
}
 

flush方法和close方法的区别

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

 
 
 
 
 
 
 
package file.字符流;
import java.io.FileWriter;
import java.io.IOException;
public class Demo03 {
    public static void main(String[] args) throws IOException {
        //1.创建Filewriter对象,构造方法中绑定要写入数据的目的地,
        FileWriter fw = new FileWriter("C:\\t430\\java\\anything\\e.txt");
        //2.使用Filewriter中的方法write,把数据写入到内存缓冲区中(字符转换为字节的过程)
        //void write(int c) 写入单个字符
        fw.write(97);
        //3.使用Filewriter中的方法flush,把内存缓冲区中的数据,刷新到文件中
        fw.flush();
        //刷新之后流可以继续使用
        fw.write(98);
        fw.close();
        //close之后流就结束了,在write就会报错
//        fw.write(99);
    }
}
 

字符流写数据的其他方法

 
 
 
 
 
 
 
package file.字符流;
/*
字符输出流写数据的其他方法:
    -void write(char[]cbuf)写入字符数组。
    -abstract void write(char[]cbuf,int off,int len)写入字符数组的某一部分,off数组的开始索引,Len写的字符个数。
    -void write(String str)写入字符串。
    -void write(String str,int off,int Len)写入字符串的某一部分,off字符串的开始索引,Len写的字符个数。
 */
import java.io.FileWriter;
import java.io.IOException;
public class Demo04 {
    public static void main(String[] args) throws IOException {
        FileWriter fw = new FileWriter("C:\\t430\\java\\anything\\f.txt");
        //    -void write(char[]cbuf)写入字符数组。
        char[] cs = {'a','b','c','d','e'};
        fw.write(cs);//abcd
        //-abstract void write(char[]cbuf,int off,int len)写入字符数组的某一部分,off数组的开
        fw.write(cs,1,3);//bcd
        //void write(String str)写入字符串。
        fw.write("牛逼666");
        //void write(String str,int off,int Len)写入字符串的某一部分
        fw.write("卧槽牛逼啊",2,3);
        fw.close();
    }
}
 

续写和换行

 
 
 
 
 
 
 
package file.字符流;
import java.io.FileWriter;
import java.io.IOException;
/*
续写和换行
续写,追加写:使用两个参数的构造方法
    Filewriter(String fileName,boolean append)
    Filewriter(File file,boolean append)
    参数:
        String fileName,File file:写入数据的目的地换行:换行符号
        boolean append:续写开关 true:不会创建新的文件覆盖源文件,可以续写;false:创建新的文件覆盖源文件
    windows:\r\n Linux:/n mac:/r
 */
public class Demo05 {
    public static void main(String[] args) throws IOException {
        FileWriter fw = new FileWriter("C:\\t430\\java\\anything\\g.txt");
        for (int i = 0; i < 10; i++) {
            fw.write("helloworld"+i+"\r\n");
        }
        fw.close();
    }
}
 

使用try catch finally处理流中的异

jdk1.7之前

 
 
 
 
 
 
 
package file.字符流;
import java.io.FileWriter;
import java.io.IOException;
/*
在jdk1.7之前使用try catch finally处理流中的异常
格式:
    try{
        可能会产出异常的代码
    }catch(异常类变量变量名){
        异常的处理逻辑
    }finally{
        一定会指定的代码
        资源释放
    }
 */
public class Demo06 {
    public static void main(String[] args) {
        //提高变量fw的 作用域,让finally可以使用
        //变量在定义的时候可以没有值,但在使用的时候必须有值
        //fw = new FileWriter("C:\\t430\\java\\anything\\g.txt");执行失败,fw没有值,fw.close报错
        FileWriter fw = null;
        try {
            //可能会产生的异常代码
            fw = new FileWriter("C:\\t430\\java\\anything\\g.txt",true);
            for (int i = 0; i < 10; i++) {
                fw.write("helloworld"+i+"\r\n");
            }
        }catch (IOException e){
            //异常的处理逻辑
            System.out.println("e");
        }finally {
            //一定会执行的代码
            //创建对象失败了,fw的默认值是null,null是不能调用方法的,会抛出nullPointException,需要增加一个判断,不是null再把资源释放
            if (fw!=null){
                try {
                    //fw.close方法声明了IOexception异常,所以我们要处理这个异常对象,要么throws,要么try catch
                    fw.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
 

jdk1.7

 
 
 
 
 
 
 
JDK7的新特性
在try的后边可以增加一个(),在括号中可以定义流对象
那么这个流对象的作用域就在try中有效
try中的代码执行完毕,会自动把流对象释放,不用写finally
格式:
    try(定义流对象;定义流对象...){
        可能会产出异常的代码
    }catch(异常类变量变量名){
        异常的处理逻辑
    }
 

jdk9


06 Properties集合

使用Properties集合存储数据,遍历

 
 
 
 
 
 
 
package file.Properties集合;
import java.util.Properties;
import java.util.Set;
/*
java.util.Properties集合 extends Hashtable<k,v> impLements Map<k,v>
Properties 类表示了一个持久的属性集。Properties 可保存在流中或从流中加载。
Properties集合是一个唯一和IO流相结合的集合
    可以使用Properties集合中的方法store,把集合中的临时数据,持久化写入到硬盘中存储
    可以使用Properties集合中的方法Load,把硬盘中保存的文件(键值对),读取到集合中使用
属性列表中每个键及其对应值都是一个字符串。
    Properties集合是一个双列集合,key和value默认都是字符串
 */
public class Demo01 {
    public static void main(String[] args) {
        show01();
    }
    /*
    使用Properties集合存储数据,遍历取出Properties集合中的数据
    Properties集合是一个双列集合,key和value默认都是字符串
    Properties集合有一些操作字符串的特有方法
        Object setProperty(String key,String value)用Hashtable 的方法 put
        String getProperty(String key)通过key找到value值,此方法相当于Map集合中的get(key)方法
        Set<string> stringPropertyNames()返回此属性列表中的键集,其中该键及其对应值是字符串,此方法相当于Map集合中的keyset方法
     */
    private static void show01() {
        //创建properties集合对象
        Properties prop = new Properties();
        //使用setProperty往集合种添加对象
        prop.setProperty("赵丽颖","168");
        prop.setProperty("迪丽热巴","165");
        prop.setProperty("古力娜扎","160");
        //使用stringPropertyNames()把Properties集合中的键取出,存储到一个Set集合中
        Set<String> set = prop.stringPropertyNames();
        //遍历Set集合,取出properties集合的每一键
        for (String key:set){
            //使用getProperrty方法通过key获取value
            String value = prop.getProperty(key);
            System.out.println(key+"="+value);
        }
    }
}
 

Properties集合中的store方法

 
 
 
 
 
 
 
package file.Properties集合;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Properties;
import java.util.Set;
/*
java.util.Properties集合 extends Hashtable<k,v> impLements Map<k,v>
Properties 类表示了一个持久的属性集。Properties 可保存在流中或从流中加载。
Properties集合是一个唯一和IO流相结合的集合
    可以使用Properties集合中的方法store,把集合中的临时数据,持久化写入到硬盘中存储
    可以使用Properties集合中的方法Load,把硬盘中保存的文件(键值对),读取到集合中使用
属性列表中每个键及其对应值都是一个字符串。
    Properties集合是一个双列集合,key和value默认都是字符串
 */
public class Demo01 {
    public static void main(String[] args) throws IOException {
        show02();
    }
/*
可以使用Properties集合中的方法store,把集合中的临时数据,持久化写入到硬盘中存储
    void store(OutputStream out,String comments)
    void store(writer writer,String comments)
参数:
    OutputStream out:字节输出流,不能写入中文
    Writer writer:字符输出流,可以写中文
    String comments:注释,用来解释说明保存的文件是做什么用的
            不能使用中文,会产生乱码,默认是Unicode编码
            一般使用"空字符串
使用步骤:
1.创建Properties集合对象,添加数据
2.创建字节输出流/字符输出流对象,构造方法中绑定要输出的目的地,
3.使用Properties集合中的方法store,把集合中的临时数据,持久化写入到硬盘中存储
4.释放资源
 */
    private static void show02() throws IOException {
        //1.创建Properties集合对象,添加数据
        Properties prop = new Properties();
        prop.setProperty("赵丽颖","168");
        prop.setProperty("迪丽热巴","165");
        prop.setProperty("古力娜扎","160");
        //2.创建字节输出流/字符输出流对象,构造方法中绑定要输出的目的地,
        FileWriter fw = new FileWriter("C:\\t430\\java\\anything\\prop.txt");
        //3.使用Properties集合中的方法store,把集合中的临时数据,持久化写入到硬盘中存储
        prop.store(fw,"save data");
        fw.close();
        //用字节流
//        prop.store(new FileOutputStream("C:\\t430\\java\\anything\\prop2.txt"),"");  //全是乱码
//字符流可以写中文,字节流不可以写中文
    }
}
 

load方法

 
 
 
 
 
 
 
package file.Properties集合;
import java.io.*;
import java.util.Properties;
import java.util.Set;
/*
java.util.Properties集合 extends Hashtable<k,v> impLements Map<k,v>
Properties 类表示了一个持久的属性集。Properties 可保存在流中或从流中加载。
Properties集合是一个唯一和IO流相结合的集合
    可以使用Properties集合中的方法store,把集合中的临时数据,持久化写入到硬盘中存储
    可以使用Properties集合中的方法Load,把硬盘中保存的文件(键值对),读取到集合中使用
属性列表中每个键及其对应值都是一个字符串。
    Properties集合是一个双列集合,key和value默认都是字符串
 */
public class Demo01 {
    public static void main(String[] args) throws IOException {
        show03();
    }
    /*
    可以使用Properties集合中的方法Load,把硬盘中保存的文件(键值对),读取到集合中使用
    void load(InputStream inStream)
    void load(Reader reader)
    参数:
        Inputstream inStream:字节输入流,不能读取含有中文的键值对
        Reader reader:字符输入流,能读取含有中文的键值对
    使用步骤:
        1.创建Properties集合对象
        2.使用Properties集合对象中的方法Load读取保存键值对的文件
        3.遍历Properties集合
    注意:
        1.存储键值对的文件中,键与值默认的连接符号可以使用=,空格(其他符号)
        2.存储键值对的文件中,可以使用#进行注释,被注释的键值对不会再被读取
        3.存储键值对的文件中,键与值默认都是字符串,不用再加引号
     */
    private static void show03() throws IOException {
        //1.创建Properties集合对象
        Properties prop = new Properties();
        // 2.使用Properties集合对象中的方法Load读取保存键值对的文件
        prop.load(new FileReader("C:\\t430\\java\\anything\\prop.txt"));//if use byte IOstream,会乱码
        //3.遍历Properties集合
        Set<String> set = prop.stringPropertyNames();
        for (String key: set){
            String value = prop.getProperty(key);
            System.out.println(key+"="+value);
        }
    }
}
 

07 缓冲流

原理

 

 

 

BufferedOutputStream字节缓冲输出流

 
 
 
 
 
 
 
package file.缓冲流;
import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
/*
java.io.Bufferedoutputstream extends OutputStream
BufferedOutputStream:字节缓冲输出流
继承自父类的共性成员方法:
    -public void close():关闭此输出流并释放与此流相关联的任何系统资源。
    -public void flush():刷新此输出流并强制任何缓冲的输出字节被写出。
    -public void write(byte[]b):将b.Length字节从指定的字节数组写入此输出流。
    -public void write(byte[]b,int off,int Len):从指定的字节数组写入Len字节,从偏移量 off开始输出到此输出流。
    -public abstract void write(int b):将指定的字节输出流。
构造方法:
    BufferedOutputStream(OutputStream out)创建一个新的缓冲输出流,以将数据写入指定的底层输出流。
    BufferedOutputStream(OutputStream out,int size)创建一个新的缓冲输出流,以将具有指定缓冲区大小的数据写入指定的底层
    参数:
        OutputStream out:字节输出流
            我们可以传递FileOutputStream,缓冲流会给FileOutputStream增加一个缓冲区,提高FileOutputStream的写入效率
        int size:指定缓冲流内部缓冲区的大小,不指定默认
使用步骤(重点)
    1.创建FileOutputStream对象,构造方法中绑定要输出的目的地
    2.创建BufferedOutputStream对象,构造方法中传递FileOutputStream对象对象,提高FileOutputStream对象效率
    3.使用BufferedOutputStream对象中的方法write,把数据写入到内部缓冲区中
    4.使用BufferedOutputStream对象中的方法flush,把内部缓冲区中的数据,刷新到文件中
    5.释放资源(会先调用fLush方法刷新数据,第4部可以省略)
 */
public class Demo01 {
    public static void main(String[] args) throws IOException {
        //1.创建FileOutputStream对象,构造方法中绑定要输出的目的地
        FileOutputStream fos = new FileOutputStream("C:\\t430\\java\\anything\\h.txt");
        //2.创建BufferedOutputStream对象,构造方法中传递FileOutputStream对象对象,提高FileOutputStream对象效率
        BufferedOutputStream bos = new BufferedOutputStream(fos);
        //3.使用BufferedOutputStream对象中的方法write,把数据写入到内部缓冲区中
        bos.write("把我的数据写入到内部缓冲区".getBytes());
        //4.使用BufferedOutputStream对象中的方法flush,把内部缓冲区中的数据,刷新到文件中
        bos.flush();
        bos.close();
    }
}
 

BufferedInputStream字节缓冲输入流

 
 
 
 
 
 
 
package file.缓冲流;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
/*
java.io.BufferedInputstream extends InputStream
BufferedInputStream:字节缓冲输入流
继承自父类的成员方法:
    int read()从输入流中读取数据的下一个字节。
    int read(byte[]b)从输入流中读取一定数量的字节,并将其存储在缓冲区数组b中。
    void close()关闭此输入流并释放与该流关联的所有系统资源。
构造方法:
    BufferedInputStream(InputStream in)创建-BufferedInputStream 并保存其参数,即输入流 in,以便将来使用。
    BufferedInputStream(InputStream in,int size)创建具有指定缓冲区大小的BufferedInputStream并保存其参数,即输入流
    参数:
        Inputstream in:字节输入流
            我们可以传递FileInputStream,缓冲流会给FileInputStream增加一个缓冲区,提高FileInputStream的读取效率
        int size:指定缓冲流内部缓冲区的大小,不指定默认
使用步骤(重点):
1.创建FileInputStream对象,构造方法中绑定要读取的数据源
2.创建BufferedInputStream对象,构造方法中传递FileInputStream对象,提高FileInputStream对象的读取效率
3.使用BufferedInputStream对象中的方法read,读取文件
4.释放资源
 */
public class Demo02 {
    public static void main(String[] args) throws IOException {
        //1.创建FileInputStream对象,构造方法中绑定要读取的数据源
        FileInputStream fis = new FileInputStream("C:\\t430\\java\\anything\\h.txt");
        //2.创建BufferedInputStream对象,构造方法中传递FileInputStream对象,提高FileInputStream对象的读取效率
        BufferedInputStream bis = new BufferedInputStream(fis);
        /*
         //3.使用BufferedInputStream对象中的方法read,读取文件
        int len = 0;//记录每次读取到的字节
        while ((len = bis.read())!=-1){
            System.out.println(len);
        }
         */
        //更高效的读取方法
        byte[] bytes = new byte[1024];//存储每次读取的数据
        int len = 0;//记录每次读取的有效字节个数
        while ((len = bis.read(bytes))!=-1){
            System.out.println(new String(bytes,0,len));
        }
        bis.close();
    }
}
 

缓冲流的效率测试

32ms vs 10ms

 
 
 
 
 
 
 
package file.缓冲流;
import com.sun.xml.internal.ws.policy.privateutil.PolicyUtils;
import java.io.*;
/*
文件复制练习:一读一写
明确:
    数据源:c:111.jpg
    数据的目的地:d:111.jpg
文件复制的步骤:
    1.创建字节缓冲输入流对象,构造方法中传递字节输入流
    2.创建字节缓冲输出流对象,构造方法中传递字节输出流
    3.使用字节缓冲输入流对象中的方法read,读取文件
    4.使用字节缓冲输出流中的方法write,把读取的数据写入到内部缓冲区中
    5·释放资源(会先把缓冲区中的数据,刷新到文件中)
 */
public class Demo03 {
    public static void main(String[] args) throws IOException {
        long s = System.currentTimeMillis();
        //1.创建字节缓冲输入流对象,构造方法中传递字节输入流
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream("C:\\t430\\java\\anything\\1.jpg"));
        //2.创建字节缓冲输出流对象,构造方法中传递字节输出流
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("C:\\t430\\java\\anything1\\1.jpg"));
        // 3.使用字节缓冲输入流对象中的方法read,读取文件
        //一次读取一个字节写入一个字节的方式
//        int len = 0;
//        while ((len = bis.read())!=-1){
//            bos.write(len);
//        }
        //使用数组
        byte[] bytes = new byte[1024];
        int len = 0;
        while ((len = bis.read(bytes))!=-1){
            bos.write(bytes,0,len);
        }
        bos.close();
        bis.close();
        long e = System.currentTimeMillis();
        System.out.println("复制文件耗时"+(e-s)+"毫秒");
    }
}
 

BufferedWriter 字符缓冲输出流

 
 
 
 
 
 
 
package file.缓冲流;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
/*
java.io.BufferedWriter extends writer
BufferedWriter:字符缓冲输出流
继承自父类的共性成员方法:
    -void write(int c)写入单个字符。
    -void write(char[] cbuf)写入字符数组。
    -abstract void write(char[] cbuf,int off,int Len)写入字符数组的某一部分,off数组的开始索引,Len写的字符个数。
    -void write(String str)写入字符。
    -void write(String str,int off,int Len)写入字符串的某一部分,off字符串的开始索引,Len写的字符个数。
    -void flush()刷新该流的缓冲。
    -void close()关闭此流,但要先刷新它。
构造方法:
    BufferedWriter(Writer out)创建一个使用默认大小输出缓冲区的缓冲字符输出流。
    BufferedWriter(Writer out,int sz)创建一个使用给定大小输出缓冲区的新缓冲字符输出流。
    参数:
        Writer out:字符输出流
            我们可以传递FileWriter,缓冲流会给FileWriter增加一个缓冲区,提高Filewriter的写入效率
        int sz:指定缓冲区的大小,不写默认大小
特有的成员方法:
    void newline()写入一个行分隔符。会根据不同的操作系统,获取不同的行分隔符换行:
    换行符号
        windows:\r\n
        Linux:/n
        mac:/r
    使用步骤:
        1.创建字符缓冲输出流对象,构造方法中传递字符输出流
        2.调用字符缓冲输出流中的方法write,把数据写入到内存缓冲区中
        3.调用字符缓冲输出流中的方法flush,把内存缓冲区中的数据,刷新到文件中
        4.释放资源
 */
public class Demo04 {
    public static void main(String[] args) throws IOException {
        // 1.创建字符缓冲输出流对象,构造方法中传递字符输出流
        BufferedWriter bw = new BufferedWriter(new FileWriter("C:\\t430\\java\\anything1\\b.txt"));
        //2.调用字符缓冲输出流中的方法write,把数据写入到内存缓冲区中
        for (int i = 0; i < 10; i++) {
            bw.write("传智播客");
            //bw.write("\r\n");
            bw.newLine();
        }
        //3.调用字符缓冲输出流中的方法flush,把内存缓冲区中的数据,刷新到文件中
        bw.flush();
        bw.close();
    }
}
 

BufferedReader 字符缓冲输入流

 
 
 
 
 
 
 
package file.缓冲流;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
/*
java.io.BufferedReader extends Reader继承自父类的共性成员方法:int read()读取单个字符并返回。
    int read(char[]cbuf)一次读取多个字符,将字符读入数组。
    void close()关闭该流并释放与之关联的所有资源。
构造方法:
    BufferedReader(Reader in)创建一个使用默认大小输入缓冲区的缓冲字符输入流。
    BufferedReader(Reader in,int sz)创建一个使用指定大小输入缓冲区的缓冲字符输入流。
    参数:
        Reader in:字符输入流
            我们可以传递FileReader,缓冲流会给FileReader增加一个缓冲区,提高FiLeReader的读取效率
特有的成员方法:
    String readline()读取一个文本行。读取一行数据
        行的终止符号:通过下列字符之一即可认为某行已终止:换行('\n')、回车('\r')或回车后直接跟着换行(\r\n)
    返回值:
        包含该行内容的字符串,不包含任何行终止符,如果已到达流末尾,则返回null
使用步骤:
1.创建字符缓冲输入流对象,构造方法中传递字符输入流
2.使用字符缓冲输入流对象中的方法read/readLine读取文本
3.释放资源
 */
public class Demo05 {
    public static void main(String[] args) throws IOException {
        //1.创建字符缓冲输入流对象,构造方法中传递字符输入流
        BufferedReader br = new BufferedReader(new FileReader("C:\\t430\\java\\anything1\\b.txt"));
        //2.使用字符缓冲输入流对象中的方法read/readLine读取文本
//        String line = br.readLine();
//        System.out.println(line);
//
//        line = br.readLine();
//        System.out.println(line);
//
//        line = br.readLine();
//        System.out.println(line);
        //以上代码重复,,不知道有多少行数,结束条件为读取到null
        String line;
        while ((line = br.readLine())!=null){
            System.out.println(line);
        }
        br.close();
    }
}
 

对文本进行排序

 


08转换流

txt以ANSI格式存储,读取的时候出现了乱码

 
 
 
 
 
 
 
package file.转换流;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
/*
FileReader可以读取IDE默认编码格式(UTF-8)的文件
FileReader读取系统默认编码(中文GBK)会产生乱码
 */
public class Demo01 {
    public static void main(String[] args) throws IOException {
        FileReader fr = new FileReader("C:\\t430\\java\\anything1\\我是GBK格式的文本.txt");
        int len = 0;
        while ((len = fr.read())!=-1){
            System.out.print((char)len);
        }
        fr.close();
    }
}
 

 

转换流的原理

 

 

 

 

 

 

OutputStreamWriter

 
 
 
 
 
 
 
package file.转换流;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
/*
java.io.OutputStreamWriter extends Writer
OutputStreamWriter:是字符流通向字节流的桥梁:可使用指定的charset将要写入流中的字符编码成字节。(编码:把能看懂的变成看不懂)
继续自父类的共性成员方法:
    -void write(int c)写入单个字符。
    -void write(char[] cbuf)写入字符数组。
    -abstract void write(char[] cbuf,int off,int Len)写入字符数组的某一部分,off数组的开始索引,Len写的字符个数。
    -void write(String str)写入字符。
    -void write(string str,int off,int Len)写入字符串的某一部分,off字符串的开始索引,Len写的字符个数。
    -void flush()新该流的缓冲。
    -void close()关闭此流,但要先刷新它。
构造方法:
    OutputStreamWriter(OutputStream out)创建使用字符编码的OutputStreamWriter
    OutputStreamWriter(OutputStream out,String charsetName)创建使用指定字符集的 OutputStreamWriter
    参数:
        OutputStream out:字节输出流,可以用来写转换之后的字节到文件中
        String charsetName:指定的编码表名称,不区分大小写,可以是utf-8/UTF-8,gbk/GBK,..不指定默认使用UTF-8
使用步骤:
    1.创建OutputStreamWriter对象,构造方法中传递字节输出流和指定的编码表名称
    2.使用OutputStreamWriter对象中的方法write,把字符转换为字节存储缓冲区中(编码)
    3.使用OutputStreamwriter对象中的方法flush,把内存缓冲区中的字节刷新到文件中(使用字节流写字节的过程)
    4.释放资源
 */
public class Demo02 {
    public static void main(String[] args) throws IOException {
        write_utf_8();
        write_gbk();
    }
    private static void write_gbk() throws IOException {
        // 1.创建OutputStreamWriter对象,构造方法中传递字节输出流和指定的编码表名称
        OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("C:\\t430\\java\\anything1\\gbk.txt"),"gbk");
        //2.使用OutputStreamWriter对象中的方法write,把字符转换为字节存储缓冲区中(编码)
        osw.write("你好");
        osw.flush();
        osw.close();
    }
    /*
    使用转换流OutputStreamWriter写UTF-8格式的文件
     */
    private static void write_utf_8() throws IOException {
        // 1.创建OutputStreamWriter对象,构造方法中传递字节输出流和指定的编码表名称
        //OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("C:\\t430\\java\\anything1\\utf_8.txt"),"utf-8");
        OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("C:\\t430\\java\\anything1\\utf_8.txt"));//不指定也是默认utf-8
        //2.使用OutputStreamWriter对象中的方法write,把字符转换为字节存储缓冲区中(编码)
        osw.write("你好");
        osw.flush();
        osw.close();
    }
}
 

InputStreamReader

 
 
 
 
 
 
 
package file.转换流;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
/*
java.io.InputStreamReader extends Reader
InputStreamReader:是字节流通向字符流的桥梁:它使用指定的charset 读取字节并将其解码为字符。(解码:把看不懂的变成能看懂的)
继承自父类的共性成员方法:
    int read()读取单个字符并返回。
    int read(char[]cbuf)一次读取多个字符,将字符读入数组。
    void close()关闭该流并释放与之关联的所有资源。
构造方法:
    InputStreamReader(InputStream in)创建一个使用默认字符集的InputStreamReader
    InputStreamReader(InputStream in,String charsetName)创建使用指定字符集 InputStreamReader
参数:
    InputStream in:字节输入流,用来读取文件中保存的字节
    String charsetName:指定的编码表名称,不区分大小写,可以是utf-8/UTF-8,gbk/GBK,...不指定默认使用UTF-8
使用步骤:
    1.创建InputStreamReader对象,构造方法中传递字节输入流和指定的编码表名称
    2.使用InputStreamReader对象中的方法read读取文件
    3.释放资源
注意事项:构造方法中指定的编码表名称要和文件的编码相同,否则会发生乱码
 */
public class Demo03 {
    public static void main(String[] args) throws IOException {
        read_utf_8();
        read_gbk();
    }
    private static void read_gbk() throws IOException {
        InputStreamReader isr = new InputStreamReader(new FileInputStream("C:\\t430\\java\\anything1\\gbk.txt"),"gbk");
        int len =0;
        while ((len = isr.read())!=-1){
            System.out.println((char)len);
        }
        isr.close();
    }
    /*
    使用InputStreamReader读取UTF-8格式的文件
     */
    private static void read_utf_8() throws IOException {
        InputStreamReader isr = new InputStreamReader(new FileInputStream("C:\\t430\\java\\anything1\\utf_8.txt"),"utf-8");//不知名就是默认u8
        int len =0;
        while ((len = isr.read())!=-1){
            System.out.println((char)len);
        }
        isr.close();
    }
}
 

联系:转换文件编码

 
 
 
 
 
 
 
package file.转换流;
import java.io.*;
/*
练习:转换文件编码
将GBK编码的文本文件,转换为UTF-8编码的文本文件。
分析:
1.创建InputStreamReader对象,构造方法中传递字节输入流和指定的编码表名称GBK
2.创建OutputStreamlwriter对象,构造方法中传递字节输出流和指定的编码表名称UTF-8
3.使用InputStreamReader对象中的方法read读取文件
4.使用OutputStreamwriter对象中的方法write,把读取的数据写入到文件中
5.释放资源
 */
public class Demo04 {
    public static void main(String[] args) throws IOException {
        //1.创建InputStreamReader对象,构造方法中传递字节输入流和指定的编码表名称GBK
        InputStreamReader isr = new InputStreamReader(new FileInputStream("C:\\t430\\java\\anything1\\我是GBK格式的文本.txt"),"gbk");
        //2.创建OutputStreamlwriter对象,构造方法中传递字节输出流和指定的编码表名称UTF-8
        OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("C:\\t430\\java\\anything1\\我是UTF_8格式的文本.txt"),"UTF-8");
        //3.使用InputStreamReader对象中的方法read读取文件
        int len = 0;
        while ((len = isr.read())!=-1){
            //4.使用OutputStreamwriter对象中的方法write,把读取的数据写入到文件中
            osw.write(len);
        }
        osw.close();
        isr.close();
    }
}
 

09 序列化流

序列化与反序列化

 

 

 

对象的序列化流ObjectOutputStream

 
 
 
 
 
 
 
package file.序列化流;
import java.io.Serializable;
/*
序列化和反序列化的时候,会抛出NotSerializableException没有序列化异常
类通过实现java.io.Serializable接口以启用其序列化功能。未实现此接口的类将无法使其任何状态序列化或反序列化。
Serializable接口也叫标记型接口
    要进行序列化和反序列化的类必须实现Serializable接口,就会给类添加一个标记
    当我们进行序列化和反序列化的时候,就会检测类上是否有这个标记
        有:就可以序列化和反序列化
        没有:就会抛出NotSerializableException异常
去市场买肉-->肉上有一个蓝色章(检测合格)-->放心购买-->买回来怎么吃随意
 */
public class Person implements Serializable {   //没有这个接口就会异常
    private String name;
    private int age;
    public Person() {
    }
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
}
 
 
 
 
 
 
 
 
package file.序列化流;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
/*
java.io.ObjectOutputStream extends OutputStream
ObjectOutputStream:对象的序列化流
作用:把对象以流的方式写入到文件中保存
构造方法:
    ObjectOutputStream(OutputStream out)创建SA指定 OutputStreamObjectOutputStream
    参数:OutputStream out:字节输出流
特有的成员方法:
void writeObject(Object obj)指定的对象写A ObjectOutputStream
使用步骤:
1.创建ObjectOutputStream对象,构造方法中传递字节输出流
2.使用ObjectOutputStream对象中的方法writeObject,把对象写入到文件中
3.释放资源
 */
public class Demo01 {
    public static void main(String[] args) throws IOException {
        //1.创建ObjectOutputStream对象,构造方法中传递字节输出流
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("C:\\t430\\java\\anything1\\person.txt"));
        //2.使用ObjectOutputStream对象中的方法writeObject,把对象写入到文件中
        oos.writeObject(new Person("小美女",18));
        oos.close();
    }
}
 

对象的反序列化流ObjectInputStream

 
 
 
 
 
 
 
package file.序列化流;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
/*
java.io.ObjectInputStream extends InputStream
ObjectInputStream:对象的反序列化流
作用:把文件中保存的对象,以流的方式读取出来使用
构造方法:
    ObjectInputStream(InputStream in)创建A定 InputStream 取 ObjectInputStreamo
    参数:InputStream in:字节输入流
特有的成员方法:Object readObject()A ObjectInputStream 读取对象。
使用步骤:
    1.创建ObjectInputStream对象,构造方法中传递字节输入流
    2.使用ObjectInputStream对象中的方法readObject读取保存对象的文件
    3.释放资源
    4.使用读取出来的对象(打印)
readObject方法声明抛出了ClassNotFoundException(class文件找不到异常)
当不存在对象的class文件时抛出此异常
反序列化的前提:
    1.类必须实现Serializable
    2.必须存在类对应的class文件
 */
public class Demo02 {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        // 1.创建ObjectInputStream对象,构造方法中传递字节输入流
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("C:\\t430\\java\\anything1\\person.txt"));
        //2.使用ObjectInputStream对象中的方法readObject读取保存对象的文件
        Object o = ois.readObject();
        ois.close();
        System.out.println(o);
        Person o1 = (Person) o;
        System.out.println(o1.getName()+o1.getAge());
    }
}
 

transient关键字——瞬态关键字

 

 

InvalidClassException异常_原理

练习:序列化集合

 
 
 
 
 
 
 
package file.序列化流;
import java.io.*;
import java.util.ArrayList;
/*
练习:序列化集合
当我们想在文件中保存多个对象的时候
可以把多个对象存储到一个集合中
分析:对集合进序列化和反序列化
1.定义一个存储Person对象的Arraylist集合
2.往ArrayList集合中存储Person对象
3.创建一个序列化流ObjectOutputStream对象
4.使用ObjectoutputStream对象中的方法writeobject,对集合进行序列化
5,创建一个反序列化ObjectInputStream对象
6.使用ObjectInputStream对象中的方法readobject读取文件中保存的集合
7.把object类型的集合转换为ArrayList类型
8.遍历ArrayList集合
9.释放资源
 */
public class Demo03 {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        //1.定义一个存储Person对象的Arraylist集合
        ArrayList<Person> list = new ArrayList<>();
        //2.往ArrayList集合中存储Person对象
        list.add(new Person("张三",18));
        list.add(new Person("李四",19));
        list.add(new Person("王五",20));
//        3.创建一个序列化流ObjectOutputStream对象
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("C:\\t430\\java\\anything1\\list.txt"));
//        4.使用ObjectoutputStream对象中的方法writeobject,对集合进行序列化
        oos.writeObject(list);
//        5,创建一个反序列化ObjectInputStream对象
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("C:\\t430\\java\\anything1\\list.txt"));
//        6.使用ObjectInputStream对象中的方法readobject读取文件中保存的集合
        Object o = ois.readObject();
//        7.把object类型的集合转换为ArrayList类型
        ArrayList<Person> list2 = (ArrayList<Person>)o;
//        8.遍历ArrayList集合
        for (Person p:list2){
            System.out.println(p);
        }
//        9.释放资源
        ois.close();
        oos.close();
    }
}
 

 

10 打印流

 
 
 
 
 
 
 
package file.打印流;
import java.io.FileNotFoundException;
import java.io.PrintStream;
/*
java.io.PrintStream:打印流
    PrintStream 为其他输出流添加了功能,使它们能够方便地打印各种数据值表示形式。
PrintStream特点:
    1.只负责数据的输出,不负责数据的读取
    2.与其他输出流不同,PrintStream永远不会抛出IOException
    3.有特有的方法,print,println
        void print(任意类型的值)
        void println(任意类型的值并换行)
构造方法:
    PrintStream(File file):输出的目的地是一个文件
    PrintStream(OutputStream out):输出的目的地是一个字节输出流
    PrintStream(String fileName):输出的目的地是一个文件路径
PrintStream extends OutputStream
继承自父类的成员方法:
    public void close():关闭此输出流并释放与此流相关联的任何系统资源。
    public void flush():刷新此输出流并强制任何缓冲的输出字节被写出。
    public void write(byte[]b):将 b.length字节从指定的字节数组写入此输出流。
    public void write(byte[]b,int off,int Len):从指定的字节数组写入Len字节,从偏移量 off开始输出到此输出流。
    public abstract void write(int b):将指定的字节输出流。
注意:
    如果使用继承自父类的write方法写数据,那么查看数据的时候会查询编码表97->a
    如果使用自己特有的方法print/println方法写数据,写的数据原样输出97->97
 */
public class Demo01 {
    public static void main(String[] args) throws FileNotFoundException {
        //System.out.println("hello world");
        //创建打印流PrintStream对象,构造方法中绑定要输出的目的地
        PrintStream ps = new PrintStream("C:\\t430\\java\\anything1\\print.txt");
        //如果使用继承自父类的write方法写数据,那么查看数据的时候会查询编码表97->a
        ps.write(97);
        //如果使用自己特有的方法print/println方法写数据,写的数据原样输出97->97
        ps.println(97);
        ps.println(8.8);
        ps.println('a');
        ps.println("hello world");
        ps.println(true);
        ps.close();
    }
}
 

打印流可以改变语句的目的地(打印流的流向)

 
 
 
 
 
 
 
package file.打印流;
import java.io.FileNotFoundException;
import java.io.PrintStream;
/*
可以改变输出语句的目的地(打印流的流向)
输出语句,默认在控制台输出
使用System.setOut方法改变输出语句的目的地改为参数中传递的打印流的目的地
    static void setOut(PrintStream out)
    重新分配"标准"输出流。
 */
public class Demo02 {
    public static void main(String[] args) throws FileNotFoundException {
        System.out.println("我是在控制台输出");
        PrintStream ps = new PrintStream("C:\\t430\\java\\anything1\\目的地是打印流.txt");
        System.setOut(ps);//把输出流的目的地改编为打印流的目的地
        System.out.println("我在打印流的目的地输出");
        ps.close();
    }
}

 

 
posted @ 2022-03-19 23:01  CHPang  阅读(44)  评论(0)    收藏  举报