53、Java 二进制流读写与数据库基础入门

Java 二进制流读写与数据库基础入门

1. 二进制流读取

在 Java 中,读取二进制流比读取字符流稍难,但也并非难以掌握。读取二进制流时,最大的障碍在于你必须确切知道写入文件的每个数据项的类型。若文件中存在任何错误数据,程序将无法正常工作。所以,你需要确保文件包含程序所期望的数据。

1.1 涉及的类

为了读取二进制文件,通常会使用以下几个类:
- File :用于表示文件本身。
- FileInputStream :将输入流连接到文件。
- BufferedInputStream :为基本的 FileInputStream 添加缓冲功能,提高流的效率。
- DataInputStream :实际用于从流中读取各种数据类型的类。

这些类的重要构造函数和方法如下表所示:
| 类/方法 | 构造函数/方法签名 | 描述 |
| — | — | — |
| BufferedInputStream | BufferedInputStream(InputStream in) | 从任何扩展 InputStream 类的对象创建一个带缓冲的输入流,通常传入 FileInputStream 对象。 |
| DataInputStream | DataInputStream(InputStream in) | 从任何扩展 InputStream 类的对象创建一个数据输入流,通常传入 BufferedInputStream 对象。 |
| FileInputStream | FileInputStream(File file) | 从指定的 File 对象创建一个文件输入流,若文件不存在或为目录,抛出 FileNotFoundException 。 |
| FileInputStream | FileInputStream(String path) | 从指定的路径名创建一个文件输入流,若文件不存在或为目录,抛出 FileNotFoundException 。 |
| DataInputStream 方法 | boolean readBoolean() | 从输入流读取一个布尔值,抛出 EOFException IOException 。 |
| DataInputStream 方法 | byte readByte() | 从输入流读取一个字节值,抛出 EOFException IOException 。 |
| DataInputStream 方法 | char readChar() | 从输入流读取一个字符值,抛出 EOFException IOException 。 |
| DataInputStream 方法 | double readDouble() | 从输入流读取一个双精度浮点值,抛出 EOFException IOException 。 |
| DataInputStream 方法 | float readFloat() | 从输入流读取一个单精度浮点值,抛出 EOFException IOException 。 |
| DataInputStream 方法 | int readInt() | 从输入流读取一个整数值,抛出 EOFException IOException 。 |
| DataInputStream 方法 | long readLong() | 从输入流读取一个长整数值,抛出 EOFException IOException 。 |
| DataInputStream 方法 | short readShort() | 从输入流读取一个短整数值,抛出 EOFException IOException 。 |
| DataInputStream 方法 | String readUTF() | 从输入流读取一个以 UTF 格式存储的字符串,抛出 EOFException IOException UTFDataFormatException 。 |

1.2 创建 DataInputStream

要从二进制文件读取数据,需要将 DataInputStream 对象连接到输入文件。可以使用以下嵌套构造函数的方式:

File file = new File("movies.dat");
DataInputStream in = new DataInputStream(
    new BufferedInputStream(
        new FileInputStream(file) ) );

若嵌套让你感到不适,也可以分步进行:

File file = new File("movies.dat");
FileInputStream fs = new FileInputStream(file);
BufferedInputStream bs = new BufferedInputStream(fs);
DataInputStream in = new DataInputStream(bs);

这两种方式的效果是相同的。

1.3 从数据输入流读取数据

对于二进制文件,不是将整行数据读入程序并解析为各个字段,而是使用 DataInputStream 类的各种 read 方法逐个读取字段。例如,以下代码片段读取单个电影的信息并将数据存储在变量中:

String title = in.readUTF();
int year = in.readInt();
double price = in.readDouble();

需要注意的是,这些 read 方法在文件结束时会抛出 EOFException ,在发生 I/O 错误时会抛出 IOException 。因此,需要在 try/catch 块中调用这些方法。 readUTF 方法还会抛出 UTFDataFormatException ,但该异常是 IOException 的一种,通常不需要单独捕获。

通常会使用 while 循环来读取文件中的所有数据。当文件结束时,会抛出 EOFException ,可以捕获该异常并停止循环。示例代码如下:

boolean eof = false;
while (!eof)
{
    try
    {
        String title = in.readUTF();
        int year = in.readInt();
        double price = in.readDouble();
        // 在这里对数据进行处理
    }
    catch (EOFException e)
    {
        eof = true;
    }
    catch (IOException e)
    {
        System.out.println("An I/O error has occurred!");
        System.exit(0);
    }
}

读取完文件后,可以调用 close 方法关闭流:

in.close();

此方法也会抛出 IOException ,因此需要放在 try/catch 块中。

1.4 读取 movies.dat 文件示例

以下是一个完整的程序,用于读取 movies.dat 文件,为每个电影信息创建一个 Movie 对象,并在控制台打印电影信息:

import java.io.*;
import java.text.NumberFormat;

public class ReadBinaryFile
{
    public static void main(String[] args)
    {
        NumberFormat cf = NumberFormat.getCurrencyInstance();
        DataInputStream in = getStream("movies.dat");
        boolean eof = false;
        while (!eof)
        {
            Movie movie = readMovie(in);
            if (movie == null)
                eof = true;
            else
            {
                String msg = Integer.toString(movie.year);
                msg += ": " + movie.title;
                msg += " (" + cf.format(movie.price) + ")";
                System.out.println(msg);
            }
        }
        closeFile(in);
    }

    private static DataInputStream getStream(String name)
    {
        DataInputStream in = null;
        try
        {
            File file = new File(name);
            in = new DataInputStream(
                new BufferedInputStream(
                    new FileInputStream(file) ) );
        }
        catch (FileNotFoundException e)
        {
            System.out.println("The file doesn't exist.");
            System.exit(0);
        }
        catch (IOException e)
        {
            System.out.println("I/O Error creating file.");
            System.exit(0);
        }
        return in;
    }

    private static Movie readMovie(DataInputStream in)
    {
        String title = "";
        int year = 0;
        double price = 0.0;
        try
        {
            title = in.readUTF();
            year = in.readInt();
            price = in.readDouble();
        }
        catch (EOFException e)
        {
            return null;
        }
        catch (IOException e)
        {
            System.out.println("I/O Error");
            System.exit(0);
        }
        return new Movie(title, year, price);
    }

    private static void closeFile(DataInputStream in)
    {
        try
        {
            in.close();
        }
        catch(IOException e)
        {
            System.out.println("I/O Error closing file.");
            System.out.println();
        }
    }

    private static class Movie
    {
        public String title;
        public int year;
        public double price;
        public Movie(String title, int year, double price)
        {
            this.title = title;
            this.year = year;
            this.price = price;
        }
    }
}

该程序各方法的功能如下:
- main 方法 :调用 getStream 方法获取数据输入流对象,使用 while 循环调用 readMovie 方法获取电影对象,若电影对象不为空,则打印电影数据,最后调用 closeFile 方法关闭文件。
- getStream 方法 :为传入的文件名创建 DataInputStream 对象,若抛出异常,程序退出。
- readMovie 方法 :读取单个电影的数据并创建 Movie 对象,若到达文件末尾,返回 null
- closeFile 方法 :关闭输入流。
- Movie 类 :作为私有内部类定义,用于存储电影的标题、年份和价格信息。

下面是读取二进制文件的流程 mermaid 图:

graph LR
    A[开始] --> B[创建 DataInputStream]
    B --> C[进入 while 循环]
    C --> D{是否到达文件末尾}
    D -- 否 --> E[读取电影信息]
    E --> F[处理电影信息]
    F --> C
    D -- 是 --> G[关闭输入流]
    G --> H[结束]
2. 二进制流写入

在 Java 中,若要将数据写入二进制文件,需要使用几个特定的类来实现这一功能。

2.1 涉及的类

以下是写入二进制文件时常用的类:
- FileOutputStream :该类与 File 对象连接,创建一个可向文件写入数据的输出流。不过,它的功能有限,只能向文件写入原始字节,即它不具备写入 int double 或字符串等值的能力。
- BufferedOutputStream :此类连接到 FileOutputStream 并添加输出缓冲功能。
- DataOutputStream :这个类为流添加了写入基本数据类型和字符串的能力。

这些类的重要构造函数和方法如下表所示:
| 类/方法 | 构造函数/方法签名 | 描述 |
| — | — | — |
| DataOutputStream | DataOutputStream(OutputStream out) | 为指定的输出流创建一个数据输出流。 |
| BufferedOutputStream | BufferedOutputStream(OutputStream out) | 为指定的流创建一个带缓冲的输出流,通常传入 FileOutputStream 对象。 |
| FileOutputStream | FileOutputStream(File file) | 从文件创建一个文件写入器,若发生错误,抛出 FileNotFoundException 。 |
| FileOutputStream | FileOutputStream(File file, boolean append) | 从文件创建一个文件写入器,若发生错误,抛出 FileNotFoundException 。若第二个参数为 true ,且文件已存在,则将数据添加到文件末尾。 |
| FileOutputStream | FileOutputStream(String path) | 从指定的路径名创建一个文件写入器,若发生错误,抛出 FileNotFoundException 。 |
| FileOutputStream | FileOutputStream(String path, boolean append) | 从指定的路径名创建一个文件写入器,若发生错误,抛出 FileNotFoundException 。若第二个参数为 true ,且文件已存在,则将数据添加到文件末尾。 |
| DataOutputStream 方法 | void close() | 关闭文件。 |
| DataOutputStream 方法 | void flush() | 将缓冲区的内容写入磁盘。 |
| DataOutputStream 方法 | int size() | 返回写入文件的字节数。 |
| DataOutputStream 方法 | void writeBoolean(boolean value) | 向输出流写入一个布尔值,抛出 IOException 。 |
| DataOutputStream 方法 | void writeByte(byte value) | 向输出流写入一个字节值,抛出 IOException 。 |
| DataOutputStream 方法 | void writeChar(char value) | 向输出流写入一个字符值,抛出 IOException 。 |
| DataOutputStream 方法 | void writeDouble(double value) | 向输出流写入一个双精度浮点值,抛出 IOException 。 |
| DataOutputStream 方法 | void writeFloat(float value) | 向输出流写入一个单精度浮点值,抛出 IOException 。 |
| DataOutputStream 方法 | void writeInt(int value) | 向输出流写入一个整数值,抛出 IOException 。 |
| DataOutputStream 方法 | void writeLong(long value) | 向输出流写入一个长整数值,抛出 IOException 。 |
| DataOutputStream 方法 | void writeShort(short value) | 向输出流写入一个短整数值,抛出 IOException 。 |
| DataOutputStream 方法 | void writeUTF(String value) | 向输出流写入一个以 UTF 格式存储的字符串,抛出 EOFException IOException UTFDataFormatException 。 |

2.2 创建 DataOutputStream

创建 DataOutputStream 对象时,可以使用嵌套构造函数的方式,示例如下:

File file = new File(name);
DataOutputStream out = new DataOutputStream(
    new BufferedOutputStream(
        new FileOutputStream(file) ) );

若你不喜欢嵌套构造的方式,也可以将其展开:

File file = new File(name);
FileOutputStream fos = new FileOutputStream(file);
BufferedOutputStream bos = new BufferedOutputStream(fos);
DataOutputStream out = new DataOutputStream(bos); 

FileOutputStream 类有一个可选的布尔参数,可用于指示若文件已存在是否应追加数据。若要使用此功能,可按如下方式调用构造函数:

File file = new File(name);
DataOutputStream out = new DataOutputStream(
    new BufferedOutputStream(
        new FileOutputStream(file, true) ) );

若指定 false 或完全省略该参数,现有文件将被删除,其数据也会丢失。

2.3 向二进制流写入数据

成功将 DataOutputStream 连接到文件后,向文件写入不同数据类型的数据只需调用各种 write 方法即可。例如,以下代码将 Movie 对象的数据写入文件:

out.writeUTF(movie.title);
out.writeInt(movie.year);
out.writeDouble(movie.price);

当然,这些方法会抛出 IOException ,因此必须将它们包含在 try/catch 块中。

若在流中包含了 BufferedOutputStream 类,它会在缓冲区中累积数据,直到决定将数据写入磁盘。若你想强制将缓冲区的数据写入磁盘,可以调用 flush 方法,示例如下:

out.flush();

另外,当完成向文件写入数据后,可通过调用 close 方法关闭文件:

out.close();

flush close 方法也会抛出 IOException ,因此需要使用 try/catch 块来捕获异常。

2.4 写入 movies.dat 文件示例

以下是一个程序,用于从硬编码的 Movie 对象数组写入 movies.dat 文件:

import java.io.*;

public class WriteBinaryFile
{
    public static void main(String[] args)
    {
        Movie[] movies = getMovies();
        DataOutputStream out = openOutputStream("movies.dat");
        for (Movie m : movies)
            writeMovie(m, out);
        closeFile(out);
    }

    private static Movie[] getMovies()
    {
        Movie[] movies = new Movie[10];
        movies[0] = new Movie("It’s a Wonderful Life", 1946, 14.95);
        movies[1] = new Movie("The Great Race", 1965, 12.95);
        movies[2] = new Movie("Young Frankenstein", 1974, 16.95);
        movies[3] = new Movie("The Return of the Pink Panther", 1975, 11.95);
        movies[4] = new Movie("Star Wars", 1977, 17.95);
        movies[5] = new Movie("The Princess Bride", 1987, 16.95);
        movies[6] = new Movie("Glory", 1989, 14.95);
        movies[7] = new Movie("Apollo 13", 1995, 19.95);
        movies[8] = new Movie("The Game", 1997, 14.95);
        movies[9] = new Movie("The Lord of the Rings: The Fellowship of the Ring", 2001, 19.95);
        return movies;
    }

    private static DataOutputStream openOutputStream(String name)
    {
        DataOutputStream out = null;
        try
        {
            File file = new File(name);
            out = new DataOutputStream(
                new BufferedOutputStream(
                    new FileOutputStream(file) ) );
            return out;
        }
        catch (IOException e)
        {
            System.out.println(
                "I/O Exception opening file.");
            System.exit(0);
        }
        return out;
    }

    private static void writeMovie(Movie m, DataOutputStream out)
    {
        try
        {
            out.writeUTF(m.title);
            out.writeInt(m.year);
            out.writeDouble(m.price);
        }
        catch (IOException e)
        {
            System.out.println(
                "I/O Exception writing data.");
            System.exit(0);
        }
    }

    private static void closeFile(DataOutputStream out)
    {
        try
        {
            out.close();
        }
        catch (IOException e)
        {
            System.out.println("I/O Exception closing file.");
            System.exit(0);
        }
    }

    private static class Movie
    {
        public String title;
        public int year;
        public double price;
        public Movie(String title, int year, double price)
        {
            this.title = title;
            this.year = year;
            this.price = price;
        }
    }
}

该程序各方法的功能如下:
- main 方法 :调用 getMovies 方法获取 Movie 对象数组,调用 openOutputStream 方法获取用于向文件写入数据的输出流,使用增强 for 循环调用 writeMovie 方法将电影数据写入文件,最后调用 closeFile 方法关闭文件。
- getMovies 方法 :创建要写入文件的电影数组。
- openOutputStream 方法 :为程序创建 DataOutputStream 对象,以便向文件写入数据。
- writeMovie 方法 :接受两个参数,即要写入的电影和用于写入数据的输出流。
- closeFile 方法 :关闭文件。
- Movie 类 :作为内部类包含在内,用于存储电影的标题、年份和价格信息。

下面是写入二进制文件的流程 mermaid 图:

graph LR
    A[开始] --> B[创建 DataOutputStream]
    B --> C[获取 Movie 对象数组]
    C --> D[遍历 Movie 对象数组]
    D --> E[向文件写入电影信息]
    E --> F{是否遍历完所有电影}
    F -- 否 --> D
    F -- 是 --> G[刷新缓冲区]
    G --> H[关闭输出流]
    H --> I[结束]
3. 数据库基础入门

SQL(Structured Query Language)即结构化查询语言,它是关系型数据库的通用语言,也是 Java 中数据库处理的基础。需要注意的是,Java 本身并未提供 SQL 的具体实现,而是提供了 JDBC(Java DataBase Connectivity),它允许你制定 SQL 语句,将其发送到数据库服务器并处理结果。为了使用 JDBC,你需要了解一些 SQL 数据库的基本概念以及足够的 SQL 知识来制定合理的 SQL 语句。

3.1 什么是关系型数据库

关系型数据库是计算机领域中被广泛使用和滥用的术语之一。关系型数据库可以是:
- 一种将数据存储在表中的数据库,表之间可以基于共同信息建立关系。例如,客户表和发票表可能都包含客户编号列,该列可作为表之间关系的基础。

虽然本章不会让你成为数据库专家或 SQL 高手,但它涵盖了足够的 SQL 知识,能让你开始使用 JDBC。后续将介绍更多关于数据库操作的内容,如创建表、选择数据、连接数据以及更新和删除数据等。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值