分类 软件开发 下的文章

听说,你已经开始学习 Java 编程了?很好。

你想在你的 Linux 系统上运行 Java 程序?那就更好了。

让我告诉你如何在 Ubuntu 和其他 Linux 发行版的终端中运行 Java。

在 Ubuntu 中运行 Java 程序

让我们在这里按正确的步骤进行。

第一步:安装 Java 编译器

要运行一个 Java 程序,你需要先编译该程序。为此你需要 Java 编译器。

Java 编译器是 JDK Java 开发工具包 Java Development Kit )的一部分。你需要安装 JDK,以便编译和运行 Java 程序。

首先,检查你的系统上是否已经安装了 Java 编译器:

javac --version

如果你看到类似 “Command ‘javac’ not found, but can be installed with” 的错误,这意味着你需要安装 Java 开发工具包。

Check if Java compiler is already installed or not

在 Ubuntu 上安装 JDK 的最简单方法是使用 Ubuntu 的默认包:

sudo apt install default-jdk

你会被要求输入你的账户密码。当你输入密码时,屏幕上什么也看不到。这很正常。直接输入密码即可。当询问时,按回车键或 Y 键。

Installing JDK that also contains the Java compiler

上述命令应该适用于其他基于 Debian 和 Ubuntu 的发行版,如 Linux Mint、Elementary OS 等。对于其他发行版,请使用你的发行版的包管理器。包的名称也可能不同。

安装完毕后,验证 javac 现在是否可用。

Verify that Java compiler can be used now

第二步:在 Linux 中编译 Java 程序

要编译的话,你首先需要有一个 Java 程序文件。假设你创建了一个名为 HelloWorld.java 的新的 Java 程序文件,它的内容如下:

class HelloWorld{
    public static void main(String args[]){
     System.out.println("Hello World");
    }
}

你可以 使用终端下的 Nano 编辑器 或 Gedit 图形化文本编辑器来编写你的 Java 程序。

javac HelloWorld.java

如果没有错误,上面的命令不会产生输出。

当你编译 Java 程序时,它会生成一个 .class 文件,文件名是你在程序中使用的类。你需要运行这个类文件。

第三步:运行 Java 类文件

你不需要在这里指定类的扩展名。只需要类的名称。而这一次,你使用 java 命令,而不是 javac

java HelloWorld

我的程序将在屏幕上打印 “Hello World”。

Running java programs in the Linux terminal

这就是你如何在 Linux 终端中运行一个 Java 程序。

这是最简单的一个例子。这个示例程序只有一个类。Java 编译器为你程序中的每个类都创建一个类文件。对于较大的程序和项目来说,事情会变得很复杂。

这就是为什么我建议 在 Ubuntu 上安装 Eclipse 来进行 Java 编程。在 IDE 中编程更容易。

希望本教程对你有所帮助。有问题或建议吗?评论区都是你的。


via: https://itsfoss.com/run-java-program-ubuntu/

作者:Abhishek Prakash 选题:lujun9972 译者:geekpi 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

大家好!昨天我与一位朋友聊天,他正在准备编程面试,并试图学习一些算法基础知识。

我们聊到了 二次时间 quadratic-time 线性时间 linear-time 算法的话题,我认为在这里写这篇文章会很有趣,因为避免二次时间算法不仅在面试中很重要——有时在现实生活中了解一下也是很好的!后面我会快速解释一下什么是“二次时间算法” :)

以下是我们将要讨论的 3 件事:

  1. 二次时间函数比线性时间函数慢得非常非常多
  2. 有时可以通过使用 hashmap 把二次算法变成线性算法
  3. 这是因为 hashmap 查找非常快(即时查询!)

我会尽量避免使用数学术语,重点关注真实的代码示例以及它们到底有多快/多慢。

目标问题:取两个列表的交集

我们来讨论一个简单的面试式问题:获取 2 个数字列表的交集。 例如,intersect([1,2,3], [2,4,5]) 应该返回 [2]

这个问题也是有些现实应用的——你可以假设有一个真实程序,其需求正是取两个 ID 列表的交集。

“显而易见”的解决方案:

我们来写一些获取 2 个列表交集的代码。下面是一个实现此需求的程序,命名为 quadratic.py

import sys

# 实际运行的代码
def intersection(list1, list2):
    result = []
    for x in list1:
        for y in list2:
            if x == y:
                result.append(y)
    return result

# 一些样板,便于我们从命令行运行程序,处理不同大小的列表
def run(n):
    # 定义两个有 n+1 个元素的列表
    list1 = list(range(3, n)) + [2]
    list2 = list(range(n+1, 2*n)) + [2]
    # 取其交集并输出结果
    print(list(intersection(list1, list2)))

# 使用第一个命令行参数作为输入,运行程序
run(int(sys.argv[1]))

程序名为 quadratic.py(LCTT 译注:“quadratic”意为“二次方的”)的原因是:如果 list1list2 的大小为 n,那么内层循环(if x == y)会运行 n^2 次。在数学中,像 x^2 这样的函数就称为“二次”函数。

quadratic.py 有多慢?

用一些不同长度的列表来运行这个程序,两个列表的交集总是相同的:[2]

$ time python3 quadratic.py 10
[2]

real    0m0.037s
$ time python3 quadratic.py 100
[2]

real    0m0.053s
$ time python3 quadratic.py 1000
[2]

real    0m0.051s
$ time python3 quadratic.py 10000 # 10,000
[2]

real    0m1.661s

到目前为止,一切都还不错——程序仍然只花费不到 2 秒的时间。

然后运行该程序处理两个包含 100,000 个元素的列表,我不得不等待了很长时间。结果如下:

$ time python3 quadratic.py 100000 # 100,000
[2]

real    2m41.059s

这可以说相当慢了!总共花费了 160 秒,几乎是在 10,000 个元素上运行时(1.6 秒)的 100 倍。所以我们可以看到,在某个点之后,每次我们将列表扩大 10 倍,程序运行的时间就会增加大约 100 倍。

我没有尝试在 1,000,000 个元素上运行这个程序,因为我知道它会花费又 100 倍的时间——可能大约需要 3 个小时。我没时间这样做!

你现在大概明白了为什么二次时间算法会成为一个问题——即使是这个非常简单的程序也会很快变得非常缓慢。

快速版:linear.py

好,接下来我们编写一个快速版的程序。我先给你看看程序的样子,然后再分析。

import sys

# 实际执行的算法
def intersection(list1, list2):
    set1 = set(list1) # this is a hash set
    result = []
    for y in list2:
        if y in set1:
            result.append(y)
    return result

# 一些样板,便于我们从命令行运行程序,处理不同大小的列表
def run(n):
    # 定义两个有 n+1 个元素的列表
    list1 = range(3, n) + [2]
    list2 = range(n+1, 2*n) + [2]
    # 输出交集结果
    print(intersection(list1, list2))

run(int(sys.argv[1]))

(这不是最惯用的 Python 使用方式,但我想在尽量避免使用太多 Python 思想的前提下编写代码,以便不了解 Python 的人能够更容易理解)

这里我们做了两件与慢速版程序不同的事:

  1. list1 转换成名为 set1 的 set 集合
  2. 只使用一个 for 循环而不是两个

看看 linear.py 程序有多快

在讨论 为什么 这个程序快之前,我们先在一些大型列表上运行该程序,以此证明它确实是很快的。此处演示该程序依次在大小为 10 到 10,000,000 的列表上运行的过程。(请记住,我们上一个的程序在 100,000 个元素上运行时开始变得非常非常慢)

$ time python3 linear.py 100
[2]

real    0m0.056s
$ time python3 linear.py 1000
[2]

real    0m0.036s
$ time python3 linear.py 10000 # 10,000
[2]

real    0m0.028s
$ time python3 linear.py 100000 # 100,000
[2]

real    0m0.048s <-- quadratic.py took 2 minutes in this case! we're doing it in 0.04 seconds now!!! so fast!
$ time python3 linear.py 1000000 # 1,000,000
[2]

real    0m0.178s
$ time python3 linear.py 10000000 # 10,000,000
[2]

real    0m1.560s

在极大型列表上运行 linear.py

如果我们试着在一个非常非常大的列表(100 亿 / 10,000,000,000 个元素)上运行它,那么实际上会遇到另一个问题:它足够 了(该列表仅比花费 4.2 秒的列表大 100 倍,因此我们大概应该能在不超过 420 秒的时间内完成),但我的计算机没有足够的内存来存储列表的所有元素,因此程序在运行结束之前崩溃了。

$ time python3 linear.py 10000000000
Traceback (most recent call last):
  File "/home/bork/work/homepage/linear.py", line 18, in <module>
    run(int(sys.argv[1]))
  File "/home/bork/work/homepage/linear.py", line 13, in run
    list1 = [1] * n + [2]
MemoryError

real    0m0.090s
user    0m0.034s
sys 0m0.018s

不过本文不讨论内存使用,所以我们可以忽略这个问题。

那么,为什么 linear.py 很快呢?

现在我将试着解释为什么 linear.py 很快。

再看一下我们的代码:

def intersection(list1, list2):
    set1 = set(list1) # this is a hash set
    result = []
    for y in list2:
        if y in set1:
            result.append(y)
    return result

假设 list1list2 都是大约 10,000,000 个不同元素的列表,这样的元素数量可以说是很大了!

那么为什么它还能够运行得如此之快呢?因为 hashmap!!!

hashmap 查找是即时的(“常数级时间”)

我们看一下快速版程序中的 if 语句:

if y in set1:
    result.append(y)

你可能会认为如果 set1 包含 1000 万个元素,那么这个查找——if y in set1 会比 set1 包含 1000 个元素时慢。但事实并非如此!无论 set1 有多大,所需时间基本是相同的(超级快)。

这是因为 set1 是一个哈希集合,它是一种只有键没有值的 hashmap(hashtable)结构。

我不准备在本文中解释 为什么 hashmap 查找是即时的,但是神奇的 Vaidehi Joshi 的 basecs 系列中有关于 hash tablehash 函数 的解释,其中讨论了 hashmap 即时查找的原因。

不经意的二次方:现实中的二次算法!

二次时间算法真的很慢,我们看到的的这个问题实际上在现实中也会遇到——Nelson Elhage 有一个很棒的博客,名为 不经意的二次方,其中有关于不经意以二次时间算法运行代码导致性能问题的故事。

二次时间算法可能会“偷袭”你

关于二次时间算法的奇怪之处在于,当你在少量元素(如 1000)上运行它们时,它看起来并没有那么糟糕!没那么慢!但是如果你给它 1,000,000 个元素,它真的会花费几个小时去运行。

所以我认为它还是值得深入了解的,这样你就可以避免无意中使用二次时间算法,特别是当有一种简单的方法来编写线性时间算法(例如使用 hashmap)时。

总是让我感到一丝神奇的 hashmap

hashmap 当然不是魔法(你可以学习一下为什么 hashmap 查找是即时的!真的很酷!),但它总是让人 感觉 有点神奇,每次我在程序中使用 hashmap 来加速,都会使我感到开心 :)


via: https://jvns.ca/blog/2021/09/10/hashmaps-make-things-fast/

作者:Julia Evans 选题:lujun9972 译者:unigeorge 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

外部库填补了 Java 核心库中的一些功能空白。

 title=

Java 自带有一组核心库,其中包含了定义常用数据类型和相关行为的库(例如 StringDate)、与主机操作系统交互的实用程序(例如 SystemFile),以及一些用来管理安全性、处理网络通信、创建或解析 XML的有用的子系统。鉴于核心库的丰富性,程序员通常很容易在其中找到有用的组件,以减少需要编写的代码量。

即便如此,核心库仍有一些功能上的不足,因此发现这些不足的程序员们还额外创建了很多有趣的 Java 库。例如,Apache Commons“是一个专注于可重用 Java 组件所有方面的 Apache 项目”,提供了大约 43 个开源库的集合(截至撰写本文时),涵盖了 Java 核心库之外的一系列功能 (例如 geometrystatistics),并增强或替换了 Java 核心库中的原有功能(例如 mathnumbers)。

另一种常见的 Java 库类型是系统组件的接口(例如数据库系统接口),本文会着眼于使用此类接口连接到 PostgreSQL 数据库,并得到一些有趣的信息。首先,我们来回顾一下库的重要部分。

什么是库?

library 里自然包含的是一些有用的代码。但为了发挥用处,代码需要以特定方式进行组织,特定的方式使 Java 程序员可以访问其中组件来解决手头问题。

可以说,一个库最重要的部分是它的应用程序编程接口(API)文档。这种文档很多人都熟悉,通常是由 Javadoc 生成的。Javadoc 读取代码中的结构化注释并以 HTML 格式输出文档,通常 API 的 package 在页面左上角的面板中显示, class 在左下角显示,同时右侧会有库、包或类级别的详细文档(具体取决于在主面板中选择的内容)。例如,Apache Commons Math 的顶级 API 文档 如下所示:

 title=

单击主面板中的包会显示该包中定义的 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 文件。

要访问外部库提供的功能,有两个主要步骤:

  1. 确保通过类路径(或者命令行中的 -cp 参数或者 CLASSPATH 环境变量),库可用于 Java 编译步骤(javac)和执行步骤(java)。
  2. 使用恰当的 import 语句访问程序源代码中的包和类。

其余的步骤就与使用 String 等 Java核心类相同,使用库提供的类和接口定义来编写代码。很简单对吧?不过也没那么简单。首先,你需要了解库组件的预期使用模式,然后才能编写代码。

示例:连接 PostgreSQL 数据库

在数据库系统中访问数据的典型使用步骤是:

  1. 访问正在使用的特定数据库软件代码。
  2. 连接到数据库服务器。
  3. 构建查询字符串。
  4. 执行查询字符串。
  5. 针对返回的结果,做需要的处理。
  6. 断开与数据库服务器的连接。

所有这些面向程序员的部分由接口包 java.sql 提供,它独立于数据库,定义了核心客户端 Java 数据库连接(JDBC)API。java.sql 包是 Java 核心库的一部分,因此无需提供 .jar 文件即可编译。但每个数据库提供者都会创建自己的 java.sql 接口实现(例如 Connection 接口),并且必须在运行步骤中提供这些实现。

接下来我们使用 PostgreSQL,看看这一过程是如何进行的。

访问特定数据库的代码

以下代码使用 Java 类加载器Class.forName() 调用)将 PostgreSQL 驱动程序代码加载到正在执行的虚拟机中:

import java.sql.*;

public class Test1 {

    public static void main(String args[]) {

        // Load the driver (jar file must be on class path) [1]

        try {
            Class.forName("org.postgresql.Driver");
            System.out.println("driver loaded");
        } catch (Exception e1) {
            System.err.println("couldn't find driver");
            System.err.println(e1);
            System.exit(1);
        }

        // If we get here all is OK

        System.out.println("done.");
    }
}

因为类加载器可能失败,失败时会抛出异常,所以将对 Class.forName() 的调用放在 try-catch 代码块中。

如果你使用 javac 编译上面的代码,然后用 java 运行,会报异常:

me@mymachine:~/Test$ javac Test1.java
me@mymachine:~/Test$ java Test1
couldn't find driver
java.lang.ClassNotFoundException: org.postgresql.Driver
me@mymachine:~/Test$

类加载器要求类路径中有包含 PostgreSQL JDBC 驱动程序实现的 .jar 文件:

me@mymachine:~/Test$ java -cp ~/src/postgresql-42.2.5.jar:. Test1
driver loaded
done.
me@mymachine:~/Test$

连接到数据库服务器

以下代码实现了加载 JDBC 驱动程序和创建到 PostgreSQL 数据库的连接:

import java.sql.*;

public class Test2 {

        public static void main(String args[]) {

                // Load the driver (jar file must be on class path) [1]

                try {
                        Class.forName("org.postgresql.Driver");
                        System.out.println("driver loaded");
                } catch (Exception e1) {
                        System.err.println("couldn't find driver");
                        System.err.println(e1);
                        System.exit(1);
                }

                // Set up connection properties [2]

                java.util.Properties props = new java.util.Properties();
                props.setProperty("user","me");
                props.setProperty("password","mypassword");
                String database = "jdbc:postgresql://myhost.org:5432/test";

                // Open the connection to the database [3]

                try (Connection conn = DriverManager.getConnection(database, props)) {
                        System.out.println("connection created");
                } catch (Exception e2) {
                        System.err.println("sql operations failed");
                        System.err.println(e2);
                        System.exit(2);
                }
                System.out.println("connection closed");

                // If we get here all is OK

                System.out.println("done.");
        }
}

编译并运行上述代码:

me@mymachine:~/Test$ javac Test2.java
me@mymachine:~/Test$ java -cp ~/src/postgresql-42.2.5.jar:. Test2
driver loaded
connection created
connection closed
done.
me@mymachine:~/Test$

关于上述的一些注意事项:

  • 注释 [2] 后面的代码使用系统属性来设置连接参数(在本例中参数为 PostgreSQL 用户名和密码)。代码也可以从 Java 命令行获取这些参数并将所有参数作为参数包传递,同时还有一些其他 Driver.getConnection() 选项可用于单独传递参数。
  • JDBC 需要一个用于定义数据库的 URL,它在上述代码中被声明为 String database 并与连接参数一起传递给 Driver.getConnection() 方法。
  • 代码使用 try-with-resources 语句,它会在 try-catch 块中的代码完成后自动关闭连接。Stack Overflow 上对这种方法进行了长期的讨论。
  • try-with-resources 语句提供对 Connection 实例的访问,并可以在其中执行 SQL 语句;所有错误都会被同一个 catch 语句捕获。

用数据库的连接处理一些有趣的事情

日常工作中,我经常需要知道为给定的数据库服务器实例定义了哪些用户,这里我使用这个 简便的 SQL 来获取所有用户的列表:

import java.sql.*;

public class Test3 {

        public static void main(String args[]) {

                // Load the driver (jar file must be on class path) [1]

                try {
                        Class.forName("org.postgresql.Driver");
                        System.out.println("driver loaded");
                } catch (Exception e1) {
                        System.err.println("couldn't find driver");
                        System.err.println(e1);
                        System.exit(1);
                }

                // Set up connection properties [2]

                java.util.Properties props = new java.util.Properties();
                props.setProperty("user","me");
                props.setProperty("password","mypassword");
                String database = "jdbc:postgresql://myhost.org:5432/test";

                // Open the connection to the database [3]

                try (Connection conn = DriverManager.getConnection(database, props)) {
                        System.out.println("connection created");

                        // Create the SQL command string [4]

                        String qs = "SELECT " +
                                "       u.usename AS \"User name\", " +
                                "       u.usesysid AS \"User ID\", " +
                                "       CASE " +
                                "       WHEN u.usesuper AND u.usecreatedb THEN " +
                                "               CAST('superuser, create database' AS pg_catalog.text) " +
                        "       WHEN u.usesuper THEN " +
                                "               CAST('superuser' AS pg_catalog.text) " +
                                "       WHEN u.usecreatedb THEN " +
                                "               CAST('create database' AS pg_catalog.text) " +
                                "       ELSE " +
                                "               CAST('' AS pg_catalog.text) " +
                                "       END AS \"Attributes\" " +
                                "FROM pg_catalog.pg_user u " +
                                "ORDER BY 1";

                        // Use the connection to create a statement, execute it,
                        // analyze the results and close the result set [5]

                        Statement stat = conn.createStatement();
                        ResultSet rs = stat.executeQuery(qs);
                        System.out.println("User name;User ID;Attributes");
                        while (rs.next()) {
                                System.out.println(rs.getString("User name") + ";" +
                                                rs.getLong("User ID") + ";" +
                                                rs.getString("Attributes"));
                        }
                        rs.close();
                        stat.close();
               
                } catch (Exception e2) {
                        System.err.println("connecting failed");
                        System.err.println(e2);
                        System.exit(1);
                }
                System.out.println("connection closed");

                // If we get here all is OK

                System.out.println("done.");
        }
}

在上述代码中,一旦有了 Connection 实例,它就会定义一个查询字符串(上面的注释 [4]),创建一个 Statement 实例并用其来执行查询字符串,然后将其结果放入一个 ResultSet 实例。程序可以遍历该 ResultSet 实例来分析返回的结果,并以关闭 ResultSetStatement 实例结束(上面的注释 [5])。

编译和执行程序会产生以下输出:

me@mymachine:~/Test$ javac Test3.java
me@mymachine:~/Test$ java -cp ~/src/postgresql-42.2.5.jar:. Test3
driver loaded
connection created
User name;User ID;Attributes
fwa;16395;superuser
vax;197772;
mbe;290995;
aca;169248;
connection closed
done.
me@mymachine:~/Test$

这是在一个简单的 Java 应用程序中使用 PostgreSQL JDBC 库的(非常简单的)示例。要注意的是,由于 java.sql 库的设计方式,它不需要在代码中使用像 import org.postgresql.jdbc.*; 这样的 Java 导入语句,而是使用 Java 类加载器在运行时引入 PostgreSQL 代码的方式,也正因此无需在代码编译时指定类路径。


via: https://opensource.com/article/20/2/external-libraries-java

作者:Chris Hermansen 选题:lujun9972 译者:unigeorge 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

使用 ncurses 的灵活性和强大功能在 Linux 上创建一个猜数字游戏。

 title=

在我的 上一篇文章,我简要介绍了使用 ncurses 库通过 C 语言编写文本模式交互式应用程序。使用 ncurses,我们可以控制文本在终端上的显示位置和方式。如果你通过阅读手册页探索 ncurses 库函数,你会发现显示文本有很多不同的方式,包括粗体文本、颜色、闪烁文本、窗口、边框、图形字符和其它功能,这些都可以使你的应用脱颖而出。

如果你想探索一个更高级的程序来演示其中一些有趣的功能,有一个简单的“猜数字”游戏,我已更新为使用 ncurses 编写的了。该程序在一个范围内选择一个随机数,然后要求用户进行重复猜测,直到他们猜到这个秘密数字。当用户进行猜测时,程序会告知他们猜测的数字是太低还是太高。

请注意,程序限定可能的数字范围是 0 到 7。将值保持在有限的个位数数字范围内,可以更轻松的使用 getch() 函数从用户读取单个数字。我还使用了 getrandom 内核系统调用来生成随机数,设定数字最大值为 7,以从 0 (二进制 0000)到 7 (二进制 0111)中选择一个随机数。

#include <curses.h>;
#include <string.h>;          /* for strlen */
#include <sys/random.h>;      /* for getrandom */

int
random0_7()
{
   int num;
   getrandom(&num, sizeof(int), GRND_NONBLOCK);
   return (num & 7); /* from 0000 to 0111 */
}

int
read_guess()
{
  int ch;

  do {
    ch = getch();
  } while ((ch < '0') || (ch > '7'));

  return (ch - '0'); /* turn into a number */
}

通过使用 ncurses,我们可以增加一些有趣的视觉体验。通过添加函数,我们可以在屏幕顶部显示重要的文本信息,在屏幕底部显示状态消息行:

void
print_header(const char *text)
{
  move(0, 0);
  clrtoeol();

  attron(A_BOLD);
  mvaddstr(0, (COLS / 2) - (strlen(text) / 2), text);
  attroff(A_BOLD);
  refresh();
}

void
print_status(const char *text)
{
  move(LINES - 1, 0);
  clrtoeol();
 
  attron(A_REVERSE);
  mvaddstr(LINES - 1, 0, text);
  attroff(A_REVERSE);
  refresh();
}

通过这些函数,我们就可以构建猜数字游戏的主要部分。首先,程序为 ncurses 设置终端,然后从 0 到 7 中选择一个随机数。显示数字刻度后,程序启动一个循环,询问用户的猜测。

当用户进行猜测时,程序会在屏幕上提供反馈。如果猜测太低,程序会在屏幕上的数字下方打印一个左方括号。如果猜测太高,程序会在屏幕上的数字下方打印一个右方括号。这有助于用户缩小他们的选择范围,直到他们猜出正确的数字。

int
main()
{
  int number, guess;

  initscr();
  cbreak();
  noecho();

  number = random0_7();
  mvprintw(1, COLS - 1, "%d", number); /* debugging */

  print_header("Guess the number 0-7");

  mvaddstr(9, (COLS / 2) - 7, "0 1 2 3 4 5 6 7");

  print_status("Make a guess...");

  do {
    guess = read_guess();

    move(10, (COLS / 2) - 7 + (guess * 2));

    if (guess < number) {
      addch('[');
      print_status("Too low");
    }

    else if (guess > number) {
      addch(']');
      print_status("Too high");
    }

    else {
      addch('^');
    }
  } while (guess != number);

  print_header("That's right!");
  print_status("Press any key to quit");
  getch();

  endwin();

  return 0;
}

复制这个程序,自己尝试编译它。不要忘记你需要告诉 GCC 编译器链接到 ncurses 库:

$ gcc -o guess guess.c -lncurses

我留下了一个调试行,所以你可以看到屏幕右上角附近的秘密数字:

guess number game interface

图1:猜数字游戏。注意右上角的秘密数字。

开始使用 ncurses

该程序使用了 ncurses 的许多其它函数,你可以从这些函数开始。例如,print_header 函数在屏幕顶部居中以粗体文本打印消息,print_status 函数在屏幕左下角以反向文本打印消息。使用它来帮助你开始使用 ncurses 编程。


via: https://opensource.com/article/21/8/guess-number-game-ncurses-linux

作者:Jim Hall 选题:lujun9972 译者:perfiffer 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

使用 ncurses 在 Linux 屏幕上的特定位置放置文本,可以带来更友好的用户界面体验。

 title=

大多数的 Linux 实用程序仅仅只在屏幕的底部滚动文本。如果你想在屏幕中放置你的文本,例如一个游戏或者一个数据展示,你可以试试 ncurses。

curses 是一个旧的 Unix 库,它可以在文本终端界面控制光标。curses 的名称就来自于术语 “ 光标控制 cursor control ”。多年以后,其他人编写了新的 curses 版本用来添加新的功能,新版本被叫做 “new curses” 或者 “ncurses”。你可以在每个流行的 Linux 发行版中找到 ncurses。尽管默认情况下可能未安装开发库、头文件和文档。例如,在 Fedora 上,你需要使用以下命令安装 ncurses-devel 包:

$ sudo dnf install ncurses-devel

在程序中使用 ncurses

要在屏幕上直接寻址,你首先需要初始化 ncurses 库。大部分程序会通过以下三行来做到这一点:

  • initscr():初始化窗口对象和 ncurses 代码,返回代表整个屏幕的窗口对象
  • cbreak():禁用缓冲并使键入的输入立即可用
  • noecho():关闭回显,因此用户输入不会显示在屏幕上

这些函数定义在 curses.h 头文件中,你需要在你的程序中通过以下方式将其包含进来:

#include <curses.h>

初始化终端后,你可以自由使用任何 ncurses 函数,我们将在示例程序中探讨其中的一些函数。

当你使用完 ncurses 并想返回到常规终端模式下时,使用 endwin() 重置一切。此命令可以重置任何屏幕颜色,将光标移动到屏幕的左下角,并使光标可见。通常在退出程序之前执行此操作。

在屏幕上寻址

关于 ncurses 首先需要知道的是屏幕的坐标分为行和列,左上角的是 0,0 点。ncurses 定义了两个全局变量来帮助你识别屏幕:LINES 是屏幕的行数,COLS 是屏幕的列数。屏幕右下角的位置是 LINES-1,COLS-1

例如,如果你想要移动光标到第 10 行和第 30 列,你可以使用 move() 函数,移动到此坐标:

move(10, 30);

之后显示的任何文本都将从屏幕的该位置开始。要显示单个字符,请对单个字符使用 addch(c) 函数。要显示字符串,将对字符串使用 addstr(s) 函数。对于类似于 printf 的格式化输出,请使用带有常用选项的 printw(fmt, ...)

移动到屏幕指定位置和显示文本是一件很常见的事情,ncurses 提供了同时执行这两项操作的快捷方式。mvaddch(row, col, c) 函数将在屏幕第 row 行,第 col 列的位置显示一个字符。而 mvaddstr(row, col, s) 函数将在屏幕第 row 行,第 col 列的位置显示一个字符串。举个更直接的例子,在程序中使用 mvaddstr(10, 30, "Welcome to ncurses"); 函数将从屏幕的第 10 行和第 30 列开始显示文本 Welcome to ncurses。使用 mvaddch(0, 0, '+') 函数将在屏幕的左上角第 0 行和第 0 列处显示一个加号(+)。

在终端屏幕上绘制文本会对某些系统产生性能影响,尤其是在较旧的硬件终端上。因此 ncurses 允许你“堆叠”一堆文本以显示在屏幕上,然后使用 refresh() 函数使所有这些更改对用户可见。

让我们来看一个将以上所有内容整合在一起的简单示例:

#include <curses.h>

int
main()
{
  initscr();
  cbreak();
  noecho();

  mvaddch(0, 0, '+');
  mvaddch(LINES - 1, 0, '-');
  mvaddstr(10, 30, "press any key to quit");
  refresh();

  getch();

  endwin();
}

程序的开始初始化了一个终端窗口,然后在屏幕的左上角打印了一个加号,在左下角打印了一个减号,在第 10 行和第 30 列打印了 press any key to quit 文本。程序通过使用 getch() 函数接收了键盘输入的单个字符,接着,使用 endwin() 函数在程序完全退出前重置了终端。

getch() 是一个很有用的函数,你可以使用它来做很多事情。我经常使用它在我退出程序前用来暂停。与大多数 ncurses 函数一样,还有一个名为 mvgetch(row, col)getch() 版本,用于在等待字符输入之前移动到屏幕位置的第 row 行,第 col 列。

使用 ncurses 编译

如果你尝试以通常的方式编译该示例程序,例如 gcc pause.c,你可能会从链接器中获得大量错误列表。那是因为 GNU C 编译器不会自动链接 ncurses 库。相反,你需要使用 -l ncurses 命令行选项加载它以进行链接。

$ gcc -o pause pause.c -lncurses

运行新程序将打印一条简单的 press any key to quit消息,该消息差不多位于屏幕中央:

centered message in a program window

图 1:程序中居中的 “press any key to quit” 消息。

使用 ncurses 构建更好的程序

探索 ncurses 库函数以了解在屏幕上显示文本的其它方法。你可以在 ncurses 的手册页中找到所有 ncurses 函数的列表。这给出了 ncurses 的一般概述,并提供了不同 ncurses 函数的类似表格的列表,并参考了包含完整详细信息的手册页。例如,在 curs_printw(3X) 手册页中描述了 printw,可以通过以下方式查看:

$ man 3x curs_printw

更简单点:

$ man curs_printw

使用 ncurses,你可以创建更多有趣的程序。通过在屏幕上的特定位置打印文本,你可以创建在终端中运行的游戏和高级实用程序。


via: https://opensource.com/article/21/8/ncurses-linux

作者:Jim Hall 选题:lujun9972 译者:perfiffer 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

fastjar、gjar 和 jar 等工具可以帮助你手动或以编程方式构建 JAR 文件,而其他工具链,如 Maven 和 Gradle 提供了依赖性管理的功能。

 title=

根据我的经验,Java 的许多优点之一是它能够以整齐方便的包(称为 JAR,或 Java 归档)来提供应用程序。JAR 文件使用户很容易下载并启动他们想尝试的应用,很容易将该应用从一台计算机转移到另一台计算机(而且 Java 是跨平台的,所以可以鼓励自由分享),而且对于新的程序员来说,查看 JAR 文件的内容,以找出使 Java 应用运行的原因是很容易理解的。

创建 JAR 文件的方法有很多,包括 Maven 和 Gradle 等工具链解决方案,以及 IDE 中的一键构建功能。然而,也有一些独立的命令,如 jarfastgjar 和普通的 jar,它们对于快速和简单的构建是很有用的,并且可以演示 JAR 文件运行所需要的东西。

安装

在 Linux 上,你可能已经有了 fastjargjar 或作为 OpenJDK 包或 GCJ(GCC-Java)的一部分的 jar 命令。你可以通过输入不带参数的命令来测试这些命令是否已经安装:

$ fastjar
Try 'fastjar --help' for more information.
$ gjar
jar: must specify one of -t, -c, -u, -x, or -i
jar: Try 'jar --help' for more information
$ jar
Usage: jar [OPTION...] [ [--release VERSION] [-C dir] files] ...
Try `jar --help' for more information.

我安装了所有这些命令,但你只需要一个。所有这些命令都能够构建一个 JAR。

在 Fedora 等现代 Linux 系统上,输入一个缺失的命令你的操作系统提示安装它。

另外,你可以直接从 AdoptOpenJDK.net 为 Linux、MacOS 和 Windows 安装 Java

构建 JAR

首先,你需要构建一个 Java 应用。

为了简单起见,在一个名为 hello.java 的文件中创建一个基本的 “hello world” 应用:

class Main {
public static void main(String[] args) {
    System.out.println("Hello Java World");
}}

这是一个简单的应用,在某种程度上淡化了管理外部依赖关系在现实世界中的重要性。不过,这也足以让你开始了解创建 JAR 所需的基本概念了。

接下来,创建一个清单文件。清单文件描述了 JAR 的 Java 环境。在这个例子里,最重要的信息是识别主类,这样执行 JAR 的 Java 运行时就知道在哪里可以找到应用的入口点。

$ mdir META-INF
$ echo "Main-Class: Main" > META-INF/MANIFEST.MF

编译 Java 字节码

接下来,把你的 Java 文件编译成 Java 字节码。

$ javac hello.java

另外,你也可以使用 GCC 的 Java 组件来编译:

$ gcj -C hello.java

无论哪种方式,都会产生文件 Main.class

$ file Main.class
Main.class: compiled Java class data, version XX.Y

创建 JAR

你有了所有需要的组件,这样你就可以创建 JAR 文件了。

我经常包含 Java 源码给好奇的用户参考,这只需 META-INF 目录和类文件即可。

fastjar 命令使用类似于 tar 命令的语法。

$ fastjar cvf hello.jar META-INF Main.class

另外,你也可以用 gjar,方法大致相同,只是 gjar 需要你明确指定清单文件:

$ gjar cvf world.jar Main.class -m META-INF/MANIFEST.MF

或者你可以使用 jar 命令。注意这个命令不需要清单文件,因为它会自动为你生成一个,但为了安全起见,我明确定义了主类:

$ jar --create --file hello.jar --main-class=Main Main.class

测试你的应用:

$ java -jar hello.jar
Hello Java World

轻松打包

fastjargjarjar 这样的工具可以帮助你手动或以编程方式构建 JAR 文件,而其他工具链如 Maven 和 Gradle 则提供了依赖性管理的功能。一个好的 IDE 可能会集成这些功能中的一个或多个。

无论你使用什么解决方案,Java 都为分发你的应用代码提供了一个简单而统一的目标。


via: https://opensource.com/article/21/8/fastjar

作者:Seth Kenlon 选题:lujun9972 译者:geekpi 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出