Java基础 ----- IO

目录

File类

构造函数:

方法:

示例:

注意事项:

1.2.处理临时文件方法:

创建临时文件:

删除临时文件:

使用临时目录:

确保临时文件在程序退出时删除:

读写临时文件:

注意事项:

1.3.遍历目录及其子目录下的文件

1. listFiles() 方法

2. list() 方法

3. 递归遍历

4. 使用FileFilter

5. 使用FilenameFilter

6. 异常处理

7. 符号链接

8. 文件属性

9. 文件路径

10. 遍历性能

11. 遍历限制

12. 线程安全

1.4.删除文件和目录

删除单个文件

删除目录

符号链接(Symbolic Links)

安全和性能考虑

字节流

1. 字节输入流:InputStream

2. 字节输出流:OutputStream

 3字节流的使用

读取字节数据

写入字节数据

3.1 字节流的包装器:FilterInputStream 和 FilterOutputStream

4. 文件字节流:FileInputStream 和 FileOutputStream

5. 数据字节流:DataInputStream

6. 数组字节流:ByteArrayInputStream 和 ByteArrayOutputStream

7. 缓冲字节流:BufferedInputStream 和 BufferedOutputStream

8. 转换流:InputStreamReader 和 OutputStreamWriter

9. 打印流:PrintStream

10. 资源管理

11. 字节序

12. 管道流

13.使用字节流的注意事项:


File

 Java中的File类是java.io包的一部分,它提供了一种方式来表示文件和目录。以下是File类的一些基础知识:

构造函数:
  • File(String pathname):创建一个新File对象,表示具有指定路径名的文件或目录。
  • File(String parent, String child):创建一个新File对象,表示具有指定父路径和子路径的文件或目录。
  • File(File parent, String child):创建一个新File对象,表示具有指定父路径(File对象)和子路径的文件或目录。
方法:
  • exists():检查此File对象表示的文件或目录是否存在。
  • isDirectory():检查此File对象表示的是否是一个目录。
  • isFile():检查此File对象表示的是否是一个文件。
  • length():返回此File对象表示的文件的长度。
  • mkdir():创建此File对象表示的目录。
  • delete():删除此File对象表示的文件或目录。
  • renameTo(File dest):重命名此File对象表示的文件或目录。
  • listFiles():返回一个File数组,表示此File对象表示的目录中的文件和目录。
  • getAbsolutePath():返回此File对象表示的文件或目录的绝对路径。
  • getCanonicalPath():返回此File对象表示的文件或目录的规范路径。
  • lastModified():返回此File对象表示的文件的最后修改时间。
  • setLastModified(long time):设置此File对象表示的文件的最后修改时间。
  • 示例:
// 创建File对象,表示当前目录下的"test.txt"文件
File file = new File("test.txt");

// 检查文件是否存在
if (file.exists()) {
    System.out.println("文件存在");
}

// 检查是否是文件
if (file.isFile()) {
    System.out.println("这是一个文件");
}

// 获取文件的长度
long fileSize = file.length();
System.out.println("文件大小:" + fileSize + "字节");

// 创建一个目录
File dir = new File("newDir");
dir.mkdir();

// 删除文件
file.delete();

// 重命名文件
File newFile = new File("newName.txt");
boolean renamed = file.renameTo(newFile);
if (renamed) {
    System.out.println("文件重命名成功");
}

// 获取文件的绝对路径
String absolutePath = file.getAbsolutePath();
System.out.println("文件的绝对路径:" + absolutePath);
注意事项:
  • File类主要用于文件和目录的抽象表示,并不涉及文件内容的I/O操作。
  • 在进行文件操作时,需要处理IOException异常。
  • 文件路径可以是相对路径或绝对路径。
  • 在使用listFiles()方法时,如果File对象表示的不是一个目录,那么返回的数组长度为0。
  • renameTo()方法在不同操作系统上的行为可能不同,需要谨慎使用。

1.2.处理临时文件方法:

在Java中,处理临时文件通常涉及到java.io.File类和java.nio.file包中的类。以下是一些常见的临时文件操作:

创建临时文件:
  1. 使用File.createTempFile()
    • 这个方法可以在默认临时文件夹中创建一个临时文件,并可以指定前缀和后缀。
    • 文件创建后立即被删除,你需要手动打开它。
File tempFile = File.createTempFile("temp", ".txt");
  1. 使用Files.createTempFile()
    • 这个方法在java.nio.file包中,允许你指定更多的参数,比如文件属性。
    • 返回的是一个Path对象,代表临时文件。
Path tempFilePath = Files.createTempFile("temp", ".txt");
删除临时文件:
  • 临时文件通常在程序结束时删除,但你也可以显式地删除它们。
if (tempFile.exists()) {
    tempFile.delete();
}
使用临时目录:
  • 你可以获取系统的临时文件夹,并在其中创建文件。
File tempDir = new File(System.getProperty("java.io.tmpdir"));
File tempFileInDir = new File(tempDir, "myTempFile.txt");

确保临时文件在程序退出时删除:
  • 使用java.io.FiledeleteOnExit()方法,确保文件在JVM退出时删除。
tempFile.deleteOnExit();
读写临时文件:
  • 你可以使用标准的I/O类来读写临时文件。
// 写入临时文件
try (BufferedWriter writer = new BufferedWriter(new FileWriter(tempFile))) {
    writer.write("Hello, World!");
}

// 读取临时文件
try (BufferedReader reader = new BufferedReader(new FileReader(tempFile))) {
    String line = reader.readLine();
    System.out.println(line);
}
注意事项:
  • 临时文件通常用于存储程序运行时的中间数据,不应该用于存储重要数据。
  • 临时文件的安全性取决于操作系统和JVM的实现,可能存在被其他用户访问的风险。
  • 使用deleteOnExit()方法删除临时文件时,文件的删除操作是在JVM退出时进行的,如果JVM没有正常退出,文件可能不会被删除。
  • 在多线程环境中,创建和删除临时文件的操作应该是线程安全的。

1.3.遍历目录及其子目录下的文件

Java中File类提供了一些方法来遍历目录及其子目录下的文件,以下是一些详细的知识点:

1. listFiles() 方法

listFiles() 是最基本的方法,用于列出目录中的所有文件和子目录。

  • File[] listFiles(): 返回一个File数组,包含目录中的所有文件和子目录。
  • File[] listFiles(FileFilter filter): 返回一个经过过滤器筛选的File数组。
  • File[] listFiles(FilenameFilter filter): 返回一个经过文件名过滤器筛选的File数组。

2. list() 方法

list() 方法是listFiles()的一个简单形式,它返回一个字符串数组,包含目录中的所有文件和子目录的名称。

  • String[] list(): 返回目录中所有文件和子目录的名称。
  • String[] list(FilenameFilter filter): 返回经过文件名过滤器筛选的文件和子目录的名称。

3. 递归遍历

要遍历所有子目录,你需要使用递归方法。

void listFilesRecursively(File dir) {
    if (dir.isDirectory()) {
        File[] files = dir.listFiles();
        if (files != null) {
            for (File file : files) {
                if (file.isDirectory()) {
                    listFilesRecursively(file);
                } else {
                    // 处理文件
                }
            }
        }
    }
}

4. 使用FileFilter

FileFilter接口允许你定义过滤条件,只列出满足条件的文件或目录。

FileFilter filter = file -> file.getName().endsWith(".txt");
File[] files = dir.listFiles(filter);

5. 使用FilenameFilter

FilenameFilter接口类似于FileFilter,但它只接受文件名作为参数。

FilenameFilter filter = (dir, name) -> name.endsWith(".txt");
File[] files = dir.listFiles(filter);

6. 异常处理

在遍历文件时,可能会抛出SecurityException,因为某些目录可能没有读取权限。

try {
    File[] files = dir.listFiles();
} catch (SecurityException se) {
    // 处理安全异常
}

7. 符号链接

在遍历目录时,需要注意符号链接(symlinks)。默认情况下,listFiles()不会跟随符号链接。

8. 文件属性

File对象提供了一些方法来获取文件的属性,如exists(), isDirectory(), isFile(), canRead(), canWrite(), length(), lastModified()等。

9. 文件路径

getAbsolutePath(), getCanonicalPath(), getPath(), getName()等方法可以用来获取文件的路径和名称。

10. 遍历性能

对于大型文件系统,递归遍历可能会消耗大量资源。在这种情况下,考虑使用java.nio.file包中的Files.walk()方法,它提供了更高效的遍历方式。

11. 遍历限制

listFiles()方法可能不会列出所有文件,特别是如果文件系统在遍历过程中发生变化。此外,它也不会列出隐藏文件,除非指定了相应的过滤器。

12. 线程安全

listFiles()方法不是线程安全的。如果你需要在多线程环境中使用它,需要自己管理同步。

1.4.删除文件和目录

在Java中,使用I/O操作删除文件和目录涉及到java.io.File类的一些方法。以下是删除文件和目录的详细知识点:

删除单个文件

  1. 使用delete()方法File对象的delete()方法可以用来删除单个文件。如果文件正在被使用或没有相应的权限,删除操作可能会失败。

    File file = new File("path/to/file.txt");
    boolean isDeleted = file.delete(); // 返回一个布尔值,指示是否删除成功

  2. 异常处理: 删除操作可能会抛出SecurityException,如果你的程序没有足够的权限去删除文件。

     
    try {
        boolean isDeleted = file.delete();
        if (isDeleted) {
            System.out.println("文件已被删除");
        } else {
            System.out.println("文件删除失败");
        }
    } catch (SecurityException se) {
        System.out.println("没有权限删除文件");
    }

删除目录

  1. 使用递归方法: 删除目录通常需要递归地删除其所有子文件和子目录。

     
    public static void deleteDirectory(File dir) {
        if (dir.isDirectory()) {
            File[] items = dir.listFiles();
            if (items != null) {
                for (File item : items) {
                    deleteDirectory(item); // 递归删除子文件和子目录
                }
            }
        }
        // 删除空目录
        dir.delete();
    }

  2. 删除非空目录delete()方法无法删除包含文件或目录的目录。必须先删除所有子文件和子目录。

  3. 使用FileUtils(Apache Commons IO库): Apache Commons IO库提供了FileUtils类,它有一个deleteDirectory(File directory)方法,可以方便地删除非空目录。

     
    import org.apache.commons.io.FileUtils;
    
    public class DeleteDirectory {
        public static void main(String[] args) {
            try {
                FileUtils.deleteDirectory(new File("path/to/directory"));
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

符号链接(Symbolic Links)

  1. 符号链接的处理: 默认情况下,delete()方法不会删除符号链接指向的目标文件。它只会删除符号链接本身。

  2. 强制删除符号链接的目标: 如果你需要删除符号链接指向的目标文件,需要先解析链接,然后删除。

     
    File symlink = new File("path/to/symlink");
    if (symlink.exists() && symlink.isDirectory()) {
        File target = symlink.getCanonicalFile();
        deleteDirectory(target);
    }
    symlink.delete();

安全和性能考虑

  1. 检查文件存在性: 在尝试删除之前,检查文件是否存在可以避免异常。

     
    if (file.exists()) {
        file.delete();
    }

  2. 处理并发删除: 在多线程环境中,文件可能会被其他线程删除。处理这种情况的一种方法是在delete()方法失败时进行重试。

  3. 使用deleteOnExit()方法: 如果你希望在程序退出时删除文件,可以使用deleteOnExit()方法。这个方法会在JVM退出时删除指定的文件。

     
    file.deleteOnExit();

  4. 日志记录: 在删除文件或目录时,记录日志可以帮助调试和跟踪文件操作。

  5. 清理临时文件: 对于临时文件,确保在不再需要时删除它们,以避免磁盘空间的浪费。

字节流

Java中的I/O(输入/输出)字节流是用于处理字节数据的流。字节流可以是输入流(从数据源读取数据)或输出流(向数据源写入数据)。以下是Java I/O字节流的基础知识点详细讲解:

1. 字节输入流:InputStream

  • FileInputStream:从文件中读取字节数据。
  • ByteArrayInputStream:从字节数组中读取数据。
  • PipedInputStream:从与另一个线程连接的管道中读取数据

InputStream是所有字节输入流的抽象类超类。它提供了基本的读取字节数据的方法。

  • 常用方法
    • int read(): 读取单个字节的数据,返回读取的字节(0到255的int值),如果已到达流的末尾,则返回-1。
    • int read(byte[] b): 读取一些字节数并将它们存储到数组b中,返回实际读取的字节数,如果已到达流的末尾,则返回-1。
    • int read(byte[] b, int off, int len): 从指定位置读取最多len个字节到数组b中,从偏移量off开始存储,返回实际读取的字节数,如果已到达流的末尾,则返回-1。
    • void close(): 关闭流,释放与之关联的所有资源。

2. 字节输出流:OutputStream

  • FileOutputStream:向文件写入字节数据。
  • ByteArrayOutputStream:向字节数组写入数据。
  • PipedOutputStream:向与另一个线程连接的管道中写入数据。

OutputStream是所有字节输出流的抽象类超类。它提供了基本的写入字节数据的方法。

  • 常用方法
    • void write(int b): 写入单个字节的数据。
    • void write(byte[] b): 写入一个byte数组的数据。
    • void write(byte[] b, int off, int len): 写入byte数组中从偏移量off开始的len个字节。
    • void flush(): 清空输出流,确保所有缓冲中的数据都被写出。
    • void close(): 关闭流,释放与之关联的所有资源。

 3.字节流的使用

读取字节数据

使用InputStream读取数据的基本方法是调用read()方法,它可以返回读取的字节(int类型,范围从0到255),或者返回-1表示已到达文件末尾。

FileInputStream fis = new FileInputStream("example.bin");
int byteRead = fis.read(); // 读取单个字节
while (byteRead != -1) {
    // 处理读取的字节
    byteRead = fis.read(); // 继续读取
}
fis.close(); // 关闭流
写入字节数据

使用OutputStream写入数据的基本方法是调用write(int b)方法,传入要写入的字节。

FileOutputStream fos = new FileOutputStream("example.bin");
fos.write(someByte); // 写入单个字节
// 或者写入字节数组
byte[] data = ...;
fos.write(data);
fos.close(); // 关闭流

3.1 字节流的包装器:FilterInputStream 和 FilterOutputStream

这些类提供了对其他输入/输出流的包装,以添加额外的功能。例如,BufferedInputStreamBufferedOutputStream提供了缓冲功能来提高I/O操作的效率。

4. 文件字节流:FileInputStream 和 FileOutputStream

  • FileInputStream 和 FileOutputStream 可以直接用于文件的读写操作。
  • 写入文件时,如果文件已存在,FileOutputStream会覆盖原有内容;如果需要追加内容,需要在构造函数中传入true
  • 这些类专门用于文件操作,它们继承自InputStreamOutputStream
  • FileInputStream: 从文件中读取字节。
  • FileOutputStream: 向文件写入字节。
FileOutputStream fos = new FileOutputStream("example.bin", true); // 追加模式

5. 数据字节流:DataInputStream

DataInputStream类用于读取Java基本数据类型,它继承自FilterInputStream并提供了对读取方法的覆盖,以支持基本数据类型的读取。

6. 数组字节流:ByteArrayInputStream 和 ByteArrayOutputStream

这些类使用字节数组作为数据源。

  • ByteArrayInputStream: 从字节数组中读取数据。
  • ByteArrayOutputStream: 向字节数组中写入数据。

7. 缓冲字节流:BufferedInputStream 和 BufferedOutputStream

  • BufferedInputStream 和 BufferedOutputStream:包装其他输入/输出流,提供内部缓冲区以减少实际的I/O操作次数。
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("example.bin"));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("example.bin"));

8. 转换流:InputStreamReader 和 OutputStreamWriter

  • DataInputStream:从InputStream中读取基本Java数据类型,并支持将字节转换为字符串。
  • DataOutputStream:将基本Java数据类型写入OutputStream,并支持写入字符串。
DataInputStream dis = new DataInputStream(new BufferedInputStream(new FileInputStream("example.bin")));
DataOutputStream dos = new DataOutputStream(new BufferedOutputStream(new FileOutputStream("example.bin")));

9. 打印流:PrintStream

PrintStream是一个输出流,可以方便地打印数据,它继承自OutputStream

10. 资源管理

  • 确保在I/O操作完成后关闭流,以释放系统资源。
  • 使用try-with-resources语句自动管理资源。
try (FileInputStream fis = new FileInputStream("example.bin");
     FileOutputStream fos = new FileOutputStream("example.bin")) {
    // I/O操作
} catch (IOException e) {
    e.printStackTrace();
}

11. 字节序

在跨平台进行字节数据交换时,需要注意字节序(大端或小端)的问题。

12. 管道流

PipedInputStreamPipedOutputStream可用于线程间通信。

13.使用字节流的注意事项:

  • 关闭流:始终要记得在操作完成后关闭流,以释放系统资源。
  • 异常处理:I/O操作可能会抛出IOException,需要适当处理。
try {
    // I/O操作
} catch (IOException e) {
    e.printStackTrace();
}
  • 线程安全:大多数字节流不是线程安全的,如果需要在多线程中使用,需要采取同步措施。
  • 数据丢失:在网络或I/O错误的情况下,数据可能会丢失或损坏。

字符流

字符流是IO流中的一种,专门用于处理字符数据。字符流主要用于文本文件的读写操作,它使用字符编码来转换字节和字符。以下是Java中字符流的详细介绍:

字符流的分类:

Reader:用于从字符输入流中读取字符数据。

字符输入流(Reader 类)的方法:

  1. read()

    • 读取单个字节的数据,返回读取的字符(int 类型,范围从 0 到 65535)。
    • 如果已经到达文件末尾,则返回 -1。
  2. read(char[] cbuf)

    • 读取一些字符并存储到字符数组 cbuf 中。
    • 返回实际读取的字符数量,或者在到达文件末尾时返回 -1。
  3. read(char[] cbuf, int off, int len)

    • 从指定位置 off 开始读取最多 len 个字符到字符数组 cbuf 中。
    • 返回实际读取的字符数量,或者在到达文件末尾时返回 -1。
  4. ready()

    • 检查是否还有更多数据可读。
    • 如果下一个 read() 调用能够立即读取至少一个字符,则返回 true。
  5. close()

    • 关闭该流并释放与此流相关联的所有资源。

字符输入流(Reader):

  • FileReader:从文件中读取字符数据。
  • FilterReader:作为过滤字符输入流的基类。
  • BufferedReader:从字符输入流中读取文本并缓冲字符,以便有效地读取字符、数组和行。
  • LineNumberReader:跟踪行号的文本行读取器。
  • PushbackReader:可以单个字符地将字符推回输入流的字符流。
  1. Writer:用于向字符输出流中写入字符数据。
字符输出流(Writer 类)的方法:
  1. write(int c)

    • 写入单个字符。
  2. write(char[] cbuf)

    • 写入字符数组 cbuf 中的所有字符。
  3. write(char[] cbuf, int off, int len)

    • 写入字符数组 cbuf 中从偏移量 off 开始的 len 个字符。
  4. write(String str)

    • 写入字符串 str 中的所有字符。
  5. write(String str, int off, int len)

    • 写入字符串 str 中从偏移量 off 开始的 len 个字符。
  6. flush()

    • 清空输出流,迫使所有缓冲中的数据被写出。
  7. close()

    • 关闭该流并释放与此流相关联的所有资源。

字符输出流(Writer):

  • FileWriter:向文件写入字符数据。
  • FilterWriter:作为过滤字符输出流的基类。
  • BufferedWriter:将字符写入字符输出流并可能缓冲字符,以便有效地写入字符、数组和字符串。
  • PrintWriter:向文本输出流打印对象的格式化表示形式。
  • CharArrayWriter:字符数组输出流。

带缓冲的字符流(BufferedReader 和 BufferedWriter):

  1. BufferedReader

    • 继承自 Reader 类,提供了一个字符数组的缓冲区,可以一次性读取多行文本。

    • 特殊方法:

      • readLine():读取一行文本,包括行终止符。
      • lines()(Java 8+):返回一个由文件行组成的流。
  2. BufferedWriter

    • 继承自 Writer 类,提供了一个字符数组的缓冲区,可以一次性写入多行文本。

字符编码:

字符流在读取或写入时需要指定字符编码,Java默认使用平台默认的字符编码。字符编码用于将字符转换为字节,反之亦然。

使用字符流读取文件:

FileReader fr = null;
try {
    fr = new FileReader("example.txt");
    int i;
    while ((i = fr.read()) != -1) {
        System.out.print((char) i);
    }
} catch (IOException e) {
    e.printStackTrace();
} finally {
    if (fr != null) {
        try {
            fr.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

使用字符流写入文件:

FileWriter fw = null;
try {
    fw = new FileWriter("example.txt");
    fw.write("Hello, World!");
} catch (IOException e) {
    e.printStackTrace();
} finally {
    if (fw != null) {
        try {
            fw.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

字符流的高级特性:

  • BufferedReader 和 BufferedWriter:这两个类提供了缓冲功能,可以提高读写效率。
  • PrintWriter:提供了格式化输出的功能,类似于 System.out
  • 字符集(Charset):从Java 7开始,可以使用 java.nio.charset.Charset 来指定字符集,而不是依赖平台默认的字符集。

注意事项:

  • 总是确保在操作完成后关闭流,以释放系统资源。
  • 使用 try-with-resources 语句可以自动管理资源,确保流在使用后被关闭。
  • 处理 IOException,因为IO操作可能会遇到各种I/O错误。

转换流

也称为桥接流 是用来在字节流和字符流之间进行转换的流。由于 Java 的 IO 流库中存在两种类型的流(字节流和字符流),在某些情况下,我们可能需要在它们之间进行转换。转换流提供了一种方便的方式来实现这种转换。

转换输入流(InputStreamReader)

`InputStreamReader` 是一个字符流,它读取字节并使用指定的字符编码将其转换为字符。它通常用于将 `InputStream` 转换为 `Reader`。

常用构造函数:

- `InputStreamReader(InputStream in)`:创建一个使用平台默认字符编码的 `InputStreamReader`。

- `InputStreamReader(InputStream in, String charsetName)`:创建一个使用指定字符编码的 `InputStreamReader`。

#### 示例代码:

InputStream is = ...; // 假设已经有一个字节输入流
try (InputStreamReader isr = new InputStreamReader(is, "UTF-8")) {
    int c;
    while ((c = isr.read()) != -1) {
        System.out.print((char) c); // 将读取的字符打印出来
    }
} catch (IOException e) {
    e.printStackTrace();
}

转换输出流(OutputStreamWriter)

OutputStreamWriter 是一个字符流,它将字符转换为字节并写入到输出流中。它通常用于将 Writer 转换为 OutputStream

常用构造函数:

- `OutputStreamWriter(OutputStream out)`:创建一个使用平台默认字符编码的 `OutputStreamWriter`。

- `OutputStreamWriter(OutputStream out, String charsetName)`:创建一个使用指定字符编码的 `OutputStreamWriter`。

OutputStream os = ...; // 假设已经有一个字节输出流
try (OutputStreamWriter osw = new OutputStreamWriter(os, "UTF-8")) {
    osw.write("Hello, World!");
    osw.flush(); // 确保所有字符都被写出
} catch (IOException e) {
    e.printStackTrace();
}

### 注意事项:

- 字符编码:在使用转换流时,指定正确的字符编码非常重要,否则可能会导致字符编码错误,出现乱码。

- 转换流是字符编码的桥梁:它们在字节和字符之间进行转换,使得可以处理字符数据而不是原始字节。

转换流使得 Java 的 IO 流库更加灵活,能够处理各种不同的数据格式和编码需求。在实际开发中,根据需要选择合适的流类型和编码方式,可以提高程序的健壮性和可移植性。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值