IO流的基本介绍和实例展示

1.文件基础知识

什么是文件?

文件,我们并不陌生。文件是保存数据的地方。比如大家经常使用的word文档,txt文件,excel文件等,都是文件。它既可以保存一张图片,也可以保存声音、视频……

流:数据在数据源(文件)和程序(内存)之间经历的路径

输入流:数据从数据源(文件)到程序(内存)之间的路径

输出流:数据从程序(内存)到数据源(文件)之间的路径

2.常用的文件操作

2.1创建文件

例子:应用案例演示:FileCreate

package li.io.file; import org.junit.jupiter.api.Test;import java.io.File;import java.io.IOException; //演示创建文件public class FileCreate {    public static void main(String[] args) {            }     //方式一 new File(String pathname)//根据路径构建一个File对象    @Test    public void creat01() {        String filePath = "d:news1.txt";        File file = new File(filePath);//在内存中创建一个File对象        try {            file.createNewFile();//创建文件            System.out.println("文件创建成功");        } catch (IOException e) {            e.printStackTrace();        }    }     //方式二 new File(File patent,String child)//根据父目录文件+子路径构建    //例如:d:news.txt,父目录文件就是 d:    @Test    public void creat02() {        File parenFile = new File("d:");        String fileName = "news2.txt";        //这里的file对象,在java程序中只是一个java对象而已        //只有执行了createNewFile方法,才会真正地在磁盘创建文件        File file = new File(parenFile, fileName);        try {            file.createNewFile();//创建文件            System.out.println("创建成功~");        } catch (IOException e) {            e.printStackTrace();        }     }     //方式三 new File(String parent,String child)//根据父目录+子路径构建    @Test    public void creat03() {        String parentPath = "d:";  //这里的两个 ,其中有一个是转义符号,或者直接用 d:/        String filePath = "news3.txt";        File file = new File(parentPath, filePath);        try {            file.createNewFile();            System.out.println("创建文件成功");        } catch (IOException e) {            e.printStackTrace();        }    }}

2.2获取文件的相关信息

例子:

package li.io.file; import org.junit.jupiter.api.Test; import java.io.File; public class FileInformation {    public static void main(String[] args) {     }     //获取文件的信息    @Test    public void info() {        //先创建文件对象        File file = new File("d:news1.txt");         //调用相应方法,得到对应信息         //文件名字-getName()        System.out.println("文件名字="+file.getName());//文件名字=news1.txt         //文件的绝对路径-getAbsolutePath()        System.out.println("文件的绝对路径="+file.getAbsolutePath());//文件的绝对路径=d:
ews1.txt         //文件的父级目录-getParentFile()        System.out.println("文件的父级目录="+file.getParentFile());//文件的父级目录=d:         //文件的大小(字节)-length()        System.out.println("文件的大小(字节)="+file.length());//文件的大小(字节)=12 Hello World!                //文件是否存在-exists()        System.out.println("文件是否存在="+file.exists());//文件是否存在=true                //是否为文件-isFile()        System.out.println("是否为文件="+file.isFile());//是否为文件=true         //是否为目录-isDirectory()        System.out.println("是否为目录="+file.isDirectory());//是否为目录=false     }}

2.3目录的操作和文件删除

例子:

package li.io.file; import org.junit.jupiter.api.Test; import java.io.File; public class Directory_ {    public static void main(String[] args) {     }     //判断 d:news1.txt是否存在,如果存在就删除    @Test    public void m1() {        String filePath = "d:news1.txt";        File file = new File(filePath);        if (file.exists()) {            if (file.delete()) {                System.out.println(filePath + "删除成功");            } else {                System.out.println("删除失败...");            }        } else {            System.out.println("该文件不存在...");        }    }     //判断 d:demo02 是否存在,存在就删除,否则就提示不存在    //这里我们需要体会到,在java中,目录也被当做文件    @Test    public void m2() {        String filePath = "d:demo02";        File file = new File(filePath);        if (file.exists()) {            if (file.delete()) {                System.out.println(filePath + "删除成功");//d:demo02删除成功(先在d盘下创建一个名为 demo02的目录)            } else {                System.out.println("删除失败...");            }        } else {            System.out.println("该目录不存在...");        }    }     //判断d:demoabc目录是否存在,如果存在就提示已经存在,否则创建    @Test    public void m3() {        String directoryPath = "d:demoabc";        File file = new File(directoryPath);        if (file.exists()) {            System.out.println("该目录已经存在");        } else {            if (file.mkdirs()) {//多级目录使用 mkdirs方法,一级目录使用 mkdir                System.out.println(directoryPath + "创建成功...");            } else {                System.out.println("创建失败...");            }        }    }}

3.IO流原理及流的分类

3.1Java IO流原理

  1. I/O是Input/Output的缩写,I/O技术是非常实用的技术,用于处理数据传输。如读/写文件,网络通讯等
  2. Java程序中,对于数据的输入/输出操作以”流(Stream)“的方式进行
  3. java.io包下提供了各种“流”类和接口,用以获取不同种类的数据,并通过方法输入或输出数据
  4. 输入input:读取外部数据(磁盘、光盘等存储设备的数据)到程序(内存)中
  5. 输出output:将程序(内存)数据输出到磁盘、光盘等存储设备中

3.2流的分类

抽象基类

字节流

字符流

输入流

InputStream

Reader

输出流

OutputStream

Writer

1)Java的IO流共涉及到40 多个类,实际上非常规则,都是从如上4个抽象基类派生的

InputStream、OutputStream、Reader、Writer都是抽象类,在使用的时候要去创建它们的实现子类

2)由这四个类派生出来的子类名称都是以其父类名作为子类名后缀

4.常用的类

InputStream抽象类是所有类字节输入流的超类

InputStream常用的子类:

  1. FileInputStream:文件输入流
  2. BufferedInputStream:缓冲字节输入流
  3. ObjectInputStream:对象字节输入流

4.1文件字节流输入流-FileInputStream

InputStream抽象类是所有类字节输入流的超类

InputStream常用的子类:

  1. FileInputStream:文件字节输入流
  2. BufferedInputStream:缓冲字节输入流
  3. ObjectInputStream:对象字节输入流

常用方法:

输入流的唯一目的是提供通往数据的通道,程序可以通过这个通道读取文件中的数据。

read方法提供了一个从输入流读取数据的基本方法,read方法的格式如下:

返回值

方法

说明

int

read( )

从输入流中读取数据的下一个字节

int

read(byte[ ] b)

从输入流中读取一定数量的字节,并将其存储在缓冲区数组 b 中。以整数形式返回读取的字节数。

int

read(byte[ ] b, int off, int len)

将输入流中最多 len 个数据字节读入 byte 数组。尝试读取 len 个字节,但读取的字节也可能小于该值。以整数形式返回实际读取的字节数

void

close( )

关闭流

注:read方法在从输入流中读取源中的数据时,如果到达源的末尾,便会返回-1。

FileInputStream流顺序地读取文件,只要不关闭流,每次调用read方法就顺序的读取源中其余的内容,直至源的末尾或流被关闭。

例子:

package li.io.inputstream_; import org.junit.jupiter.api.Test; import java.io.FileInputStream;import java.io.IOException; //演示FileInputStream的使用(字节输入流 文件-->程序)public class FileInputStream_ {    public static void main(String[] args) {     }     /**     * 演示读取文件     * read():单个字节的读取,效率较低     */    @Test    public void readFile01() {        String filePath = "d:hello.txt";        int readData = 0;        FileInputStream fileInputStream = null;        try {            //创建了FileInputStream对象,用于读取文件            fileInputStream = new FileInputStream(filePath);            //read()方法:从该输入流读取一个字节的数据。 如果没有输入可用,此方法将阻止。            //如果返回-1,则表示达到文件的末尾,表示读取完毕            while ((readData = fileInputStream.read()) != -1) {                System.out.print((char) readData);//转成char显示,因此如果文件里面有中文字符(每个中文字符占三个字节),显示的时候就会出现乱码            }        } catch (IOException e) {            e.printStackTrace();        } finally {            //关闭文件流,释放资源            try {                fileInputStream.close();            } catch (IOException e) {                e.printStackTrace();            }        }    }     /**     * 使用read(byte[] b)读取文件,提高效率     */    @Test    public void readFile02() {        String filePath = "d:hello.txt";        //字节数组        byte[] buf = new byte[8];//一次读取8个字节        int readLen = 0;        FileInputStream fileInputStream = null;        try {            //创建了FileInputStream对象,用于读取文件            fileInputStream = new FileInputStream(filePath);            //read(byte[] b)方法:从该输入流读取最多b.length字节的数据到字节数组。            //如果返回-1,则表示达到文件的末尾,表示读取完毕            //如果读取正常,返回实际读取的字节数            while ((readLen = fileInputStream.read(buf)) != -1) {                System.out.print(new String(buf, 0, readLen));//显示            }        } catch (IOException e) {            e.printStackTrace();        } finally {            //关闭文件流,释放资源            try {                fileInputStream.close();            } catch (IOException e) {                e.printStackTrace();            }        }    }}

4.2文件字节输出流-FileOutputStream

常用方法:

例子:FileOutputStream应用实例1

要求:请使用FileOutputStream在a.txt文件中写入“hello,world”。如果文件不存在,就先创建文件。

(注意:前提是目录已经存在)

package li.io.outputstream_; import org.junit.jupiter.api.Test; import java.io.FileOutputStream;import java.io.IOException; public class FileOutputStream_ {    public static void main(String[] args) {     }     /**     * 演示使用FileOutputStream将数据写到文件中,如果该文件不存在,则先创建文件     */    @Test    public void writeFile() {         String filePath = "d:a.txt";        //创建FileOutputStream对象        FileOutputStream fileOutputStream = null;         try {            //得到一个FileOutputStream对象                /*                如果是以new FileOutputStream(filePath)的方式创建对象,                则当写入内容时,会覆盖原来的内容                    如果是以new FileOutputStream(filePath,true)的方式创建对象,                则当写入内容时,是在旧内容的末尾追加新内容                */            fileOutputStream = new FileOutputStream(filePath,true);//以追加的形式去添加新内容            //写入一个字节            //fileOutputStream.write('H');            //写入字符串            String str = "Hello,Jack!";                        //String的getBytes方法可以将字符串转为字符数组            // fileOutputStream.write(str.getBytes());                        /*            write(byte[] b,int off,int len)            将len长度的字节从位于偏移量off的指定字节输入写入此文件输出流            */            fileOutputStream.write(str.getBytes(), 0, 4);         } catch (IOException e) {            e.printStackTrace();        } finally {            try {                fileOutputStream.close();            } catch (IOException e) {                e.printStackTrace();            }        }    }}

追加前:

追加后:

4.2.1FileInputStream&&FileOutputStream

应用实例2:文件拷贝

要求:完成文件拷贝,将d:Koala.png拷贝到d:Koala222.png

在完成程序时,为防止读取的文件过大,应该是每读取部分数据,就写入到指定文件,这里使用循环。

package li.io.outputstream_; import java.io.FileInputStream;import java.io.FileOutputStream;import java.io.IOException; public class FileCopy {    public static void main(String[] args) {        //完成文件拷贝,将c:Koala.png拷贝到d:        /*        思路分析:        1.创建文件的输入流,将文件读入到程序        2.创建文件的输出流,将读取到的文件数据写入指定的文件         */         String srcFilePath = "d:Koala.png";        String destFilePath = "d:Koala222.png";        FileInputStream fileInputStream = null;        FileOutputStream fileOutputStream = null;         try {            fileInputStream = new FileInputStream(srcFilePath);            fileOutputStream = new FileOutputStream(destFilePath, true);            //定义一个字节数组,提高效率            byte[] buf = new byte[1024];//1K            int readLen = 0;            while ((readLen = fileInputStream.read(buf)) != -1) {                //读取到后,就通过 fileOutputStream写入到文件                //即,是一边读一边写)                fileOutputStream.write(buf, 0, readLen);//一定要使用这个方法            }            System.out.println("拷贝成功~");        } catch (IOException e) {            e.printStackTrace();        } finally {            //关闭输入流和输出流,释放资源            try {                if (fileInputStream != null) {                    fileInputStream.close();                }                if (fileOutputStream != null) {                    fileOutputStream.close();                }            } catch (IOException e) {                e.printStackTrace();            }        }    }}

4.3文件字符流FileReader&FileWriter

FileReader和FileWriter是字符流,即按照字符来操作IO

4.3.1FileReader

FileReader相关方法:

构造方法:

public FileReader(String fileName) throws FileNotFoundException//在给定从中读取数据的文件名的情况下创建一个新 FileReader。//fileName - 要从中读取数据的文件的名称
public FileReader(File file) throws FileNotFoundException//在给定从中读取数据的 File 的情况下创建一个新 FileReader。//file - 要从中读取数据的 File

常用方法:

public int read() throws IOException//每次读取单个字符,返回该字符,如果到文件末尾返回-1    public int read(char[] ) throws IOException//批量读取多个字符到数组,返回读取到的字符数,如果到文件末尾就返回-1    public void close() throws IOException//关闭该流并释放与之关联的所有资源。

相关API:

  1. new String(char[]):将char[]转换成String
  2. new String(char[],off,len):将char[]的指定部分转换成String

例子:使用FileReader从story.txt读取内容,并显示

先在d盘根目录下下新建一个story.txt文件,放入内容

package li.io.reader_; import org.junit.jupiter.api.Test; import java.io.FileReader;import java.io.IOException; public class FileReader_ {    public static void main(String[] args) {     }     /**     * 1.使用public int read() throws IOException     * 每次读取单个字符,返回该字符,如果到文件末尾返回-1     */    @Test    public void readerFile01() {        String filePath = "d:story.txt";        FileReader fileReader = null;        int data = 0;         //1.创建一个FileReader对象        try {            fileReader = new FileReader(filePath);            //循环读取 使用reader():每次读取单个字符,返回该字符,如果到文件末尾返回-1            while ((data = fileReader.read()) != -1) {                System.out.print((char) data);//将int类型强转为char类型            }        } catch (IOException e) {            e.printStackTrace();        } finally {            try {                if (fileReader != null) {                    fileReader.close();                }            } catch (IOException e) {                e.printStackTrace();            }        }    }        /**     * 2.public int read(char[] ) throws IOException     * 批量读取多个字符到数组,返回读取到的字符数,如果到文件末尾就返回-1     */    @Test    public void readerFile02() {        String filePath = "d:story.txt";        FileReader fileReader = null;        int readLen = 0;        char[] buf = new char[8];//每读取8个字符输出一次         //1.创建一个FileReader对象        try {            fileReader = new FileReader(filePath);            //循环读取 使用reader(char[]):批量读取多个字符到数组,返回读取到的字符数,            // 如果到文件末尾就返回-1            while ((readLen = fileReader.read(buf)) != -1) {                System.out.print(new String(buf, 0, readLen));//将char[]的指定部分转换成String            }        } catch (IOException e) {            e.printStackTrace();        } finally {            try {                if (fileReader != null) {                    fileReader.close();                }            } catch (IOException e) {                e.printStackTrace();            }        }    }}

4.3.2FileWriter

FileWriter常用方法:

构造方法:

public FileWriter(String fileName) throws IOException//根据给定的文件名构造一个 FileWriter 对象。//fileName - 一个字符串,表示与系统有关的文件名。  
public FileWriter(String fileName,boolean append) throws IOException//根据给定的文件名以及指示是否附加写入数据的 boolean 值来构造 FileWriter 对象。//fileName - 一个字符串,表示与系统有关的文件名。//append - 一个 boolean 值,如果为 true,则将数据写入文件末尾处,而不是写入文件开始处。//(即覆盖和追加)
public FileWriter(File file) throws IOException//根据给定的 File 对象构造一个 FileWriter 对象。//file - 要写入数据的 File 对象。

常用方法:

public void write(int c)://写入单个字符 public void write(char[] cbuf)://写入字符数组 public abstract void write(char[] cbuf, int off,  int len)://写入字符数组的指定部分 public void write(String str)://写入字符串 public void write(String str,int off, int len)://写入字符串的指定部分 public void flush() throws IOException//刷新该流的缓冲。 public void close() throws IOException//关闭此流,但要先刷新它

相关API:String类:toCharArray:将String转换成char[]

注意FileWriter使用后,必须要关闭(close)或刷新(flush),否则写入不到指定的文件!

例子:

package li.io.writer_; import java.io.FileWriter;import java.io.IOException; public class FileWriter_ {    public static void main(String[] args) {        String filePath = "d:note.txt";        FileWriter fileWriter = null ;        char[] chars = {'a','b','c'};        //创建FileWriter对象        try {            //覆盖模式写入,覆盖是建立在执行close()之后            fileWriter = new FileWriter(filePath);             //public void write(int c):写入单个字符            fileWriter.write('H');             //public void write(char[] cbuf):写入字符数组            fileWriter.write(chars);             //public abstract void write(char[] cbuf, int off,  int len):写入字符数组的指定部分            fileWriter.write("小猫想睡觉".toCharArray(),0,2);             //public void write(String str):写入字符串            fileWriter.write("你好北京~");             //public void write(String str,int off, int len):写入字符串的指定部分            fileWriter.write("上海天津",0,2);        } catch (IOException e) {            e.printStackTrace();        }finally {            //对应FileWriter,一定要关闭流,或者flush才能真正地白数据写入到文件中            try {                fileWriter.close();            } catch (IOException e) {                e.printStackTrace();            }        }    }}

在数据量大的情况下,可以使用循环操作

思考:为什么关闭流(close)或者flush才能真正地把数据写入文件中?

IO缓冲流的flush()和close()方法说明及写入文件

fileWriter.close 和fileWriter.flush的区别:

实际上,当我们写好new FileWriter ,进行下一步的操作,将数据写入文本的时候,这时的数据并没有写入文本,而是存在了计算机中的流中。这也是JAVA能够在Windows 系统中调用文本流的作用。而如果在这里我们使用fileWriter.flush,是可以将存储在计算机流中的数据放入fileWriter对象的,但是如果我们之后再想加入数据的时候,也就是说我们将写入数据这句话放在fileWriter.flush之后的话,之后输入的数据就不会放入到note.txt中去。

再说一说fileWriter.close, 我们可以去查询close的定义,很明确的写了 先刷新一次,然后关闭数据流。也就是说close是包含了两步,flush 和close 这两步。flush 刷新后,流可以继续使

4.4节点流和处理流

4.4.1基本介绍

  1. 节点流可以从一个特定的数据源读写数据,如FileReader、FileWriter
  2. 数据源就是存放数据的地方,比如文件、数组、字符串、管道等

  1. 处理流(也叫包装流)是“连接”在已经存在的流(节点流或者处理流)之上,为程序提供更为强大的读写功能,也更加灵活,如BufferedReader、BufferedWriter

  1. 节点流是底层流(低级流),直接和数据源相连接。
  2. 处理流(包装流)对节点流进行了包装,既可以消除不同节点流的实现差异,也可以提供更方便的方法来完成输入输出。
  3. 处理流对节点流进行了包装,使用了修饰器的设计模式,不会直接与数据源相连接。

4.4.2模拟包装流-修饰器模式

Reader_:

package li.io; /** * 模拟包装流的修饰器模式 */public abstract class Reader_ {//抽象类    public void readFile() {}    public void readString() {}}

FileReader_:

package li.io; public class FileReader_ extends Reader_ {//模拟节点流     public void readFile() {        System.out.println("对文件进行读取...");    }}

StringReader_:

package li.io; public class StringReader_ extends Reader_ {//模拟节点流     public void readString() {        System.out.println("读取字符串...");    }}

BufferedReader_:

package li.io; /** * 模拟处理流(包装流) */public class BufferedReader_ extends Reader_ {    private Reader_ reader_;//属性是Reader_类型     public BufferedReader_(Reader_ reader_) {        this.reader_ = reader_;    }     public void readFile(){//封装一层        reader_.readFile();    }     //让方法更加灵活,比如多次读取文件,或者加缓冲byte[]...    public void readFile(int num) {        for (int i = 0; i < num; i++) {            reader_.readFile();        }    }     //又如扩展readString方法,例如批量处理字符串数据...    public void readString(int num) {        for (int i = 0; i < num; i++) {            reader_.readString();        }    }}

Test_:

package li.io; public class Test_ {    public static void main(String[] args) {         BufferedReader_ bufferedReader_ = new BufferedReader_(new FileReader_());        bufferedReader_.readFile(3);        bufferedReader_.readFile();         BufferedReader_ bufferedReader02_ = new BufferedReader_(new StringReader_());        bufferedReader02_.readString(2);    }}

4.4.3字符处理流-BufferedReader&BufferedWriter

  1. BufferedReader构造方法:

BufferedReader常用方法:

public int read() throws IOException//读取单个字符。//作为一个整数(其范围从 0 到 65535 (0x00-0xffff))读入的字符,如果已到达流末尾,则返回 -1    public int read(char[] cbuf) throws IOException//一次读取一个字节数组//cbuf - 目标缓冲区//读取的字符数,如果已到达流的末尾,则返回 -1     public void close() throws IOException//关闭该流并释放与之关联的所有资源。        public String readLine() throws IOException//读取一个文本行。通过下列字符之一即可认为某行已终止:换行 (’
’)、回车 (’r’) 或回车后直接跟着换行。//包含该行内容的字符串,不包含任何行终止符,如果已到达流末尾,则返回 nu    

  1. BufferedWriter构造方法:

BufferedWriter常用方法:

void write(char ch);//写入单个字符。 void write(char []cbuf,int off,int len)//写入字符数据的某一部分。 void write(String s,int off,int len)//写入字符串的某一部分。 void newLine()//写入一个行分隔符。 void flush();//刷新该流中的缓冲。将缓冲数据写到目的文件中去。 void close();//关闭此流,在关闭前会先刷新

应用案例1:使用BufferedReader读取文本文件,并显示在控制台

package li.io.reader_; import java.io.BufferedReader;import java.io.FileReader;  //演示BufferedReader的使用public class BufferedReader_ {    public static void main(String[] args) throws Exception {        String filePath = "d:note.txt";        //创建        BufferedReader bufferedReader = new BufferedReader(new FileReader(filePath));         //读取        String line;        //readLine():按行读取文件,当返回null时,表示文件读取完毕        while((line = bufferedReader.readLine())!=null){            System.out.println(line);        }         //关闭流:这里只需要关闭BufferedReader,底层会去自动地关闭节点流        bufferedReader.close();    }}

思考:为什么只需要关闭外层流?

在代码bufferedReader.close();处打上断点,点击force step into,可以看到下图所示:

在底层调用的其实是:传进去的节点流对象的close()方法

这里的in就是我们传入的new FileReader(filePath)

应用案例2:使用BufferedWriter将“一只可爱的小猫咪”写入到文件中

package li.io.writer_; import java.io.BufferedWriter;import java.io.FileWriter;import java.io.IOException; //演示BufferedWriter的使用public class BufferedWriter_ {    public static void main(String[] args) throws IOException {        String filePath = "d:ok.txt";         //创建BufferedWriter对象        BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(filePath));         //写入        bufferedWriter.write("一只可爱的小猫咪");        bufferedWriter.newLine();//插入一个和系统相关的换行        bufferedWriter.write("两只可爱的小猫咪");        bufferedWriter.newLine();        bufferedWriter.write("三只可爱的小猫咪");        bufferedWriter.newLine();         //关闭外层流即可,底层自动关闭穿入的内层流        bufferedWriter.close();    }}

案例3:综合使用 BufferedReader & BufferedWriter 完成文件文本拷贝,注意文件编码。

package li.io.writer_; import java.io.BufferedReader;import java.io.BufferedWriter;import java.io.FileReader;import java.io.FileWriter; public class BufferedCopy_ {    public static void main(String[] args) throws Exception {        //注意:BufferedReader 和 BufferedWriter都是字符流操作        // 不要去操作二进制文件[声音,视频,doc,pdf等],可能造成文件损坏         String srcFilePath = "d:note.txt";//源文件        String destFilePath = "d:note2.txt";        String line = null;         //创建BufferedReader&BufferedWriter对象        BufferedReader bufferedReader = new BufferedReader(new FileReader(srcFilePath));        BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(destFilePath));         //读入和写出        while ((line = bufferedReader.readLine()) != null) {//每读取一行内容就写入            bufferedWriter.write(line);            bufferedWriter.newLine();//换行        }         System.out.println("拷贝完毕~");         //关闭两个外层流        bufferedReader.close();        bufferedWriter.close();    }}

4.4.4字节处理流-BufferedInputStream&BufferedOutputStream

  1. BufferedInputStream
  2. BufferedInputStream是字节流,在创建BufferedInputStream时会创建一个内部缓冲区数组。

常用方法:

int available()//用于返回输入流中可用的未读字节数,而不会由于下一次为此InputStream的方法的调用而阻塞。 void close()//关闭此输入流并释放与该流关联的所有系统资源。 void mark(int readlimit)//输入流的当前位置做个标记,readlimit参数是输入流在标记位置失效前允许读取的字节数。 boolean markSupported()//测试输入流是否支持mark和reset方法。 int read()//读取一个字节。 int read(byte[] b, int off, int len)//读取多个字节到字节数组b中,参数off是数组偏移量,参数len是读取数据的长度。 void reset()//重置流的当前位置到前面标记的位置。 long skip(long n)//略过流中的数据。若数据不够时,跳过仅有的字节,返回跳过的字节数。

  1. BufferedOutputStream
  2. BufferedOutputStream是字节流,实现缓冲的输出流,可以将多个字节写入底层的输出流中,而不必对每次字节写入调用底层系统

应用案例:

要求:编程完成图片/音乐的拷贝(要求使用 BufferedInputStream 和 BufferedOutputStream 流)

package li.io.outputstream_; import java.io.*; /** * 演示使用 BufferedInputStream 和 BufferedOutputStream * 使用它们可以拷贝二进制文件 * 同时也可以拷贝文本文件 */ public class BufferedCopy02 {    public static void main(String[] args) {        String srcFilePath = "d:兰亭序 - 周杰伦.mp3";        String destFilePath = "d:ltx-zhou.mp3";         //创建BufferedInputStream 和 BufferedOutputStream对象        BufferedInputStream bis = null;        BufferedOutputStream bos = null;         try {            bis = new BufferedInputStream(new FileInputStream(srcFilePath));            bos = new BufferedOutputStream(new FileOutputStream(destFilePath));            //循环写入并读取            byte[] buf = new byte[1024];            int readLen = 0;            //read(byte[]):当返回-1时就表示文件读取完毕            while ((readLen = bis.read(buf)) != -1) {                bos.write(buf, 0, readLen);            }        } catch (IOException e) {            e.printStackTrace();        } finally {            //关闭外层流            try {                if (bos != null) {                    bos.close();                }                if (bis != null) {                    bis.close();                }            } catch (IOException e) {                e.printStackTrace();            }        }    }}

4.4节点流和处理流02

4.4.5对象处理流-ObjectInputStream和ObjectOutputStream

1.序列化和反序列化

例子1:

看一个需求

  1. int num= 100这个int 类型的数据保存到文件中,注意不是100 数字,而是int 100,并且能够从文件中直接恢复int 100
  2. Dog dog = new Dog("小黄",3)这个Dog对象保存到文件中,并且能够从文件恢复。

上面的要求,就是能够将 基本数据类型 或者 对象 进行 序列化反序列化操作

2. 对象处理流基本介绍与应用

  1. 功能:提供了对基本类型或对象类型的序列化和反序列化的方法
  2. ObjectOutputStream提供序列化功能
  3. ObjectInputStream 提供反序列化功能

例子1:序列化应用

使用ObjectOutputStream序列化 基本数据类型 和一个 Dog对象(name,age),并保存到data.dat文件中。

package li.io.outputstream_; import java.io.FileOutputStream;import java.io.ObjectOutputStream;import java.io.Serializable; //演示ObjectOutputStream的使用,完成数据的序列化public class ObjectOutputStream_ {    public static void main(String[] args) throws Exception {        //序列化后,保存的文件格式不是纯文本格式,而是按照它自己的格式来保存        String filePath = "d:data.dat";         ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(filePath));         //存储        //序列化数据到文件d:data.dat        oos.writeInt(100);//100在底层实现了自动装箱,int ->Integer (Integer实现了Serializable接口)        oos.writeBoolean(true);//boolean ->Boolean (Boolean实现了Serializable接口)        oos.writeChar('a');// char ->Char (实现了Serializable接口)        oos.writeDouble(9.5);//double ->Double (实现了Serializable接口)        oos.writeUTF("你好火星");//String(实现了Serializable接口)        //保存一个Dog对象        oos.writeObject(new Dog("旺财", 10));         //关闭外层流        oos.close();        System.out.println("数据保存完毕(序列化形式)");     }} //如果需要序列化某个对象,必须实现接口Serializable或者 Externalizable接口class Dog implements Serializable {    private String name;    private int age;     public Dog(String name, int age) {        this.name = name;        this.age = age;    }     @Override    public String toString() {        return "Dog{" +                "name='" + name + ''' +                ", age=" + age +                '}';    }     public String getName() {        return name;    }}

例子2:反序列化应用

使用ObjectInputStream读取data.dat并且反序列化恢复数据

Dog:

package li.io.outputstream_; import java.io.Serializable; //如果需要序列化某个对象,必须实现接口Serializable或者 Externalizable接口public class Dog implements Serializable {    private String name;    private int age;     public Dog(String name, int age) {        this.name = name;        this.age = age;    }     @Override    public String toString() {        return "Dog{" +                "name='" + name + ''' +                ", age=" + age +                '}';    }     public String getName() {        return name;    }}

ObjectInputStream_:

package li.io.inputstream_; import java.io.FileInputStream;import java.io.IOException;import java.io.ObjectInputStream; import li.io.outputstream_.Dog; public class ObjectInputStream_ {    public static void main(String[] args) throws IOException, ClassNotFoundException {         //指定反序列化的文件        String filePath = "d:data.dat";        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filePath));         //读取        //1.读取(反序列化)的顺序要和你保存数据(序列化)的顺序一致,否则会出现异常        System.out.println(ois.readInt());        System.out.println(ois.readBoolean());        System.out.println(ois.readChar());        System.out.println(ois.readDouble());        System.out.println(ois.readUTF());         //dog的编译类型是Object ,dou的运行类型是 Dog        Object dog = ois.readObject();        System.out.println("运行类型=" + dog.getClass());//Dog        System.out.println("dog信息=" + dog);//底层 Object->Dog         //一些重要细节:        //1. 如果我们希望调用Dog的方法,需要向下转型        //2. 需要我们将Dog类的定义拷贝到可以引用的位置然后导入类(将Dog类创建为公共类,重新序列化,再反序列化)        Dog dog1 = (Dog) dog;        System.out.println(dog1.getName());         //关闭外层流        ois.close();    }}

3.对象处理流使用细节

  1. 读写顺序要一致
  2. 要求实现序列化或者反序列化的对象的类,需要实现接口Serializable或者Externalizable
  3. 序列化的类中建议添加SerialVersionUID,是为了提高版本的兼容性

在完成序列化操作后,如果对序列化对象进行了修改,比如增加某个字段,那么我们再进行反序列化就会抛出InvalidClassException异常,这种情况叫不兼容问题。

解决的方法是:在对象中手动添加一个 serialVersionUID 字段,用来声明一个序列化版本号,之后再怎么添加属性也能进行反序列化,凡是实现Serializable接口的类都应该有一个表示序列化版本标识符的静态变量。

  1. 序列化对象时,默认将里面的所有属性都进行序列化,但除了statictransient修饰的成员
  2. 序列化对象时,要求里面属性的类型也需要实现序列化接口

例如:在(实现了Serializable接口的)Dog类中创建一个Master类型的属性master,而Master类没有实现Serializable接口,所以在序列化时,Dog类中的master属性无法序列化,也不能反序列化

  1. 序列化具有可继承性,也就是说如果某类已经实现序列化,则它的所有子类也已经默认实现了序列化

4.4.6标准输入输出流

不懂这些,你敢说自己知道Java标准输入输出流? - 简书 (jianshu.com)

介绍:


类型

默认设备

System.in 标准输入

InputStream

键盘

System.out标准输出

PrintStream

显示器

例子1:

package li.io.standard; public class InputAndOutput {    public static void main(String[] args) {        // System 类 的  public final static InputStream in = null;        // System.in  编译类型  InputStream        // System.in  运行类型  BufferedInputStream        // 表示的是标准输入--用来读取键盘录入的数据         System.out.println(System.in.getClass());//class java.io.BufferedInputStream-字节处理输入流         // 1. System.out  public final static PrintStream out = null;        // 2.编译类型  PrintStream        // 3.运行类型  PrintStream        // 4.表示标准输出显示器--将数据输出到命令行        System.out.println(System.out.getClass());//class java.io.PrintStream - 字节输出流    }}

应用案例1:

传统方法: System.out.println(" ");是使用out对象将数据输出到显示器

应用案例2:

传统方法,Scanner是从标准输入 键盘接收数据

        // 给Scanner扫描器传入的就是BufferedInputStream,        // 表示的是标准输入--用来读取键盘录入的数据        // 因此 scanner会到键盘去 获取输入的数据        Scanner scanner = new Scanner(System.in);        System.out.println("请输入内容:");        //会去键盘去得到输入流        String next = scanner.next();        System.out.println(next);        scanner.close();

4.4.7转换流-InputStreamReader和OutputStreamWriter

1.文件乱码问题

先来看一个例子:

在d:盘的根目录下有一个a.txt文件,内容如下:

现在想把该文件读取到程序中:

package li.io.transformation; import java.io.BufferedReader;import java.io.FileReader;import java.io.IOException; //一个中文乱码问题public class CodeQuestion {    public static void main(String[] args) throws IOException {        //读取a.txt文件到程序中         //思路:        // 1.创建一个字符输入流 这里用 BufferedRead[处理流]        // 2.使用 BufferedRead 对象读取 a.txt        // 3.在默认情况下,读取文件是按照 UTF-8 编码        String filePath = "d:a.txt";        BufferedReader br = new BufferedReader(new FileReader(filePath));        String s = br.readLine();        System.out.println("读取到的内容=" + s);        br.close();    }}

在a.txt文件编码为uft-8的时候,输出:

在a.txt文件编码为ANSI的时候,输出:

出现乱码的根本问题是:没有指定文件的编码方式。

2.基本介绍与应用

转换流也是一种处理流,它提供了字节流和字符流之间的转换。

在Java IO流中提供了两个转换流:InputStreamReader 和 OutputStreamWriter,这两个类都属于字符流。


转换流的原理是:字符流 = 字节流 + 编码表

我们需要明确的是字符编码和字符集是两个不同层面的概念。

编码是依赖于字符集的,一个字符集可以有多个编码实现,就像代码中的接口实现依赖于接口一样。

在转换流中选择正确的编码非常的重要,因为指定了编码,它所对应的字符集自然就指定了,否则很容易出现乱码,所以编码才是我们最终要关心的。

转换流的特点:其是字符流和字节流之间的桥梁。

可对读取到的字节数据经过指定编码转换成字符

可对读取到的字符数据经过指定编码转换成字节

那么何时使用转换流?

当字节和字符之间有转换动作时

流操作的数据需要编码或解码时

Java IO流详解(六)----转换流(字节流和字符流之间的转换) - 唐浩荣 - 博客园 (cnblogs.com)


应用案例1:

编程将字节流FileInputStream转换成(包装成)字符流InputStreamReader,对文件进行读取(按照UTFF-8格式),进而再包装成 BufferedReader

package li.io.transformation; import java.io.BufferedReader;import java.io.FileInputStream; import java.io.InputStreamReader;import java.io.IOException; //演示使用 InputStreamReader 转换流解决中文乱码问题//将字节流转 FileInputStream 换成字符流 InputStreamReader,并指定编码 gbk/utf-8public class InputStreamReader_ {    public static void main(String[] args) throws IOException {         String filePath = "d:a.txt";        // 1.将 FileInputStream流(字节流),转成了 InputStreamReader流(字符流)        // 2.指定了gbk编码        InputStreamReader isr = new InputStreamReader(new FileInputStream(filePath), "UTF-8");        // 3. 把 InputStreamReader 传入 BufferedReader        BufferedReader br = new BufferedReader(isr);        // 4. 读取        String s = br.readLine();        System.out.println("读取内容=" + s);         // 5.关闭外层流        br.close();     }}

应用案例2:

编程将字节流FileOutputStream 包装(转换)成字符流OutputStreamWriter,对文件进行写入(按照GBK格式,可以指定其他,比如UTF-8)

package li.io.transformation; import java.io.FileOutputStream;import java.io.OutputStreamWriter;import java.io.IOException; /** * 演示使用 OutputStreamWriter * 将字节流转 FileOutputStream 换成字符流 OutputStreamWriter * 指定处理的编码 gbk/utf-8/utf8 */public class OutputStreamWriter_ {    public static void main(String[] args) throws IOException {        String filePath = "d:a.txt";        String charSet = "GBK";         //创建对象        OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(filePath), charSet);         //写入        osw.write("我只是一只小猫咪,我不懂你在说什么");         //关闭外层流        osw.close();         System.out.println("按照 " + charSet + " 保存文件成功~");    }}JAVA 复制 全屏

4.4节点流和处理流03

4.4.8打印流-PrintStream和PrintWriter

打印流只有输出流,没有输入流

1.简单介绍及应用

例子1:演示PrintStream(字节打印流/输出流)

package li.io.printstream; import java.io.IOException;import java.io.PrintStream; /** * 演示PrintStream(字节打印流/输出流) */public class PrintStream_ {    public static void main(String[] args) throws IOException {        PrintStream out = System.out;        //在默认情况下,PrintStream 输出数据的位置是 标准输出,即显示器        out.print("jack,hello");        /**         *   public void print(String s) {         *         if (s == null) {         *             s = "null";         *         }         *         write(s);         *     }         */        //因为print底层使用的是write,所以我们可以直接调用write进行打印/输出        out.write("Hello北京".getBytes());        out.close();         //我们可以去修改打印流输出的位置/设备        // 1.修改为打印到d:f1.txt        // 2."落霞与孤鹜齐飞,秋水共长天一色" 这句话就会打印到d:f1.txt里        // 3.System.setOut底层:        /**         *     public static void setOut(PrintStream out) {         *         checkIO();         *         setOut0(out);//native方法 ,修改了 out         *     }         */        System.setOut(new PrintStream("d:f1.txt"));        System.out.println("落霞与孤鹜齐飞,秋水共长天一色");//打印到d:f1.txt     }}

运行结果:

如上所示:在修改了打印流 输出的位置/设备之后,再调用System.out.println方法,打印/输出的地方就变为指定的文件路径,点击System.setOut方法,可以看到底层是调用了setOut0方法,该方法是本地方法(native)。它会去修改out,即修改输出数据的位置:


例子2:

package li.io.printstream; import java.io.FileWriter;import java.io.IOException;import java.io.PrintWriter; /** * 演示PrintWriter的使用方式 */public class PrintWriter_ {    public static void main(String[] args) throws IOException {         //PrintWriter printWriter = new PrintWriter(System.out);        //向PrintWriter构造器中传入一个FileWriter对象        PrintWriter printWriter = new PrintWriter(new FileWriter("d:f2.txt"));        printWriter.print("hi,北京你好~");        printWriter.close();//flush()+关闭流,才会将数据写入到文件中     }}

4.5Properties类

如下一个配置文件 mysql.properties:

ip=192.168.0.13user=rootpwd=12345

问编程读取ip、user、pwd的值是多少要怎么做?

分析:

  1. 传统的方法
  2. 使用Properties类可以方便实现

例子:传统方法

在scr文件夹下创建一个mysql.properties文件,内容为

package li.io.properties_; import java.io.BufferedReader;import java.io.FileReader;import java.io.IOException; public class Properties01 {    public static void main(String[] args) throws IOException {         //读取mysql.properties文件,并得到ip,user,pwd        //创建        BufferedReader br = new BufferedReader(new FileReader("srcmysql.properties"));        String line = "";        //读取        while ((line = br.readLine()) != null) {//循环读取            String[] split = line.split("=");            System.out.println(split[0] + "值是: " + split[1]);        }        //关闭        br.close();    }}

如上所示,如果mysql.properties的参数很多,并且要求读取修改其中一项或者n项参数,那么使用传统的读取方法,就需要我们对读取的参数进行条件判断,一旦要读取的参数过多,代码就会变得非常繁琐。这时候就需要使用到Properties类。

4.5.1基本介绍

Properties是Hashtable的子类,是专门用于读写配置文件的集合类:

1)配置文件的格式:

键=值键=值

2)注意:键值对之间不需要有空格,值不需要用引号括起来。默认的类型是String

3)Properties的常见方法:

应用案例1:使用Properties类完成对mysql.properties 的读取

package li.io.properties_; import java.io.IOException;import java.io.FileReader;import java.util.Properties; public class Properties02 {    public static void main(String[] args) throws IOException {        //使用Properties类来读取 mysql.properties 文件         // 1.创建Properties对象        Properties properties = new Properties();         // 2.加载指定的配置文件        properties.load(new FileReader("srcmysql.properties"));         // 3.将 k-v 显示到控制台        properties.list(System.out);         // 4.根据key获取对应的值        String user = properties.getProperty("user");        String pwd = properties.getProperty("pwd");        System.out.println("用户名=" + user);        System.out.println("密码=" + pwd);    }}

应用案例2:使用Properties类添加 key-value 到新文件 mysql2.properties 中,并修改某个 key-value

package li.io.properties_; import java.io.FileOutputStream;import java.io.IOException;import java.util.Properties; public class Properties03 {    public static void main(String[] args) throws IOException {        //使用Properties类添加 key-value 到新文件 mysql2.properties 中         //创建对象        Properties properties = new Properties();        //创建        //如果该文件没有key,就是创建        //如果该文件有key,就是修改/替换        /**         * Properties类的父类就是 Hashtable ,底层就是 Hashtable的核心方法         * public synchronized V put(K key, V value) {         *         // Make sure the value is not null         *         if (value == null) {         *             throw new NullPointerException();         *         }         *         *         // Makes sure the key is not already in the hashtable.         *         Entry<?,?> tab[] = table;         *         int hash = key.hashCode();         *         int index = (hash & 0x7FFFFFFF) % tab.length;         *         @SuppressWarnings("unchecked")         *         Entry entry = (Entry)tab[index];         *         for(; entry != null ; entry = entry.next) {         *             if ((entry.hash == hash) && entry.key.equals(key)) {         *                 V old = entry.value;//如果key存在,就替换         *                 entry.value = value;         *                 return old;         *             }         *         }         *         *         addEntry(hash, key, value, index);//如果是新的key,就添加新 key         *         return null;         *     }         */        properties.setProperty("charset", "utf-8");        properties.setProperty("user", "汤姆");//注意:保存的是中文的Unicode码值        properties.setProperty("pwd", "abcd123");        properties.setProperty("pwd", "1111");//替换        //将k-v存储到文件中即可        properties.store(new FileOutputStream("srcmysql2.properties"), null);//第二个参数是添加注释        System.out.println("保存配置文件成功~");     }}

查询发现 汤姆 对应的中文就是 汤姆

5.IO习题

5.1Homework01

(1)判断d盘下是否有文件夹mytemp,如果没有就创建mytemp

(2)在d:mytemp目录下,创建文件hello.txt

(3)如果hello.txt已经存在,就提示该文件已经存在,就不要重复创建了

(4)并在hello.txt文件中写入内容"hello,world".

package li.io.homework; import java.io.File;import java.io.FileOutputStream;import java.io.IOException; public class HomeWork01 {    public static void main(String[] args) throws IOException {         String filePath = "d:mytemphello.txt";        String dictionaryPath = "d:mytemp";         //创建对象        File file = new File(dictionaryPath);//目录        File file2 = new File(filePath);//文件        FileOutputStream fileOutputStream = null;         if (!file.exists()) {//如果目录不存在            if (file.mkdir()) {//创建目录                System.out.println("创建目录mytemp成功~");            }        } else {            System.out.println("目录mytemp已存在");        }         if (file2.exists()) {            System.out.println("hello.txt文件已存在");            //如果文件存在,就写入数据            fileOutputStream = new FileOutputStream(filePath);            fileOutputStream.write("hello,world".getBytes());            System.out.println("已写入数据~");        } else {            if (file2.createNewFile()) {                System.out.println("创建hello.txt文件成功~");                //如果文件存在,就写入数据                fileOutputStream = new FileOutputStream(filePath);                fileOutputStream.write("hello,world".getBytes());                System.out.println("已写入数据~");            }        }         //关闭流        fileOutputStream.close();    }}

5.2Homework02

编程题:要求:使用BufferedReader读取一个文本文件,为每行加上行号,再连同内容一并输出到屏幕上。

package li.io.homework; import java.io.BufferedReader;import java.io.FileReader;import java.io.IOException; public class HomeWork02 {    public static void main(String[] args) throws IOException {                String filePath = "d:story.txt";        String line = "";        int i = 0;//行号        //创建对象        BufferedReader br = new BufferedReader(new FileReader(filePath));        //读取        while ((line = br.readLine()) != null) {            System.out.println((++i) + "	" + line);        }        //关闭流        br.close();    }}

要求2:如果将文本的编码改为GBK,怎么将其输出到控制台上而不使其乱码?

使用转换流,FileInputStream-->InputStreamReader(指定编码)-->BufferedReader

package li.io.homework; import java.io.*; public class HomeWork02 {    public static void main(String[] args) throws IOException {         String filePath = "d:story.txt";        String line = "";        int i = 0;//行号        //创建对象        BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(filePath),"GBK"));//使用转换流,选择编码为“GBK”        //读取        while ((line = br.readLine()) != null) {//循环读取            System.out.println((++i) + "	" + line);        }        //关闭流        br.close();    }}

5.3Homework03

编程题:

  1. 要编写一个dog.properties
  2. name=tomage=5color=red
  3. 编写Dog类(name,age,color)创建一个dog对象,读取dog.properties 用相应的内容完成初始化,并输出
  4. 将创建的Dog对象,序列化到 文件 dog.dat文件
  5. 再反序列化dog对象
package li.io.homework; import java.io.*;import java.util.Properties; public class HomeWork03 {    public static void main(String[] args) throws IOException, ClassNotFoundException {         //1.编写dog.properties        //创建Properties对象        Properties properties = new Properties();        //在properties对象中添加k-v        properties.setProperty("name", "tom");        properties.setProperty("age", "5");        properties.setProperty("color", "red");        //将properties对象的k-v存储到文件中        properties.store(new FileOutputStream("srcdog.properties"), null);         //2.读取dog.properties完成Dog对象 的初始化        int age = Integer.parseInt(properties.getProperty("age"));        Dog dog = new Dog(properties.getProperty("name"), age, properties.getProperty("color"));        System.out.println(dog);         //3.将创建的Dog对象,序列化到 dog.dat文件        String filePath = "d:dog.dat";        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(filePath));        oos.writeObject(dog);        //关闭流        oos.close();        System.out.println("Dog序列化完成");         //4.反序列化dog对象        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filePath));        Dog dog1 = (Dog) ois.readObject();        System.out.println(dog1);        //关闭流        ois.close();        System.out.println("反序列化完成");    }} //序列化的类要实现Serializable接口class Dog implements Serializable {    private String name;    private int age;    private String red;     public Dog(String name, int age, String red) {        this.name = name;        this.age = age;        this.red = red;    }     @Override    public String toString() {        return "Dog{" +                "name='" + name + ''' +                ", age=" + age +                ", red='" + red + ''' +                '}';    }}JAVA 折叠 复制 全屏

展开阅读全文

页面更新:2024-03-07

标签:末尾   数组   字节   字符   实例   接口   对象   文件   目录   方法   数据

1 2 3 4 5

上滑加载更多 ↓
推荐阅读:
友情链接:
更多:

本站资料均由网友自行发布提供,仅用于学习交流。如有版权问题,请与我联系,QQ:4156828  

© CopyRight 2008-2024 All Rights Reserved. Powered By bs178.com 闽ICP备11008920号-3
闽公网安备35020302034844号

Top