蚊章汇 - Chris Hermansen
type: channel
link: https://linuxcn.bwsl.wang/author/662/
description: Chris Hermansen
items: 为你的 awk 脚本注入 Groovy
items: 我如何使用 Groovy 分析我的音乐目录
items: 通过编写“猜数字”游戏来学习 Awk
items: 使用 apt 进行 Linux 包管理
items: 如何以非代码形式贡献开源
items: 在 Java 和 Groovy 中创建和初始化列表的不同
items: 在 Groovy 和 Java 中创建并初始化映射的不同
items: 如何从 Linux 发行版的仓库中安装 Java
items: 在 Java 中使用外部库
items: 我家的 Linux 故事

为你的 awk 脚本注入 Groovy
type: item
date: 2022-11-09T18:01:00+08:00
description: awk 和 Groovy 相辅相成，可以创建强大、有用的脚本。最近我写了一个使用 Groovy 脚本来清理我的音乐文件中的标签的系列。我开发了一个 框架，可以识别我的音乐目录的结构，并使用它来遍历音乐文件。在该系列的最后一篇文章中，我从框架中分离出一个实用类，我的脚本可以用它来处理文件。这个独立的框架让我想起了很多 awk 的工作方式。对于那些不熟悉 awk 的人来说，你学习下这本电子书：《awk 实用指南》我从 1984 年开始大量使用 awk，当时我们的小公司买了第一台“真正的”计算机，它运行的是 System V Unix。对我来说，awk 是非常完美的：它有 关联内存 associative memory ——将数组视为由字符串而不是数字来索引的。它内置了正则表达式，似乎专为处理数据而生，尤其是在处理数据列时，而且结构紧凑，易于学习。最后，它非常适合在 Unix 工作流使用，从标准输入或文件中读取数据并写入到输出，数据不需要经过其他的转换就出现在了输入流中。说 awk 是我日常计算工具箱中的一个重要部分一点也不为过。然而，在我使用 awk 的过程中，有几件事让我感到不满意。可能主要的问题是 awk 善于处理以分隔字段呈现的数据，但很奇怪它不善于处理 CSV 文件，因为 CSV 文件的字段被引号包围时可以嵌入逗号分隔符。另外，自 awk 发明以来，正则表达式已经有了很大的发展，我们需要记住两套正则表达式的语法规则，而这并不利于编写无 bug 的代码。一套这样的规则已经很糟糕了。由于 awk 是一门简洁的语言，因此它缺少很多我认为有用的东西，比如更丰富的基础类型、结构体、switch 语句等等。相比之下，Groovy 拥有这些能力：可以使用 OpenCSV 库，它很擅长处理 CSV 文件、Java 正则表达式和强大的匹配运算符、丰富的基础类型、类、switch 语句等等。Groovy 所缺乏的是简单的面向管道的概念，即把要处理数据作为一个传入的流，以及把处理过的数据作为一个传出的流。但我的音乐目录处理框架让我想到，也许我可以创建一个 Groovy 版本的 awk “引擎”。这就是我写这篇文章的目的。安装 Java 和 GroovyGroovy 是基于 Java 的，需要先安装 Java。最新的、合适的 Java 和 Groovy 版本可能都在你的 Linux 发行版的软件库中。Groovy 也可以按照 Groovy 主页 上的说明进行安装。对于 Linux 用户来说，一个不错的选择是 SDKMan，它可以用来获得多个版本的 Java、Groovy 和其他许多相关工具。在这篇文章中，我使用的是 SDK 的版本：Java：OpenJDK 11 的 11.0.12 的开源版本Groovy：3.0.8使用 Groovy 创建 awk这里的基本想法是将打开一个或多个文件进行处理、将每行分割成字段、以及提供对数据流的访问等复杂情况封装在三个部分：在处理数据之前在处理每行数据时在处理完所有数据之后我并不打算用 Groovy 来取代 awk。相反，我只是在努力实现我的典型用例，那就是：使用一个脚本文件而不是在命令行写代码处理一个或多个输入文件设置默认的分隔符为 |，并基于这个分隔符分割所有行使用 OpenCSV 完成分割工作（awk 做不到）框架类下面是用 Groovy 类实现的 “awk 引擎”：@Grab('com.opencsv:opencsv:5.6') import com.opencsv.CSVReader public class AwkEngine { // With admiration and respect for // Alfred Aho // Peter Weinberger // Brian Kernighan // Thank you for the enormous value // brought my job by the awk // programming language Closure onBegin Closure onEachLine Closure onEnd private String fieldSeparator private boolean isFirstLineHeader private ArrayList<String> fileNameList public AwkEngine(args) { this.fileNameList = args this.fieldSeparator = "|" this.isFirstLineHeader = false } public AwkEngine(args, fieldSeparator) { this.fileNameLi…

我如何使用 Groovy 分析我的音乐目录
type: item
date: 2022-09-06T17:12:00+08:00
description: 为了简化 Java 的繁琐，我制作了一个 Groovy 工具来分析我的音乐目录。最近，我一直在研究 Groovy 是如何简化略微繁琐的 Java 的。在这篇文章中，我开始了一个简短的系列，通过创建一个分析我的音乐目录的工具来演示 Groovy 脚本。在本文中，我将演示 groovy.File 类如何扩展和精简 java.File 并简化其使用。这为查看音乐文件夹的内容提供了一个框架，以确保预期的内容（例如，cover.jpg 文件）就位。我使用 JAudiotagger 库 来分析音乐文件的标签。安装 Java 和 GroovyGroovy 基于 Java，需要安装 Java。 Java 和 Groovy 的最新和稳定的版本可能都在你的 Linux 发行版的仓库中。 Groovy 也可以直接从 Apache Foundation 网站 安装。对于 Linux 用户来说，一个不错的选择是 SDKMan，它可用于获取 Java、Groovy 和许多其他相关工具的多个版本。对于本文，我使用以下 SDK 版本：Java：版本 11.0.12-open 的 OpenJDK 11Groovy：版本 3.0.8音乐元数据最近，我重整了我的音乐消费方式。我决定使用优秀的开源 Cantata 音乐播放器，它是开源 MPD 音乐播放器 的一个前端。我所有的电脑的音乐都存储在 /var/lib/mpd/music 目录下。在该音乐目录下有艺术家子目录，在每个艺术家子目录下有专辑子目录，包含音乐文件、cover.jpg，偶尔还有 PDF 格式的内页说明。我绝大部分的音乐文件都是 FLAC 格式的，有一些是 MP3 格式，可能还有一小部分是 OGG 格式。我选择 JAudiotagger 库的一个原因是它可以透明地处理不同的标签格式。当然，JAudiotagger 是开源的！那么查看音频标签有什么意义呢？以我的经验，音频标签的管理极差。（提到音频标签，）我的脑海中浮现出“粗心”这个词。这是标签本身真正存在的问题，也可能是出于我自己的学究倾向。无论如何，这是一个可以通过使用 Groovy 和 JAudiotagger 解决的重要问题。不过，它不仅适用于音乐收藏。许多其他现实世界的问题也适用，如需要下沉到文件系统中的目录树来处理在那里找到的内容。使用 Groovy 脚本这是此任务所需的基本代码。我在脚本中加入了注释，这些注释反映了我通常留给自己的（相对简写的）“注释提醒”：// 定义音乐库目录 def musicLibraryDirName = '/var/lib/mpd/music' // 输出 CSV 文件标题行 println "artistDir|albumDir|contentFile" // 迭代音乐库目录中的每个目录 // 这一层应该是艺术家目录 new File(musicLibraryDirName).eachDir { artistDir -> // 迭代艺术家目录中的每个目录 // 这一层应该是专辑目录 artistDir.eachDir { albumDir -> // 迭代专辑目录中的每个目录 // 这里应该是内容 // 或相关内容（如 `cover.jpg`，PDF 格式的内页说明） albumDir.eachFile { contentFile -> println "$artistDir.name|$albumDir.name|$contentFile.name" } } } 如上所述，我使用 groovy.File 在目录树中移动。具体来说：第 7 行创建一个新的 groovy.File 对象并在其上调用 groovy.File.eachDir()，第 7 行的 { 和第 18 行的结尾的 } 之间的代码是传给 eachDir() 的 groovy.Colsue 参数。这意味着 eachDir() 为目录中找到的每个子目录执行该代码。这类似于 Java lambda（也称为“匿名函数”）。 Groovy 闭包不会像 lambda 那样限制对调用环境的访问（在最新版本的 Groovy 中，如果你愿意，也可以使用 Java lambda）。如上所述，音乐库目录中的子目录应该是艺术家目录（例如，“Iron Butterfly” 或 “Giacomo Puccini”），因此 artistDir 是 eachDir() 传递给闭包的参数。第 10 行对每个 artistDir 调用 eachDir()，第 10 行的 { 和第 17 行的 } 之间的代码形成另一个处理 albumDir 的闭包。第 14 行，在每个 albumDir 上调用 eachFile()，第 14 行的 { 和第 16 行的 } 之间的代码形成了处理专辑内…

通过编写“猜数字”游戏来学习 Awk
type: item
date: 2022-06-03T21:06:00+08:00
description: 编程语言往往具有许多共同特征。学习一门新语言的好方法是去写一个熟悉的程序。在本文中，我将会使用 Awk 编写一个“猜数字”程序来展示熟悉的概念。当你学习一门新的编程语言时，最好把重点放在大多数编程语言都有的共同点上:变量 —— 存储信息的地方表达式 —— 计算的方法语句 —— 在程序中表示状态变化的方法这些概念是大多是编程语言的基础。一旦你理解了这些概念，你就可以开始把其他的弄清楚。例如，大多数语言都有由其设计所支持的“处理方式”，这些方式在不同语言之间可能有很大的不同。这些方法包括模块化（将相关功能分组在一起）、声明式与命令式、面向对象、低级与高级语法特性等等。许多程序员比较熟悉的是编程“仪式”，即，在处理问题之前设置场景所需花费的工作。据说 Java 编程语言有一个源于其设计的重要仪式要求，就是所有代码都在一个类中定义。但从根本上讲，编程语言通常有相似之处。一旦你掌握了一种编程语言，就可以从学习另一种语言的基本知识开始，品味这种新语言的不同之处。一个好方法是创建一组基本的测试程序。有了这些，就可以从这些相似之处开始学习。你可以选择创建的一个测试程序是“猜数字”程序。电脑从 1 到 100 之间选择一个数字，让你猜这个数字。程序一直循环，直到你猜对为止。“猜数字”程序练习了编程语言中的几个概念:变量输入输出条件判断循环这是学习一门新的编程语言的一个很好的实践实验。注：本文改编自 Moshe Zadka 在 Julia 中使用这种方法和 Jim Hall在 Bash 中使用这种方法的文章。在 awk 程序中猜数让我们编写一个实现“猜数字”游戏的 Awk 程序。Awk 是动态类型的，这是一种面向数据转换的脚本语言，并且对交互使用有着令人惊讶的良好支持。Awk 出现于 20 世纪 70 年代，最初是 Unix 操作系统的一部分。如果你不了解 Awk，但是喜欢电子表格，这就是一个你可以 去学习 Awk 的信号！您可以通过编写一个“猜数字”游戏版本来开始对 Awk 的探索。以下是我的实现（带有行号，以便我们可以查看一些特定功能）： 1 BEGIN { 2 srand(42) 3 randomNumber = int(rand() * 100) + 1 4 print "random number is",randomNumber 5 printf "guess a number between 1 and 100\n" 6 } 7 { 8 guess = int($0) 9 if (guess < randomNumber) { 10 printf "too low, try again:" 11 } else if (guess > randomNumber) { 12 printf "too high, try again:" 13 } else { 14 printf "that's right\n" 15 exit 16 } 17 } 我们可以立即看到 Awk 控制结构与 C 或 Java 的相似之处，但与 Python 不同。 在像 if-then-else、while 这样的语句中，then、else 和 while 部分接受一个语句或一组被 { 和 } 包围的语句。然而，Awk 有一个很大的区别需要从一开始就了解：根据设计，Awk 是围绕数据管道构建的。这是什么意思呢？大多数 Awk 程序都是一些代码片段，它们接收一行输入，对数据做一些处理，然后将其写入输出。认识到这种转换管道的需要，Awk 默认情况下提供了所有的转换管道。让我们通过关于上面程序的一个基本问题来探索：“从控制台读取数据”的结构在哪里？答案是——“内置的”。特别的，第 7-17 行告诉 Awk 如何处理被读取的每一行。在这种情况下，很容易看到第 1-6 行是在读取任何内容之前被执行的。更具体地说，第 1 行上的 BEGIN 关键字是一种“模式”，在本例中，它指示 Awk 在读取任何数据之前，应该先执行 { ... } 中 BEGIN 后面的内容。另一个类似的关键字 END，在这个程序中没有被使用，它指示 Awk 在读取完所有内容后要做什么。回到第 7-17 行，我们看到它们创建了一个类似代码块 { ... } 的片段，但前面没有关键字。因为在 { 之前没有任何东西可以让 Awk 匹配，所以它将把这一行用于接收每一行输入。每一行的输入都将由用户输入作为猜测。让我们看看正在执行的代码。首先，是在读取任何输入之前发生的序言部分。在第 2 行，我们用数字 42 初始化随机数生成器（如果不提供参数，则使用系统时钟）。为什么要用 42？当然要选 42！#The_Hitchhiker's_Guide_to_the_Galaxy) 第 3 行计算 1 到 100 之间的随…

使用 apt 进行 Linux 包管理
type: item
date: 2022-05-07T18:42:00+08:00
description: 学习如何使用 apt 命令在基于 Debian 的 Linux 发行版上安装软件包，然后下载我们的速查表，让正确的命令触手可及。包管理器 可帮助你处理 Linux 系统的计算机上软件的更新、卸载、故障排除等问题。Seth Kenlon 写了 使用 dnf 进行 Linux 包管理 一文，介绍了如何使用 dnf 这款命令行包管理工具，在 RHEL、CentOS、Fedora、Mageia、OpenMandriva 等 Linux 发行版中安装软件。Debian 和基于 Debian 的发行版（例如 MX Linux、Deepin、Ubuntu）以及基于 Ubuntu 的发行版（例如 Linux Mint 和 Pop!\_OS）都有 apt，这是一个“相似但不同”的工具。在本文中，我将按照 Seth 的示例（但使用 apt）向你展示如何使用它。在一开始，我想先提一下四个跟 apt 相关的软件安装工具：Synaptic 是为 apt 服务的一个基于 GTK+ 的图形用户界面（GUI）的前端工具。Aptitude 是为 apt 服务的一个基于 Ncurses 的全屏命令行前端工具。apt 的前身有 apt-get、apt-cache 等工具。Dpkg 是在 apt 包管理器背后处理繁杂事务的”幕后工作者“。还有其他的包管理系统，例如 Flatpak 和 Snap，你可能会在 Debian 和基于 Debian 的系统上遇到它们，但我不打算在这里讨论。还有一些应用程序“商店”，例如 GNOME “软件”，与 apt 和其他打包技术重叠；我也不打算在这里讨论它们。最后，还有其他 Linux 发行版，例如 Arch 和 Gentoo 既不使用 dnf 也不使用 apt，我也不打算在这里讨论它们！上面我讲了这么多我不想提及的内容，你可能怀疑 apt 到底还能处理多少软件。这么说吧，在我的 Ubuntu 20.04 上，apt 可以让我使用 69,371 个软件包，从 0ad（一款古代战争题材的即时战略游戏）到 zzuf（一个透明的应用程序模糊测试工具），一点也不差。使用 apt 搜索软件使用 apt 软件包管理器的第一步是找到感兴趣的软件包。Seth 的 dnf 文章以 Cockpit 服务器管理应用程序为例。用 apt 我会输入如下命令：$ apt search cockpit Sorting... Done Full Text Search... Done 389-ds/hirsute,hirsute 1.4.4.11-1 all 389 Directory Server suite - metapackage cockpit/hirsute,hirsute 238-1 all Web Console for Linux servers ... $ 上面的第二个包就是你要的那个（以 cockpit/hirsute 开头的那一行）。如果你决定要安装它，输入：$ sudo apt install cockpit apt 将负责安装 Cockpit 以及使其工作所需的所有部件或 依赖。有时我们不太确定这是我们所需要的。了解更多的信息可能有助于你决定是否真的要安装此应用程序。包元数据要了解有关软件包的更多信息，使用 apt show 命令：$ apt show cockpit Package: cockpit Version: 238-1 Priority: optional Section: universe/admin Origin: Ubuntu Maintainer: Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com> Original-Maintainer: Utopia Maintenance Team <pkg-utopia-maintainers@lists.alioth.debian.org> Bugs: https://bugs.launchpad.net/ubuntu/+filebug Installed-Size: 88.1 kB Depends: cockpit-bridge (>= 238-1), cockpit-ws (>= 238-1), cockpit-system (>= 238-1) Recommends: cockpit-storaged (>= 238-1), cockpit-networkmanager (>= 238-1), cockpit-packagekit (>= 238-1) Suggests: cockpit-doc (>= 238-1), cockpit-pcp (>= 238-1), cockpit-machines (>=…

如何以非代码形式贡献开源
type: item
date: 2022-04-27T03:01:38+08:00
description: 事实上，有无穷无尽的方法来为开源做贡献，其中一个简单的方法就是回答我们的投票问题。你是如何参与开源贡献的呢？我们组织了一个投票，结果如下：提交错误报告 - 67 票（35%）解答用户的问题 - 39 票（20%）写作（指南、故事、文档等） - 73 票（38%）其他 - 12 票（6%）我的第一次开源贡献可以追溯到 20 世纪 80 年代中期，当时我们的机构第一次连上了 UseNet，在那里我们发现了贡献代码，以及在其开发和支持过程中和别人分享的机会。在今天，我们有无尽的贡献开源的机会。无论是贡献代码，还是制作一个视频教程，都是贡献的一种途径。不过，我将直接跳过整个贡献代码的部分。诚然，我们中有许多写代码但不认为自己是开发者的人，他们也可以 贡献代码。但是，我更想提醒大家，还存在很多 非代码形式可以贡献开源。接下来，我会谈到其中的三种。提交错误报告有一种重要而具体的贡献形式，它可以被描述为“不要畏惧 提交一个像样的错误报告”以及 与此相关的所有后果。有时，要 提交一个像样的错误报告 是很有挑战性的。比如说：某些错误可能很难记录或描述。当计算机启动时，屏幕上可能会出现又长又复杂的信息，其中包含各种不能理解的代码。或者屏幕上可能显示有一些“异常行为”，但是却没有提供具体的错误信息。某些错误可能很难重现。它可能只发生在某些特定的硬件/软件配置上，或者它可能很少被触发，或者错误的产生场景不明确。某些错误可能与一个非常特殊的开发环境配置有关，但是这个配置庞杂混乱，无法分享，需要先耗费大量精力创建一个精简后的例子才行。当向发行版报告一个错误时，维护者可能会建议将该错误提交给上游，这有时会需要付出大量的工作，因为发行版所提供的版本不是上游社区感兴趣的主要版本。（当发行版提供的版本落后于官方支持的发布和开发版本时，就会有这种情况发生）。尽管如此，我还是鼓励那些潜在的错误报告者（包括我）继续努力，并尝试让错误得到完整的记录和确认。但如何开始呢？你可以使用你最喜欢的搜索工具寻找类似的错误报告，看看它们是如何描述的，它们被归档在哪里，等等。你也可以留意你使用的发行版（例如，Fedora、openSUSE、Ubuntu）或软件包（LibreOffice、Mozilla）的错误报告页面，它们定义了正式的报告机制，你可以按步骤为他们报告相关错误。解答用户的问题我潜伏在各种邮件列表和 论坛 里，偶尔也会冒个泡，例如 Ubuntu 质量控制团队 和 论坛、LinuxQuestions.org，以及 ALSA 用户的邮件列表 等。在这里，我的贡献可能与错误报告的关系不大，更多的是记录复杂的用例。不过，看到有人热心帮助他人，解决他人在某个问题上的遇到的麻烦，对每个人来说，这都是无疑一种很棒的体验。从事开源相关的写作最后，另一个我非常喜欢贡献的领域是 撰写 关于使用开源软件的文章。无论是使用指南，还是对某一特定问题的不同解决方案进行比较评估，或者只是笼统地探索一个感兴趣的领域（就我而言，是使用开源音乐播放软件来享受音乐）。一个类似的选择是制作一个教学视频。你很容易就可以做到边演示一些复杂的桌面操作（比如用 GIMP 创建一个绚丽的标志），边 录制桌面。而那些精通两种或多种语言的人，也可以考虑将现有的使用指南或视频翻译成另一种语言。（LCTT 译注：读了这篇文章，你是不是想要马上投身于开源贡献呢？那么请考虑加入“Linux 中国翻译组（LCTT）”吧！我们有能帮助你快速上手翻译的 维基，有热心友爱的 QQ 群，你甚至还能够在我们的官网上获得属于自己的译者专页……心动了吗？那就立刻行动起来吧！阅读 维基 以了解如何加入我们。）via: https://opensource.com/article/19/4/contribute-without-code作者：Chris Hermansen 选题：lkxed 译者：lkxed 校对：校对者ID本文由 LCTT 原创编译，Linux中国 荣誉推出

在 Java 和 Groovy 中创建和初始化列表的不同
type: item
date: 2022-03-30T05:40:29+08:00
description: 首先在 Java 中创建初始化一个整数列表，然后在 Groovy 中做同样的事。我非常喜欢 Groovy 编程语言。我喜欢它是因为我喜欢 Java，尽管 Java 有时候感觉很笨拙。正因为我是那么喜欢 Java，其他运行在 JVM 上语言都不能吸引我。比方说 Kotlin、Scala 还有 Clojure 语言，它们感觉上就和 Java 不一样，因为它们对于什么是好的编程语言的理解不同。Groovy 和它们都不一样，在我看来，Groovy 是一个完美的选项，特别是对于一部分程序员来说，他们喜欢 Java，但是又需要一个更灵活、更紧凑，并且有时候更直接的语言。 列表 List 这种数据结构是一个很好的例子，它可以容纳一个无序的列表，列表中的元素可以是数字、字符串或者对象，程序员可以用某种方式高效地遍历这些元素，特别是对于编写和维护脚本的人来说，“高效”的关键就是要有简洁清晰的表达，而不需要一大堆“仪式”，把代码的意图都变模糊了。安装 Java 和 GroovyGroovy 是基于 Java 的，因此需要同时安装一个 Java 才行。你的 Linux 发行版的仓库中可能有最近的比较好的 Java 版本。或者，你也可以在根据 这些指导 来安装 Groovy。对于 Linux 用户来说，SDKMan 是一个不错的代替选项，你可以使用它来获取多个 Java 和 Groovy 版本，以及许多其他的相关工具。在这篇文章中，我使用的 SDK 发行版是：Java: OpenJDK 11 的 11.0.12-open 版本Groovy: 3.0.8 版本言归正传Java 中有很多方法可以实例化并初始化列表，从它最初被引入的时候就有了（我记得是在 Java 1.5 的时候，但请不要引用我的话）。在这些方法里，有两个有趣的方法，它们涉及到了 java.util.Arrays 和 java.util.List 这两个类。使用 java.util.Arrays 类java.util.Arrays 类定义了一个 asList() 静态方法，它可以被用来创建一个基于数组的列表，因此大小是不可变的，尽管其中的元素是可以被修改的。下面是它的使用方式：var a1 = Arrays.asList(1,2,3,4,5,6,7,8,9,10); // immutable list of mutable elements System.out.println("a1 = " + a1); System.out.println("a1 is an instance of " + a1.getClass()); // output is // a1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] // a1 is an instance of class java.util.Arrays$ArrayList a1.set(0,0); // succeeds System.out.println("a1 = " + a1); // output is // a1 = [0, 2, 3, 4, 5, 6, 7, 8, 9, 10] a1.add(11); // fails producing // Exception in thread "main" java.lang.UnsupportedOperationException System.out.println("a1 = " + a1); // not reached 使用 java.util.List 类java.util.List 类定义了一个 of() 静态方法，它可以被用来创建一个不可变的列表，其中的元素是否可变要取决于它们本身是否支持修改。下面是它的使用方式：var a2 = List.of(1,2,3,4,5,6,7,8,9,10); System.out.println("a2 = " + a2); System.out.println("a2 is an instance of " + a2.getClass()); // output is // a2 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] // a2 is an instance of class java.util.ImmutableCollections$ListN a2.set(0,0); // fails producing // Exception in thread "main" java.lang.UnsupportedOperationException System.out.println("a2 = " + a2); // not reached a2.add(11); // als…

在 Groovy 和 Java 中创建并初始化映射的不同
type: item
date: 2022-03-21T17:25:38+08:00
description: Java 和 Groovy 中的 映射 map 都是非常通用的，它允许 关键字 key 和 值 value 为任意类型，只要继承了 Object 类即可。我最近在探索 Java 与 Groovy 在 创建并初始化 列表 List 和 在运行时构建 列表 List 方面的一些差异。我观察到，就实现这些功能而言，Groovy 的简洁和 Java 的繁复形成了鲜明对比。在这篇文章中，我将实现在 Java 和 Groovy 中创建并初始化 映射 Map 。映射为开发支持根据 关键字 key 检索的结构提供了可能，如果找到了这样一个关键字，它就会返回对应的 值 value 。今天，很多编程语言都实现了映射，其中包括 Java 和 Groovy，也包括了 Python（它将映射称为 字典 dict ）、Perl、awk 以及许多其他语言。另一个经常被用来描述映射的术语是 关联数组 associative array ，你可以在 这篇维基百科文章 中了解更多。Java 和 Groovy 中的映射都是非常通用的，它允许关键字和值为任意类型，只要继承了 Object 类即可。安装 Java 和 GroovyGroovy 基于 Java，因此你需要先安装 Java。你的 Linux 发行版的仓库中可能有最近的比较好的 Java 和 Groovy 版本。或者，你也可以在根据上面链接中的指示来安装 Groovy。对于 Linux 用户来说，SDKMan 是一个不错的代替选项，你可以使用它来获取多个 Java 和 Groovy 版本，以及许多其他的相关工具。在这篇文章中，我使用的 SDK 发行版是：Java: version 11.0.12-open of OpenJDK 11;Groovy: version 3.0.8.言归正传Java 提供了非常多的方式来实例化和初始化映射，并且从 Java 9 之后，添加了一些新的方式。其中最明显的方式就是使用 java.util.Map.of() 这个静态方法，下面介绍如何使用它：var m1 = Map.of( "AF", "Afghanistan", "AX", "Åland Islands", "AL", "Albania", "DZ", "Algeria", "AS", "American Samoa", "AD", "Andorra", "AO", "Angola", "AI", "Anguilla", "AQ", "Antarctica"); System.out.println("m1 = " + m1); System.out.println("m1 is an instance of " + m1.getClass()); 事实证明，在此种情况下，Map.of() 有两个重要的限制。其一，这样创建出来的映射实例是 不可变的 immutable 。其二，你最多只能提供 20 个参数，用来表示 10 个 键值对 key-value pair 。你可以尝试着添加第 10 对和第 11 对，比方说 "AG", "Antigua and Barbuda" 和 "AR", "Argentina"，然后观察会发生什么。你将发现 Java 编译器尝试寻找一个支持 11 个键值对的 Map.of() 方法而遭遇失败。快速查看 java.util.Map 类的文档，你就会找到上述第二个限制的原因，以及解决这个难题的一种方式：var m2 = Map.ofEntries( Map.entry("AF", "Afghanistan"), Map.entry("AX", "Åland Islands"), Map.entry("AL", "Albania"), Map.entry("DZ", "Algeria"), Map.entry("AS", "American Samoa"), Map.entry("AD", "Andorra"), Map.entry("AO", "Angola"), Map.entry("AI", "Anguilla"), Map.entry("AQ", "Antarctica"), Map.entry("AG", "Antigua and Barbuda"), Map.entry("AR", "Argentina"), Map.entry("AM", "Armenia"), Map.entry("AW", "Aruba"), Map.entry("AU", "Australia"), Map.entry("AT", "Austria"), Map.entry("AZ", "Azerbaijan"), Map.entry("BS", "Bahamas"), Map.entry("BH", "B…

如何从 Linux 发行版的仓库中安装 Java
type: item
date: 2021-10-09T17:27:00+08:00
description: 无论你喜欢哪个发行版和包管理器，都可以都很容易地在你的 Linux 系统上安装 Java。把 Java 安装到你的 Linux 桌面上有多种方法。一个明显的方式是使用你的 Linux 发行版中提供的软件包。请注意，这并不适合所有人。例如，有些用户可能需要一个非常具体的 Java 版本。在你开始之前，你必须确定你需要“哪种 Java”。你是否只需要运行一些 .class 文件或 .jar 文件？还是你正在编写一些需要编译的代码？就我而言，我运行的大部分 Java 都是我自己（至少部分）编写的 Java，所以安装完整的 Java 开发工具包（或称 JDK）总是有意义的，它包含了 Java 编译器、库和一些非常有用的工具。当然，在这里，我们更倾向于使用开源的 JDK，称为 OpenJDK。由于我主要在 Ubuntu Linux 发行版上工作，我的软件包管理器是 apt。我可以用 apt 来查找哪些 OpenJDK 包是可用的：apt list OpenJDK\* 这个命令的输出看起来像这样：Listing... Done openjdk-11-dbg/hirsute-updates,hirsute-security 11.0.11+9-0ubuntu2 amd64 openjdk-11-dbg/hirsute-updates,hirsute-security 11.0.11+9-0ubuntu2 i386 openjdk-11-demo/hirsute-updates,hirsute-security 11.0.11+9-0ubuntu2 amd64 openjdk-11-demo/hirsute-updates,hirsute-security 11.0.11+9-0ubuntu2 i386 openjdk-11-doc/hirsute-updates,hirsute-updates,hirsute-security,hirsute-security 11.0.11+9-0ubuntu2 all openjdk-11-jdk-headless/hirsute-updates,hirsute-security 11.0.11+9-0ubuntu2 amd64 openjdk-11-jdk-headless/hirsute-updates,hirsute-security 11.0.11+9-0ubuntu2 i386 openjdk-11-jdk/hirsute-updates,hirsute-security 11.0.11+9-0ubuntu2 amd64 openjdk-11-jdk/hirsute-updates,hirsute-security 11.0.11+9-0ubuntu2 i386 openjdk-11-jre-dcevm/hirsute 11.0.10+1-1 amd64 openjdk-11-jre-headless/hirsute-updates,hirsute-security 11.0.11+9-0ubuntu2 amd64 openjdk-11-jre-headless/hirsute-updates,hirsute-security 11.0.11+9-0ubuntu2 i386 openjdk-11-jre-zero/hirsute-updates,hirsute-security 11.0.11+9-0ubuntu2 amd64 openjdk-11-jre-zero/hirsute-updates,hirsute-security 11.0.11+9-0ubuntu2 i386 openjdk-11-jre/hirsute-updates,hirsute-security 11.0.11+9-0ubuntu2 amd64 openjdk-11-jre/hirsute-updates,hirsute-security 11.0.11+9-0ubuntu2 i386 openjdk-11-source/hirsute-updates,hirsute-updates,hirsute-security,hirsute-security 11.0.11+9-0ubuntu2 all openjdk-15-dbg/hirsute 15.0.3+3-1 amd64 openjdk-15-dbg/hirsute 15.0.3+3-1 i386 openjdk-15-demo/hirsute 15.0.3+3-1 amd64 ... openjdk-8-jre/hirsute-updates,hirsute-security 8u292-b10-0ubuntu1 i386 openjdk-8-source/hi…

在 Java 中使用外部库
type: item
date: 2021-09-10T15:57:59+08:00
description: 外部库填补了 Java 核心库中的一些功能空白。Java 自带有一组核心库，其中包含了定义常用数据类型和相关行为的库（例如 String 和 Date）、与主机操作系统交互的实用程序（例如 System 和 File），以及一些用来管理安全性、处理网络通信、创建或解析 XML的有用的子系统。鉴于核心库的丰富性，程序员通常很容易在其中找到有用的组件，以减少需要编写的代码量。即便如此，核心库仍有一些功能上的不足，因此发现这些不足的程序员们还额外创建了很多有趣的 Java 库。例如，Apache Commons“是一个专注于可重用 Java 组件所有方面的 Apache 项目”，提供了大约 43 个开源库的集合（截至撰写本文时），涵盖了 Java 核心库之外的一系列功能 （例如 geometry 或 statistics），并增强或替换了 Java 核心库中的原有功能（例如 math 或 numbers）。另一种常见的 Java 库类型是系统组件的接口（例如数据库系统接口），本文会着眼于使用此类接口连接到 PostgreSQL 数据库，并得到一些有趣的信息。首先，我们来回顾一下库的重要部分。什么是库？ 库 library 里自然包含的是一些有用的代码。但为了发挥用处，代码需要以特定方式进行组织，特定的方式使 Java 程序员可以访问其中组件来解决手头问题。可以说，一个库最重要的部分是它的应用程序编程接口（API）文档。这种文档很多人都熟悉，通常是由 Javadoc 生成的。Javadoc 读取代码中的结构化注释并以 HTML 格式输出文档，通常 API 的 包 package 在页面左上角的面板中显示， 类 class 在左下角显示，同时右侧会有库、包或类级别的详细文档（具体取决于在主面板中选择的内容）。例如，Apache Commons Math 的顶级 API 文档 如下所示：单击主面板中的包会显示该包中定义的 Java 类和接口。例如，org.apache.commons.math4.analysis.solvers 显示了诸如 BisectionSolver 这样的类，该类用于使用二分算法查找单变量实函数的零点。单击 BisectionSolver 链接会列出 BisectionSolver 类的所有方法。这类文档可用作参考文档，不适合作为学习如何使用库的教程。比如，如果你知道什么是单变量实函数并查看包 org.apache.commons.math4.analysis.function，就可以试着使用该包来组合函数定义，然后使用 org.apache.commons.math4.analysis.solvers 包来查找刚刚创建的函数的零点。但如果你不知道，就可能需要更多学习向的文档，也许甚至是一个实际例子，来读懂参考文档。这种文档结构还有助于阐明 包 package （相关 Java 类和接口定义的集合）的含义，并显示特定库中捆绑了哪些包。这种库的代码通常是在 .jar 文件) 中，它基本上是由 Java 的 jar 命令创建的 .zip 文件，其中还包含一些其他有用的信息。.jar 文件通常被创建为构建过程的端点，该构建过程编译了所定义包中的所有 .java 文件。要访问外部库提供的功能，有两个主要步骤：确保通过类路径（或者命令行中的 -cp 参数或者 CLASSPATH 环境变量），库可用于 Java 编译步骤（javac）和执行步骤（java）。使用恰当的 import 语句访问程序源代码中的包和类。其余的步骤就与使用 String 等 Java核心类相同，使用库提供的类和接口定义来编写代码。很简单对吧？不过也没那么简单。首先，你需要了解库组件的预期使用模式，然后才能编写代码。示例：连接 PostgreSQL 数据库在数据库系统中访问数据的典型使用步骤是：访问正在使用的特定数据库软件代码。连接到数据库服务器。构建查询字符串。执行查询字符串。针对返回的结果，做需要的处理。断开与数据库服务器的连接。所有这些面向程序员的部分由接口包 java.sql 提供，它独立于数据库，定义了核心客户端 Java 数据库连接（JDBC）API。java.sql 包是 Java 核心库的一部分，因此无需提供 .jar 文件即可编译。但每个数据库提供者都会创建自己的 java.sql 接口实现（例如 Connection 接口），并且必须在运行步骤中提供这些实现。接下来我们使用 PostgreSQL，看看这一过程是如何进行的。访问特定数据库的代码以下代码使用 Java 类加载器（Class.forName() 调用）将 PostgreSQL 驱动程序代码加载到正在执行的虚拟机中：import java.sql.*; public clas…

我家的 Linux 故事
type: item
date: 2021-07-25T14:50:56+08:00
description: 我们在 Linux 的第一次尝试只是一个 apt-get 的距离。我在 Linux 的第一次尝试是那种“或许我应该试一试”的情况。那是 1990 年代，我在一些软盘上找到了用某种打包方式打包的红帽发行版，我为家里的笔记本电脑买了第二个硬盘，然后开始安装它。这是一件有趣的实验，但是我记得当时家人还没有准备好在电脑上使用 Linux。转眼到了 2005 年，我最终放弃了这种做法，买了一台可爱的东芝笔记本电脑，来运行 Windows XP。在工作中，我有一台有点年头的 SUN SPARCStation 5，并且我不太喜欢当时整个 Solaris 的发展方向（基于 Motif 的桌面）。我真的想要用 GIMP 来完成一些这样或那样的项目，但是在 Solaris 上安装 GNOME 1.x（也许是 1.4？）的曲折旅程是很有挑战性的。所以，我实际上是在考虑跳槽到 Windows XP。但是在我的家用机上用了几个月之后，我发现我更不喜欢在 Solaris 上运行 GNOME，所以我安装了 Ubuntu Hoary Hedgehog 5.04，随后在我的笔记本电脑上安装了 Breezy Badger 5.10。这太棒了，那台拥有 3.2GHz 奔腾处理器、2GB 内存和 100GB 的硬盘的机器就在我的 SPARCStation 5 旁边运行。突然之间，我不再用拼凑起来的 Solaris 安装包来试图去让东西运行起来，而只是用 apt-get 就可以了。并且这个时机也很好。我家庭和我从 2006 年 8 月到 2007 年 7 月居住在法国格勒诺布尔，当时我的妻子在休假。因为有了运行 Linux 的东芝笔记本，我可以带着我的工作一起走。那个时候我在几个大的项目上做了大量的 GIS 数据处理，我发现我可以在 PostGIS / PostgreSQL 上做同样的事情，比我们在加拿大家中使用的昂贵得多的商业 GIS 软件要快得多。大家都很开心，尤其是我。这一路上发生的有趣的事情是，我们把另外两台电脑带到了法国 —— 我妻子的类似的东芝电脑（运行 XP，对她来说很好用）和我们孩子最近新买的东芝牌笔记本电脑，也运行 XP。也就在圣诞节过后，他们有一些朋友过来，无意中在他们的电脑上安装了一个讨厌的、无法清除的病毒。经过几个小时甚至几天后，我的一个孩子问我：“爸爸，我们就不能安装和你电脑上一样的东西吗？”然后，三个新的 Linux 用户就这样产生了。我的儿子，29 岁了，依然是一个快乐的 Linux 用户，我猜他有第四或第五台 Linux 笔记本电脑了，最后几台都是由 System 76 提供的。我的一个女儿三年前开始读法学院时被迫转换为 Windows，因为她所在的学校有一个强制性的测试框架，只能在 Windows 上运行，而且据称会检测虚拟机之类的东西（请不要让我开始骂人）。而我的另一个女儿被她的公司为她买的 Macbook Air 诱惑了。哦，好吧，不可能全都赢了吧！via: https://opensource.com/article/21/5/my-linux-story作者：Chris Hermansen 选题：lujun9972 译者：shiboi77 校对：wxy本文由 LCTT 原创编译，Linux中国 荣誉推出