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<files.length;i++){
           System.out.println(files[i].getPath());
           //输出的是  C:\  D:\
       }    
       TestFile tf = new TestFile();
       //打印Silly目录下的所有子目录和文件
       File file=new File("d:\\Silly");
       tf.printFile(file,"");       
    }   
    /**
     * 打印指定目录下的所有子文件和子目录
     * @param file
     * @param str
     */
    public void printFile(File file,String str){
        System.out.println(str + file.getName());
        if(file.isDirectory()){
            File[] files=file.listFiles();
            for(int i=0;i<files.length;i++){
                //递归调用
                printFile(files[i], str+"\t");     
            }               
        }       
    }
}

字节流

字节输入流

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类型,可以节省空间和时间,提高可序列化的性能
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,753评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,668评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,090评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,010评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,054评论 6 395
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,806评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,484评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,380评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,873评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,021评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,158评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,838评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,499评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,044评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,159评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,449评论 3 374
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,136评论 2 356

推荐阅读更多精彩内容

  • Java输入输出流(一)# Android 是基于 Java 语言编写的,在安卓程序的很多时候会用到有关 I/O ...
    梁加盐阅读 1,412评论 0 4
  • 概述 java.io 包几乎包含了所有操作输入、输出需要的类。所有这些流类代表了输入源和输出目标。java.io ...
    Steven1997阅读 9,199评论 1 25
  • 语沫领着子寒走进了靠东面的大楼,大楼坐北朝南建设,分别在东南西北四向各设一拱门,进入拱门后是一条走道,走道两旁...
    凱鏾阅读 225评论 0 0
  • 【日精进打卡第49天】 【知~学习】 《六项精进》2遍 共100遍 《大学》2遍 共100遍 【经典名句分享】 常...
    陈威杰阅读 133评论 0 0
  • boss带我们去团建,出发前就制定好了满满的行程: 集合时间:8月7号早上9点公司集合行程安排:9点20—11点3...
    懒猫张小张阅读 390评论 0 3