Java输入/输出(I/O)流

2023-05-16

文章目录

    • 本章学习要点
  • Java流是什么?输入/输出流又是什么?
    • 什么是输入/输出流
    • 输入流
    • 输出流
  • Java系统流
        • 例 1
  • Java字符编码介绍
  • Java File类(文件操作类)详解
    • 获取文件属性
        • 例 1
    • 创建和删除文件
        • 例 2
    • 创建和删除目录
        • 例 3
    • 遍历目录
        • 1. String[] list()
        • 2. String[] list(FilenameFilter filter)
        • 例 4
        • 例 5
  • Java动态读取文件内容
        • 例 1
  • Java字节流的使用:字节输入/输出流、文件输入/输出流、字节数组输入/输出流
    • 字节输入流
    • 字节输出流
    • 字节数组输入流
        • 例 1
    • 字节数组输出流
        • 例 2
    • 文件输入流
        • 例 3
    • 文件输出流
        • 例 4
  • Java字符流的使用:字符输入/输出流、字符文件和字符缓冲区的输入/输出流
    • 字符输入流
    • 字符输出流
    • 字符文件输入流
        • 例 1
    • 字符文件输出流
        • 例 2
    • 字符缓冲区输入流
        • 例 3
    • 字符缓冲区输出流
  • Java字节流和字符流的区别,如何区分输入流和输出流?
    • Java字节流和字符流的区别
        • 字节流:
        • 字符流:
        • 区别:
    • 区分输入流和输出流
  • Java项目实战:猜数字小游戏

转载于:http://c.biancheng.net/java/40/

在变量、数组、对象和集合中存储的数据是暂时存在的,一旦程序结束它们就会丢失。为了能够永久地保存程序创建的数据,需要将其保存到磁盘文件中,这样就可以在其他程序中使用它们。Java 的 I/O(输入/输出)技术可以将数据保存到文本文件和二进制文件中, 以达到永久保存数据的要求。

本章首先从基础的流概念、流的分类、系统流的使用开始介绍,进而介绍如何操作文件、读取文件和写入文件。掌握 Java 中 I/O 处理技术能够提高读者对数据的处理能力。

本章学习要点

  1. 了解输入/输出流的概念
  2. 了解 java.io 包中类的层次结构
  3. 掌握 File 类操作文件的方法
  4. 熟悉 RandomAccessFile 类的应用
  5. 熟练掌握字节数组输入/输出流的应用
  6. 掌握文件字节输入/输出流的应用
  7. 掌握对象输入/输出流的应用
  8. 掌握缓冲区输入/输出流的应用
  9. 掌握数据输入/输出流的应用
  10. 了解字节打印流和字符打印流
  11. 掌握文件字符文件输入/输出流的应用
  12. 掌握字符缓沖区输入/输出流的应用

Java流是什么?输入/输出流又是什么?

在 Java 中所有数据都是使用流读写的。流是一组有序的数据序列,将数据从一个地方带到另一个地方。根据数据流向的不同,可以分为输入(Input)流和输出(Output)流两种。

在学习输入和输出流之前,我们要明白为什么应用程序需要输入和输出流。例如,我们平时用的 Office 软件,对于 Word、Excel 和 PPT 文件,我们需要打开文件并读取这些文本,和编辑输入一些文本,这都需要利用输入和输出的功能。在现实生活中,输入和输出的例子比比皆是。

在前面章节中,我们经常使用“System.out.println”方法,它就是一个输出方法。后面我们会讲述更多的方法,并通过大量的实例来展示它们的用法。

下面详细介绍什么是输入流和输出流,以及 Java 中流的类型及每种类型的相关类。

什么是输入/输出流

Java 程序通过流来完成输入/输出,所有的输入/输出以流的形式处理。因此要了解 I/O 系统,首先要理解输入/输出流的概念。

输入就是将数据从各种输入设备(包括文件、键盘等)中读取到内存中,输出则正好相反,是将数据写入到各种输出设备(比如文件、显示器、磁盘等)。例如键盘就是一个标准的输入设备,而显示器就是一个标准的输出设备,但是文件既可以作为输入设备,又可以作为输出设备。

数据流是 Java 进行 I/O 操作的对象,它按照不同的标准可以分为不同的类别。

  • 按照流的方向主要分为输入流和输出流两大类。
  • 数据流按照数据单位的不同分为字节流和字符流。
  • 按照功能可以划分为节点流和处理流。

数据流的处理只能按照数据序列的顺序来进行,即前一个数据处理完之后才能处理后一个数据。数据流以输入流的形式被程序获取,再以输出流的形式将数据输出到其它设备。图 1 为输入流模式,图 2 为输出流模式。

输入流模式
图 1 输入流模式

输出流模式
图 2 输出流模式

输入流

Java 流相关的类都封装在 java.io 包中,而且每个数据流都是一个对象。所有输入流类都是 InputStream 抽象类(字节输入流)和 Reader 抽象类(字符输入流)的子类。其中 InputStream 类是字节输入流的抽象类,是所有字节输入流的父类,其层次结构如图 3 所示。

InputStream类的层次结构图
图 3 InputStream类的层次结构图

InputStream 类中所有方法遇到错误时都会引发 IOException 异常。如下是该类中包含的常用方法。

名称作用
int read()从输入流读入一个 8 字节的数据,将它转换成一个 0~ 255 的整数,返回一个整数,如果遇到输入流的结尾返回 -1
int read(byte[] b)从输入流读取若干字节的数据保存到参数 b 指定的字节数组中,返回的字节数表示读取的字节数,如果遇到输入流的结尾返回 -1
int read(byte[] b,int off,int len)从输入流读取若干字节的数据保存到参数 b 指定的字节数组中,其中 off 是指在数组中开始保存数据位置的起始下标,len 是指读取字节的位数。返回的是实际读取的字节数,如果遇到输入流的结尾则返回 -1
void close()关闭数据流,当完成对数据流的操作之后需要关闭数据流
int available()返回可以从数据源读取的数据流的位数。
skip(long n)从输入流跳过参数 n 指定的字节数目
boolean markSupported()判断输入流是否可以重复读取,如果可以就返回 true
void mark(int readLimit)如果输入流可以被重复读取,从流的当前位置开始设置标记,readLimit 指定可以设置标记的字节数
void reset()使输入流重新定位到刚才被标记的位置,这样可以重新读取标记过的数据

上述最后 3 个方法一般会结合在一起使用,首先使用 markSupported() 判断,如果可以重复读取,则使用 mark(int readLimit) 方法进行标记,标记完成之后可以使用 read() 方法读取标记范围内的字节数,最后使用 reset() 方法使输入流重新定位到标记的位置,继而完成重复读取操作。

Java 中的字符是 Unicode 编码,即双字节的,而 InputerStream 是用来处理单字节的,在处理字符文本时不是很方便。这时可以使用 Java 的文本输入流 Reader 类,该类是字符输入流的抽象类,即所有字符输入流的实现都是它的子类,该类的方法与 InputerSteam 类的方法类似,这里不再介绍。

输出流

在 Java 中所有输出流类都是 OutputStream 抽象类(字节输出流)和 Writer 抽象类(字符输出流)的子类。其中 OutputStream 类是字节输出流的抽象类,是所有字节输出流的父类,其层次结构如图 4 所示。

OutputStream类的层次结构图
图 4 OutputStream 类的层次结构图

OutputStream 类是所有字节输出流的超类,用于以二进制的形式将数据写入目标设备,该类是抽象类,不能被实例化。OutputStream 类提供了一系列跟数据输出有关的方法,如下所示。

名称作用
int write(b)将指定字节的数据写入到输出流
int write (byte[] b)将指定字节数组的内容写入输出流
int write (byte[] b,int off,int len)将指定字节数组从 off 位置开始的 len 字节的内容写入输出流
close()关闭数据流,当完成对数据流的操作之后需要关闭数据流
flush()刷新输出流,强行将缓冲区的内容写入输出流

Java系统流

每个 Java 程序运行时都带有一个系统流,系统流对应的类为 java.lang.System。Sytem 类封装了 Java 程序运行时的 3 个系统流,分别通过 in、out 和 err 变量来引用。这 3 个系统流如下所示:

  • System.in:标准输入流,默认设备是键盘。
  • System.out:标准输出流,默认设备是控制台。
  • System.err:标准错误流,默认设备是控制台。

以上变量的作用域为 public 和 static,因此在程序的任何部分都不需引用 System 对象就可以使用它们。

例 1

下面的程序演示了如何使用 System.in 读取字节数组,使用 System.out 输出字节数组。

public class Test01 {
    public static void main(String[] args) {
        byte[] byteData = new byte[100]; // 声明一个字节数组
        System.out.println("请输入英文:");
        try {
            System.in.read(byteData);
        } catch (IOException e) {
            e.printStackTrace();
        }
        System.out.println("您输入的内容如下:");
        for (int i = 0; i < byteData.length; i++) {
            System.out.print((char) byteData[i]);
        }
    }
}

该程序的运行结果如下所示:

请输入英文:
abcdefg hijklmn opqrst uvwxyz
您输入的内容如下:
abcdefg hijklmn opqrst uvwxyz

System.in 是 InputStream 类的一个对象,因此上述代码的 System.in.read() 方法实际是访问 InputStream 类定义的 read() 方法。该方法可以从键盘读取一个或多个字符。对于 System.out 输出流主要用于将指定内容输出到控制台。

System.out 和 System.error 是 PrintStream 类的对象。因为 PrintStream 是一个从 OutputStream 派生的输出流,所以它还执行低级别的 write() 方法。因此,除了 print() 和 println() 方法可以完成控制台输出以外,System.out 还可以调用 write() 方法实现控制台输出。

write() 方法的简单形式如下:

void write(int byteval) throws IOException

该方法通过 byteval 参数向文件写入指定的字节。在实际操作中,print() 方法和 println() 方法比 write() 方法更常用。

注意:尽管它们通常用于对控制台进行读取和写入字符,但是这些都是字节流。因为预定义流是没有引入字符流的 Java 原始规范的一部分,所以它们不是字符流而是字节流,但是在 Java 中可以将它们打包到基于字符的流中使用。

Java字符编码介绍

计算机中,任何的文字都是以指定的编码方式存在的,在 Java 程序的开发中最常见的是 ISO8859-1、GBK/GB2312、Unicode、 UTF 编码。

Java 中常见编码说明如下:

  • ISO8859-1:属于单字节编码,最多只能表示 0~255 的字符范围。
  • GBK/GB2312:中文的国标编码,用来表示汉字,属于双字节编码。GBK 可以表示简体中文和繁体中文,而 GB2312 只能表示简体中文。GBK 兼容 GB2312。
  • Unicode:是一种编码规范,是为解决全球字符通用编码而设计的。UTF-8 和 UTF-16 是这种规范的一种实现,此编码不兼容 ISO8859-1 编码。Java 内部采用此编码。
  • UTF:UTF 编码兼容了 ISO8859-1 编码,同时也可以用来表示所有的语言字符,不过 UTF 编码是不定长编码,每一个字符的长度为 1~6 个字节不等。一般在中文网页中使用此编码,可以节省空间。

在程序中如果处理不好字符编码,就有可能出现乱码问题。例如现在本机的默认编码是 GBK,但在程序中使用了 ISO8859-1 编码,则就会出现字符的乱码问题。就像两个人交谈,一个人说中文,另外一个人说英语,语言不同就无法沟通。为了避免产生乱码,程序编码应与本地的默认编码保持一致。

本地的默认编码可以使用 System 类查看。Java 中 System 类可以取得与系统有关的信息,所以直接使用此类可以找到系统的默认编码。方法如下所示:

public static Properties getProperty()

使用上述方法可以查看 JVM 的默认编码,代码如下:

public static void main(String[] args) {
    // 获取当前系统编码
    System.out.println("系统默认编码:" + System.getProperty("file.encoding"));
}

运行结果如下:

系统默认编码:GBK

可以看出,现在操作系统的默认编码是 GBK。

下面通过一个示例讲解乱码的产生。现在本地的默认编码是 GBK,下面通过 ISO8859-1 编码对文字进行编码转换。如果要实现编码的转换可以使用 String 类中的 getBytes(String charset) 方法,此方法可以设置指定的编码,该方法的格式如下:

public byte[] getBytes(String charset);

示例代码如下:

public class Test {
    public static void main(String[] args) throws Exception {
        File f = new File("D:" + File.separator + "test.txt");
        // 实例化输出流
        OutputStream out = new FileOutputStream(f);
        // 指定ISO8859-1编码
        byte b[] = "C语言中文网,你好!".getBytes("ISO8859-1");
        // 保存转码之后的数据
        out.write(b);
        // 关闭输出流
        out.close();
    }
}

运行结果如下:

img

可以发现,因为编码不一致,所以在保存时出现了乱码。在 Java 的开发中,乱码是一个比较常见的问题,乱码的产生就有一个原因,即输出内容的编码与接收内容的编码不一致。

Java File类(文件操作类)详解

在 Java 中,File 类是 java.io 包中唯一代表磁盘文件本身的对象,也就是说,如果希望在程序中操作文件和目录,则都可以通过 File 类来完成。File 类定义了一些方法来操作文件,如新建、删除、重命名文件和目录等。

File 类不能访问文件内容本身,如果需要访问文件内容本身,则需要使用输入/输出流。

File 类提供了如下三种形式构造方法。

  1. File(String path):如果 path 是实际存在的路径,则该 File 对象表示的是目录;如果 path 是文件名,则该 File 对象表示的是文件。
  2. File(String path, String name):path 是路径名,name 是文件名。
  3. File(File dir, String name):dir 是路径对象,name 是文件名。

使用任意一个构造方法都可以创建一个 File 对象,然后调用其提供的方法对文件进行操作。在表 1 中列出了 File 类的常用方法及说明。

方法名称说明
boolean canRead()测试应用程序是否能从指定的文件中进行读取
boolean canWrite()测试应用程序是否能写当前文件
boolean delete()删除当前对象指定的文件
boolean exists()测试当前 File 是否存在
String getAbsolutePath()返回由该对象表示的文件的绝对路径名
String getName()返回表示当前对象的文件名或路径名(如果是路径,则返回最后一级子路径名)
String getParent()返回当前 File 对象所对应目录(最后一级子目录)的父目录名
boolean isAbsolute()测试当前 File 对象表示的文件是否为一个绝对路径名。该方法消除了不同平台的差异,可以直接判断 file 对象是否为绝对路径。在 UNIX/Linux/BSD 等系统上,如果路径名开头是一条斜线/,则表明该 File 对象对应一个绝对路径;在 Windows 等系统上,如果路径开头是盘符,则说明它是一个绝对路径。
boolean isDirectory()测试当前 File 对象表示的文件是否为一个路径
boolean isFile()测试当前 File 对象表示的文件是否为一个“普通”文件
long lastModified()返回当前 File 对象表示的文件最后修改的时间
long length()返回当前 File 对象表示的文件长度
String[] list()返回当前 File 对象指定的路径文件列表
String[] list(FilenameFilter)返回当前 File 对象指定的目录中满足指定过滤器的文件列表
boolean mkdir()创建一个目录,它的路径名由当前 File 对象指定
boolean mkdirs()创建一个目录,它的路径名由当前 File 对象指定
boolean renameTo(File)将当前 File 对象指定的文件更名为给定参数 File 指定的路径名

File 类中有以下两个常用常量:

  • public static final String pathSeparator:指的是分隔连续多个路径字符串的分隔符,Windows 下指;。例如 java -cp test.jar;abc.jar HelloWorld
  • public static final String separator:用来分隔同一个路径字符串中的目录的,Windows 下指/。例如 C:/Program Files/Common Files

注意:可以看到 File 类的常量定义的命名规则不符合标准命名规则,常量名没有全部大写,这是因为 Java 的发展经过了一段相当长的时间,而命名规范也是逐步形成的,File 类出现较早,所以当时并没有对命名规范有严格的要求,这些都属于 Java 的历史遗留问题。

Windows 的路径分隔符使用反斜线“\”,而 Java 程序中的反斜线表示转义字符,所以如果需要在 Windows 的路径下包括反斜线,则应该使用两条反斜线或直接使用斜线“/”也可以。Java 程序支持将斜线当成平台无关的路径分隔符。

假设在 Windows 操作系统中有一文件 D:\javaspace\hello.java,在 Java 中使用的时候,其路径的写法应该为 D:/javaspace/hello.java 或者 D:\\javaspace\\hello.java

获取文件属性

在 Java 中获取文件属性信息的第一步是先创建一个 File 类对象并指向一个已存在的文件, 然后调用表 1 中的方法进行操作。

例 1

假设有一个文件位于 C:\windows\notepad.exe。编写 Java 程序获取并显示该文件的长度、是否可写、最后修改日期以及文件路径等属性信息。实现代码如下:

public class Test02 {
    public static void main(String[] args) {
        String path = "C:/windows/"; // 指定文件所在的目录
        File f = new File(path, "notepad.exe"); // 建立File变量,并设定由f变量引用
        System.out.println("C:\\windows\\notepad.exe文件信息如下:");
        System.out.println("============================================");
        System.out.println("文件长度:" + f.length() + "字节");
        System.out.println("文件或者目录:" + (f.isFile() ? "是文件" : "不是文件"));
        System.out.println("文件或者目录:" + (f.isDirectory() ? "是目录" : "不是目录"));
        System.out.println("是否可读:" + (f.canRead() ? "可读取" : "不可读取"));
        System.out.println("是否可写:" + (f.canWrite() ? "可写入" : "不可写入"));
        System.out.println("是否隐藏:" + (f.isHidden() ? "是隐藏文件" : "不是隐藏文件"));
        System.out.println("最后修改日期:" + new Date(f.lastModified()));
        System.out.println("文件名称:" + f.getName());
        System.out.println("文件路径:" + f.getPath());
        System.out.println("绝对路径:" + f.getAbsolutePath());
    }
}

在上述代码中 File 类构造方法的第一个参数指定文件所在位置,这里使用C:/作为文件的实际路径;第二个参数指定文件名称。创建的 File 类对象为 f,然后通过 f 调用方法获取相应的属性,最终运行效果如下所示。

C:\windows\notepad.exe文件信息如下:
============================================
文件长度:193536字节
文件或者目录:是文件
文件或者目录:不是目录
是否可读:可读取
是否可写:可写入
是否隐藏:不是隐藏文件
最后修改日期:Mon Dec 28 02:55:19 CST 2016
文件名称:notepad.exe
文件路径:C:\windows\notepad.exe
绝对路径:C:\windows\notepad.exe

创建和删除文件

File 类不仅可以获取已知文件的属性信息,还可以在指定路径创建文件,以及删除一个文件。创建文件需要调用 createNewFile() 方法,删除文件需要调用 delete() 方法。无论是创建还是删除文件通常都先调用 exists() 方法判断文件是否存在。

例 2

假设要在 C 盘上创建一个 test.txt 文件,程序启动时会检测该文件是否存在,如果不存在则创建;如果存在则删除它再创建。

实现代码如下:

public class Test03 {
    public static void main(String[] args) throws IOException {
        File f = new File("C:\\test.txt"); // 创建指向文件的File对象
        if (f.exists()) // 判断文件是否存在
        {
            f.delete(); // 存在则先删除
        }
        f.createNewFile(); // 再创建
    }
}

运行程序之后可以发现,在 C 盘中已经创建好了 test.txt 文件。但是如果在不同的操作系统中,路径的分隔符是不一样的,例如:

  • Windows 中使用反斜杠\表示目录的分隔符。
  • Linux 中使用正斜杠/表示目录的分隔符。

那么既然 Java 程序本身具有可移植性的特点,则在编写路径时最好可以根据程序所在的操作系统自动使用符合本地操作系统要求的分隔符,这样才能达到可移植性的目的。要实现这样的功能,则就需要使用 File 类中提供的两个常量。

代码修改如下:

public static void main(String[] args) throws IOException {
    String path = "C:" + File.separator + "test.txt"; // 拼凑出可以适应操作系统的路径
    File f = new File(path);
    if (f.exists()) // 判断文件是否存在
    {
        f.delete(); // 存在则先删除
    }
    f.createNewFile(); // 再创建
}

程序的运行结果和前面程序一样,但是此时的程序可以在任意的操作系统中使用。

注意:在操作文件时一定要使用 File.separator 表示分隔符。在程序的开发中,往往会使用 Windows 开发环境,因为在 Windows 操作系统中支持的开发工具较多,使用方便,而在程序发布时往往是直接在 Linux 或其它操作系统上部署,所以这时如果不使用 File.separator,则程序运行就有可能存在问题。关于这一点我们在以后的开发中一定要有所警惕。

创建和删除目录

File 类除了对文件的创建和删除外,还可以创建和删除目录。创建目录需要调用 mkdir() 方法,删除目录需要调用 delete() 方法。无论是创建还是删除目录都可以调用 exists() 方法判断目录是否存在。

例 3

编写一个程序判断 C 盘根目录下是否存在 config 目录,如果存在则先删除再创建。实现代码如下:

public class Test04 {
    public static void main(String[] args) {
        String path = "C:/config/"; // 指定目录位置
        File f = new File(path); // 创建File对象
        if (f.exists()) {
            f.delete();
        }
        f.mkdir(); // 创建目录
    }
}

遍历目录

通过遍历目录可以在指定的目录中查找文件,或者显示所有的文件列表。File 类的 list() 方法提供了遍历目录功能,该方法有如下两种重载形式。

1. String[] list()

该方法表示返回由 File 对象表示目录中所有文件和子目录名称组成的字符串数组,如果调用的 File 对象不是目录,则返回 null。

提示:list() 方法返回的数组中仅包含文件名称,而不包含路径。但不保证所得数组中的相同字符串将以特定顺序出现,特别是不保证它们按字母顺序出现。

2. String[] list(FilenameFilter filter)

该方法的作用与 list() 方法相同,不同的是返回数组中仅包含符合 filter 过滤器的文件和目录,如果 filter 为 null,则接受所有名称。

例 4

假设要遍历 C 盘根目录下的所有文件和目录,并显示文件或目录名称、类型及大小。使用 list() 方法的实现代码如下:

public class Test05 {
    public static void main(String[] args) {
        File f = new File("C:/"); // 建立File变量,并设定由f变量变数引用
        System.out.println("文件名称\t\t文件类型\t\t文件大小");
        System.out.println("===================================================");
        String fileList[] = f.list(); // 调用不带参数的list()方法
        for (int i = 0; i < fileList.length; i++) { // 遍历返回的字符数组
            System.out.print(fileList[i] + "\t\t");
            System.out.print((new File("C:/", fileList[i])).isFile() ? "文件" + "\t\t" : "文件夹" + "\t\t");
            System.out.println((new File("C:/", fileList[i])).length() + "字节");
        }
    }
}

由于 list() 方法返回的字符数组中仅包含文件名称,因此为了获取文件类型和大小,必须先转换为 File 对象再调用其方法。如下所示的是实例的运行效果:

文件名称  文件类型  文件大小
===================================================
$Recycle.Bin  文件夹  4096字节
Documents and Settings  文件夹  0字节
Download  文件夹  0字节
DRIVERS  文件夹  0字节
FibocomLog  文件夹  0字节
Gateface  文件夹  0字节
GFPageCache  文件夹  0字节
hiberfil.sys  文件  3375026176字节
Intel  文件夹  0字节
KuGou  文件夹  0字节
logs  文件夹  0字节
msdia80.dll  文件  904704字节
MSOCache  文件夹  0字节
MyDownloads  文件夹  0字节
MyDrivers  文件夹  0字节
news.template  文件  417字节
NVIDIA  文件夹  0字节
OneDriveTemp  文件夹  0字节
opt  文件夹  0字节
pagefile.sys  文件  6442450944字节
PerfLogs  文件夹  0字节
Program Files  文件夹  12288字节
Program Files (x86)  文件夹  8192字节
ProgramData  文件夹  12288字节
QMDownload  文件夹  0字节
Recovery  文件夹  0字节
swapfile.sys  文件  268435456字节
System Volume Information  文件夹  12288字节
Users  文件夹  4096字节
Windows  文件夹  16384字节

例 5

假设希望只列出目录下的某些文件,这就需要调用带过滤器参数的 list() 方法。首先需要创建文件过滤器,该过滤器必须实现 java.io.FilenameFilter 接口,并在 accept() 方法中指定允许的文件类型。

如下所示为允许 SYS、TXT 和 BAK 格式文件的过滤器实现代码:

public class ImageFilter implements FilenameFilter {
    // 实现 FilenameFilter 接口
    @Override
    public boolean accept(File dir, String name) {
        // 指定允许的文件类型
        return name.endsWith(".sys") || name.endsWith(".txt") || name.endsWith(".bak");
    }
}

上述代码创建的过滤器名称为 ImageFilter,接下来只需要将该名称传递给 list() 方法即可实现筛选文件。如下所示为修改后的 list() 方法,其他代码与例 4 相同,这里不再重复。

String fileList[] = f.list(new ImageFilter());

再次运行程序,遍历结果如下所示:

文件名称        文件类型        文件大小
===================================================
offline_FtnInfo.txt        文件        296字节
pagefile.sys        文件        8436592640字节

Java动态读取文件内容

所谓动态读取是指从文件的任意位置开始访问文件,而不是必须从文件开始位置读取到文件末尾。动态读取需要用到 Java 中的 RandomAccessFile 类。

RandomAccessFile 是 Java 输入/输出流体系中功能最丰富的文件内容访问类,它提供了众多的方法来访问文件内容,它既可以读取文件内容,也可以向文件输出数据。由于 RandomAccessFile 可以从任意位置访问文件,所以在只需要访问文件部分内容的情况下,使用 RandonAccessFile 类是一个很好的选择。

RandomAccessFile 对象包含了一个记录指针,用以标识当前读写处的位置,当程序新创建一个 RandomAccessFile 对象时,该对象的文件记录指针位于文件头(也就是 0 处),当读/写了 n 个字节后,文件记录指针将会向后移动 n 个字节。除此之外,RandonAccessFile 可以自由移动该记录指针,既可以向前移动,也可以向后移动。

RandomAccessFile 类中提供了一些常用读取和写入数据的方法,如表 1 所示。

方法名及返回值类型说明
boolean readBoolean()从文件中读取一个 boolean 值
byte readByte()从文件中读取一个带符号位的字节
char readChar()从文件中读取一个字符
int readlnt()从文件中读取一个带符号位的整数
long readLong()从文件中读取一个带符号位的 long 值
String readLine()从文件中读取下一行文本
void seek(long pos)指定从文件起始位置开始的指针偏移量
void writeBoolean(boolean v)以字节的形式向文件中写入一个 boolean 值
void writeByte(int v)以单字节的形式向文件中写入一个 byte 值
void writeChar(int v)以双字节的形式向文件中写入一个 char 值
void writelnt(int v)以4字节的形式向文件中写入一个整数
writeLong(long v)以8字节的形式向文件中写入一个 long 值
void writeBytes(String s)以字节序列的形式向文件中写入一个字符串
void skipBytes(int n)以当前文件指针位置为起始点,跳过 n 字节

RandomAccessFile 类的构造方法有如下两种重载形式。

  1. RandomAccessFile(File file, String mode):访问参数 file 指定的文件,访问形式由参数 mode 指定,mode 参数有两个常用的可选值 r 和 rw,其中 r 表示只读,rw 表示读写。
  2. RandomAccessFile(String name, String mode):访问参数 name 指定的文件,mode 参数的含义同上。

注意:如果使用 rw 方式声明 RandomAccessFile 对象时,要写入的文件不存在,系统将自动进行创建。

例 1

编写一个程序,使用 RandomAccessFileDemo 类创建一个 words.txt 文件,然后写入一个长中文字符串,再从第 6 个字节开始读取并输出。

1)创建一个 RandomAccessFileDemo 类对象。在 main() 方法中创建到 D:\JavaCodes\words.txt 的 File 对象,如果该文件已经存在则先删除再创建,代码如下所示。

public class RandomAccessFileDemo {
    public static void main(String[] args) {
        try {
            File file = new File("D:\\myJava\\words.txt"); // 指定文件路径
            if (file.exists()) { // 判断文件是否存在
                file.delete();
                file.createNewFile();
            }
        } catch (IOException e) {
            System.out.print(e);
        }
    }
}

2)创建 RandomAccessFile 对象,以读写方式操作 File 对象。定义一个要写入的字符串,再将其进行格式的转换。这样是为了使其写入文件的内容不出现乱码,再将转换后的内容写入文件,代码如下所示。

RandomAccessFile raf = new RandomAccessFile(file,"rw");
String str1 = "晴天,阴天,多云,小雨,大风,中雨,小雪,雷阵雨";    // 要写入的字符串
String str2 = new String(str1.getBytes("GBK"),"ISO-8859-1");    // 编码转换
raf.writeBytes(str2);    //写入文件

3)打印出当前指针的位置,然后将其移动到第 6 个字节。再定义一个长度为 2 的 byte 数组,然后开始进行内容的循环读取,将读出的内容以字符串的形式输出到控制台,代码如下所示。

System.out.println("当前文件指针的位置:" + raf.getFilePointer());
raf.seek(6); // 移动文件指针
System.out.println("从文件头跳过6个字节,现在文件内容如下:");
byte[] buffer = new byte[2];
int len = 0;
while ((len = raf.read(buffer, 0, 2)) != -1) {
    System.out.print(new String(buffer, 0, len)); // 输出文件内容
}

4)运行程序,程序运行结果如下所示,显示了写入字符串后指针的位置,以及从文件开关跳过 6 个字节后读取到的字符串,图 1 为写入文件中的字符串内容。

当前文件指针的位置:48
从文件头跳过6个字节,现在文件内容如下:
阴天,多云,小雨,大风,中雨,小雪,雷阵雨

word.txt 文件内容
图 1 word.txt 文件内容

对比输出结果和图 1 发现,中文字符串已成功写入记事本中,但是读出的字符串内容少了两个字和一个逗号,这是由于使用 RandomAccessFile 的 seek() 方法跳过了前 6 字节(1 个汉字占两字节)。在本程序中将中文字符串进行了编码转换,然后再写入文件。读取时调用的是带有 3 个参数的 read() 方法将记事本中的内容读取出来。

注意:要写进文本的内容是中文,如果不进行字符的转换,写进去的会是乱码,读取出来的内容也会是乱码。

Java字节流的使用:字节输入/输出流、文件输入/输出流、字节数组输入/输出流

在本章的第一节《Java流的概念》中就提到 InputStream 是 Java 所有字节输入流类的父类,OutputStream 是 Java 所有字节输出流类的父类,它们都是一个抽象类,因此继承它们的子类要重新定义父类中的抽象方法。

下面首先介绍上述两个父类提供的常用方法,然后介绍如何使用它们的子类输入和输出字节流,包括 ByteArrayInputStream 类、ByteArrayOutputStream 类、FileInputStream 类和 FileOutputStream 类。

字节输入流

InputStream 类及其子类的对象表示字节输入流,InputStream 类的常用子类如下。

  • ByteArrayInputStream 类:将字节数组转换为字节输入流,从中读取字节。
  • FileInputStream 类:从文件中读取数据。
  • PipedInputStream 类:连接到一个 PipedOutputStream(管道输出流)。
  • SequenceInputStream 类:将多个字节输入流串联成一个字节输入流。
  • ObjectInputStream 类:将对象反序列化。

使用 InputStream 类的方法可以从流中读取一个或一批字节。表 1 列出了 InputStream 类的常用方法。

方法名及返回值类型说明
int read()从输入流中读取一个 8 位的字节,并把它转换为 0~255 的整数,最后返回整数。 如果返回 -1,则表示已经到了输入流的末尾。为了提高 I/O 操作的效率,建议尽量 使用 read() 方法的另外两种形式
int read(byte[] b)从输入流中读取若干字节,并把它们保存到参数 b 指定的字节数组中。 该方法返回 读取的字节数。如果返回 -1,则表示已经到了输入流的末尾
int read(byte[] b, int off, int len)从输入流中读取若干字节,并把它们保存到参数 b 指定的字节数组中。其中,off 指 定在字节数组中开始保存数据的起始下标;len 指定读取的字节数。该方法返回实际 读取的字节数。如果返回 -1,则表示已经到了输入流的末尾
void close()关闭输入流。在读操作完成后,应该关闭输入流,系统将会释放与这个输入流相关 的资源。注意,InputStream 类本身的 close() 方法不执行任何操作,但是它的许多 子类重写了 close() 方法
int available()返回可以从输入流中读取的字节数
long skip(long n)从输入流中跳过参数 n 指定数目的字节。该方法返回跳过的字节数
void mark(int readLimit)在输入流的当前位置开始设置标记,参数 readLimit 则指定了最多被设置标记的字 节数
boolean markSupported()判断当前输入流是否允许设置标记,是则返回 true,否则返回 false
void reset()将输入流的指针返回到设置标记的起始处

注意:在使用 mark() 方法和 reset() 方法之前,需要判断该文件系统是否支持这两个方法,以避免对程序造成影响。

字节输出流

OutputStream 类及其子类的对象表示一个字节输出流。OutputStream 类的常用子类如下。

  • ByteArrayOutputStream 类:向内存缓冲区的字节数组中写数据。
  • FileOutputStream 类:向文件中写数据。
  • PipedOutputStream 类:连接到一个 PipedlntputStream(管道输入流)。
  • ObjectOutputStream 类:将对象序列化。

利用 OutputStream 类的方法可以从流中写入一个或一批字节。表 2 列出了 OutputStream 类的常用方法。

方法名及返回值类型说明
void write(int b)向输出流写入一个字节。这里的参数是 int 类型,但是它允许使用表达式, 而不用强制转换成 byte 类型。为了提高 I/O 操作的效率,建议尽量使用 write() 方法的另外两种形式
void write(byte[] b)把参数 b 指定的字节数组中的所有字节写到输出流中
void write(byte[] b,int off,int len)把参数 b 指定的字节数组中的若干字节写到输出流中。其中,off 指定字节 数组中的起始下标,len 表示元素个数
void close()关闭输出流。写操作完成后,应该关闭输出流。系统将会释放与这个输出 流相关的资源。注意,OutputStream 类本身的 close() 方法不执行任何操 作,但是它的许多子类重写了 close() 方法
void flush()为了提高效率,在向输出流中写入数据时,数据一般会先保存到内存缓冲 区中,只有当缓冲区中的数据达到一定程度时,缓冲区中的数据才会被写 入输出流中。使用 flush() 方法则可以强制将缓冲区中的数据写入输出流, 并清空缓冲区

字节数组输入流

ByteArrayInputStream 类可以从内存的字节数组中读取数据,该类有如下两种构造方法重载形式。

  1. ByteArrayInputStream(byte[] buf):创建一个字节数组输入流,字节数组类型的数据源由参数 buf 指定。
  2. ByteArrayInputStream(byte[] buf,int offse,int length):创建一个字节数组输入流,其中,参数 buf 指定字节数组类型的数据源,offset 指定在数组中开始读取数据的起始下标位置,length 指定读取的元素个数。

例 1

使用 ByteArrayInputStream 类编写一个案例,实现从一个字节数组中读取数据,再转换为 int 型进行输出。代码如下:

public class test08 {
    public static void main(String[] args) {
        byte[] b = new byte[] { 1, -1, 25, -22, -5, 23 }; // 创建数组
        ByteArrayInputStream bais = new ByteArrayInputStream(b, 0, 6); // 创建字节数组输入流
        int i = bais.read(); // 从输入流中读取下一个字节,并转换成int型数据
        while (i != -1) { // 如果不返回-1,则表示没有到输入流的末尾
            System.out.println("原值=" + (byte) i + "\t\t\t转换为int类型=" + i);
            i = bais.read(); // 读取下一个
        }
    }
}

在该示例中,字节输入流 bais 从字节数组 b 的第一个元素开始读取 4 字节元素,并将这 4 字节转换为 int 类型数据,最后返回。

提示:上述示例中除了打印 i 的值外,还打印出了 (byte)i 的值,由于 i 的值是从 byte 类型的数据转换过来的,所以使用 (byte)i 可以获取原来的 byte 数据。

该程序的运行结果如下:

原值=1   转换为int类型=1
原值=-1   转换为int类型=255
原值=25   转换为int类型=25
原值=-22   转换为int类型=234
原值=-5   转换为int类型=251
原值=23   转换为int类型=23

从上述的运行结果可以看出,字节类型的数据 -1 和 -22 转换成 int 类型的数据后变成了 255 和 234,对这种结果的解释如下:

  • 字节类型的 1,二进制形式为 00000001,转换为 int 类型后的二进制形式为 00000000 00000000 0000000000000001,对应的十进制数为 1。
  • 字节类型的 -1,二进制形式为 11111111,转换为 int 类型后的二进制形式为 00000000 00000000 0000000011111111,对应的十进制数为 255。

可见,从字节类型的数转换成 int 类型的数时,如果是正数,则数值不变;如果是负数,则由于转换后,二进制形式前面直接补了 24 个 0,这样就改变了原来表示负数的二进制补码形式,所以数值发生了变化,即变成了正数。

提示:负数的二进制形式以补码形式存在,例如 -1,其二进制形式是这样得来的:首先获取 1 的原码 00000001,然后进行反码操作,1 变成 0,0 变成 1,这样就得到 11111110,最后进行补码操作,就是在反码的末尾位加 1,这样就变成了 11111111。

字节数组输出流

ByteArrayOutputStream 类可以向内存的字节数组中写入数据,该类的构造方法有如下两种重载形式。

  1. ByteArrayOutputStream():创建一个字节数组输出流,输出流缓冲区的初始容量大小为 32 字节。
  2. ByteArrayOutputStream(int size):创建一个字节数组输出流,输出流缓冲区的初始容量大小由参数 size 指定。

ByteArrayOutputStream 类中除了有前面介绍的字节输出流中的常用方法以外,还有如下两个方法。

  1. intsize():返回缓冲区中的当前字节数。
  2. byte[] toByteArray():以字节数组的形式返回输出流中的当前内容。

例 2

使用 ByteArrayOutputStream 类编写一个案例,实现将字节数组中的数据输出,代码如下所示。

public class Test09 {
    public static void main(String[] args) {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        byte[] b = new byte[] { 1, -1, 25, -22, -5, 23 }; // 创建数组
        baos.write(b, 0, 6); // 将字节数组b中的前4个字节元素写到输出流中
        System.out.println("数组中一共包含:" + baos.size() + "字节"); // 输出缓冲区中的字节数
        byte[] newByteArray = baos.toByteArray(); // 将输出流中的当前内容转换成字节数组
        System.out.println(Arrays.toString(newByteArray)); // 输出数组中的内容
    }
}

该程序的输出结果如下:

数组中一共包含:6字节
[1, -1, 25, -22, -5, 23]

文件输入流

FileInputStream 是 Java 流中比较常用的一种,它表示从文件系统的某个文件中获取输入字节。通过使用 FileInputStream 可以访问文件中的一个字节、一批字节或整个文件。

在创建 FileInputStream 类的对象时,如果找不到指定的文件将拋出 FileNotFoundException 异常,该异常必须捕获或声明拋出。

FileInputStream 常用的构造方法主要有如下两种重载形式。

  1. FileInputStream(File file):通过打开一个到实际文件的连接来创建一个 FileInputStream,该文件通过文件系统中的 File 对象 file 指定。
  2. FileInputStream(String name):通过打开一个到实际文件的链接来创建一个 FileInputStream,该文件通过文件系统中的路径名 name 指定。

下面的示例演示了 FileInputStream() 两个构造方法的使用。

try {
    // 以File对象作为参数创建FileInputStream对象
    FileInputStream fis1 = new FileInputStream(new File("F:/mxl.txt"));
    // 以字符串值作为参数创建FilelnputStream对象
    FileInputStream fis2 = new FileInputStream("F:/mxl.txt");
} catch(FileNotFoundException e) {
    System.out.println("指定的文件找不到!");
}

例 3

假设有一个 D:\myJava\HelloJava.java 文件,下面使用 FileInputStream 类读取并输出该文件的内容。具体代码如下:

public class Test10 {
    public static void main(String[] args) {
        File f = new File("D:/myJava/HelloJava.java");
        FileInputStream fis = null;
        try {
            // 因为File没有读写的能力,所以需要有个InputStream
            fis = new FileInputStream(f);
            // 定义一个字节数组
            byte[] bytes = new byte[1024];
            int n = 0; // 得到实际读取到的字节数
            System.out.println("D:\\myJava\\HelloJava.java文件内容如下:");
            // 循环读取
            while ((n = fis.read(bytes)) != -1) {
                String s = new String(bytes, 0, n); // 将数组中从下标0到n的内容给s
                System.out.println(s);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                fis.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

如上述代码,在 FileInputDemo 类的 main() 方法中首先创建了一个 File 对象 f,该对象指向 D:\myJava\HelloJava.java 文件。接着使用 FileInputStream 类的构造方法创建了一个 FileInputStream 对象 fis,并声明一个长度为 1024 的 byte 类型的数组,然后使用 FileInputStream 类中的 read() 方法将 HelloJava.java 文件中的数据读取到字节数组 bytes 中,并输出该数据。最后在 finally 语句中关闭 FileInputStream 输入流。

图 1 所示为 HelloJava.java 文件的原始内容,如下所示的是运行程序后的输出内容。

D:\myJava\HelloJava.java文件内容如下:
/*
*第一个java程序
*/
public class HelloJava {
// 这里是程序入口
public static void main(String[] args) {
// 输出字符串
System.out.println(“你好 Java”);
}
}

HelloJava.java文件内容
图 1 HelloJava.java文件内容

注意:FileInputStream 类重写了父类 InputStream 中的 read() 方法、skip() 方法、available() 方法和 close() 方法,不支持 mark() 方法和 reset() 方法。

文件输出流

FileOutputStream 类继承自 OutputStream 类,重写和实现了父类中的所有方法。FileOutputStream 类的对象表示一个文件字节输出流,可以向流中写入一个字节或一批字节。在创建 FileOutputStream 类的对象时,如果指定的文件不存在,则创建一个新文件;如果文件已存在,则清除原文件的内容重新写入。

FileOutputStream 类的构造方法主要有如下 4 种重载形式。

  1. FileOutputStream(File file):创建一个文件输出流,参数 file 指定目标文件。
  2. FileOutputStream(File file,boolean append):创建一个文件输出流,参数 file 指定目标文件,append 指定是否将数据添加到目标文件的内容末尾,如果为 true,则在末尾添加;如果为 false,则覆盖原有内容;其默认值为 false。
  3. FileOutputStream(String name):创建一个文件输出流,参数 name 指定目标文件的文件路径信息。
  4. FileOutputStream(String name,boolean append):创建一个文件输出流,参数 name 和 append 的含义同上。

注意:使用构造方法 FileOutputStream(String name,boolean append) 创建一个文件输出流对象,它将数据附加在现有文件的末尾。该字符串 name 指明了原文件,如果只是为了附加数据而不是重写任何已有的数据,布尔类型参数 append 的值应为 true。

对文件输出流有如下四点说明:

  1. 在 FileOutputStream 类的构造方法中指定目标文件时,目标文件可以不存在。
  2. 目标文件的名称可以是任意的,例如 D:\abc、D:\abc.de 和 D:\abc.de.fg 等都可以,可以使用记事本等工具打开并浏览这些文件中的内容。
  3. 目标文件所在目录必须存在,否则会拋出 java.io.FileNotFoundException 异常。
  4. 目标文件的名称不能是已存在的目录。例如 D 盘下已存在 Java 文件夹,那么就不能使用 Java 作为文件名,即不能使用 D:\Java,否则抛出 java.io.FileNotFoundException 异常。

例 4

同样是读取 D:\myJava\HelloJava.java 文件的内容,在这里使用 FileInputStream 类实现,然后再将内容写入新的文件 D:\myJava\HelloJava.txt 中。具体的代码如下:

public class Test11 {
    public static void main(String[] args) {
        FileInputStream fis = null; // 声明FileInputStream对象fis
        FileOutputStream fos = null; // 声明FileOutputStream对象fos
        try {
            File srcFile = new File("D:/myJava/HelloJava.java");
            fis = new FileInputStream(srcFile); // 实例化FileInputStream对象
            File targetFile = new File("D:/myJava/HelloJava.txt"); // 创建目标文件对象,该文件不存在
            fos = new FileOutputStream(targetFile); // 实例化FileOutputStream对象
            byte[] bytes = new byte[1024]; // 每次读取1024字节
            int i = fis.read(bytes);
            while (i != -1) {
                fos.write(bytes, 0, i); // 向D:\HelloJava.txt文件中写入内容
                i = fis.read(bytes);
            }
            System.out.println("写入结束!");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                fis.close(); // 关闭FileInputStream对象
                fos.close(); // 关闭FileOutputStream对象
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

如上述代码,将 D:\myJava\HelloJava.java 文件中的内容通过文件输入/输出流写入到了 D:\myJava\HelloJava.txt 文件中。由于 HelloJava.txt 文件并不存在,所以在执行程序时将新建此文件,并写入相应内容。

运行程序,成功后会在控制台输出“写入结束!”。此时,打开 D:\myJava\HelloJava.txt 文件会发现,其内容与 HelloJava.java 文件的内容相同,如图 2 所示。

img
图 2

技巧:在创建 FileOutputStream 对象时,如果将 append 参数设置为 true,则可以在目标文件的内容末尾添加数据,此时目标文件仍然可以暂不存在。

Java字符流的使用:字符输入/输出流、字符文件和字符缓冲区的输入/输出流

尽管 Java 中字节流的功能十分强大,几乎可以直接或间接地处理任何类型的输入/输出操作,但利用它却不能直接操作 16 位的 Unicode 字符。这就要用到字符流。本节将重点介绍字符流的操作。

字符输入流

Reader 类是所有字符流输入类的父类,该类定义了许多方法,这些方法对所有子类都是有效的。

Reader 类的常用子类如下。

  • CharArrayReader 类:将字符数组转换为字符输入流,从中读取字符。
  • StringReader 类:将字符串转换为字符输入流,从中读取字符。
  • BufferedReader 类:为其他字符输入流提供读缓冲区。
  • PipedReader 类:连接到一个 PipedWriter。
  • InputStreamReader 类:将字节输入流转换为字符输入流,可以指定字符编码。

与 InputStream 类相同,在 Reader 类中也包含 close()、mark()、skip() 和 reset() 等方法,这些方法可以参考 InputStream 类的方法。下面主要介绍 Reader 类中的 read() 方法,如表 1 所示。

方法名及返回值类型说明
int read()从输入流中读取一个字符,并把它转换为 0~65535 的整数。如果返回 -1, 则表示 已经到了输入流的末尾。为了提高 I/O 操作的效率,建议尽量使用下面两种 read() 方法
int read(char[] cbuf)从输入流中读取若干个字符,并把它们保存到参数 cbuf 指定的字符数组中。 该方 法返回读取的字符数,如果返回 -1,则表示已经到了输入流的末尾
int read(char[] cbuf,int off,int len)从输入流中读取若干个字符,并把它们保存到参数 cbuf 指定的字符数组中。其中, off 指定在字符数组中开始保存数据的起始下标,len 指定读取的字符数。该方法返 回实际读取的字符数,如果返回 -1,则表示已经到了输入流的末尾

字符输出流

与 Reader 类相反,Writer 类是所有字符输出流的父类,该类中有许多方法,这些方法对继承该类的所有子类都是有效的。

Writer 类的常用子类如下。

  • CharArrayWriter 类:向内存缓冲区的字符数组写数据。
  • StringWriter 类:向内存缓冲区的字符串(StringBuffer)写数据。
  • BufferedWriter 类:为其他字符输出流提供写缓冲区。
  • PipedWriter 类:连接到一个 PipedReader。
  • OutputStreamReader 类:将字节输出流转换为字符输出流,可以指定字符编码。

与 OutputStream 类相同,Writer 类也包含 close()、flush() 等方法,这些方法可以参考 OutputStream 类的方法。下面主要介绍 Writer 类中的 write() 方法和 append() 方法,如表 2 所示。

方法名及返回值类型说明
void write(int c)向输出流中写入一个字符
void write(char[] cbuf)把参数 cbuf 指定的字符数组中的所有字符写到输出流中
void write(char[] cbuf,int off,int len)把参数 cbuf 指定的字符数组中的若干字符写到输出流中。其中,off 指定 字符数组中的起始下标,len 表示元素个数
void write(String str)向输出流中写入一个字符串
void write(String str, int off,int len)向输出流中写入一个字符串中的部分字符。其中,off 指定字符串中的起 始偏移量,len 表示字符个数
append(char c)将参数 c 指定的字符添加到输出流中
append(charSequence esq)将参数 esq 指定的字符序列添加到输出流中
append(charSequence esq,int start,int end)将参数 esq 指定的字符序列的子序列添加到输出流中。其中,start 指定 子序列的第一个字符的索引,end 指定子序列中最后一个字符后面的字符 的索引,也就是说子序列的内容包含 start 索引处的字符,但不包括 end 索引处的字符

注意:Writer 类所有的方法在出错的情况下都会引发 IOException 异常。关闭一个流后,再对其进行任何操作都会产生错误。

字符文件输入流

为了读取方便,Java 提供了用来读取字符文件的便捷类——FileReader。该类的构造方法有如下两种重载形式。

  1. FileReader(File file):在给定要读取数据的文件的情况下创建一个新的 FileReader 对象。其中,file 表示要从中读取数据的文件。
  2. FileReader(String fileName):在给定从中读取数据的文件名的情况下创建一个新 FileReader 对象。其中,fileName 表示要从中读取数据的文件的名称,表示的是一个文件的完整路径。

在用该类的构造方法创建 FileReader 读取对象时,默认的字符编码及字节缓冲区大小都是由系统设定的。要自己指定这些值,可以在 FilelnputStream 上构造一个 InputStreamReader。

注意:在创建 FileReader 对象时可能会引发一个 FileNotFoundException 异常,因此需要使用 try catch 语句捕获该异常。

字符流和字节流的操作步骤相同,都是首先创建输入流或输出流对象,即建立连接管道,建立完成后进行读或写操作,最后关闭输入/输出流通道。

例 1

要将 D:\myJava\HelloJava.java 文件中的内容读取并输出到控制台,使用 FileReader 类的实现代码如下:

public class Test12 {
    public static void main(String[] args) {
        FileReader fr = null;
        try {
            fr = new FileReader("D:/myJava/HelloJava.java"); // 创建FileReader对象
            int i = 0;
            System.out.println("D:\\myJava\\HelloJava.java文件内容如下:");
            while ((i = fr.read()) != -1) { // 循环读取
                System.out.print((char) i); // 将读取的内容强制转换为char类型
            }
        } catch (Exception e) {
            System.out.print(e);
        } finally {
            try {
                fr.close(); // 关闭对象
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

如上述代码,首先创建了 FileReader 字符输入流对象 fr,该对象指向 D:\myJava\HelloJava.java 文件,然后定义变量 i 来接收调用 read() 方法的返回值,即读取的字符。在 while 循环中,每次读取一个字符赋给整型变量 i,直到读取到文件末尾时退出循环(当输入流读取到文件末尾时,会返回值 -1)。

字符文件输出流

Java 提供了写入字符文件的便捷类——FileWriter,该类的构造方法有如下 4 种重载形式。

  1. FileWriter(File file):在指定 File 对象的情况下构造一个 FileWriter 对象。其中,file 表示要写入数据的 File 对象。
  2. FileWriter(File file,boolean append):在指定 File 对象的情况下构造一个 FileWriter 对象,如果 append 的值为 true,则将字节写入文件末尾,而不是写入文件开始处。
  3. FileWriter(String fileName):在指定文件名的情况下构造一个 FileWriter 对象。其中,fileName 表示要写入字符的文件名,表示的是完整路径。
  4. FileWriter(String fileName,boolean append):在指定文件名以及要写入文件的位置的情况下构造 FileWriter 对象。其中,append 是一个 boolean 值,如果为 true,则将数据写入文件末尾,而不是文件开始处。

在创建 FileWriter 对象时,默认字符编码和默认字节缓冲区大小都是由系统设定的。要自己指定这些值,可以在 FileOutputStream 上构造一个 OutputStreamWriter 对象。

FileWriter 类的创建不依赖于文件存在与否,如果关联文件不存在,则会自动生成一个新的文件。在创建文件之前,FileWriter 将在创建对象时打开它作为输出。如果试图打开一个只读文件,将引发一个 IOException 异常。

注意:在创建 FileWriter 对象时可能会引发 IOException 或 SecurityException 异常,因此需要使用 try catch 语句捕获该异常。

例 2

编写一个程序,将用户输入的 4 个字符串保存到 D:\myJava\book.txt 文件中。在这里使用 FileWriter 类中的 write() 方法循环向指定文件中写入数据,实现代码如下:

public class Test13 {
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        FileWriter fw = null;
        try {
            fw = new FileWriter("D:\\myJava\\book.txt"); // 创建FileWriter对象
            for (int i = 0; i < 4; i++) {
                System.out.println("请输入第" + (i + 1) + "个字符串:");
                String name = input.next(); // 读取输入的名称
                fw.write(name + "\r\n"); // 循环写入文件
            }
            System.out.println("录入完成!");
        } catch (Exception e) {
            System.out.println(e.getMessage());
        } finally {
            try {
                fw.close(); // 关闭对象
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

如上述代码,首先创建了一个指向 D:\myJava\book.txt 文件的字符文件输出流对象 fw,然后使用 for 循环录入 4 个字符串,并调用 write() 方法将字符串写入到指定的文件中。最后在 finally 语句中关闭字符文件输出流。

运行该程序,根据提示输入 4 个字符串,如下所示。接着打开 D:\myJava\book.txt 文件,将看到写入的内容,如图 1 所示。

请输入第1个字符串:
热点要闻
请输入第2个字符串:
个性推荐
请输入第3个字符串:
热搜新闻词
请输入第4个字符串:
本地看点
录入完成!

img
图 1 book.txt 文件内容

字符缓冲区输入流

BufferedReader 类主要用于辅助其他字符输入流,它带有缓冲区,可以先将一批数据读到内存缓冲区。接下来的读操作就可以直接从缓冲区中获取数据,而不需要每次都从数据源读取数据并进行字符编码转换,这样就可以提高数据的读取效率。

BufferedReader 类的构造方法有如下两种重载形式。

  1. BufferedReader(Reader in):创建一个 BufferedReader 来修饰参数 in 指定的字符输入流。
  2. BufferedReader(Reader in,int size):创建一个 BufferedReader 来修饰参数 in 指定的字符输入流,参数 size 则用于指定缓冲区的大小,单位为字符。

除了可以为字符输入流提供缓冲区以外,BufferedReader 还提供了 readLine() 方法,该方法返回包含该行内容的字符串,但该字符串中不包含任何终止符,如果已到达流末尾,则返回 null。readLine() 方法表示每次读取一行文本内容,当遇到换行(\n)、回车(\r)或回车后直接跟着换行标记符即可认为某行已终止。

例 3

使用 BufferedReader 类中的 readLine() 方法逐行读取 D:\myJava\Book.txt 文件中的内容,并将读取的内容在控制台中打印输出,代码如下:

public class Test13 {
    public static void main(String[] args) {
        FileReader fr = null;
        BufferedReader br = null;
        try {
            fr = new FileReader("D:\\myJava\\book.txt"); // 创建 FileReader 对象
            br = new BufferedReader(fr); // 创建 BufferedReader 对象
            System.out.println("D:\\myJava\\book.txt 文件中的内容如下:");
            String strLine = "";
            while ((strLine = br.readLine()) != null) { // 循环读取每行数据
                System.out.println(strLine);
            }
        } catch (FileNotFoundException e1) {
            e1.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                fr.close(); // 关闭 FileReader 对象
                br.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

如上述代码,首先分别创建了名称为 fr 的 FileReader 对象和名称为 br 的 BufferedReader 对象,然后调用 BufferedReader 对象的 readLine() 方法逐行读取文件中的内容。如果读取的文件内容为 Null,即表明已经读取到文件尾部,此时退出循环不再进行读取操作。最后将字符文件输入流和带缓冲的字符输入流关闭。

运行该程序,输出结果如下所示:

D:\myJava\book.txt 文件中的内容如下:
热点要闻
个性推荐
热搜新闻词
本地看点

字符缓冲区输出流

BufferedWriter 类主要用于辅助其他字符输出流,它同样带有缓冲区,可以先将一批数据写入缓冲区,当缓冲区满了以后,再将缓冲区的数据一次性写到字符输出流,其目的是为了提高数据的写效率。

BufferedWriter 类的构造方法有如下两种重载形式。

  1. BufferedWriter(Writer out):创建一个 BufferedWriter 来修饰参数 out 指定的字符输出流。
  2. BufferedWriter(Writer out,int size):创建一个 BufferedWriter 来修饰参数 out 指定的字符输出流,参数 size 则用于指定缓冲区的大小,单位为字符。

该类除了可以给字符输出流提供缓冲区之外,还提供了一个新的方法 newLine(),该方法用于写入一个行分隔符。行分隔符字符串由系统属性 line.separator 定义,并且不一定是单个新行(\n)符。

提示:BufferedWriter 类的使用与 FileWriter 类相同,这里不再重述。

Java字节流和字符流的区别,如何区分输入流和输出流?

本节主要讲解 Java 字节流和字符流的区别,以及如何区分 Java 输入流和输出流。

Java字节流和字符流的区别

首先我们先大概总结一下前面学习的内容,可分为以下几点:

  1. 以 Stream 结尾都是字节流,Reader 和 Writer 结尾都是字符流。
  2. InputStream 是所有字节输入流的父类,OutputStream 是所有字节输出流的父类。
  3. Reader 是字符输入流的父类,Writer 是字符输出流的父类。

字节流:

  • 文件流:FileOutputStream 和 FileInputStream
  • 缓冲流:BufferedOutputStream 和 BufferedInputStream
  • 对象流:ObjectOutputStream 和 ObjectInputStream

字符流:

  • 转换流:InputStreamReader 和 OutputStreamWriter
  • 缓冲字符流:PrintWriter 和 BufferedReader

区别:

  1. 读写的时候字节流是按字节读写,字符流按字符读写。
  2. 字节流适合所有类型文件的数据传输,因为计算机字节(Byte)是电脑中表示信息含义的最小单位。字符流只能够处理纯文本数据,其他类型数据不行,但是字符流处理文本要比字节流处理文本要方便。
  3. 在读写文件需要对内容按行处理,比如比较特定字符,处理某一行数据的时候一般会选择字符流。
  4. 只是读写文件,和文件内容无关时,一般选择字节流。

区分输入流和输出流

对于初学者,看到输入流与输出流的部分,大部分都不明白到底是输入流写入还是输出流写入文件呢?要将文件读出是用输入流还是输出流呢?程序在内存中运行,文件在磁盘上,把文件从磁盘上读入内存中来,这就需要输入流。反之,把内存中的数据写到磁盘上的文件里就需要输出流。

Windows 里所说的写(将内容写入到文件里,如:存盘)是输入,而读(把内容从文件里读出来,如:显示)是输出,与 Java 的输入输出不一样。Java 里的输入流与输出流是针对内存而言的,它是从内存中读写,而不是所说的显示与存盘。输入流与输出流都可以将内容从屏幕上显示出来。

屏幕和键盘也是区别于内存的设备,System.out.println()用于将内存中的数据输出到屏幕上,而System.in用来在终端读取键盘输入内容。

程序操作的数据都应该是在内存里面,内存是操作的主对象,把数据从其他资源中传送到内存,就是输入。反之,把数据从内存传送到其他资源,就是输出。

不管从磁盘、网络还是键盘读,读到内存中就是 InputStream。例如:

BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(“infilename”)));

不管写到磁盘、网络,或者写到屏幕,都是使用 OutputStream。例如:

BufferedWriter out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(“outfilename”)));

Java项目实战:猜数字小游戏

猜数字是一个经典的小游戏,程序先产生一个随机数,然后用户输入数字,程序将输入的数字与随机数进行对比,给出用户相应的提示信息。

本节实现了一个基于 IO 流的猜数字游戏,游戏中限制玩家游戏次数,游戏试玩为 5 次,超过 5 次后,则提示玩家试玩结束,请付费。具体实现步骤和代码如下:

1)创建 count.txt 文件,存储游戏次数,文件内容如下:

count=0

2)创建 way.txt 文件,存储支付状态(1 为已付费,0 为未付费),文件内容如下:

way=0

3)为了简化代码,本节将多个实现方法写在同一个类中。创建 BullCows 类,代码如下:

public class BullCows {
    /**
     * 负责调用对应的方法,实现整个案例的逻辑关系
     *
     * @param args
     * @throws IOException
     */
    public static void main(String[] args) throws IOException {
        while (true) {
            // 获取游戏次数
            int count = getCount();
            // 获取付费状态
            boolean flag = getCondition();
            // 如果已付费,提示用户游戏次数解封可以继续游戏
            if (flag) {
                System.out.println("游戏已经付费,游戏次数已解封!");
                game();
            } else {
                // 未付费且游戏次数超过5次时,提示试玩结束,要付费
                if (count >= 5) {
                    System.out.println("试玩已经结束,请付费!");
                    getMoney();
                } else {
                    // 未付费且游戏次数未超过5次时,继续游戏,游戏次数加1
                    System.out.println("----" + "试玩第" + (count + 1) + "次" + "----");
                    game();
                    writeCount();
                }
            }
        }
    }
    /**
     * 获取已经玩过的次数
     *
     * @return temp count.txt文件中的游戏次数
     * @throws IOException
     */
    private static int getCount() throws IOException {
        // 创建Properties对象
        Properties prop = new Properties();
        // 使用FileReader对象获取count文件中的游戏次数
        prop.load(new FileReader("count.txt"));
        String property = prop.getProperty("count");
        int temp = Integer.parseInt(property);
        return temp;
    }
    /**
     * 支付方法,支付成功则把支付状态改为“1”并存到数据库,之后可以无限次玩游戏
     *
     * @throws IOException
     */
    private static void getMoney() throws IOException {
        System.out.println("请支付5元!");
        // 获取键盘录入数据
        Scanner sc = new Scanner(System.in);
        int nextInt = sc.nextInt();
        if (nextInt == 5) {
            // 创建Properties对象
            Properties prop = new Properties();
            prop.setProperty("way", "1");
            // 使用FileWriter类将支付状态写入到way文件
            prop.store(new FileWriter("way.txt"), null);
        }
    }
    /**
     * 将试玩的次数写入文档并保存
     *
     * @throws IOException
     */
    private static void writeCount() throws IOException {
        // 创建Properties对象
        Properties prop = new Properties();
        int count = getCount();
        // 写入文件
        prop.setProperty("count", (count + 1) + "");
        prop.store(new FileWriter("count.txt"), null);
    }
    /**
     * 用来获取每次启动时的付费状态
     *
     * @return flag 是否付费
     * @throws FileNotFoundException
     * @throws IOException
     */
    private static boolean getCondition() throws FileNotFoundException, IOException {
        boolean flag = false;
        // 创建Properties对象
        Properties prop = new Properties();
        // 读取way.txt文件,获取支付状态
        prop.load(new FileReader("way.txt"));
        String property = prop.getProperty("way");
        int parseInt = Integer.parseInt(property);
        // way的值等于1时,为已付费
        if (parseInt == 1) {
            flag = true;
        } else {
            flag = false;
        }
        return flag;
    }
    /**
     * 实现游戏产生数字,获取玩家所猜数字等, 并对玩家每次输入,都会有相应的提示
     */
    private static void game() {
        // 产生随机数1~100
        int random = (int) (Math.random() * 100 + 1);
        // 获取键盘录入数据
        Scanner sc = new Scanner(System.in);
        System.out.println("欢迎来到猜数字小游戏!");
        // while循环进行游戏
        while (true) {
            System.out.println("请输入你猜的数据:");
            int guess = sc.nextInt();
            if (guess > random) {
                System.out.println("大了");
            } else if (guess < random) {
                System.out.println("小了");
            } else {
                System.out.println("猜对了哦!");
                break;
            }
        }
    }
}

第一次运行时,结果如下:

----试玩第1次----
欢迎来到猜数字小游戏!
请输入你猜的数据:
1
小了
请输入你猜的数据:
5
小了
请输入你猜的数据:
8
小了
请输入你猜的数据:
9
小了
请输入你猜的数据:
10
猜对了哦!

此时可以看到 count.txt 文件中 count 的值为 1。当进行 5 次游戏后,运行结果如下:

试玩已经结束,请付费!
请支付5元!
5
游戏已经付费,游戏次数已解封!
欢迎来到猜数字小游戏!
请输入你猜的数据:

此时 count.txt 文件中 count 的值为 5,way.txt 文件中 way 的值为 1。

示例中用到 Properties 类的几个方法,方法说明如下:

  1. getProperty (String key):用指定的键在此属性列表中搜索属性。也就是通过参数 key,得到 key 所对应的 value。
  2. load (InputStream inStream):从输入流中读取属性列表(键和元素对)。通过对指定的文件进行装载来获取该文件中的所有键值对。以供 getProperty (String key) 来搜索。
  3. setProperty (String key, String value) :调用 Hashtable 的方法 put,通过调用基类的 put 方法来设置键值对。
  4. store (OutputStream out, String comments):与 load 方法相反,该方法是将键值对写入到指定的文件中。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Java输入/输出(I/O)流 的相关文章

  • 如何为最终用户方便地启动Java GUI程序

    用户想要从以下位置启动 Java GUI 应用程序Windows 以及一些额外的 JVM 参数 例如 javaw Djava util logging config file logging properties jar MyGUI jar
  • Java Swing:从 JOptionPane 获取文本值

    我想创建一个用于 POS 系统的新窗口 用户输入的是客户拥有的金额 并且窗口必须显示兑换金额 我是新来的JOptionPane功能 我一直在使用JAVAFX并且它是不同的 这是我的代码 public static void main Str
  • 为什么 i++ 不是原子的?

    Why is i Java 中不是原子的 为了更深入地了解 Java 我尝试计算线程中循环的执行频率 所以我用了一个 private static int total 0 在主课中 我有两个线程 主题 1 打印System out prin
  • Java - 将节点添加到列表的末尾?

    这是我所拥有的 public class Node Object data Node next Node Object data Node next this data data this next next public Object g
  • JAXb、Hibernate 和 beans

    目前我正在开发一个使用 Spring Web 服务 hibernate 和 JAXb 的项目 1 我已经使用IDE hibernate代码生成 生成了hibernate bean 2 另外 我已经使用maven编译器生成了jaxb bean
  • 列出jshell中所有活动的方法

    是否有任何命令可以打印当前 jshell 会话中所有新创建的方法 类似的东西 list但仅适用于方法 您正在寻找命令 methods all 它会打印所有方法 包括启动 JShell 时添加的方法 以及失败 被覆盖或删除的方法 对于您声明的
  • Liferay ClassNotFoundException:DLFileEntryImpl

    在我的 6 1 0 Portal 实例上 带有使用 ServiceBuilder 和 DL Api 的 6 1 0 SDK Portlet 这一行 DynamicQuery query DynamicQueryFactoryUtil for
  • 无法解析插件 Java Spring

    我正在使用 IntelliJ IDEA 并且我尝试通过 maven 安装依赖项 但它给了我这些错误 Cannot resolve plugin org apache maven plugins maven clean plugin 3 0
  • 斯坦福 NLP - 处理文件列表时 OpenIE 内存不足

    我正在尝试使用斯坦福 CoreNLP 中的 OpenIE 工具从多个文件中提取信息 当多个文件 而不是一个 传递到输入时 它会给出内存不足错误 All files have been queued awaiting termination
  • JRE 系统库 [WebSphere v6.1 JRE](未绑定)

    将项目导入 Eclipse 后 我的构建路径中出现以下错误 JRE System Library WebSphere v6 1 JRE unbound 谁知道怎么修它 右键单击项目 特性 gt Java 构建路径 gt 图书馆 gt JRE
  • 使用Caliper时如何指定命令行?

    我发现 Google 的微型基准测试项目 Caliper 非常有趣 但文档仍然 除了一些示例 完全不存在 我有两种不同的情况 需要影响 JVM Caliper 启动的命令行 我需要设置一些固定 最好在几个固定值之间交替 D 参数 我需要指定
  • 总是使用 Final?

    我读过 将某些东西做成最终的 然后在循环中使用它会带来更好的性能 但这对一切都有好处吗 我有很多地方没有循环 但我将 Final 添加到局部变量中 它会使速度变慢还是仍然很好 还有一些地方我有一个全局变量final 例如android Pa
  • 如何在控制器、服务和存储库模式中使用 DTO

    我正在遵循控制器 服务和存储库模式 我只是想知道 DTO 在哪里出现 控制器应该只接收 DTO 吗 我的理解是您不希望外界了解底层域模型 从领域模型到 DTO 的转换应该发生在控制器层还是服务层 在今天使用 Spring MVC 和交互式
  • simpleframework,将空元素反序列化为空字符串而不是 null

    我使用简单框架 http simple sourceforge net http simple sourceforge net 在一个项目中满足我的序列化 反序列化需求 但在处理空 空字符串值时它不能按预期工作 好吧 至少不是我所期望的 如
  • 静态变量的线程安全

    class ABC implements Runnable private static int a private static int b public void run 我有一个如上所述的 Java 类 我有这个类的多个线程 在里面r
  • 编译器抱怨“缺少返回语句”,即使不可能达到缺少返回语句的条件

    在下面的方法中 编译器抱怨缺少退货声明即使该方法只有一条路径 并且它包含一个return陈述 抑制错误需要另一个return陈述 public int foo if true return 5 鉴于Java编译器可以识别无限循环 https
  • 当我从 Netbeans 创建 Derby 数据库时,它存储在哪里?

    当我从 netbeans 创建 Derby 数据库时 它存储在哪里 如何将它与项目的其余部分合并到一个文件夹中 右键单击Databases gt JavaDB in the Service查看并选择Properties This will
  • java.lang.IllegalStateException:驱动程序可执行文件的路径必须由 webdriver.chrome.driver 系统属性设置 - Similiar 不回答

    尝试学习 Selenium 我打开了类似的问题 但似乎没有任何帮助 我的代码 package seleniumPractice import org openqa selenium WebDriver import org openqa s
  • 按日期对 RecyclerView 进行排序

    我正在尝试按日期对 RecyclerView 进行排序 但我尝试了太多的事情 我不知道现在该尝试什么 问题就出在这条线上适配器 notifyDataSetChanged 因为如果我不放 不会显示错误 但也不会更新 recyclerview
  • Spring Boot @ConfigurationProperties 不从环境中检索属性

    我正在使用 Spring Boot 1 2 1 并尝试创建一个 ConfigurationProperties带有验证的bean 如下所示 package com sampleapp import java net URL import j

随机推荐

  • PPT画图文章总结

    一图抵千言 xff0c 在平常的PPT汇报中 xff0c 一张好的图片可以让我们的展示更加清晰 xff0c 也让听得人更快的了解我们的内容 要想起之前师兄发了文章 xff0c 需要提供一个封面示意图 xff0c 当时好像是花钱请别人做的 x
  • 传统学科怎么和深度学习领域结合

    这篇博客 程序员读论文 LeCun Y Bengio Y amp Hinton G Deep learning Nature 521 436 444 2015 中的论文提到深度学习将在很多行业上有广阔的前景 最近看到毕导的公众号发文菜鸡程序
  • 现在快2022年了,c++为什么还要实现(.cpp)和声明(.h)分开?像 Java 或 C# 都不需要声明头文件,C++ 委员会为什么不解决这个问题?

    链接 xff1a https www zhihu com question 506962663 answer 2278836594 因为 C 43 43 牵扯面更广 xff0c 改起来更麻烦 很多语言其实都有一个事实上的实现标准 xff0c
  • Java程序设计基础

    文章目录 Java标识符和关键字标识符关键字 Java注释 xff1a 单行 多行和文档注释1 xff09 单行注释2 xff09 多行注释3 xff09 文档注释 Javadoc xff08 文档注释 xff09 详解Javadoc标签J
  • 几本对于笔试和面试有用的书(干货~)

    黑客帝国 jpg 这儿放几本对程序员笔试和面试有益的书籍o o the power of coding coder jpg 4本408核心书籍 xff1a 数据结构计算机操作系统计算机网络计算机组成原理 面试宝典 xff1a 程序员面试宝典
  • Java类和对象

    文章目录 本章学习要点 Java面向对象 xff1a 对象的概念及面向对象的三个基本特征对象的概念面向对象的三大核心特性继承性封装性多态性 Java认识类和对象Java类的定义及定义类时可用的关键字例 1 Java类的属性 xff1a 成员
  • Java流程控制语句

    文章目录 Java语句 xff1a Java空语句 复合语句和表达式语句语句编写方式空语句表达式语句复合语句例 1 Java if else分支结构精讲if 结构例 1例 2例 3 if else 结构例 4 多条件 if else if
  • Java数组:针对数组(Array)的各种操作

    文章目录 本章学习要点 Java数组简介 xff1a 数组是什么 xff1f Java一维数组的定义 赋值和初始化创建一维数组分配空间例 1 初始化一维数组1 xff09 使用 new 指定数组大小后进行初始化例 22 xff09 使用 n
  • java中类的main方法总结

    一 java中每个类都需要有main方法吗 xff1f 每个类可以有也可以没有main方法 xff0c 甚至所有类里可以都没有main方法 如果你想从某个类做为入口开始运行整个程序 那么就把他设成 public xff0c 之后再里面写个m
  • java中文件名、类名之间的关系

    1 Java保存的文件名必须与类名一致 xff1b 2 如果文件中只有一个类 xff0c 文件名必须与类名一致 xff1b 3 一个Java文件中只能有一个public类 xff1b 4 如果文件中不止一个类 xff0c 文件名必须与pub
  • Java 包(package)详解

    为了更好地组织类 xff0c Java 提供了包机制 xff0c 用于区别类名的命名空间 包的作用 1 把功能相似或相关的类或接口组织在同一个包中 xff0c 方便类的查找和使用 2 如同文件夹一样 xff0c 包也采用了树形目录的存储方式
  • 软件项目开发流程以及人员职责,软件工程中五种常用的软件开发模型整理

    文章目录 一 软件项目开发流程逻辑图开发流程需求分析概要设计详细设计编码测试软件交付验收维护 软件维护软件升级 软件项目开发流程以及人员职责软件工程中五种常用的软件开发模型整理软件系统开发流程七大详细步骤完整介绍 一 软件项目开发流程逻辑图
  • 如何保持专注

    文章目录 部分 1 做一个井井有条的人部分 2 提高专注力部分 3 在集中期间保持动力 专家建议小提示 转载于 xff1a https zh wikihow com E4 BF 9D E6 8C 81 E4 B8 93 E6 B3 A8 不
  • 让开始学java的我困惑的问题解析

    前面已经对java一些基础概念进行了理解 xff1a Java 包 package 详解 java中文件名 类名之间的关系 java中类的main方法总结 文章目录 一个java文件中可以有多个class xff0c 但是只能有一个是pub
  • Jar包详解

    jar包的一些事儿 关于 JAR 包我们应该知道的s
  • astra 深度相机 + orbslam2 ~ 稠密建图

    在ROS下运行ORB SLAM2 主要包括以下几步 xff1a 一 创建ROS工作空间 二 下载usb cam xff08 单目相机驱动包 xff09 三 下载深度相机驱动包 四 下载ORB SLAM2稠密建图代码 五 运行 一 创建ROS
  • Java字符串的处理

    文章目录 本章学习要点 Java定义字符串 xff08 2种方式 xff09 直接定义字符串例 1 使用 String 类定义1 String 2 String String original 3 String char value 4 S
  • Java数字和日期处理:Java数字处理和日期类

    文章目录 本章学习要点 Java Math类的常用方法静态常量例 1 求最大值 最小值和绝对值例 2 求整运算例 3 三角函数运算例 4 指数运算例 5 Java生成随机数 xff08 random 和Random类 xff09 例 1例
  • Java内置的包装类

    文章目录 本章学习要点 Java包装类 装箱和拆箱装箱和拆箱包装类的应用1 实现 int 和 Integer 的相互转换2 将字符串转换为数值类型3 将整数转换为字符串 Java Object类详解toString 方法equals 方法例
  • Java输入/输出(I/O)流

    文章目录 本章学习要点 Java流是什么 xff1f 输入 输出流又是什么 xff1f 什么是输入 输出流输入流输出流 Java系统流例 1 Java字符编码介绍Java File类 xff08 文件操作类 xff09 详解获取文件属性例