Java中的I/O操作

首先先了解一下文件类型,我们都知道数据是以二进制形式保存的,但是为了数据处理方便,高级语言中引入了数据类型的概念。文件也引入了文件类型的概念。

文件类型通常是以扩展名体现出来的,每种文件类型都有一定的格式,代表文件含义和二进制之间的映射关系。一个World文件,其中有文本、图片、表格,文本可能有颜色、字体、字号等,doc文件类型就定义了这些内容和二进制表示之间的映射关系。

在操作系统中,一种扩展名往往关联一个应用程序,比如.doc文件关联Word应用。用户通过双击,操作系统找到对应的应用程序,并启动该程序,传递该文件路径给它,程序再打开文件。

文件是放在硬盘上的,每个文件都有文件路径的概念,路径有两种形式:

绝对路径:从根目录到当前文件的完整路径

相对路径:相对路径是相对于当前目录而言的。

程序处理文件需将文件读入内存,修改后,再写回硬盘。硬盘的访问延时,相比内存,是很慢的,操作系统一般是按块批量传输,而不是按字节。

1.文件和目录操作

文件和目录操作最终是与操作系统和文件系统相关的,不同的系统实现是不一样的,但Java中的java.io.File类提供了统一的接口,底层会通过本地方法调用操作系统和文件系统的具体细节。

1.1 构造方法:

    public File(String pathname)//pathname表示完整路径,该路径可以是相对路径,也可以是绝对路径
    public File(String parent, String child)
    public File(File parent, String child) 

新建一个File对象只是创建一个文件或目录对象,new之后,File对象的路径是不可变的

1.2文件元操作:

文件元数据主要包括文件名、路径、文件基本信息以及一些安全和权限相关的信息,主要有下面的方法:

    public String getName()//返回文件或目录名,不含路径
    public File getParentFile()//返回父目录的File对象
    public String getPath()//返回构造File对象时的完整路径名,包括路径和文件名称
    public boolean isAbsolute()//判断File中的路径是否是绝对路径
    public String getAbsolutePath()//返回完整的绝对路径名
    public boolean isFile()//是否为文件
    public boolean isDirectory()//是否为目录
    public boolean exists()//文件或目录是否存在
    public boolean canWrite()//是否可写
    public boolean canRead()//是否可读
    public boolean isHidden()//是否为隐藏文件
    public long lastModified()//最后一次修改时间
    public long length()//文件长度,字节数,对目录无意义

下面是对上述方法的使用,以及结果: 

public class Test {
    public static void main(String[] args) throws IOException {
        //File(String pathname);
        File file = new File("C:"+File.separator+"Users"+File.separator+"acer"+File.separator+"Desktop");
        //File(File parent, String child)
       File file1 = new File(file,"Text");
       //File(String parent, String child)
       File file2 = new File("C:"+File.separator+"Users"+File.separator+"acer"+File.separator+"Desktop"+File.separator+"Text","JavaIO");
        System.out.println(file.getName());
        System.out.println(file1.getName());
        System.out.println(file2.getName());

        System.out.println(file.getParentFile());
        System.out.println(file1.getParentFile());
        System.out.println(file2.getParentFile());

        System.out.println(file.getPath());
        System.out.println(file1.getPath());
        System.out.println(file2.getPath());

        System.out.println(file.isAbsolute());
        System.out.println(file1.isAbsolute());
        System.out.println(file2.isAbsolute());

        System.out.println(file.getAbsolutePath());
        System.out.println(file1.getAbsolutePath());
        System.out.println(file2.getAbsolutePath());

        System.out.println(file.isFile());
        System.out.println(file1.isFile());
        System.out.println(file2.isFile());

        System.out.println(file.isDirectory());
        System.out.println(file1.isDirectory());
        System.out.println(file2.isDirectory());

        System.out.println(file.exists());
        System.out.println(file1.exists());
        System.out.println(file2.exists());
    }
}

1.3 文件操作:

文件操作主要有创建、删除、重命名

    public boolean createNewFile() throws IOException//创建文件

创建成功返回true、否则返回false,创建新的文件内容为空。如果文件已存在,不会创建。

    public static File createTempFile(String prefix, String suffix, File directory) throws IOException//创建临时文件
    public static File createTempFile(String prefix, String suffix) throws IOException//创建临时文件

临时文件的完整路径名是系统指定的、唯一的,但可以通过参数制定前缀(prefix)、后缀(suffix)和目录(directory)。prefix是必须的,且至少要三个字符;suffix如果为空,则默认为.tmp;directory如果不指定获指定为null,则使用系统默认目录。

File 类的删除方法:

public boolean delete()
public void deleteOnExit()

delete 删除文件或目录,成功返回true,失败返回false。如果File是目录且不为空,则delete不会成功,返回false。

deleteOnExit,是先将File对象加入到待删除列表,在Java虚拟机正常退出的时候进行实际删除。

1.4 目录操作

当File代表目录的时候,可以执行目录相关的操作,如创建,遍历

创建目录:

public boolean  mkdir()
public boolean  mkdirs()

创建成功返回true,否则返回false,如果目录已存在,则返回false。

当某个中间不存在的时候,mkdir会失败,但是mkdirs会创建该目录。

遍历目录:

public  String[] list()
public  File[] listFiles()

返回的直接子目录或文件。

public class Test {
    public static void main(String[] args) throws IOException {

       File file = new File("C:"+File.separator+"Users"+File.separator+"acer"+File.separator+"Desktop"+File.separator+"Text","JavaIO01");
       file.mkdirs();
       File file1 = new File(file,"JavaIO.txt");
       file1.createNewFile();
    }
}

下面就是文件File使用的一些例子:

    public static void main(String[] args) throws IOException, ClassNotFoundException {
        new Thread(()->{
            listAllFiles(new File("C:"+File.separator+"Users"+File.separator+"acer"+File.separator+"Desktop"));
        },"输出线程").start();
        System.out.println("开始进行文件输出...");
    }
    public static void listAllFiles(File file){
        if (file.isDirectory()){
            File[] result = file.listFiles();
            if (result!=null){
                for(File file1:result){
                    listAllFiles(file);
                }
            }
        }
        else {
            System.out.println(file);
        }
    }

 

 2.二进制文件和字节流

2.1InputStream/OutputStream

这是基类,他们都是抽象类

1.InputStream:

    public abstract int read() throws IOException;

从流中读取一个字节,返回值为int,取值范围在0~255,当读到流结尾的时候,返回-1,如果流中没有数据,read方法会阻塞直到数据到来、流关闭、或异常出现。

public int read(byte b[]) throws IOException 

读入的字节放入参数数组中,一次最多放入的字节数为数组b的长度,但实际上读入的长度可能小于数组长度,返回值为实际读入的字节个数。

    public int read(byte b[], int off, int len) throws IOException

跟上述方法类似,只不过读入的第一个字节放入b[off],最多读取len个字节。

    public void close() throws IOException {}

不管是read方法是否抛出异常,都应该调用close方法,所以close方法通常被放在finally语句内。 

2.OutputStream

    public abstract void write(int b) throws IOException

向流中输入一个字节,参数虽然是int,但是只会用到最低的8位。

同样还有两个向流中写入的批量方法:

    public void write(byte b[]) throws IOException
    public void write(byte b[], int off, int len) throws IOException

将b数组中的字节输入到流中。

    public void flush() throws IOException
    public void close() throws IOException

flush方法是将缓冲而未实际写入数据进行实际写入,OutputStream中没有缓冲,flush代码为空,close方法就是关闭流操作。

2.2FileInputStream/FileOutputStream

FileInputStrea和FileOutputStream的输入源和输出目标是文件

1.FileOutputStream

    public class FileOutputStream extends OutputStream
    public FileOutputStream(String name) throws FileNotFoundException
    public FileOutputStream(File file) throws FileNotFoundException
    public FileOutputStream(String name, boolean append) throws FileNotFoundException
    public FileOutputStream(File file, boolean append) throws FileNotFoundException

可以从上面看出,FileOutputStream为OutputStream的子类。

在它的构造方法中,name,file都表示文件路径,append参数表示追加还是覆盖,true表示追加,false表示覆盖,当没有传入append参数值时,默认表示为覆盖。

2.FileInputStream

    public class FileInputStream extends InputStream
    public FileInputStream(String name) throws FileNotFoundException
    public FileInputStream(File file) throws FileNotFoundException

参数同样可以是文件路径或File对象,但必须是一个已经存在的文件,不能是目录。

2.3ByteArrayInputStream/ByteArrayOutputStream

ByteArrayInputStream和ByteArrayOutputStream输入源和输出目标是字节数组

1.ByteArrayOutputStream:

    public class ByteArrayOutputStream extends OutputStream
    public ByteArrayOutputStream(int size)
    public ByteArrayOutputStream()

 ByteArrayOutputStream输出目标是一个byte数组,这个数组的长度是根据数据内容动态扩展的,size指定的就是初始数组大小,如果没有指定,则长度为32.

再调用write方法的过程中,如果数组大小不够,会进行扩展,扩展策略同样是指数扩展,每次增加至少一倍。

2. ByteArrayInputStream:

public class ByteArrayInputStream extends InputStream
    public ByteArrayInputStream(byte buf[])
    public ByteArrayInputStream(byte buf[], int offset, int length)

ByteArrayInputStream是将byte数组包装为一个输入流,是一种适配器模式。第二个构造方法中以buf中的offset开始的length个字节为背后的数据,ByteArrayInputStream的所有数据都在内存,支持重复读取。 

2.4DataInputStream/DataOutputStream

DataInputStream和DataOutputStream都是装饰类,可以进行其他类型的读写,比如int、double。

1.DataOutputStream:

public class DataOutputStream extends FilterOutputStream implements DataOutput;
public class FilterOutputStream extends OutputStream

DataOutputStream是FilterOutputStream的子类,而 FilterOutputStream是OutputStream的子类;

FilterOutputStream的构造方法:

    public FilterOutputStream(OutputStream out)

而DataOutputStream的构造方法:

    public DataOutputStream(OutputStream out) {
        super(out);
    }

 接受了一个OutputStream的实例对像。

2.DataInputStream:

public class DataInputStream extends FilterInputStream implements DataInput
    public DataInputStream(InputStream in) {
        super(in);
    }

DataInputStream可以以各种基本和字符串读取, 

2.5BufferedInputStream/BufferedOutputStream

FileInputStream/FileOutputStream是没有缓冲区的,按单个字节读取的方式效率太过低下,方法就是将文件流包装到缓冲流中。

    public class BufferedInputStream extends FilterInputStream
    public class BufferedOutputStream extends FilterOutputStream 
    //构造方法
    public BufferedInputStream(InputStream in)
    public BufferedInputStream(InputStream in, int size)
    public BufferedOutputStream(OutputStream out)
    public BufferedOutputStream(OutputStream out, int size)

下面是BufferedInputStream的用法示例:

           InputStream inputStream = new BufferedInputStream(new FileInputStream("hello.txt"));
           OutputStream outputStream = new BufferedOutputStream(new FileOutputStream("hello.txt"));

总结:

(1)InputStream/OutputStream:是抽象基类,有很多面向流的代码,以他们为参数。

(2)FileInputStream/FileOutputStream:流的源和目的是文件

(3)ByteArrayInputStream/ByteArrayOutputStream:源和目的地是字节数组

(4)DataInputStream/DataOutputStream:装饰类,按基本类型和字符串读写流

(5)BufferedInputStream/BufferedOutputStream:装饰类,提供缓冲。

下面为一个文件拷贝的程序:

class CopyFileUtil{
    private CopyFileUtil(){

    }
    public static boolean fileIsExists(String path){
        return new File(path).exists();
    }
    public static void createParentsDir(String path){
        File file = new File(path);
        if (!file.getParentFile().exists()){
            file.getParentFile().exists();
        }
    }
    public static boolean copyFile(String sourcePath,String destPath){
        File inFile = new File(sourcePath);
        File outFile = new File(destPath);
        FileInputStream fileInputStream = null;
        FileOutputStream fileOutputStream = null;
        try {
            fileInputStream=new FileInputStream(inFile);
            fileOutputStream =new FileOutputStream(outFile);
            copyFileHandle(fileInputStream,fileOutputStream);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }finally {
            try {
                fileInputStream.close();
                fileOutputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return true;
    }
    private static void copyFileHandle(InputStream inputStream,OutputStream outputStream) throws IOException {
        long start = System.currentTimeMillis();
        int temp = 0;
        do{
            temp=inputStream.read();
            outputStream.write(temp);

        }while (temp!=-1);
        long end = System.currentTimeMillis();
        System.out.println("拷贝文件所花费的时间"+(end-start));
    }
}



public class Test {
    public static final File FILE = new File("C:"+File.separator+"Users"+File.separator+"acer"+File.separator+"Desktop","Text"+File.separator+"JavaIO"+File.separator+"JavaIO.txt");

    public static void main(String[] args) {

        if(args.length!=2){
            System.out.println("非法操作,命令为:java  CopyFile 原文件路径 目标文件路径");
            return;
        }
        String sourcePath = args[0];
        String destPath = args[1];
        if (CopyFileUtil.fileIsExists(sourcePath)){
            CopyFileUtil.createParentsDir(sourcePath);
            System.out.println(CopyFileUtil.copyFile(sourcePath,destPath)?"文件拷贝成功":"文件拷贝失败");
        }else {
            System.out.println("源文件不存在,无法进行拷贝" );
        }
    }
}

3.文本文件和字符流

上面都是如何以字节流的方式处理文件,对于文本文件,字节流没有编码的概念,不能按行处理,使用起来不太方便。

字节流是按字节读取的,而字符流是按char读取的,一个char在文件中保存的是几个字节预编码有关。

在Java中字符流的相关类:

3.1 Reader/Writer

其中主要方法有:

    public int read() throws IOException
    public int read(char cbuf[]) throws IOException
    abstract public int read(char cbuf[], int off, int len) throws IOException
     abstract public void close() throws IOException;
    public void write(int c) throws IOException
    public void write(char cbuf[]) throws IOException
    abstract public void write(char cbuf[], int off, int len) throws IOException;
    public void write(String str) throws IOException
    public void write(String str, int off, int len) throws IOException
    abstract public void flush() throws IOException;
    abstract public void close() throws IOException;

方法名称和含义与InputStream/OutputStream中的对应方法基本类似,区别就是,Reader/Writer处理的单位是char,例如:read读取的就是一个char,取值范围是0~65535. 

Writer还接受String类型的参数。

3.2 InputStreamReader/OutputStreamWriter

这是适配器,是可以将InputStream/OutputStream转化为Reader/Writer。

3.2.1 OutputStreamWriter:

    public class OutputStreamWriter extends Writer
    public OutputStreamWriter(OutputStream out, String charsetName)
    public OutputStreamWriter(OutputStream out)

 可以通过构造方法看出,除了OutputStream参数之外,还有一个charsetName,这是编码类型。如果没有传入,则按照系统默认编码。

3.2.2InputStreamReader:

    public class InputStreamReader extends Reader
    public InputStreamReader(InputStream in)
    public InputStreamReader(InputStream in, String charsetName)

同样,InputStreamReader里面依然有一个重要的参数是编码类型。 

3.3 FileReader/FileWriterr

public class FileReader extends InputStreamReader
    public FileReader(String fileName) throws FileNotFoundException
    public FileReader(File file) throws FileNotFoundException

public class FileWriter extends OutputStreamWriter
    public FileWriter(String fileName) throws IOException
    public FileWriter(String fileName, boolean append) throws IOException

上面fileName 表示文件名称。append表示追加还是覆盖。

FileReader/FileWriter不指定文件编码类型,只能使用默认编码。

3.4 CharArrayReader/CharArrayWriter

CharArrayWriter是与ByteArrayOutputStream类似,输出目标是char数组。

CharArrayReader与ByteArrayInputStream类似,将Char数组包装为一个Reader,是一种适配器模式。

public class CharArrayReader extends Reader
    public CharArrayReader(char buf[])
    public CharArrayReader(char buf[], int offset, int length)
public class CharArrayWriter extends Writer
    public CharArrayWriter(int initialSize)

3.5 StringReader/StringWriter

与CharArrayReader/CharArrayWriter类似,只是输入源为String。

3.6 BufferedReader/BufferedWriter

装饰类,提供缓冲,以及按行读写功能。

    public class BufferedWriter extends Writer
    public BufferedWriter(Writer out)
    public BufferedWriter(Writer out, int sz)
    public void newLine() throws IOException

参数中sz是缓冲大小,如果没有提供,默认为8192.其中newLine方法,可以输出平台特定的换行符。

    public class BufferedReader extends Reader
    public BufferedReader(Reader in, int sz)
    public BufferedReader(Reader in)
    public String readLine() throws IOException

 参数中sz是缓冲大小,如果没有提供,默认为8192.readLine方法返回一行内容,但不会包括换行符('\r'或'\n'或'\r\n'),读到流结尾时,返回null。

3.7 PrintWriter

    public class PrintWriter extends Writer
    public PrintWriter (Writer out)
    public PrintWriter(Writer out,boolean autoFlush)
    public PrintWriter(OutputStream out)
    public PrintWriter(String fileName) throws FileNotFoundException
    public PrintWriter(File file) throws FileNotFoundException

PrintWriter有很多的构造方法,可以接受文件路径名,文件对象,OutputStream,Writer等,构造方法中的autoFlush参数表示同步缓冲区的时机,如果为true,则在调用println、print、format方法的时候,同步缓冲区,如果没有传入,则不会自动同步。

在PrintWrite方法中的write(int b),PrintWriter是使用最低的两位,输出一个char。

首先看看往一个文件里输入:

class PrintUtil{
    private OutputStream outputStream;

    public PrintUtil(OutputStream outputStream) {
        this.outputStream = outputStream;
    }
    public void print(String str) {
        try {
            this.outputStream.write(str.getBytes());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    public void pringtln(String str){
        this.print(str+"\r\n");
    }
    public void print(int data){
        this.print(String.valueOf(data));
    }
    public void println(int data){
        this.pringtln(String.valueOf(data));
    }
    public void print(double data){
        this.pringtln(String.valueOf(data));
    }
    public void println(double data){
        this.pringtln(String.valueOf(data));
    }
}

public class Test {


    public static void main(String[] args) throws IOException {

        PrintUtil printUtil = new PrintUtil(new FileOutputStream(new File("C:"+File.separator+"Users"+File.separator+"acer"+File.separator+"Desktop"+File.separator+"Text"+File.separator+"JavaIO"+File.separator+"JavaIO1.txt")));
        printUtil.print("姓名:");
        printUtil.pringtln("王昊");
        printUtil.print("年龄");
        printUtil.println(23);
        printUtil.print("工资:");
        printUtil.println(2243.32);

    }

然后看看使用PrintWriter:

public class Test {


    public static void main(String[] args) throws IOException {

        PrintWriter printUtil = new PrintWriter(new FileOutputStream(new File("C:"+File.separator+"Users"+File.separator+"acer"+File.separator+"Desktop"+File.separator+"Text"+File.separator+"JavaIO"+File.separator+"JavaIO1.txt")));
        printUtil.print("姓名:");
        printUtil.println("王昊");
        printUtil.print("年龄");
        printUtil.println(23);
        printUtil.print("工资:");
        printUtil.println(2243.32);
        printUtil.close();
    }

 

3.8 Scanner

Scanner有很多形式的next()方法,可以读取下一个基本类型或行。

Scanner也有很多构造方法,可以接收File对象,InputStream、Reader作为参数。

    public boolean hasNext()
    public String next()

下面为用Scanner接收键盘上传入的生日: 

    public static void main(String[] args) throws IOException {

        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入生日:");
        if (scanner.hasNext("\\d{4}-\\d{2}-\\d{2}")){//利用正则表达式来判断格式
            String birthday = scanner.next();
            System.out.println("输入的生日为:"+birthday);
        }else {
            System.out.println("输入格式非法");
        }
        scanner.close();
    }

 下面是将文件的内容接收进来:

    public static void main(String[] args) throws IOException {
        Scanner scanner = new Scanner(new FileInputStream(new File("C:"+File.separator+"Users"+File.separator+"acer"+File.separator+"Desktop"+File.separator+"Text"+File.separator+"JavaIO"+File.separator+"JavaIO1.txt")));
        scanner.useDelimiter("\n");//以"\n"为分隔符
        while(scanner.hasNext()){
            System.out.println(scanner.next());
        }
        scanner.close();
}

3.9PrintStream

之前一直使用的System .out向屏幕上输出,其实就是一个PrintStream,输出目标是屏幕。除了System.out,java中还有System.in和System.err。

System.in表示标准输入,它是一个InputStream对象,输入源经常是键盘。

比如:

System in = new Scanner(System.in);

System.err表示标准错误流,一般异常和错误信息输入到这个流,它也是一个PrintStream对象,输入目标一般也是屏幕。

4.序列化和反序列化

对象序列化就是将内存中保存的对象变为二进制数据流的形式进行传输,或者是将其保存在文本中。要想实现序列化的类对象往往需要传输使用,同时这个类必须实现java.io.Serializable接口。这个接口没有任何方法,只是一个标识。

反序列化就是从流中恢复Java对象到内存。

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

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

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
public class Test {
    public static final File FILE = new File("C:"+File.separator+"Users"+File.separator+"acer"+File.separator+"Desktop","Text"+File.separator+"JavaIO"+File.separator+"JavaIO.txt");

    public static void main(String[] args) throws IOException, ClassNotFoundException {
        ser(new Person("wanghao",26));
        dser();
    }
    public static void ser(Object obj) throws IOException {
        ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream(FILE));
        oos.writeObject(obj);
        oos.close();
    }
    public static void dser() throws IOException, ClassNotFoundException {
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(FILE));
        System.out.println(ois.readObject());
        ois.close();
    }
}

 输出结果为:Person{name='null', age=26},transient关键字修饰的就是不可以序列化。

如果有错误请指正!!!

下次再补充,不多说了,敲代码了!!!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值