【黑马程序员】Java输入/输出

 -------android培训java培训、期待与您交流! ----------

        Java输入输出,允许成都读取外部数据、用户输入的数据,允许程序记录运行状态,将程序数据输出到磁盘、光盘等存储设备中。

1. File类
        File类是java.io包下代表与平台无关的文件和目录。在程序中对文件和目录进行操作都是通过File类完成。File类可以新建、删除、重命名文件和目录,但File类不能修改文件本身。
        1)访问文件和目录
       File类可是使用文件路径字符串来穿件File类实例,路径可以是绝对路径也可以是相对路径。一旦创建了File对象后就可以调用File对象的方法来访问文件和目录。以下是File类使用的示例:
public class FileTest {
    public static void main(String[] args)
        throws IOException {
        // 以当前路径来创建一个File对象
        File file = new File("."); 
        // 直接获取文件名,输出一点
        System.out.println(file.getName());
        // 获取相对路径的父路径可能出错,下面代码输出null
        System.out.println(file.getParent());
        // 获取绝对路径
        System.out.println(file.getAbsoluteFile());
        // 获取上一级路径
        System.out.println(file.getAbsoluteFile().getParent());
        // 在当前路径下创建一个临时文件
        File tmpFile = File.createTempFile("aaa", ".txt", file);
        // 指定当JVM退出时删除该文件
        tmpFile.deleteOnExit();
        // 以系统当前时间作为新文件名来创建新文件
        File newFile = new File(System.currentTimeMillis() + "");
        System.out.println("newFile对象是否存在:" + newFile.exists());
        // 以指定newFile对象来创建一个文件
        newFile.createNewFile();
        // 以newFile对象来创建一个目录,因为newFile已经存在,
        // 所以下面方法返回false,即无法创建该目录
        newFile.mkdir();
        // 使用list()方法来列出当前路径下的所有文件和路径
        String[] fileList = file.list();
        System.out.println("====当前路径下所有文件和路径如下====");
        for (String fileName : fileList)
        {
            System.out.println(fileName);
        }
        // listRoots()静态方法列出所有的磁盘根路径。
        File[] roots = File.listRoots();
        System.out.println("====系统所有根路径如下====");
        for (File root : roots)
        {
            System.out.println(root);
        }
    }
}

        运行上面的程序,可以看见程序列出了当前路径的所有文件和路径是、列出了程序创建的临时文件,但程序结束时,aaa.txt文件并不存在,因此程序指定虚拟机退出时自动删除该文件。
        注意:windows的路径使用反斜线。而Java程序中的反斜线表示的是转移序列。所以在使用反斜线时,需要在加上一条反斜线才能正确的表示路径。
2. 文件过滤器
        在File类的list()方法中可以接受一个FilenameFilter参数,通过该参数可以只列出符合条件的文件。这里的FilenameFilter接口和java.swing.filehoose包下的FileFilter抽象类功能类似。使用方法如下:
        
public class FilenameFilterTest {
    public static void main(String[] args) {
        File file = new File(".");
        String[] nameList = file.list(new MyFilenameFilter());
        for(String name : nameList) {
            System.out.println(name);
        }
    }
}
// 实现自己的FilenameFilter实现类
class MyFilenameFilter implements FilenameFilter {
    public boolean accept(File dir, String name) {
        // 如果文件名以.java结尾,或者文件对应一个路径,返回true
        return name.endsWith(".java")
            || new File(name).isDirectory();
    }
}
3. Java的IO流
        Java的IO流体系中一共有40多了接口和类,我们按照其功能划分如下:

分类字节输入流字节输出流字符输入流字符输出流
抽象基类InputStreamOutputStreamReaderWriter
访问文件FileInputStreamFileOutputStreamFileReaderFileWriter
访问数组ByteArrayInputStreamByteArrayOutputStream
CharArrayReaderCharArrayWriter
访问管道PipedInputStreamPipedOutputStream
PipedReader
PipedWriter
访问字符串  StringReaderStringWriter
缓冲流BufferedInputStreamBufferedOutputStreamBufferedReaderBufferedWriter
转换流  InputStreamReaderOutputStreamWriter
对象流ObjectInputStreamObjectOutputStream  
抽象基类FilterInputStreamFilterOutputStreamFilterReaderFilterWriter
打印流 PrintStream PrintWriter
推回输入流PushbackInputStream PushbackReader 
特殊流DataInputStreamDataOutputStream  

















        注:表中粗体代表节点流,必须直接与指定的物理节点关联。斜体是抽象基类,无法直接操作流。

        Java的IO流是实现输入输出的基础,他可以方便地实现数据的输入输出操作,在Java中把不同的输入输出源抽线表述为”流“,通过流的方式允许Java程序使用相同的方式访问不同的输入输出流。

        1)流的分类
        输入流和输出流
  • 输入流:只能读取数据而不能写入
  • 输出流:只能写不能读
        ①字节流和字符流
        字节流和字符流的用法几乎完全一样,区别在于字节流和字符流操作的数据单元不同——字节流操作的数据单元是8位的字节,字符流操作的是16位的字符。
        字节流主要由InputStream和OutputStream作为基类;而字符流主要由Write和Reader作为基类。
        ②节点流和处理流
        可以从/向一个特定的IO设备读/写数据的流,称为节点流。节点流也被称为低级流。
        处理流则用于对一个已存在的流进行连接或封装,通过封装后的流来实现数据的读和写功能。处理流也被称为高级流。

        当使用处理流的一个好处是,只要使用相同的处理流,程序就可以采用完全相同的输入输出代码来访问不同的数据源,屏蔽了底层数据源处理的特异性。


        2)流的概念模型

        Java把所有的有序数据抽象成流模型,简化了输入输出处理。Java的IO流共涉及40多类。

4. 字节流和字符流
        1)InputStream和Reader
        InputerStream和Reader是所有输入流的抽象基类,本身并不能创建实例来来执行输入,但它们成为了所有输入流的模板:
        在InputStream中包含如下3个方法
  • int read():从输入流中读取单个字节,返回所读取的字节数据(字节数据可转换为int类型)
  • int read(byte[] b): 从输入流中读取最多b.length个字节的数据,并存放在字节数组b中,返回实际读取的字节数。
  • int read(bytep[] b, int off, int len):从输入流中最多读取len个字节的数据,并将其存储在数组b中,放入数组时,从off位置开始存放,返回实际读取的字节数。
        InputStream和Reader是抽象类,无法实例化。但他们各有一个用于读取文件的输入流:FileInputStream和FileReader,它们都是节点流——会直接与指定的文件关联。下面的程序演示了FileInputStream读取自身的效果。
public class FileInputStreamTest {
    public static void main(String[] args) throws IOException {
        // 创建字节输入流
        FileInputStream fis = new FileInputStream(
            "FileInputStreamTest.java");
        // 创建一个长度为1024的“竹筒”
        byte[] bbuf = new byte[1024];
        // 用于保存实际读取的字节数
        int hasRead = 0;
        // 使用循环来重复“取水”过程
        while ((hasRead = fis.read(bbuf)) > 0 ) {
            // 取出“竹筒”中水滴(字节),将字节数组转换成字符串输入!
            System.out.print(new String(bbuf , 0 , hasRead ));
        }
        // 关闭文件输入流,放在finally块里更安全
        fis.close();
    }
} 

        上面的程序运行会输出上面程序的源代码。
        
        下面使用FileReader读取文件:
public class FileReaderTest {
    public static void main(String[] args) {
        try(
            // 创建字符输入流
            FileReader fr = new FileReader("FileReaderTest.java")) {
            // 创建一个长度为32的“竹筒”
            char[] cbuf = new char[32];
            // 用于保存实际读取的字符数
            int hasRead = 0;
            // 使用循环来重复“取水”过程
            while ((hasRead = fr.read(cbuf)) > 0 ) {
                // 取出“竹筒”中水滴(字符),将字符数组转换成字符串输入!
                System.out.print(new String(cbuf , 0 , hasRead));
            }
        }
        catch (IOException ex) {
            ex.printStackTrace();
        }
    }
}

        上面的程序与FileInputStream没有太大的不同,只是将缓存数组设置为了32,这就需要多次读取文件。同时程序还在try中声明了输入流对象,这样在程序执行完毕后,输入流会自动关闭。

        2)OutputStream和Writer
        它们同样非常相似。两个流都提供了如下方法:
  • void write(int c): 将指定的字节/字符输出到输出流中。
  • void write(byte[]/char[] buf):将字节数组/字符数组的内容输出到输出流中。
  • void write(byte[] buf, int off, int len): 将字节数组/字符数组从off位置开始,长度为Len的数据输出到输出流中。
        下面程序使用FileInputStream来执行输入,并使用FileOutputStream来执行输出,实现复制的功能。
public class FileOutputStreamTest {
    public static void main(String[] args) {
        try(
            // 创建字节输入流
            FileInputStream fis = new FileInputStream(
                "FileOutputStreamTest.java");
            // 创建字节输出流
            FileOutputStream fos = new FileOutputStream("newFile.txt")) {
            byte[] bbuf = new byte[32];
            int hasRead = 0;
            // 循环从输入流中取出数据
            while ((hasRead = fis.read(bbuf)) > 0 ) {
                // 每读取一次,即写入文件输出流,读了多少,就写多少。
                fos.write(bbuf , 0 , hasRead);
            }
        }
        catch (IOException ioe) {
            ioe.printStackTrace();
        }
    }
}
5. 输入输出流体系
        1)处理流的用法
        处理流可以隐藏底层设备上节点流的差异。让程序员只需关心高级流的操作。
        我们使用处理流的典型思路是,使用处理流包装节点流,程序通过处理流来执行输入输出功能。让节点流与底层的IO设备、文件交互。
        下面使用PrintStream处理流来包装OutputStream,使用处理流后的输出流在输出时更加方便。
public class PrintStreamTest {
    public static void main(String[] args) {
        try(
            FileOutputStream fos = new FileOutputStream("test.txt");
            PrintStream ps = new PrintStream(fos)) {
            // 使用PrintStream执行输出
            ps.println("普通字符串");
            // 直接使用PrintStream输出对象
            ps.println(new PrintStreamTest());
        }
        catch (IOException ioe) {
            ioe.printStackTrace();
        }
    }
} 

        上面的程序使用PrintStream输出字符串,十分的简单易用,实际上,System.out同样是PrintStream输出流。


        2)转换流
        输出输入体系中还提供了两个转换流,用于将字节流转换为字符流,其中InputStreamReader将字节输入流转换为字符输入流,OutputStreamWriter将字节输出流转换为字符输出流。
        下面以获取键盘输入为例介绍转换流的用法。Java使用System.in代表标准输入,寄键盘输入,但这个便准输入是InputStream的示例,使用不太方便。我们可以将其转换为Reader字符输入流。但Reader读取数据太麻烦,所以使用BufferedReader包装Reader输入流,利用BufferedReader的ReadLine()方法可以一次读取一行的内容:
public class KeyinTest {
    public static void main(String[] args) {
        try(
            // 将Sytem.in对象转换成Reader对象
            InputStreamReader reader = new InputStreamReader(System.in);
            //将普通Reader包装成BufferedReader
            BufferedReader br = new BufferedReader(reader)) {
            String buffer = null;
            //采用循环方式来一行一行的读取
            while ((buffer = br.readLine()) != null) {
                //如果读取的字符串为"exit",程序退出
                if (buffer.equals("exit")) {
                    System.exit(1);
                }
                //打印读取的内容
                System.out.println("输入内容为:" + buffer);
            }
        }
        catch (IOException ioe) {
            ioe.printStackTrace();
        }
    }
}

        3)推回输入流
        在输入输出流体系中,有两个特殊的流与众不同,就是PushbachInputStream和PushbackReader,它们都有一下三个方法:
  • void unread(byte[] buf):将一个字节数组推回到推回缓冲区里,从而允许重复读取刚刚读取的内容。
  • void unread(byte[] b, int off, int len):将一个字节数组从off开始,长度为len的字节推回到推回缓冲区中。
  • void unread(int b):将一个字节推回到缓冲区中。
        这两个推回输入流都有一个推回缓冲区,当程序调用这两个推回输入流的Unread()方法的时候,系统会吧指定数组的内容推回到缓冲区,而推回输入流每次调用read()方法时总是先从缓冲区中读取,只有完全读取了缓冲区的内容后,但还没有转满read()所需的数组时才会从员输入流中继续读取数据,一下是程序示例:
public class PushbackTest {
    public static void main(String[] args) {
        try(
            // 创建一个PushbackReader对象,指定推回缓冲区的长度为64
            PushbackReader pr = new PushbackReader(new FileReader(
                "PushbackTest.java") , 64)) {
            char[] buf = new char[32];
            // 用以保存上次读取的字符串内容
            String lastContent = "";
            int hasRead = 0;
            // 循环读取文件内容
            while ((hasRead = pr.read(buf)) > 0) {
                // 将读取的内容转换成字符串
                String content = new String(buf , 0 , hasRead);
                int targetIndex = 0;
                // 将上次读取的字符串和本次读取的字符串拼起来,
                // 查看是否包含目标字符串, 如果包含目标字符串
                if ((targetIndex = (lastContent + content)
                    .indexOf("new PushbackReader")) > 0){
                    // 将本次内容和上次内容一起推回缓冲区
                    pr.unread((lastContent + content).toCharArray());
                    // 指定读取前面len个字符
                    int len = targetIndex > 32 ? 32 : targetIndex;
                    // 再次读取指定长度的内容(就是目标字符串之前的内容)
                    pr.read(buf , 0 , len);
                    // 打印读取的内容
                    System.out.print(new String(buf , 0 ,len));
                    System.exit(0);
                }
                else {
                    // 打印上次读取的内容
                    System.out.print(lastContent);
                    // 将本次内容设为上次读取的内容
                    lastContent = content;
                }
            }
        }
        catch (IOException ioe) {
            ioe.printStackTrace();
        }
    }
}

        上面的程序中实现了指定的内容推回到缓冲区,于是当程序再次调用read()方法的时候,实际只读取了推回缓冲区的部分,从而实现了只打印目标字符串前面内容的功能。


6. 对象序列化

      对象序列化是指将对象保存硬盘中,或允许在网络中直接传输对象。对象序列化将文件装换为二进制保存在磁盘上,通过网络将何种二进制传输到另一个网络节点。

为了让某个对象时刻序列化的,副对象的类必须实现如下两个接口之一:

Serializable

Externalizable


        1)使用Serializable接口序列化

        Serializable是一个标记接口,实现该接口无需实现任何方法,它只是表明该类的示例是可序列化的。

        一旦一个类实现了Serializable接口,该类的对象就是可序列化的,程序可以通过如下的两个步骤来序列化对象:

        ①创建一个ObjectOutputStream,这是一个处理流,所以必须建立在其他节点流之上:






        


        
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值