Java I/O

1、File文件操作类

1.1 File类使用

java.io.File是是一个普通的类,可以通过两个构造方法直接产生实例化对象:
public File(String pathname);
public File(String parent,String child); —— 设置父路径和子路径

File类提供了以下方法对文件进行基本操作:

  • 创建一个新文件:
public boolean createNewFile() throws IOException 
  • 判断文件是否存在
public boolean exists();
  • 删除文件
public boolean delete();

一个简单应用:

public class Test {
    public static void main(String[] args) throws  IOException {
        //定义要操作的文件路径
        File file = new File("E:\\比特\\JavaSE\\code\\TestIO.java");
        if (file.exists()){
            file.delete();
        }else {
            file.createNewFile();
        }
    }
}

注意:定义要操作的文件路径时,不同的操作系统使用的斜杠不同,因此我们可以采用File类的一个常量public static final String separator来描述。separator由不同操作系统下的JVM来决定到底是哪个杠杠。

上面代码优化为:

public class Test {
    public static void main(String[] args) throws IOException {
        //定义要操作的文件路径
        File file = new File("E:" + File.separator + "比特"
                + File.separator + "JavaSE" + File.separator + "code"
                + File.separator + "TestIO.java");
        if (file.exists()) {
            file.delete();
        } else {
            file.createNewFile();
        }
    }
}
1.2 目录操作
  • 取得父路径或父File对象

public String getParent();

public File getParentFile();

  • 创建目录(无论有多少级父目录,都会创建)

public boolean mkdirs()
简单应用:

public class Test {
    public static void main(String[] args) throws IOException {
        //定义要操作的文件路径
        File file = new File("E:" + File.separator + "比特"
                + File.separator + "JavaSE" + File.separator + "code"
                + File.separator + "JavaIO.java" + File.separator + "Nanfeng"
                + File.separator + "TestIO.java");
        if (!file.getParentFile().exists()) {
            file.getParentFile().mkdirs();  //有多少级父目录就创建多少级
        }
        if (file.exists()) {
            file.delete();
        } else {
            file.createNewFile();
        }
    }
}
1.3 文件信息
  • 判断路径是否是文件:public boolean isFile();
  • 判断路径是否是目录:public boolean isDirectory();
  • 取得文件大小(字节):public long length();
  • 最后一次修改日期:public long lastModified();
  • 列出一个目录的全部组成:public File[] listFiles();

2、字节流与字符流

File类不支持文件内容的处理,如果要处理文件内容,必须要通过流的操作模式来完成。流分为输入流和输出流。
java.io 包中,流分为两种:字节流和字符流:
1)字节流:InputStream、OutputStream
2)字符流:Reader、Writer
字节流与字符流的本质区别:
字节流是原生的操作,而字符流是经过处理后的操作。

不论是字节流还是字符流,操作流程都是一样的,以文件操作为例:
1)根据文件路径创建File类对象
2)根据字节流或字符流的子类实例化父类对象
3)进行数据的读取或写入操作
4)关闭流(close())

2.1 字节输出流(OutputStream)

由于OutputStream是一个抽象类,所以要想为父类实例化,就必须使用子类。由于方法名称都由父类声明好了,所以我们只需要关心子类的构造方法。如果要进行文件的操作,可以使用FileOutputStream类来处理,这个类的构造方法为:
1)接收File类(覆盖):public FileOutputStream(File file) throws FileNotFoundException;
2)接收File类(追加):public FileOutputStream(File file,boolean append)
简单应用:

public class Test {
    public static void main(String[] args) throws IOException {
        //定义要操作的文件路径
        File file = new File("E:" + File.separator + "比特"
                + File.separator + "JavaSE" + File.separator + "code"
                + File.separator + "hello.txt");
        if (!file.getParentFile().exists()) {
            file.getParentFile().mkdirs();  //有多少级父目录就创建多少级
        }
        OutputStream output = new FileOutputStream(file);
        //要求输出到文件的内容
        String msg = "南风知我意";
        //将内容变为字节数组
        output.write(msg.getBytes());
        //关闭输出流
        output.close();
    }
}

上面这段代码如果重复执行,内容会不停的覆盖,如果要实现内容的追加,则需要用另外一种构造方法。

public class Test {
    public static void main(String[] args) throws IOException {
        //定义要操作的文件路径
        File file = new File("E:" + File.separator + "比特"
                + File.separator + "JavaSE" + File.separator + "code"
                + File.separator + "hello.txt");
        if (!file.getParentFile().exists()) {
            file.getParentFile().mkdirs();  //有多少级父目录就创建多少级
        }
        OutputStream output = new FileOutputStream(file, true);
        //要求输出到文件的内容
        String msg = "   念你已成疾";
        //将内容变为字节数组
        output.write(msg.getBytes());
        //关闭输出流
        output.close();
    }
}

在这里插入图片描述
JDK 1.7之后,追加了一个AutoCloseable接口,主要目的是自动进行关闭处理。

class Message implements AutoCloseable {
    public Message() {
        System.out.println("创建一条新消息");
    }

    @Override
    public void close() throws Exception {
        System.out.println("[AutoCloseable]自动关闭方法");
    }

    public void print() {
        System.out.println("www.nanfeng.java");
    }
}

public class Test {
    public static void main(String[] args) {
        //必须在try中定义对象
        try (Message msg = new Message()) {
            msg.print();
        } catch (Exception e) {

        }
    }
}

因此上面的例子可以改为:

public class Test {
    public static void main(String[] args) throws IOException {
        //定义要操作的文件路径
        File file = new File("E:" + File.separator + "比特"
                + File.separator + "JavaSE" + File.separator + "code"
                + File.separator + "hello.txt");
        if (!file.getParentFile().exists()) {
            file.getParentFile().mkdirs();  //有多少级父目录就创建多少级
        }
        try (OutputStream output = new FileOutputStream(file, true)) {
            //要求输出到文件的内容
            String msg = "吹梦到西洲";
            //将内容变为字节数组
            output.write(msg.getBytes());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
2.2 字节输入流(InputStream)

和字节输出流一样,字节输入流也是一个抽象类,如果要对它进行实例化,同样也需要使用子类。如果要对文件进行处理,则使用FileInputStream类。

public class Test {
    public static void main(String[] args) throws IOException {
        //定义文件路径
        File file = new File("E:" + File.separator + "比特"
                + File.separator + "JavaSE" + File.separator + "code"
                + File.separator + "hello.txt");
        //保证文件存在
        if (file.exists()) {
            InputStream input = new FileInputStream(file);
            byte[] data = new byte[1024];   //每次可以读取的最大数量
            int len = input.read(data);     //此时数据读取到数组中
            String result = new String(data, 0, len);     //将字节数组转为String
            System.out.println("读取内容【" + result + "】");
            input.close();
        }
    }
}
2.3 字符输出流:Writer

字符适合处理中文数据,Writer是字符输出流的处理类。
在Writer类里也提供了write()方法,而且该方法接收的类型都是char型。
需要注意的是,Writer类提供了一个直接输出字符串的方法:public void write(String str) throws IOException;如果要操作文件,使用FileWriter类。

简单应用:

public class Test {
    public static void main(String[] args) throws IOException {
        //定义文件路径
        File file = new File("E:" + File.separator + "比特"
                + File.separator + "JavaSE" + File.separator + "code"
                + File.separator + "hello.txt");
        if (!file.getParentFile().exists()) {
            file.getParentFile().mkdirs();
        }
        String msg = "南风知我意,念你已成疾!";
        Writer out = new FileWriter(file);
        out.write(msg);
        out.close();
    }
}

在这里插入图片描述

2.4 字符输入流:Reader

Reader也是一个抽象类,如果要进行文件的读取,要使用FileReader。
Reader类中没有方法可以直接读取字符串类型,只能通过字符数组进行读取操作。

public class Test {
    public static void main(String[] args) throws IOException {
        //定义文件路径
        File file = new File("E:" + File.separator + "比特"
                + File.separator + "JavaSE" + File.separator + "code"
                + File.separator + "hello.txt");
        if (file.exists()) {
            Reader in = new FileReader(file);
            char[] data = new char[1024];
            int len = in.read(data);    //将数据读取到字符数组中
            String result = new String(data, 0, len);
            System.out.println("读取内容【" + result + "】");
            in.close();
        }
    }
}

在这里插入图片描述

所有字符流操作,无论是写入还是输出,数据都先保存在缓存中。如果字符流不关闭,数据就有可能保存在缓存中并没有输出到目标源,这时就必须使用out.flush()强制刷新才能得到完整数据。

3、转换流

字节流和字符流的相互转换:

  • OutputStreamWriter:将字节输出流转成字符输出流
  • InputStreamReader:将字节输入流转成字符输入流

简单应用:

public class Test {
    public static void main(String[] args) throws IOException {
        //定义文件路径
        File file = new File("E:" + File.separator + "比特"
                + File.separator + "JavaSE" + File.separator + "code"
                + File.separator + "hello.txt");
        if (!file.getParentFile().exists()) {
            file.getParentFile().mkdirs();
        }
        OutputStream output = new FileOutputStream(file);
        Writer out = new OutputStreamWriter(output);
        String msg = "南风知我意,吹梦到西洲!";
        out.write(msg);
        out.close();
    }
}

4、字符编码

开发中常用UTF-8编码,解码和编码不一致就会产生乱码。

读取Java运行属性:
System.getProperties().list(System.out);

5、内存操作流

之前所有操作都是针对文件进行IO处理,其实IO操作也可以发生在内存中,这种称为内存操作流。
文件流的操作一定会产生一个文件数据,如果现在需要进行IO处理,但又不希望产生文件,这种情况就可以使用内存作为操作终端。
内存操作流最核心的就是将所有OutputStream输出的程序保存在了程序里面。

内存流也分为两类:
1)字节内存流:ByteArrayInputStream、ByteArrayOutputStream
2)字符内存流:CharArrayReader、CharArrayWriter
简单应用:通过内存流实现大小写转换

public class Test {
    public static void main(String[] args) throws IOException {
        String msg = "Hello Nanfeng";
        //实例化InputStream类对象,实例化的时候需要将你操作的数据保存到内存中,最终读取的就是你设置的内容
        InputStream input = new ByteArrayInputStream(msg.getBytes());
        OutputStream output = new ByteArrayOutputStream();
        int temp = 0;
        while ((temp = input.read()) != -1) {
            //对每个字节进行处理,处理之后所有数据都在outputStream类中
            output.write(Character.toUpperCase(temp));
        }
        //直接输出output对象
        System.out.println(output);
        input.close();
        output.close();
    }
}

使用内存流实现将两个文件合并处理(文件量不大)

public class JavaIO {
    public static void main(String[] args) throws IOException {
        File[] files = new File[]{
                new File("E:" + File.separator + "比特" + File.separator + "JavaSE" + File.separator + "code" + File.separator + "data-a.txt"),
                new File("E:" + File.separator + "比特" + File.separator + "JavaSE" + File.separator + "code" + File.separator + "data-b.txt")
        };
        String[] data = new String[2];
        for (int i = 0; i < files.length; i++) {
            data[i] = readFile(files[i]);
        }
        StringBuffer buf = new StringBuffer();  //组合操作
        String contentA[] = data[0].split(" ");
        String contentB[] = data[1].split(" ");
        for (int i = 0; i < contentA.length; i++) {
            buf.append(contentA[i]).append("(").append(contentB[i]).append(")").append(" ");
        }
        System.out.println(buf);
    }

    public static String readFile(File file) throws IOException {
        if (file.exists()) {
            InputStream input = new FileInputStream(file);
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            int temp = 0;
            byte[] data = new byte[10];
            while ((temp = input.read(data)) != -1) {
                //将数据保存在bos中
                bos.write(data, 0, temp);
            }
            bos.close();
            input.close();
            //将读取内容返回
            return new String(bos.toByteArray());
        }
        return null;
    }
}

6、打印流

打印流解决的是OutputStream的设计缺陷,如果操作的不是二进制数据,只是想通过程序向终端目标输出信息的话,OutputStream不是很方便,其缺点有两个:
1)所有的数据必须转为字节数组
2)如果要输出的是int,double等类型就不方便了
打印流的设计属于装饰设计模式,核心依然是某个类的功能。

打印流分为字节打印流PrintStream和字符打印流PrintWriter,后者使用频率较高。

6.1 使用打印流:
public class DaYinLiu {
    public static void main(String[] args) throws FileNotFoundException {
        PrintWriter printutil = new PrintWriter(new FileOutputStream(new File("E:\\比特\\JavaSE\\code\\test.txt")));
        printutil.print("姓名:");
        printutil.println("Nanfeng");
        printutil.print("年龄:");
        printutil.println(21);
        printutil.print("工资:");
        printutil.println(10.000000000000000001);
        printutil.close();
    }
}
6.2 格式化输出

格式化输出:
public PrintStream printf(String format,Object ... args)

public class DaYinLiu {
    public static void main(String[] args) throws FileNotFoundException {
        String name = "Nanfeng";
        int age = 21;
        double salary = 1.1063726327;
        PrintWriter printutil = new PrintWriter(new FileOutputStream(new File("E:\\比特\\JavaSE\\code\\test.txt")));
        printutil.printf("姓名:%s,年龄:%d,工资:%1.2f", name, age, salary);
        printutil.close();
    }
}

格式化字符串:
public static String format(String fromat,Object ... args);

public class DaYinLiu {
    public static void main(String[] args) throws FileNotFoundException {
        String name = "Nanfeng";
        int age = 21;
        double salary = 1.1063726327;
        String str = String.format("姓名:%s,年龄:%d,工资:%1.2f", name, age, salary);
        System.out.println(str);
    }
}

7、System类对IO的支持

在System类中定义了三个操作的常量:
1)标准输出(显示器):public final static PrintStream out;
2)错误输出:public final static PrintStream err;
3)标准输入(键盘):public final static InputStream;

8、两种输入流

8.1 BufferedReader类

BufferedReader类属于一个缓冲的输入流,而且是一个字符流的操作对象。在Java中缓冲流也分为两种:字节缓冲流(BufferedInputStream)、字符缓冲流(BufferedReader)。

BufferedReader类提供了直接读取一行数据的方法(以回车为换行符):
String readLine() throws IOException;
利用BufferedReader类实现键盘输入:

public class ShuRuLiu {
    public static void main(String[] args) throws IOException {
        BufferedReader buf = new BufferedReader(new InputStreamReader(System.in));
        System.out.println("请输入信息:");
        String str = buf.readLine();    
        System.out.println("输入信息为:" + str);
    }
}

使用以上形式实现的键盘输入有两个特点:
1)默认使用回车换行
2)由于接收的数据类型为String,可以使用String类的各种操作进行数据处理并且可以变为各种常见数据类型

8.2 java.util.Scanner类

Scanner是一个专门进行输入流处理的程序类,利用这个类可以方便处理各种数据类型,也可以直接结合正则表达式进行各项处理。Scanner类解决了BufferedReader类的缺陷,替代了BufferedReader类。

Scanner类提供的方法:

  • 判断是否有指定数据类型:public boolean hasNextXxx();
  • 取得指定类型的数据:public 数据类型 nextXxx();
  • 定义分隔符:public Scanner useDelimiter(Pattern pattern);
  • 构造方法:public Scanner(InputStream source);

使用Scanner类实现数据输入:

public class ShuRuLiu {
    public static void main(String[] args) throws IOException {
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入信息:");
        if (scanner.hasNext()) {
            System.out.println("输入信息为:" + scanner.next());
        }
        scanner.close();
    }
}

使用Scanner类操作文件:

public class ShuRuLiu {
    public static void main(String[] args) throws IOException {
        Scanner scanner = new Scanner(new FileInputStream(new File("E:\\比特\\JavaSE\\code\\test.txt")));
        scanner.useDelimiter("\n");     //自定义分隔符
        while (scanner.hasNext()) {
            System.out.println(scanner.next());
        }
        scanner.close();
    }
}

9、序列化

对象序列化是指:将内存中保存的对象变为二进制数据流的形式进行传输,或者是将其保存在文本中。但是并不是所有类的对象都可以被序列化。
严格来说,需要被序列化的类对象需要传输使用,同时这个类必须实现java.io.Serializable接口,但是这个接口没有任何的方法定义,只是一个标识而已。

实现对象序列化:

class Person implements Serializable {
    private String name;
    private int age;

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

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

public class XuLieHua {
    public static final File FILE = new File("E:\\比特\\JavaSE\\code\\testSer.txt");

    public static void main(String[] args) throws IOException {
        ser(new Person("Nanfeng", 21));
    }

    public static void ser(Object obj) throws IOException {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(FILE));
        oos.writeObject(obj);
        oos.close();
    }
}

补充:transient关键字
序列化的处理在java.io包里有两类,Serializable是使用最多的序列化接口,它采用自动化模式完成,在默认情况下所有的属性都会被序列化保存下来。如果现在某些属性不希望被保存了,那么就可以使用transient关键字。
还有一个Externalizable接口需要自己动手来处理序列化,很少使用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值