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。后续将介绍更多关于数据库操作的内容,如创建表、选择数据、连接数据以及更新和删除数据等。

内容概要:本文详细介绍了一个基于Java和Vue的联邦学习隐私保护推荐系统的设计实现。系统采用联邦学习架构,使用户数据在本地完成模型训练,仅上传加密后的模型参数或梯度,通过中心服务器进行联邦平均聚合,从而实现数据隐私保护协同建模的双重目标。项目涵盖完整的系统架构设计,包括本地模型训练、中心参数聚合、安全通信、前后端解耦、推荐算法插件化等模块,并结合差分隐私同态加密等技术强化安全性。同时,系统通过Vue前端实现用户行为采集个性化推荐展示,Java后端支撑高并发服务日志处理,形成“本地训练—参数上传—全局聚合—模型下发—个性化微调”的完整闭环。文中还提供了关键模块的代码示例,如特征提取、模型聚合、加密上传等,增强了项目的可实施性工程参考价值。 适合人群:具备一定Java和Vue开发基础,熟悉Spring Boot、RESTful API、分布式系统或机器学习相关技术,从事推荐系统、隐私计算或全栈开发方向的研发人员。 使用场景及目标:①学习联邦学习在推荐系统中的工程落地方法;②掌握隐私保护机制(如加密传输、差分隐私)模型聚合技术的集成;③构建高安全、可扩展的分布式推荐系统原型;④实现前后端协同的个性化推荐闭环系统。 阅读建议:建议结合代码示例深入理解联邦学习流程,重点关注本地训练全局聚合的协同逻辑,同时可基于项目架构进行算法替换功能扩展,适用于科研验证工业级系统原型开发。
源码来自:https://pan.quark.cn/s/a4b39357ea24 遗传算法 - 简书 遗传算法的理论是根据达尔文进化论而设计出来的算法: 人类是朝着好的方向(最优解)进化,进化过程中,会自动选择优良基因,淘汰劣等基因。 遗传算法(英语:genetic algorithm (GA) )是计算数学中用于解决最佳化的搜索算法,是进化算法的一种。 进化算法最初是借鉴了进化生物学中的一些现象而发展起来的,这些现象包括遗传、突变、自然选择、杂交等。 搜索算法的共同特征为: 首先组成一组候选解 依据某些适应性条件测算这些候选解的适应度 根据适应度保留某些候选解,放弃其他候选解 对保留的候选解进行某些操作,生成新的候选解 遗传算法流程 遗传算法的一般步骤 my_fitness函数 评估每条染色体所对应个体的适应度 升序排列适应度评估值,选出 前 parent_number 个 个体作为 待选 parent 种群(适应度函数的值越小越好) 从 待选 parent 种群 中随机选择 2 个个体作为父方和母方。 抽取父母双方的染色体,进行交叉,产生 2 个子代。 (交叉概率) 对子代(parent + 生成的 child)的染色体进行变异。 (变异概率) 重复3,4,5步骤,直到新种群(parentnumber + childnumber)的产生。 循环以上步骤直至找到满意的解。 名词解释 交叉概率:两个个体进行交配的概率。 例如,交配概率为0.8,则80%的“夫妻”会生育后代。 变异概率:所有的基因中发生变异的占总体的比例。 GA函数 适应度函数 适应度函数由解决的问题决定。 举一个平方和的例子。 简单的平方和问题 求函数的最小值,其中每个变量的取值区间都是 [-1, ...
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值