Java输入输出流

IO流

IO流用来处理设备之间的数据传输,传输是通过流的方式
Java用于操作流的对象都在java.io包中
流就是指一连串流动的字符,以先进先出的方式发送信息的通道。

流的方式
按照流的方向划分

输入流:将外部数据源的数据转换成流,程序通过读取流中的数据,完成对数据源读取的访问
输出流:将流中的数据转换到对应的数据源中,程序通过向流中写入数据,完成对数据源写入

按照类型划分

字节流:以字节为单位(8位),可以访问所有文件
字符流:以字符为单位(16位Unicode),只能访问文本文件

输出流

比如我们经常使用的 System.out.println("cocci"); 就是将字符串输出到控制台中,或者说输出到屏幕中,这里的屏幕就是输出设备,由程序将cocci通过流输出到目的地。

输出设备除了屏幕,还有打印机、文件等。

输入流

比如使用键盘接收数据 Scanner sc = new Scanner(System.in); 这里的System.in就是输入流,程序从数据源这里指键盘去读取数据通过流输入到程序当中。

输入设备除了键盘,还有扫描仪、文件等。

File类

文件和目录路径名的抽象表示形式,表示磁盘上的文件或目录
文件:可认为是相关记录或放在一起的数据的集合

构造方法

示例:以下三种方式等价,各有不同的使用场景

    //Windows中路径分隔可使用/或者\\
    File file1 = new File("d:\\java\\a.txt");
    File file2 = new File("d:\\java","a.txt");
    File file3 = new File(new File("d:\\"),"java\\a.txt");
常用方法
注意事项
  1. delete()方法在删除目录时如果其内有内容则无法删除,需要先清空目录下的内容,再删除目录本身
  2. isDirectory()isFile()方法在判断是否为文件或者目录时,如果文件或目录不存在则返回false
  3. createNewFile()方法在创建文件时,如果文件所在的目录不存在则创建失败并抛出异常
  4. String[] list()File[] listFiles() 都是返回对象包含的所有的文件和目录,返回类型不同
文件与目录的相关操作
    public static void main(String[] args) {
        
        /*目录的创建与删除*/
        File file=new File("d:\\test\\java");
        if(file.exists()){
            System.out.println("目录存在,删除目录");
            file.delete(); //只会删除一级目录,即java
        }else{
            boolean b=file.mkdirs();  //创建多级目录
            System.out.println("创建文件目录结果:" + b);
        }
        /*文件的创建与删除*/
        File file2=new File("d:\\test\\a.txt");
        if(file2.exists()){
            System.out.println("文件存在,删除文件");
            file2.delete();
        }else{
            try {
                file2.createNewFile(); //创建文件
            } catch (IOException e) {
                e.printStackTrace();
            }
            System.out.println("创建文件");
        }
        //重命名,把a.txt重命名为b.txt
        //file2.renameTo(new File("d:\\test\\b.txt"));
        //把b.txt剪切到java目录下并重命名为c.txt
        file2.renameTo(new File("d:\\test\\java\\c.txt"));
    }
获取File对象的所有子文件和目录
public class TestFile {

    public static void main(String[] args) {
        //获取磁盘的所有根目录
       File[] files =File.listRoots();
       for(int i=0;i

字节流

字节输入流

InputStream,此抽象类是表示字节输入流的所有类的超类,其主要子类如下

字节输出流

OutputStream,此抽象类是字节输出流的所有类的超类,其主要子类如下

过滤器输入输出流

FileInputStream

从文件系统中的某个文件中获得输入字节
用于读取诸如图像数据之类的原始字节流

构造方法
主要方法

read方法不带参数的返回值为读取的单个字节值,带参数的返回值表示读取的字节长度。如果返回值为-1,则表示已经达到文件末尾!

FileOutputStream
构造方法
主要方法
演示Demo
public class IOTest {
    
    public static void main(String[] args) {
        OutputStream fos = null;
        InputStream fis = null;
        try {
            /********* 输出流写文件 ***********/
            fos=new FileOutputStream("d:\\silly.txt");
            String str="最好的我们隔了一整个青春";
            byte[] words=str.getBytes(); //把字符串编码成字节序列
            //写入操作
            fos.write(words, 0, words.length);
            System.out.println("写入成功!");
            
            /********* 输入流读文件 ***********/
            fis = new FileInputStream("d:\\silly.txt");
            StringBuilder sb = new StringBuilder();
            byte[] buf = new byte[1024]; //字节数组缓存数据
            int n = 0; //记录读取的字节长度
            //循环读取数据
            while((n = fis.read(buf)) != -1){
                //这里使用三个参数的构造方法,因为最后一次读取的长度可能达不到buf数组的长度
                //所以根据实际读取的长度n去构造对象更合理
                sb.append(new String(buf, 0, n));
                buf = new byte[1024]; //重新初始化,避免数据重复
            }
            System.out.println(sb.toString());
            
        } catch (IOException e) {
            e.printStackTrace();
        }finally{
            try {
                //释放资源
                if(fos != null) fos.close();
                if(fis != null) fis.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
文件复制
    public static void main(String[] args) {
        //将d盘的图片复制到桌面
         InputStream is = null;  
         OutputStream os = null;
         try {
           //实例化输入输出流对象
            is = new FileInputStream("d:\\girl.jpg");
            os = new FileOutputStream("C:\\Users\\ruoxiyuan\\Desktop\\mm.jpg");
            //定义一个2048字节的缓存
             byte[] buffer=new byte[2048];
             int len= 0; //读取的字节长度
             while((len = is.read(buffer)) != -1){
                 //最后一次读取的长度len不一定能达到buffer数组的长度,也就是空间有可能富余
                 //如果直接使用write(buffer)方法可能导致写入更多的字节,新文件会稍大
                 os.write(buffer, 0, len);
             }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }finally{
            try {
                //释放资源
                if(is != null) is.close();
                if(os != null) os.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

字符流

字符输入流

Reader,此抽象类是表示字符输入流的所有类的超类,其主要子类如下

字符输出流

Writer,此抽象类是表示字符输出流的所有类的超类,其主要子类如下

字节字符转换流

InputStreamReader:是字节流通向字符流的桥梁,它使用指定的 charset 读取字节并将其解码为字符。
OutputStreamWriter:是字符流通向字节流的桥梁,它使用指定的 charset 将写入其中的字符编码成字节。
其使用的字符集可以由构造方法指定,或者使用平台默认的字符集。

FileReader

用来读取字符文件的便捷类,使用默认字符集进行编码,InputStreamReader的子类

构造方法
常用方法
FileWriter

用来写入字符文件的便捷类,使用默认字符集进行编码,OutputStreamWriter的子类

构造方法

如果使用new FileWriter(file),已有文件内容此时会被清空,设置第二个参数为true表示追加内容

常用方法
演示Demo
public class IOTest {
    public static void main(String[] args) {
        try {
            /*****写文件*****/
            FileWriter writer = new FileWriter("d:\\silly.txt");
            String str = "成功的人只会去做他们该做的事情,只会面对他们的困难,不会有抱怨,"
                    + "更不会有羡慕,因为他们心中有目标,理想,动力以及那颗沉淀的心";
            writer.write(str);
            writer.flush(); //刷新缓冲区
            
            /*****读文件*****/
            FileReader reader = new FileReader("d:\\silly.txt");
            char[] buffer = new char[1024];//定义缓冲区
            int len = 0;
            while((len = reader.read(buffer)) != -1){
                String res = new String(buffer, 0, len);
                System.out.println(res);
            }
            writer.close();
            reader.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

缓冲流

使用缓冲流可以提高读写速度,注意缓冲流在调用write方法写入数据时,是写入的缓冲区,如果缓冲区满了自动执行写操作,缓冲区不满则需要执行flush方法强制写入到输出设备,调用close方法也会强制写入

字节缓冲流

缓冲输入流BufferedInputStream,缓冲输出BufferedOutputStream
只是对字节流进行了包装,使用方式和字节流基本一致
构造方法:
public BufferedInputStream(InputStream in)
public BufferedOutputStream(OutputStream out)

字符缓冲流
缓冲输入流BufferedReader

构造方法:public BufferedReader(Reader in)
特殊方法:String readLine() 读取一个文本行

缓冲输出流BufferedWriter

构造方法:public BufferedWriter(Writer out)
特殊方法:void newLine() 写入一个行分隔符

演示Demo
public class IOTest {
    
    public static void main(String[] args) {
        try {
            /*****写文件*****/
            FileWriter writer = new FileWriter("d:\\silly.txt");
            BufferedWriter bw = new BufferedWriter(writer);
            bw.write("书山有路勤为径");
            bw.newLine(); //写入换行符(根据平台写入对应的换行符)
            bw.write("学海无涯苦作舟");
            bw.newLine();
            //windows下使用\r\n也能换行
            bw.write("世间安得两全法" + "\r\n");
            bw.write("不负如来不负卿");
            bw.flush(); //必须刷新缓冲区
            
            /*****读文件*****/
            String record = null; //存储文件的内容
            int count = 0; //记录行数
            FileReader reader = new FileReader("d:\\silly.txt");
            BufferedReader br = new BufferedReader(reader);
            //每次读取一整行数据,返回值为空时说明读取到文件末尾
            while((record = br.readLine()) != null){
                count++;
                System.out.println("当前行数"+count+":"+record);
            }
            /**输出结果
            当前行数1:书山有路勤为径
            当前行数2:学海无涯苦作舟
            当前行数3:世间安得两全法
            当前行数4:不负如来不负卿
            */
            bw.close();
            br.close();
            writer.close();
            reader.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

对象序列化与反序列化

序列化:把Java对象转换为字节序列的过程
反序列化:把字节序列恢复为Java对象的过程
只有实现了Serializable接口的类的对象才能被序列化

序列化用途
  1. 把对象的字节序列永久保存在硬盘上,通常放在一个文件中
  2. 在网络上传送对象的字节序列
相关类

对象输入流类:ObjectInPutStream
对象输出流类:ObjectOutPutStream

序列化步骤
  1. 创建一个对象输出流:ObjectOutPutStream out = new ObjectOutPutStream(OutPutStream out);
  2. 调用方法对对象进行序列化 out.writeObject(Object obj); 把对象序列化,得到字节序列写入流中
  3. 刷新缓冲并关闭流 out.flush(); out.close();
反序列化步骤
  1. 创建一个对象输入流:ObjectInPutStream in = new ObjectInPutStream(InPutStream in);
  2. 调用方法 in.readObject(); 读取字符序列,把参数反序列化成对象,返回该对象
  3. 关闭流 in.close();
演示Demo

定义一个学生类实现Serializable接口

public class Student implements Serializable{
    private String name;
    private int age;
    public Student(String name, int age) {
        super();
        this.name = name;
        this.age = age;
    }
    @Override
    public String toString() {
        return "Student [name=" + name + ", age=" + age + "]";
    }
}

实现对象序列化与反序列化

public class IOTest {
    
    public void save(Student stu){
        OutputStream os=null;
        try {
            os = new FileOutputStream("d:\\stu.dat");
            ObjectOutputStream oos = new ObjectOutputStream(os);
            oos.writeObject(stu);
            //写入其他类型数据
            oos.writeBoolean(true);
            oos.flush();
            oos.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }finally{
            if(os!=null){
                try {
                    os.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    public Student read(){
        InputStream is=null;
        Student stu=null;
        try {
            is = new FileInputStream("d:\\stu.dat");
            ObjectInputStream ois = new ObjectInputStream(is);
            stu = (Student)ois.readObject();
            //注意读取顺序要和写入顺序保持一致
            System.out.println(ois.readBoolean());
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }finally{
            if(is!=null){
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return stu;
    }
    
    public static void main(String[] args) {
        Student stu = new Student("小小", 18);
        IOTest test = new IOTest();
        test.save(stu);
        Student res = test.read();
        System.out.println(res);
        //true
        //Student [name=小小, age=18]
    }
}

Serializable接口

实现该接口会按默认方式进行序列化和反序列化

默认方式序列化
  • 这种序列化方式仅对非transient的实例变量进行序列化
  • 不会序列化对象的transient的实例变量,也不会序列化静态变量
默认方式反序列化
  • 如果内存中对象所属的类还没有被加载,那么会先加载并初始化这个类,如果classpath中不存在该类文件,那么抛出ClassNotFoundException;
  • 在反序列化时不会调用类的任何构造方法
如果希望控制类的序列化方式添加如下方法
private void writeObject(ObjectOutputStream out) throws IOException
private void readObject(ObjectInputStream in) throws IOException,ClassNotFoundException

可序列化类添加该方法后进行可序列化操作会执行该方法,否则按默认方式执行。在以上方法中可以先调用默认的defaultWriteObject()和read方法

序列化说明
  • 对于包含敏感信息的对象,可以先加密后再序列化,反序列化时需要解密
  • 默认序列化方式会序列化整个对象图,需要递归遍历对象图,如果对象图复杂,递归遍历需要消耗大量空间和时间,图内部数据结构为双向列表
  • 实际应用中,如果对某些成员变量改为transient类型,可以节省空间和时间,提高可序列化的性能

你可能感兴趣的:(Java输入输出流)