Java语言入门:从基础语法到第一个可运行的程序

Java语言入门:从基础语法到第一个可运行的程序

Java语言入门:从基础语法到第一个可运行的程序

Java 是一种静态类型、面向对象的通用编程语言。它常用于企业后端服务、金融与业务系统、Android 应用、桌面工具和大数据生态,也可以编写命令行程序。Java 程序通常先编译为字节码,再由 Java 虚拟机(JVM)执行;这让同一份字节码有机会运行在不同操作系统上,但前提是目标环境安装了兼容的 Java 运行时,且程序没有依赖特定平台的功能。

本文从安装开发工具开始,依次介绍一个可运行的程序、基础类型、条件与循环、方法、类、集合、异常和输入校验。示例面向初学者,使用命令行编译运行,不依赖第三方库。代码中的尖括号等字符已按 HTML 规则转义;复制代码时应只复制代码内容,不要复制页面标签。

一、准备 JDK

编写和编译 Java 程序需要 JDK(Java Development Kit),其中包括编译器 javac 和 Java 运行命令 java。只安装运行环境不一定足以编译程序。可以从 OpenJDK 发行版或其他可信供应商获取 JDK。初学者可选用仍在维护的 LTS 版本,例如 JDK 17 或 JDK 21;具体版本应以项目要求为准。

安装后打开终端或命令提示符,检查命令是否可用:

java -version
javac -version

如果系统提示找不到命令,需要检查 JDK 是否安装成功,以及 PATH 环境变量是否包含 JDK 的 bin 目录。不同操作系统的安装和环境变量设置方式不同,不宜直接照搬其他系统的路径。

初学阶段,文本编辑器加命令行就够用;也可以使用 IntelliJ IDEA、Eclipse 或 VS Code 等编辑器。IDE 能提供补全、错误提示和调试器,但了解编译与运行过程仍然有帮助。

二、第一个可运行的 Java 程序

新建文件 HelloWorld.java,写入以下内容:

public class HelloWorld {
    public static void main(String[] args) {
        System.out.println("你好,Java!");
    }
}

在文件所在目录执行:

javac HelloWorld.java
java HelloWorld

第一条命令把源文件编译为字节码,通常生成 HelloWorld.class;第二条命令启动 JVM 并运行名为 HelloWorld 的类。运行时写的是类名,不带 .class 后缀。成功时会看到一行“你好,Java!”。

逐行理解程序结构

  • public class HelloWorld 声明一个公开类。使用 public 修饰的顶层类,源文件名必须与类名一致,并区分大小写,因此这个文件应命名为 HelloWorld.java。
  • 大括号 {} 标记代码块的范围。
  • main 是 JVM 启动该程序时调用的入口方法。常见入口签名是 public static void main(String[] args)。
  • System.out.println 向标准输出打印一行文本;println 会在内容后换行。
  • 分号 ; 用于结束一条语句。漏写分号是常见编译错误。

Java 对大小写敏感:HelloWorld、helloworld 是不同标识符。类名常采用大驼峰命名(如 AccountService),变量和方法名常采用小驼峰命名(如 accountBalance)。这属于普遍约定,不是语法强制。

三、变量、类型与运算

Java 是静态类型语言,变量声明时需要指定类型。基本类型包括整数类型 byte、short、int、long,浮点类型 float、double,字符类型 char 和布尔类型 boolean。此外,字符串和数组等属于引用类型。

public class Variables {
    public static void main(String[] args) {
        int count = 3;
        long population = 8_000_000_000L;
        double price = 19.95;
        boolean available = true;
        char grade = 'A';
        String name = "Lin";

        System.out.println(name + " 有 " + count + " 个项目");
        System.out.println("价格:" + price);
        System.out.println("是否可用:" + available);
    }
}

整数常量默认按 int 处理;超出 int 范围时,通常要使用 long,并在字面量末尾加 L。浮点字面量默认是 double;float 字面量需要加 F。下划线可用于提高数字可读性,例如 8_000_000,但不能随意放在数字的开头、结尾或小数点旁边。

char 用单引号表示单个 UTF-16 代码单元,例如 'A';字符串 String 用双引号表示,例如 "Java"。一个可见字符不一定只占一个 char,因为某些 Unicode 字符由多个 UTF-16 代码单元组成。因此,遍历字符串时若需要按 Unicode 码点处理,不能简单地假设一个 char 就是一个完整的“字符”。

整型边界与算术注意事项

int 的取值范围是 -231 到 231-1。整数运算溢出时,Java 不会自动报错,结果会按该类型的位宽回绕。例如,最大 int 加一会变成最小 int。处理计数、金额或外部输入时,要根据业务范围选择类型,并在可能溢出的运算前做校验;需要精确金额计算时,不应直接用二进制浮点类型表示货币,常见做法是使用以最小货币单位存储的整数,或使用 BigDecimal 并明确舍入规则。

public class Arithmetic {
    public static void main(String[] args) {
        int a = 7;
        int b = 2;

        System.out.println(a + b);       // 9
        System.out.println(a / b);       // 3,整数除法会丢弃小数部分
        System.out.println((double) a / b); // 3.5
        System.out.println(a % b);       // 1,余数
    }
}

两个整数相除得到整数。若想得到小数,需要在运算前将至少一个操作数转换为浮点类型。浮点数存在精度限制,像 0.1 + 0.2 这样的计算结果可能不能精确等于十进制的 0.3;不宜用浮点数的直接相等比较来判断需要精确的小数结果。

引用类型变量可以指向对象,也可以为 null,表示当前没有对象引用。对 null 调用方法或访问成员会导致 NullPointerException。使用对象前应根据程序设计明确是否允许为空,并在适当位置检查。

四、条件判断与循环

条件语句让程序根据布尔表达式选择执行路径。Java 的条件表达式必须是 boolean,不会把数字零或非零自动当作真假。

public class Branching {
    public static void main(String[] args) {
        int score = 82;

        if (score >= 90) {
            System.out.println("优秀");
        } else if (score >= 60) {
            System.out.println("及格");
        } else {
            System.out.println("需要继续练习");
        }

        String day = "周一";
        switch (day) {
            case "周一":
                System.out.println("新的一周");
                break;
            case "周末":
                System.out.println("休息时间");
                break;
            default:
                System.out.println("普通的一天");
        }
    }
}

switch 的具体可用类型和写法与 Java 版本有关。较旧的写法中,break 用于避免执行完一个分支后继续进入后续分支。不要只凭语法记忆判断某种新写法能否在目标 JDK 编译,必要时查看对应版本的语言文档。

for 适合循环次数或迭代范围清晰的场景,while 适合重复执行直到条件不成立的场景:

public class Loops {
    public static void main(String[] args) {
        for (int i = 1; i <= 3; i++) {
            System.out.println("第 " + i + " 次");
        }

        int remaining = 3;
        while (remaining > 0) {
            System.out.println("剩余:" + remaining);
            remaining--;
        }
    }
}

循环条件、更新语句和退出条件应一起检查。遗漏计数器更新,或条件始终成立,可能导致无限循环。数组和集合的下标从 0 开始;访问超出范围的下标会在运行时抛出异常,而不是在编译时总能被发现。

五、数组与字符串

数组用于存放固定长度、同一类型的一组元素。创建后长度不能改变;若需要按运行情况增减元素,通常使用集合。

public class ArraysExample {
    public static void main(String[] args) {
        int[] numbers = {10, 20, 30};
        int sum = 0;

        for (int number : numbers) {
            sum += number;
        }

        System.out.println("元素个数:" + numbers.length);
        System.out.println("总和:" + sum);
    }
}

增强 for 循环适合依次读取元素;如果需要使用元素下标,或需要在遍历过程中修改特定位置,可使用普通 for 循环。对长度为 n 的数组,有效下标是 0 到 n - 1。

String 是不可变对象,拼接会产生新的字符串。少量拼接直接使用 + 容易阅读;在循环中反复拼接大量文本时,可以考虑 StringBuilder。比较字符串内容应使用 equals,而不是 ==:后者比较的是引用是否相同,不是文本内容是否相同。

public class StringExample {
    public static void main(String[] args) {
        String first = new String("Java");
        String second = new String("Java");

        System.out.println(first.equals(second)); // true:内容相同
        System.out.println(first == second);      // false:通常是不同对象
    }
}

六、方法与参数

方法把一段操作组织成可复用的单元。声明中要写明返回类型、方法名和参数类型;没有返回值时使用 void。

public class Methods {
    public static int add(int left, int right) {
        return left + right;
    }

    public static void main(String[] args) {
        int result = add(4, 5);
        System.out.println(result);
    }
}

调用方法时,实参必须与形参类型兼容、数量匹配,并遵守 Java 的转换规则。方法参数的数量没有适合初学者套用的一个通用“业务上限”;实际编译和运行会受 JVM、类文件格式及实现约束,参数过多通常也会让接口难以理解,宜拆分为有意义的数据类型。命令行参数的长度限制则不是 Java 语言统一规定的固定数值:操作系统、启动方式和环境可能带来不同限制。

Java 采用值传递。传入基本类型时,方法得到该值的副本;传入对象时,传递的也是引用值的副本。方法可以通过这个引用访问并修改对象内容,但把方法参数重新指向另一个对象,不会让调用方的变量随之改指向新对象。

public class ParameterExample {
    static void changeNumber(int value) {
        value = 100;
    }

    static void append(StringBuilder text) {
        text.append("!");
    }

    public static void main(String[] args) {
        int number = 1;
        changeNumber(number);
        System.out.println(number); // 1

        StringBuilder message = new StringBuilder("Hello");
        append(message);
        System.out.println(message); // Hello!
    }
}

方法可以重载,即在同一个类中定义多个同名但参数列表不同的方法。仅改变返回类型不能构成合法重载。初学时应让方法职责清晰,避免参数过多、隐式依赖过多,或让调用者无法判断方法是否会修改传入对象。

七、类、对象与封装

类是对象的定义,描述对象拥有的数据和行为。对象是根据类创建的实例。下面的示例演示字段、构造方法和实例方法:

public class Counter {
    private int value;

    public Counter(int initialValue) {
        if (initialValue < 0) {
            throw new IllegalArgumentException("初始值不能为负数");
        }
        this.value = initialValue;
    }

    public void increment() {
        value++;
    }

    public int getValue() {
        return value;
    }

    public static void main(String[] args) {
        Counter counter = new Counter(0);
        counter.increment();
        System.out.println(counter.getValue());
    }
}

把代码保存为 Counter.java,执行 javac Counter.java,再执行 java Counter。这个类既定义了可复用的计数器,也包含入口方法,因此可以直接运行。

  • private 限制字段只能由类内部访问,是封装的一种常见方式。
  • this.value 表示当前对象的字段;当参数或局部变量与字段同名时,this 有助于区分。
  • 构造方法用于初始化对象,没有返回类型,名称与类名一致。
  • static 成员属于类,不依赖某个实例;非静态成员通常通过对象访问。

封装不是把字段全部设为 private 后机械地生成访问器。更重要的是让对象维护有效状态,并通过清晰的公开方法表达允许的操作。构造方法中的检查可以阻止不合理状态进入对象,但所有业务规则仍应结合实际需求设计。

八、集合:让长度可变化

数组长度固定。需要动态增删元素时,可以使用 Java 集合框架中的 ArrayList。使用集合时常通过泛型指定元素类型,例如 ArrayList<String>,使编译器能帮助检查类型。

import java.util.ArrayList;
import java.util.List;

public class ListExample {
    public static void main(String[] args) {
        List<String> names = new ArrayList<>();
        names.add("Aki");
        names.add("Bo");

        for (String name : names) {
            System.out.println(name);
        }

        System.out.println("数量:" + names.size());
    }
}

集合的元素数量使用 size() 获取,数组则使用 length。泛型集合不能直接写基本类型,例如 List<int> 不合法,应使用包装类型 List<Integer>。自动装箱和拆箱能在许多场景下转换基本类型与包装类型,但包装对象可能为 null;对空值拆箱会导致 NullPointerException。

九、接收命令行参数

入口方法中的 args 是字符串数组,保存启动程序时传入的命令行参数。空格通常会分隔参数;想让包含空格的内容作为一个参数,需按当前命令行环境使用引号。参数数量和内容都应由程序检查,不能假设用户一定提供了预期值。

public class Greet {
    public static void main(String[] args) {
        if (args.length == 0) {
            System.out.println("用法:java Greet <姓名>");
            return;
        }

        String name = args[0].trim();
        if (name.isEmpty()) {
            System.out.println("姓名不能为空");
            return;
        }

        System.out.println("你好," + name + "!");
    }
}

保存为 Greet.java,编译后运行:

javac Greet.java
java Greet 林

命令行参数来自启动程序的环境,不会自动变成安全或可信的数据。应检查参数数量、格式、长度和业务范围,再用于文件路径、查询条件或其他操作。不要把密码、令牌等秘密放在命令行参数里:它们可能出现在进程信息、脚本、日志或历史记录中,具体暴露方式取决于操作系统和运行环境。需要处理秘密时,应使用适合部署环境的秘密管理机制,并限制访问和日志输出。

十、异常与错误处理

编译器会发现一部分错误,例如缺少分号、类型不匹配或找不到类。运行时还可能发生异常,例如数组越界、除数为零、空引用或解析失败。异常用于表示运行期间发生的问题,不能用一个宽泛的捕获语句假装所有情况都已正确处理。

public class ParseNumber {
    public static void main(String[] args) {
        String input = "123";

        try {
            int value = Integer.parseInt(input);
            System.out.println("解析结果:" + value);
        } catch (NumberFormatException exception) {
            System.out.println("输入不是有效整数:" + input);
        }
    }
}

Integer.parseInt 在文本不符合整数格式或数值超出 int 范围时,会抛出 NumberFormatException。捕获具体异常,才能采取针对性的恢复或提示。对无法合理恢复的问题,可以让异常继续向上交给调用方处理,而不是返回一个容易被误认为正常的默认值。

十一、常见错误排查

  • 文件名与公开类名不一致:检查 public class 的名称、文件名和大小写是否一致。
  • 把扩展名带进运行命令:运行时通常用 java HelloWorld,而不是 java HelloWorld.class。
  • 当前目录不对:编译命令需要能找到源文件;运行命令也需要能找到类文件。确认终端所在目录,或按项目的类路径配置运行。
  • 漏掉分号或括号:阅读编译器报错所在行附近的代码。真正原因也可能在报错行之前,例如少了一个右括号。
  • 混淆 = 与 ==:= 用于赋值,== 用于比较基本值或引用身份;字符串内容比较使用 equals。
  • 整数除法结果意外:确认参与运算的操作数类型,并在运算前进行所需的类型转换。
  • 数组下标越界:检查循环终止条件。长度为 n 的数组最后一个有效下标是 n - 1。
  • 出现空指针异常:根据异常堆栈定位具体语句,检查该对象是否可能为 null;不要只在最外层捕获后忽略。

编译器输出的诊断信息往往包含文件、行号和错误类型。先处理最早出现的错误,再重新编译:一个语法错误可能引发多条后续报错。运行时异常则查看堆栈中属于自己代码的行号,并结合输入数据和对象状态定位原因。

十二、Java 的适用场景与边界

Java 的静态类型、成熟的工具链和丰富的标准库,适合团队协作、长期维护的业务服务和跨平台应用。JVM 生态中也有许多成熟框架和监控、构建工具。对于需要快速尝试小脚本的任务,其他脚本语言可能更省步骤;对严格依赖极小体积、快速启动或直接控制硬件的场景,也应评估 Java 的运行时、打包方式和部署成本,而不是只依据“跨平台”三个字做决定。

跨平台并不意味着所有程序无需调整就能在任何环境运行。文件路径分隔符、字符编码、时区、区域设置、文件权限、系统命令和本地库都可能带来平台差异。使用标准库中的跨平台接口,并在目标操作系统上测试,才能降低这类问题。

Java 的类型检查和内存管理可以减少一部分常见错误,但不等于程序天然安全。输入仍需验证,访问控制仍需正确配置,依赖库仍需维护,敏感数据也需要妥善处理。不要把输入验证、授权检查或加密需求交给语言本身“自动保证”。对于 Web 应用,输出到 HTML、SQL 或 shell 命令时还需要针对目标上下文采用安全的编码或参数化接口;单纯检查字符串非空,并不能防止注入。

十三、建议的练习顺序

  1. 修改 HelloWorld,分别打印多行文本,体会 print 和 println 的差别。
  2. 编写一个方法,接收两个整数并返回较大的值;测试相等、正数和负数。
  3. 创建数组并计算平均值,检查空数组如何处理,以及整数除法是否符合预期。
  4. 将命令行参数转换为整数,处理参数缺失、格式错误和超出范围的情况。
  5. 定义一个简单的类,用构造方法保证对象初始状态合理,再用集合保存多个对象。

完成这些练习后,可以继续学习包与访问控制、继承与接口、异常处理、泛型、文件读写、单元测试和构建工具。初学时不必一次记住全部语法:先能编译运行,再逐步学会检查输入、理解错误信息,并在每个小程序中明确数据的类型、范围和有效状态。

© 版权声明

相关文章