javaFile类与I/O流

14

1.File类的使用

/**
 * @ClassName: java高级进阶
 * @Description:File类的使用
 *
 * 1.File类的一一个对象,代表-一个文件或一一个文件 目录(俗称:文件夹)
 * 2.File类声明在java. io包下
 * 3. File类中涉及 到关于文件或文件目录的创建、删除、重命名、修改时间、文件大小等方法,
 *    并未涉及到写入或读取文件内容的操作。如果需要读取或写入文件内容,必须使用IO流来完成。
 * 4.后续Filej类的对象常会作为参数传递到流的构造器中,指明读取或写入的"终点".
 **/
public class FileTest {
    /*
       如何创建File类的实例
       public File(String pathname)
            以pathname为路径创建File对象,可以是绝对路径或者相对路径,如果
             pathname是相对路径,则默认的当前路径在系统属性user.dir中存储。
                 ➢绝对路径:是一个固定的路径,从盘符开始
                 ➢相对路径:是相对于某个位置开始
       public File(String parent,String child)
            以parent为父路径,child为 子路径创建File对象。
       public File(File parent,String child)
            根据一个父File对象和子文件路径创建File对象

       2.路径分隔符
            windows: \ \
            unix:/
     */
    @Test
    public void test(){
        //内存层面的对象
        //构造器1
        File file = new File("hello.txt");//相对于当前module 相对路径
        File file2 = new File("G:\\OneDrive\\OneDrive - sdtbu.edu.cn\\java知识准备\\java编程源码\\java高级进阶\\src\\com\\test\\IO\\he.txt");//绝对路径
        System.out.println(file);
        System.out.println(file2);

        //构造器二
        File file3 = new File("G:\\OneDrive\\OneDrive - sdtbu.edu.cn\\java知识准备\\java编程源码\\java高级进阶\\src\\com\\test\\IO","he");//文件目录
        System.out.println(file3);
        //构造器三
        File file4 = new File(file3,"he.txt");//文件目录
        System.out.println(file4);
    }
    /*
    获取操作:
    public String getAbsolutePath(): 获取绝对路径
    public String getPath() :获取路径
    public String getName() :获取名称
    public String getParent(): 获取上层文件目录路径。若无,返回null
    public Long length() :获取文件长度(即:字节数)。不能获取目录的长度。
    public long LastModified() :获取最后一次的修改时间,毫秒值
    如下的两个方法适用于文件目录:
    public String[] list() :获取指定目录下的所有文件或者文件目录的名称数组
    public File[] listFiles() :获取指定目录下的所有文件或者文件目录的File数组

    public boolean renameTo(File dest): 把文件重命名为指定的文件路径
    比如: file1. renameTo(file2)为例:返回布尔值
    要想保证返回true,需要file1在硬盘中是存在的,且file2不能在硬盘中存在。
    */

    /*
   判断功能
    public boolean isDirectory(): 判断是否是文件目录
    public boolean isFile() :判断是否是文件
    public boolean exists() :判断是否存在
    public boolean canRead() :判断是否可读
    public boolean canWrite() :判断是否可写
    public boolean isHidden() :判断是否隐藏
     */

    /*
    创建硬盘中对应的文件或文件目录
    public boolean createNewFile() :创建文件。若文件存在,则不创建,返回false
    public boolean mkdir() :创建文件目录。如果此文件目录存在,就不创建了。
    public boolean mkdirs() :创建文件目录。如果上层文件目录不存在,一并创建。

    删除磁盘中的文件或文件目录
    public boolean delete(): 删除文件或者文件夹
    删除注意事项:
    Java中的删除不走回收站。
     */
}
package com.fq.lz.Day21.Day20_3_IO;

import java.io.File;
import java.io.IOException;

/**
 * @author GoodTime0313
 * @version 1.0
 * @Description File类表示文件
 * @date 2021/3/25 14:39
 */
public class FileDemo1 {
    public static void main(String[] args) throws IOException {
        //File表示硬盘中的一个文件
        //1.创建
        //1.1 调用构造方法创建File对象(文件可以存在也可以不存在)
        File file = new File("file_1.txt");
        //1.2 如果文件不存在,创建
        if (!file.exists()) {
            boolean newFile = file.createNewFile();
            System.out.println("创建的结果"+newFile);
        }else{
            System.out.println("文件已存在");
        }
        System.out.println("-------------------");
        //2.删除
        //2.1 直接删除
        //boolean delete = file.delete();
        //System.out.println("删除的结果:" + delete);
        //2.2 JVM退出时删除 void
       // file.deleteOnExit();
        System.out.println("-------------------");
        //3.获取
        //3.1 获取相对路径
        System.out.println(file.getPath());
        //3.2 获取相对路径
        System.out.println(file.getAbsolutePath());
        //3.3 获取名称
        System.out.println(file.getName());
        //3.4 获取文件大小
        System.out.println(file.length());
        //3.5 获取最后一次修改的时间
        System.out.println(file.lastModified());

        System.out.println("-------------------");
        //4.判断
        //4.1 是不是文件
        System.out.println(file.isFile());
        //4.2 是不是隐藏的
        System.out.println(file.isHidden());
        //4.3 是不是可读
        System.out.println(file.canRead());
        //4.4 是不是可写
        System.out.println(file.canWrite());

        //5. 重命名
        System.out.println(file.renameTo(new File("file_2.txt")));
        //可以实现剪切功能 改位置,改文件名
        //System.out.println(file.renameTo(new File("d:/file_2.txt")));

    }
}

/**
 * @author GoodTime0313
 * @version 1.0
 * @Description File表示硬盘中的目录(文件夹)
 * @date 2021/3/25 15:10
 */
public class FileDemo2 {
    public static void main(String[] args) {
        //1.创建
        //1.1 创建File对象,并指定一个目录
        File file = new File("c.txt");
        System.out.println(file.toString());
        //1.2 创建目录
        if (!file.exists()) {
            //单级目录
            file.mkdir();
            //多级目录
            file.mkdirs();
        } else {
            System.out.println("目录已经存在");
        }

        //2.删除
        //2.1 直接删除 只能删除最深层的,必须是空目录
        System.out.println(file.delete());
        //2.2  JVM来删除
        // file.deleteOnExit();

        //3.获取
        System.out.println(file.getPath());
        System.out.println(file.getAbsolutePath());
        //最里层的目录
        System.out.println(file.getName());

        //4.判断
        //4.1 是不是文件夹
        System.out.println(file.isFile());
        System.out.println(file.isHidden());
        System.out.println(file.isAbsolute());

        //5.重命名:只是该最底层的文件夹,父目录不存在为false
        System.out.println(file.renameTo(new File("d.txt")));

        //6.遍历目录
        //6.1 获取当前目录下的文件夹和文件夹名
        String[] list1 = file.list();
        for (String s : list1) {
            System.out.println(s);
        }
        //6.2 按条件过滤文件
        String[] list = file.list(new FilenameFilter() {
            @Override
            public boolean accept(File dir, String name) {
                //return true 符合要求  false 不符合要求
                if (name.endsWith(".jpg")) {
                    return true;
                }
                return false;
            }
        });

        //6,3 获取当前目录下的文件夹和文件夹名 (file数组)
        File[] files = file.listFiles();
        for (File file1 : files) {
            System.out.println(file1.getName());
        }
        //6.4 获取当前目录下的文件
        File[] files1 = file.listFiles(new FileFilter() {
            @Override
            public boolean accept(File pathname) {
                if (pathname.getName().endsWith(".jpg")) {
                    return true;
                }
                return false;
            }
        });
    }
}

案例1:递归遍历文件夹

/**
 * @author GoodTime0313
 * @version 1.0
 * @Description 递归遍历文件夹
 * @date 2021/3/25 16:11
 */
public class FileDemo1 {
    public static void main(String[] args) {
        File file = new File("E:\\百度网盘文件\\a");
        listDir(file,0);
    }

    public static void listDir(File file,int level) {
        File[] files = file.listFiles();
        System.out.println(getSeparator(level)+file.getName());
        level++;
        if (files != null && files.length > 0) {
            for (File file1 : files) {
                if (file1.isDirectory()) {
                    listDir(file1,level);
                } else {
                    System.out.println(getSeparator(level)+file1.getName());
                }
            }
        }
    }

    public static String getSeparator(int level){
        if (level == 0){
            return "";
        }
        StringBuilder builder = new StringBuilder();
        for (int i = 0; i < level; i++) {
            builder.append("___");
        }
        return builder.toString();
    }
}

案例2:递归删除文件夹

/**
 * @author GoodTime0313
 * @version 1.0
 * @Description 递归删除文件夹
 * @date 2021/3/25 16:31
 */
public class FileDemo2 {
    public static void main(String[] args) {
        File file = new File("E:\\百度网盘文件\\a");
        listDeleteDir(file);
    }

    public static void listDeleteDir(File file) {
        File[] files = file.listFiles();
        if (files != null && files.length > 0) {
            for (File file1 : files) {
                if (file1.isDirectory()) {
                    listDeleteDir(file1);
                } else {
                    file1.delete();
                }
            }
        }
        file.delete();
    }
}

File类的限制,只能看见文件或文件夹的大小,名字等,但是里面的内容控制不了。

[wppay]

2.IO流简单介绍

什么是流?

流的分类

按数据的流向:

  • 输入流 input:文件->内存
  • 输出流 output:内存->文件

按处理的数据单位:

  • 字节流(byte 8bit 图片视频等非文本)
  • 字符流(char 16bit 文本):所有的字符都有一个编码

按流的功能:

  • 节点流(直接作用于文件的流),具有实际传输数据的读写功能。
  • 处理流(已有的流之上包了一层),在节点流的基础之上增强功能
/**
 * 3.抽象基类 不能实例化,具体实现由其子类
 * (抽象基类)    字节流         字符流
 * 输入流      InputStream     Reader
 * 输出流      OutputStream    Writer
 *
 * 4.流的体系结构
 * 抽象基类            节点流(或文件流)         缓冲流(处理流的-种)
 * InputStream       FileInputStream      BufferedInputStream
 * OutputStream      FileOutputStream     BufferedOutputStream
 * Reader              FileReader            BufferedReader
 * Writer              Filewriter            BufferedWriter
 * 
 * 5.具体操作基本按四步走,例题上面有显示。
 **/
file

3.节点流的使用

3.1 字符流

字符编码

所有的字符流都有缓存区

FileReader

public class FileReaderDemo {
    public static void main(String[] args) throws Exception {
        //使用字符流读取文件
        //1.创建FileReader对象
        FileReader reader = new FileReader("a.txt");
        //2.读取
        //2.1 单个字符读取
        /*int data;
        while ((data = reader.read()) != -1) {
            System.out.print((char) data);
        }*/
        //2.2 读取多个字符
        char[] data = new char[5];
        int num;
        while ((num = reader.read(data)) != -1) {
            String s = new String(data, 0, num);
            System.out.println(s);
        }
        //3.关闭
        reader.close();
    }
}

Filewriter

public class FileWriterDemo {
    public static void main(String[] args) throws Exception {
        //1.创建流
        FileWriter fw = new FileWriter("a.txt");
        //2.写入文件
        for (int i = 0; i < 10; i++) {
            fw.write("写入写入\n");
            fw.flush();
        }
        //3.关闭
        fw.close();
    }
}

例一:将FileReader.txt文件内容读入程序中,并输出到控制台

    @Test
    public void testFileReader(){//相较于当前Module
        FileReader reader = null;
        try {
            //1.实例化File类的对象,指明要操作的文件
            File file = new File("FileReader.txt");
            //2.提供具体的操作
            reader = new FileReader(file);
            //3.数据的读入过程 一个一个读取
            //read():返回读入的一个字符。如果达到文件末尾,返回-1
            //方式一:
//        int read = reader.read();
//        while (read!=-1){
//            System.out.print((char) read);
//            read = reader.read();
//        }
            //方式二:语法上的修改
            int read;
            while((read = reader.read())!= -1){
                System.out.print((char) read);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            //垃圾回收机制只回收JVM堆内存里的对象空间。
            //对其他物理连接比如数据库连接、输入流输出流、Socket连接无能为力
            //4.流的关闭操作
            try {
                if (reader!=null){
                    reader.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

注意

    public static void main(String[] args) {
        File file = new File( "hello. txt");//在main函数中实例化File类的对象,指明要操作的文件是相较于当前工程
}

说明点:
1.reader.read()的理解:返回读入的一个字符。如果达到文件末尾,返回-1。
2.异常的处理:为了保证流资源-定可以执行关闭操作。需要使用try-catch-finally处理,写的时候可以先抛出异常,最后按上述的1,2,3,4步骤将1,2,3全部添加进try-catch,finally中去关闭流。
3.读入的文件一定要存在,否则就会报FileNotFoundException。

例二:在例一下,对read()操作升级:使用read的重载方法,一次读取一个数组的字符

    @Test
    public void testFileReader1(){
        FileReader reader = null;
        try {
            //1. File类的实例化
            File file = new File("FileReader.txt");
            //2. FileReader流的实例化
            reader = new FileReader(file);
            //3.读入的操作(数据很多时 不能一个一个读)
            //read(char[] cbuf): 返回每次读入cbuf数组中的字符的个数。如果达到文件末尾,返回-1
            char[] cbuf = new char[5];
            int len;
            while ((len = reader.read(cbuf))!=-1){
                //方法一:
                //这里一定要写len 若为cbuf.length会读取不正确
                //每次读取是覆盖前面 若缺少时 之前的内容无法覆盖也会显示
//                for (int i = 0; i < len; i++) {
//                    System.out.print(cbuf[i]);
//                }


                //String错误的写法
//                String str =new String(cbuf);
//                System. out. print(str);
                //String正确的方法 从头开始取 每次取len个
                String str = new String(cbuf, 0, len);
                System.out.print(str);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //4.资源的关闭
            try {
                if (reader!=null){
                    reader.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

例三:从内存中写出数据到硬盘的文件里。

说明:

1.输出操作,对应的File可以不存在的。如果不存在,在输出的过程中,会自动创建此文件。
2.File对应的硬盘中的文件如果存在: .
FileWriter writer = new FileWriter(file,false); //覆盖 默认
FileWriter writer = new FileWriter(file,true);//追加
    @Test
    public void testFileWriter()  {
        FileWriter writer = null;
        try {
            //1.提供File类的对象,指明写出到的文件
            File file = new File("FileWriter.txt");
            //2.提供FileWriter的对象,用于数据的写出
            writer = new FileWriter(file,true);
            //3.写出的操作
            writer.write("i have a dream!\n");
            writer.write("i need!\n");
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //4.流资源的关闭
            try {
                if(writer!=null){
                    writer.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }

综合:字符流读写操作结合

    /*
        字符流读写操作结合
        不能使用字符流来处理图片等字节数据
     */
    @Test
    public void test4(){
        FileReader fr = null;
        FileWriter fw = null;
        try {
            //1.创建File类的对象,指明读入和写出的文件
            File srcFile = new File("FileReader.txt");
            File destFile = new File("FileWriter.txt");
            //2.创建输入流和输出流的对象
            fr = new FileReader(srcFile);
            fw = new FileWriter(destFile);
            //3.数据的读入和写出操作
            char[] cbuf = new char[5];
            int len;
            while ((len = fr.read(cbuf))!=-1){
                fw.write(cbuf,0,len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //4.关闭流资源
            try {
                if (fr!=null) {
                    fr.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    if (fw!=null) {
                        fw.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

        }
    }

结论:
1.对于文本文件(. txt, .java,.c.cpp),使用字符流处理
2.对于非文本文件(. jpg, . mp3, . mp4, .avi,.doc,.ppt....), 使用字节流处理

3.2 字节流

new FileOutputStream("name")会产生一个新文件;

new FileInputStream("name")不会创建新文件,若文件不存在会报错;

file=new File("name")+new FileInputStream(file)

不会创建新文件,若文件不存在也会报错;

file=new File("name")+new FileOutputStream(file),会创建新文件。

FileInputStream

public class FileInputStreamDemo {
    public static void main(String[] args) throws Exception {
        //1.创建流
        FileInputStream inputStream = new FileInputStream("a.txt");
        System.out.println("字节个数:" + inputStream.available());
        //2.读取
        //2.1 单个字节读取
        /*int read = inputStream.read();
        System.out.println(read);*/
        //2.2 循环读取单个字节
        /*int data;
        while ((data = inputStream.read()) != -1) {
            System.out.print((char) data);
        }*/
        //2.3 读取多个字节
        byte[] buffer = new byte[5];
        int len;
        while ((len = inputStream.read(buffer))!=-1) {
            //asdas
            //d1231
            //23
            String str = new String(buffer,0,len);
            System.out.println(str);
        }
        //3.关闭
        inputStream.close();
    }
}

对String str = new String(buffer,0,len);的理解,不加与加上范围的区别?

    public String(byte bytes[]) {
        this(bytes, 0, bytes.length);
    }
    public String(byte bytes[], int offset, int length) {
        checkBounds(bytes, offset, length);
        this.value = StringCoding.decode(bytes, offset, length);
    }

如果不加范围,在上次读取时如果读满了,下次读不满,没有全部覆盖数组,会把上次读的也输出。

FileOutpuStream

public class FileOutputStreamDemo {
    public static void main(String[] args) throws Exception {
        //1.创建文件字节输出流
        //不追加 - 覆盖
        //FileOutputStream stream = new FileOutputStream("a.txt");
        //追加 - 不覆盖
        FileOutputStream stream = new FileOutputStream("a.txt",true);
        //2.写入
        //2.1 写入一个字节
        /*stream.write(97);
        stream.write(98);
        stream.write(99);*/
        //2.2 写入多个字节
        String s = "啊实打实啊实打实大苏打\r\n";
        byte[] bytes = s.getBytes();
        for (int i = 0; i < 10; i++) {
            stream.write(bytes);
        }
        //3.关闭
        stream.close();
    }
}

例一:使用字节流FileInputStream处理文本文件。(可能出现乱码)

和字符流一样按四步走去操作。

    @Test
    public void testFileInputStream() {
        FileInputStream inputStream = null;
        try {
            File srcFile = new File("FileReader.txt");
            inputStream = new FileInputStream(srcFile);
            byte[] buffer = new byte[5];
            int len;
            while ((len=inputStream.read(buffer))!=-1){
                String str = new String(buffer,0,len);
                System.out.println(str);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (inputStream!=null) {
                    inputStream.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

综合:实现对图片的复制操作。

  public class CopyFile {
    public static void main(String[] args) throws Exception {
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream("E:\\a.png"));
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("E:\\a\\bb.png"));
        byte[] data = new byte[10];
        while (bis.read(data) != -1) {
            bos.write(data, 0, data.length);
        }
        bos.close();
        bis.close();
        System.out.println("复制成功");
    }
}

4. 处理流的使用

4.1 缓冲流的使用

1.缓冲流:

没有读写文件的功能,只是增加一些功能。

BufferedInputStream、BufferedOutputStream

BufferedReader、BufferedWriter

2.作用:

  • 提升了IO的读取、写入的速度,内部提供了一个缓冲区
  • 数据存储在缓冲区中,flush是将缓存区的内容写入文件中,也可以直接close。

BufferedInputStream

public class BufferInputStreamDemo {
    public static void main(String[] args) throws Exception{
        //1.创建字节缓冲输入流
        FileInputStream fis = new FileInputStream("a.txt");
        BufferedInputStream bis = new BufferedInputStream(fis);
        //2.读取文件
        //我们这里读了一个字节,但是缓冲区读取了8K
        int data;
        while ((data = bis.read()) != -1) {
            System.out.print((char) data);
        }
        //3.关闭 只关闭缓冲流,内部把节点流关了
        bis.close();
    }
}

BufferedOutputStream

public class BufferOutputStreamDemo {
    public static void main(String[] args) throws Exception {
        //1.创建字节缓冲输出流写入文件
        FileOutputStream fos = new FileOutputStream("a.txt");
        BufferedOutputStream bos = new BufferedOutputStream(fos);
        //2.写入文件
        String s = "java是世界上最好的语言\n";
        byte[] bytes = s.getBytes();
        for (int i = 0; i < 10; i++) {
            bos.write(bytes);//8k
            bos.flush();//即时刷新缓冲 (看关闭时的注意)
        }
        //3.关闭
        //注意:关闭缓冲流,相当于关闭了底层流,会自动刷新缓冲区
        bos.close();
        //以上close的源码
        /*public void close() throws IOException {
            try (OutputStream ostream = out) {
                flush();
            }
        }*/
    }
}

代码里close源码实例演示:

class TestTry {
    public static void main(String[] args) throws Exception {
        //补充 try的特殊用法:自动调用close方法,对象必须实现一个接口
        try (Student student = new Student()) {
            System.out.println(student.toString());
            //student.close(); 可以不用写
        }
    }
}

//要实现Closeable接口
class Student implements Closeable {
    @Override
    public void close() throws IOException {
    }
}
  • 支持输入换行符
  • 可一次写一行,读一行

BufferedWriter

class BufferWriterDemo{
    public static void main(String[] args) throws Exception {
         BufferedWriter bw = new BufferedWriter(new FileWriter("a.txt"));
        //10.写入
        for (int i = 0; i < 10; i++) {
            bw.write("阿三大苏打实打实");
            bw.newLine();
            //bw.flush();
        }
        bw.close();
    }
}

BufferedReader

public class BufferReaderDemo {
    public static void main(String[] args) throws Exception {
        //读取
        //(1) 创建流
        BufferedReader br = new BufferedReader(new FileReader("a.txt"));
        //(2) 读取
        String line;
        while ((line = br.readLine())!=null){
            System.out.println(line);
        }
        /*String s = br.readLine();
        System.out.println(s);*/
        //(3) 关闭
        br.close();
    }
}

案例:实现非文本文件的复制

    @Test
    public void BufferedStreamTest(){
        BufferedInputStream bis = null;
        BufferedOutputStream bos = null;
        try {
            //1.造文件
            File srcFile = new File("a.jpg");
            File desFile = new File("a3.jpg");
            //2.造流
            //2.1造节点流
            FileInputStream fis = new FileInputStream(srcFile);
            FileOutputStream fos = new FileOutputStream(desFile);
            //2.2造缓冲流
            bis = new BufferedInputStream(fis);
            bos = new BufferedOutputStream(fos);

            //3.复制的细节:读取、入
            byte[] buffer = new byte[10];
            int len;
            while ((len = bis.read(buffer))!=-1 ){
                bos.write(buffer,0,len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //4.资源关闭
            //要求:先关闭外层的流,再关闭内层的流
            //说明:关闭外层流的同时,内层流也会自动的进行关闭。关于内层流的关闭,我们可以省略.
            if (bos!=null) {
                try {
                    bos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (bis!=null) {
                try {
                    bis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
      /*fos.close();
        fis.close();*/
    }

4.2 转换流的使用

1.转换流: 属于字符流
InputStreamReader 将一个字节的输入流转换为字符的输入流
OutputStreamWriter 将一个字符的输出流转换为字节的输出流
2.作用:提供字节流与字符流之间的转换

解码:字节、字节数组--->字符数组、字符串
编码:字符数组、字符串--->字节、字节数组

InputStreamReader

public class InputStreamReaderDemo {
    public static void main(String[] args) throws Exception {
        //1.创建一个转换流
        InputStreamReader isr = new InputStreamReader(new FileInputStream("a.txt"),"UTF-8");
        //2.读取
        char[] buf = new char[5];
        int len;
        while ((len = isr.read(buf)) != -1) {
            String s = new String(buf, 0, len);
            System.out.println(s);
        }
        //3.关闭
        isr.close();
    }
}

OutputStreamWriter

public class OutputStreamReaderDemo {
    public static void main(String[] args) throws Exception {
        //1.创建转换流 字符—-》字节
        OutputStreamWriter osw = new OutputStreamWriter(
            new FileOutputStream("a.txt")
        );
        //2.写出
        for (int i = 0; i < 10; i++) {
            osw.write("北京\n");
        }
        //3.关闭
        osw.close();
    }
}

综合:使用InputStreamReader和OutputStreamWriter

    @Test
    public void test2() throws IOException {
        File file1 = new File("FileReader.txt");
        File file2 = new File("FileReader2.txt");

        FileInputStream fis = new FileInputStream(file1);
        FileOutputStream fos = new FileOutputStream(file2);

        InputStreamReader isr = new InputStreamReader(fis);
        OutputStreamWriter osw = new OutputStreamWriter(fos);
        //读写过程
        char[] cbuf = new char[20];
        int len;
        while ((len = isr.read(cbuf)) != -1) {
            osw.write(cbuf, 0, len);
        }
        isr.close();
        osw.close();
    }

5. 其他流的使用

5.1 打印流

内存打印到硬盘,只有输出。

  • PrintStream\PrintWriter
    • 封装了print() / println()方法,支持写入后换行。
  • System.out
    • PrintStream类型
    • 默认打印到控制台
      • 重定向标准输出流
  • System.in
    • FileInputStream

PrintStream/PrintWriter

public class PrintStreamDemo {
    public static void main(String[] args) throws Exception{
        //1创建
        //字节流打印流
        //PrintStream ps=new PrintStream("a.txt");
        //字符流打印流
        PrintWriter ps=new PrintWriter("a.txt");
        //2打印
        ps.println(97);
        ps.println("hello");
        ps.println(true);
        ps.println(3.14);
        ps.println('a');

        //3关闭
        ps.close();
        System.out.println("执行完毕");
    }
}

System.out

public class SystemDemo {
    public static void main(String[] args) throws Exception{
        System.out.println("hello");//打印到控制台
        //重定向标准输出流
        System.setOut(new PrintStream("d:\\console.txt"));
        System.out.println(97);
        System.out.println(3.14);
        System.out.println(true);
        System.out.println("================");
    }
}

System.in

public class SystemDemo2 {
    public static void main(String[] args) throws Exception{
        //(1)System.in读取一个字节
//        int data= System.in.read();//读取一个字节
//        System.out.println((char)data);

        //(2)System.in读取一个字符
//        InputStreamReader isr=new InputStreamReader(System.in);
//        int data=isr.read();//读取一个字符
//        System.out.println((char)data);

        //(3)System.in读取一行字符
        InputStreamReader isr=new InputStreamReader(System.in);
        BufferedReader br=new BufferedReader(isr);
        String line=br.readLine();
        System.out.println(line);

        Scanner input=new Scanner(System.in);
        input.nextInt();
        input.next();
        input.nextDouble();
    }
}

5.2 RandomAccessFile

可以读取文件内容,也可以向文件中写入内容。

但是和其他输入输出流不同的是,程序可以直接跳到文件的任意位置来读写数据。

读写模式:

  • r :只读
  • rw : 读写

作用:

  • 快速定位数据,支持并发读写
  • 方便操作二进制文件
public class RandomAccessFileDemo {
    public static void main(String[] args) throws Exception {
        read();
    }

    public static void write() throws Exception {
        //1.创建RandomAccessFile对象
        RandomAccessFile rw = new RandomAccessFile("a.txt", "rw");
        //2.写入
        rw.writeUTF("liu");
        rw.writeInt(20);
        rw.writeBoolean(true);
        rw.writeDouble(123.2);
        rw.writeUTF("zhi");
        rw.writeInt(20);
        rw.writeBoolean(true);
        rw.writeDouble(123.3);

        //3.关闭
        rw.close();
        System.out.println("写入完毕");
    }

    public static void read() throws Exception {
        //1.创建RandomAccessFile对象
        RandomAccessFile rw = new RandomAccessFile("a.txt", "r");
        //2.读取
        //设置读取的指针位置  从0开始
        //rw.seek(2);
        //跳过指定的字节个数 从当前位置开始
        //rw.skipBytes(3);
        String s = rw.readUTF();
        int i = rw.readInt();
        boolean b = rw.readBoolean();
        double aDouble = rw.readDouble();
        System.out.println(s + " " + i + " " + b + " " + aDouble);
        //3.关闭
        rw.close();
    }
}

案例多线程实现下载

public class RandomAccessFileDemo2 {
    public static void main(String[] args) throws Exception{
        ExecutorService es = Executors.newFixedThreadPool(4);
        byte[] data=new byte[1024*1024];//1M
        CountDownLatch countDownLatch=new CountDownLatch(4);
        long start=System.currentTimeMillis();
        for(int i=0;i<4;i++){
            int n=i;
            es.submit(new Runnable() {
                @Override
                public void run() {
                    RandomAccessFile raf= null;
                    try {
                        raf = new RandomAccessFile("d:\\电影.mp4", "rw");
                        System.out.println(Thread.currentThread().getName()+"开始下载了...");
                        raf.seek(n*(1024*1024));
                        raf.write(data);
                        Thread.sleep(2000);
                        raf.close();
                        System.out.println(Thread.currentThread().getName()+"下载完毕了...");
                        countDownLatch.countDown();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }

                }
            });
        };
        es.shutdown();
        countDownLatch.await();
        long end=System.currentTimeMillis();
        System.out.println("总用时:"+(end-start));
    }
}

5.3 Properties集合

Properties:属性集合

特点:

  • 存储属性名和属性值
  • 属性名和属性值都是字符串类型
  • 没有泛型
  • 和流有关
public class PropertiesDemo {
    public static void main(String[] args) throws Exception {
        //1创建集合
        Properties properties=new Properties();
        //2添加数据
        properties.setProperty("name", "张三");
        properties.setProperty("age","20" );
        properties.setProperty("address", "北京");
        //3与流有关的方法
        //-------1 list 使用打印流打印数据--------
        properties.list(System.out);
        //-------2 store 存储到硬盘---------------
        FileOutputStream fos=new FileOutputStream("d:\\user.properties");
        properties.store(fos,"注释");
        fos.close();
        //-------3 load 加载属性文件---------
        System.out.println("------load-----");
        Properties properties2=new Properties();
        FileInputStream fis=new FileInputStream("d:\\user.properties");
        properties2.load(fis);
        fis.close();
        properties2.list(System.out);
    }
}

6. 对象流的使用

1.objectInputStream和objectOutputStream

2.作用:

  • 用于存储读取基本数据类型数据对象的处理流
  • 把Java中的对象写入到数据源中,也能把对象从数据源中还原回来
  • 增强缓冲区的功能

3.要想一个java对象是可序列化的,需要满足相应的要求。见Person. java

4.概念:

  • 序列化过程:将内存中的java对象保存到磁盘中或通过网络传输出去,使用ObjectOutputstream实现。
  • 反序列化:将磁盘文件中的对象还原为内存中的一java对象,使用ObjectInputStreand

objectOutputStream

public class ObjectOutputStreamDemo {
    /*    public static void main(String[] args) throws Exception {
            //实现序列化功能,把对象写入硬盘或网络的过程
             Student student = new Student("liu",22);
             //1.创建对象流
             FileOutputStream fos = new FileOutputStream("a.txt");
             ObjectOutputStream oos = new ObjectOutputStream(fos);
            //2.序列化
            oos.writeObject(student);
            //3.关闭
            oos.close();
        }*/
    public static void main(String[] args) throws Exception {
        //实现序列化功能,把对象写入硬盘或网络的过程
        Student student = new Student("liu", 22);
        Student student2 = new Student("liu", 22);
        Student student3 = new Student("liu", 22);
        final ArrayList<Student> students = new ArrayList<>();
        students.add(student);
        students.add(student2);
        students.add(student3);
        //1.创建对象流
        FileOutputStream fos = new FileOutputStream("a.txt");
        ObjectOutputStream oos = new ObjectOutputStream(fos);
        //2.序列化
        oos.writeObject(students);
        //3.关闭
        oos.close();
    }
}

class Student implements Serializable {
    private static final long serialVersionUID = -4742896679480581118L;
    private String name;
    private int age;

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "Student{" +
            "name='" + name + '\'' +
            ", age=" + age +
            '}';
    }
}

改了文件的后缀,里面的内容没有改,只是用什么软件打开

objectInputStream

public class ObjectInputStreamDemo {
    /*    public static void main(String[] args) throws Exception {
            //实现反序列化功能,把硬盘或网络的二进制文件读取到内存
            //1.创建流
            FileInputStream fis = new FileInputStream("a.txt");
            ObjectInputStream ois = new ObjectInputStream(fis);
            //2.反序列化
            Student student = (Student) ois.readObject();
            System.out.println(student.toString());
            //3.关闭
            ois.close();
        }*/
    public static void main(String[] args) throws Exception {
        //实现反序列化功能,把硬盘或网络的二进制文件读取到内存
        //1.创建流
        FileInputStream fis = new FileInputStream("a.txt");
        ObjectInputStream ois = new ObjectInputStream(fis);
        //2.反序列化
        ArrayList students = (ArrayList) ois.readObject();
        System.out.println(students.toString());
        //3.关闭
        ois.close();
    }
}

Person类

Person需要满足如下的要求,方可序列化

  • 1.需要实现接口: Serializable
  • 2.当前类提供一个全局常量:seriaLVersionUID
  • 3.除了当前Person类需要实现Serializable接口之外,还必须保证其内部所有属性也必须是可序列化的。(默认情况下,基本数据类型可序列化)
  • ObjectOutputStream和ObjectInputStream不能序列化static和transient修饰的成员变量
public class Person implements Serializable {
    public static final long serialVersionUID = 475463534532L;
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.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;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

对象流测试

public class ObjectInputOutputStreamTest {
    /*
    序列化过程:将内存中的java对象保存到磁盘中或通过网络传输出去
    使用ObjectOutputstream实现
    */
    @Test
    public void testObjectOutputStream(){
        ObjectOutputStream oss = null;
        try {
            //1.造文件 造流
            oss = new ObjectOutputStream(new FileOutputStream("object.dat"));
            //2.写出
            oss.writeObject(new String("我爱读书"));
            oss.flush();//刷新操作

            oss.writeObject(new Person("刘志",123));
            oss.flush();

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //关闭流
            try {
                oss.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /*
    反序列化:将磁盘文件中的对象还原为内存中的一java对象
    使用ObjectInputStreand
    */
    @Test
    public void testObjectInputStream(){
        ObjectInputStream ois = null;
        try {
            ois = new ObjectInputStream(new FileInputStream("object.dat"));
            Object obj = ois.readObject();
            String str = (String)obj;

            Person p = (Person)ois.readObject();

            System.out.println(str);
            System.out.println(p);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } finally {
            try {
                ois.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

对象流说明与扩展

说明:

  • 1.序列化多个对象时:集合放对象 - 见两个代码
  • 2.序列化类及其对象属性必须实现Serializable接口
  • 3.transient修改为瞬间(临时)属性,static属性不能序列化
  • 4.读取到文件尾部的标志:java.io.EOFException
  • 5.使用serialVersionUID属性保证序列化的类和反序列化的类是同一个类。
    • 不带,会自动生成

扩展:
其他的接口实现序列化:

  • 自动序列化接口:Serializable
  • 手动序列化接口:Externalizable、 - 见代码
    • 必须实现里面的方法
    • 需要无参构造器

transient的属性一定不能序列化吗?

  • Serializable 不能
  • Externalizable 能
public class Exter_Demo {
    public static void main(String[] args) throws Exception {
        //实现序列化功能,把对象写入硬盘或网络的过程
        Student2 student = new Student2("liu", 22);
        //1.创建对象流
        FileOutputStream fos = new FileOutputStream("b.txt");
        ObjectOutputStream oos = new ObjectOutputStream(fos);
        //2.序列化
        oos.writeObject(student);
        //3.关闭
        oos.close();
    }
}

class Student2 implements Externalizable {
    private String name;
    private transient int age;

    public Student2(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public Student2() {
    }

    @Override
    public String toString() {
        return "Student{" +
            "name='" + name + '\'' +
            ", age=" + age +
            '}';
    }

    @Override
    public void writeExternal(ObjectOutput out) throws IOException {
        out.writeUTF(name);
        out.writeInt(age);
    }

    @Override
    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
        name = in.readUTF();
        age = in.readInt();
    }
}
public class ExternalizableDemo {
    public static void main(String[] args) throws Exception {
        //实现反序列化功能,把硬盘或网络的二进制文件读取到内存
        //1.创建流
        FileInputStream fis = new FileInputStream("b.txt");
        ObjectInputStream ois = new ObjectInputStream(fis);
        //2.反序列化
        Student2 student = (Student2) ois.readObject();
        System.out.println(student.toString());
        //3.关闭
        ois.close();
    }
}

7.内存流

ByteArrayInputStream / ByteArrayOutputSteam

  • 操作的数据来自内存,流可以不用关闭。
public class ByteArrayInputStreamDemo {
    public static void main(String[] args) throws Exception {
        //1.创建内存流
        byte[] bytes = "hellworld".getBytes();
        ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
        //2.读取
        int data;
        while ((data = bais.read()) != -1) {
            System.out.print((char) data);
        }
        //3.关闭
        bais.close();
    }
}
public class ByteArrayOutputStreamDemo {
    public static void main(String[] args) throws Exception {
        //创建内存输出流
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        //写入到缓存数组中
        byte[] data = "java是世界上最好的语言".getBytes();
        baos.write(data);
        //关闭
        baos.close();
        //获取数据
        byte[] byteArray = baos.toByteArray();
        String s = new String(byteArray);
        System.out.println(s);
    }
}

微软记事本

记事本编码转换文件,文件的大小改变,原因? BOM头 字节序的标记 Byte Order Mark

UniCode编码、Utf-8、GBK

大端、小端:

  • 数字小的在后面 : 小端
  • 数字大的在后面 : 大端

联通问题

GBK一个中文两个字节,但是再打开乱码。编码规则符合utf-8的规则。再另存为UTF-8时显示正常。UTF-8一个中文三个字节。

[/wppay]