Java学习笔记(一):Java概述及程序设计起步

声明:本篇笔记部分摘自《Java核心技术(卷Ⅰ) - 机械工业出版社》Java教程-廖雪峰-2025-06-16,遵循CC BY 4.0协议。这本书以及这个系列的笔记会将Java与一些编程语言的特性作比较;建议拥有一定的C/C++或其他编程语言的学习基础后,再阅读这本书或这个系列的笔记来学习Java。如果是第一次接触编程语言,建议从相对简单的C语言或Python开始;或者参考一些体系化的视频课程来进行学习,否则会感到有一定的压力。
存在由AI生成的小部分内容,仅供参考,请仔细甄别可能存在的错误。

一、Java概述

1.Java发展史

Java最早是由SUN公司(已被Oracle收购)的詹姆斯·高斯林(高司令,人称Java之父)在上个世纪 90年代初开发的一种编程语言,最初被命名为Oak,目标是针对小型家电设备的嵌入式应用(结果市场没啥反响)。互联网的崛起让Oak重新焕发了生机,于是SUN公司改造了Oak,由于Oak已经被注册,在1995年以Java的名称正式发布。随着互联网的高速发展,Java逐渐成为最重要的网络编程语言。
Java介于编译型语言和解释型语言之间。编译型语言如C、C++,代码是直接编译成机器码执行,但是不同的平台(x86、ARM等)CPU的指令集不同,因此,需要编译出每一种平台的对应机器码。解释型语言如Python、Ruby没有这个问题,可以由解释器直接加载源码然后运行,代价是运行效率太低。而Java是将代码编译成一种“字节码”,它类似于抽象的CPU指令,然后,针对不同平台编写虚拟机,不同平台的虚拟机负责加载字节码并执行,这样就实现了“一次编写,到处运行”的效果。当然,这是针对Java开发者而言。对于虚拟机,需要为每个平台分别开发。为了保证不同平台、不同公司开发的虚拟机都能正确执行Java字节码,SUN公司制定了一系列的Java虚拟机规范。从实践的角度看,JVM的兼容性做得非常好,低版本的Java字节码完全可以正常运行在高版本的JVM上。

随着Java的不断发展,出现了三个版本:

  • Java SE: standard edition, 标准版本
  • Java EE: enterprise edition, 企业版本
  • Java ME: mico edition, 微服务版本

这三者是 Java EE > Java SE > Java ME 的关系,,Java SE就是标准版,包含标准的JVM和标准库,而Java EE是企业版,它在Java SE的基础上添加了大量的API和库,以便方便开发Web应用、数据库、消息服务等,Java EE的应用使用的虚拟机和Java SE完全相同。Java ME就和Java SE不同,它是一个针对嵌入式设备的“瘦身版”,Java SE的标准库无法在Java ME上使用,Java ME的虚拟机也是“瘦身版”。

2.Java学习路线

对于常规的Java软件应用开发者来说,学习Java SE、Java EE就足够了。Java ME的流行程度不高,如果没有特殊需求无需学习。下面是推荐的学习路线:

  1. 首先学习Java SE,掌握Java语言本身、Java核心开发技术以及Java标准库的使用;
  2. 继续学习Java EE,重点学习Spring框架、数据库开发、分布式架构;
  3. 如果打算学习大数据开发,那么Hadoop、Spark、Flink这些大数据平台就是需要学习的,他们都基于Java或Scala开发;
  4. 如果想要学习移动开发,那么就深入Android平台,掌握Android App开发。

3.JDK与JRE、JVM

  • JDK:Java Development Kit,Java程序开发工具包
  • JRE:Java Runtime Environment,Java运行时环境
  • JVM:Java Virtual Machine,Java虚拟机

打个简单的比方,JDK类似于厨房里的食材和食谱(标准类库和方法),我们可以用来设计各种菜品(源代码);JRE是餐厅,让厨师有地方做菜,并且能够让顾客有地方享受美食(运行环境);JVM就是制作食物的厨师,负责将我们设计的菜品做成食物(编译执行)。

与上面例子稍有不同的是,JDK中也包含了JRE与调试器、编译器等开发工具,而JRE当然是包含JVM的了。即:我们通过JDK编写代码,在JRE中经过JVM的编译之后再运行。它们之间的关系如下图:

玩过Java版MC的同学应该都知道,在启动游戏之前会检查Java版本,这里的“Java版本”指的是JRE,即我们需要准备好在自己的电脑上运行Java的环境。

4.JSR与JCP

  • JSR:Java Specification Request,Java规范要求
  • JCP:Java Community Process,Java社区组织

为了保证Java语言有良好的可拓展性和可移植性,需要很高的代码规范性。JSR这一规范就规定了开发者在为Java添砖加瓦时的代码功能。JCP这一组织的任务之一就是审核和修订JSR。

5.Java的特点

下面是Java白皮书上提到的11个关键术语,描述了Java这门编程语言的一些特点:

  • 简单性
  • 面向对象
  • 分布式
  • 健壮性
  • 安全性
  • 体系结构和中立
  • 可移植性
  • 解释性
  • 高性能
  • 多线程
  • 动态性

二、Hello World!

像学习其他的编程语言一样,从最简单的输出"Hello World!"开始:

1
2
3
4
5
6
7
// HelloWorld.java

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

仔细分析一下这段程序:通过访问修饰符publlic定义了一个公有的类HelloWorld,其中有一个main方法,通过system.out.println()打印了"Hello World!"这么一个字符串。需要注意以下几点:

  • 必须存在一个公共类与源代码的文件名相同,作为程序的入口;类似于C/C++中的main函数;并且该类中必须包含一个main方法,且该方法也必须声明为public。(在VS Code中修改主类或文件名的名称,文件名或主类的名称也会自动跟随修改)
  • 类的标准命名方式为驼峰命名法,即所有单词的首字母均大写;Java区分大小写,mainMain不同。
  • 是Java应用的构建模块,所有的Java程序都必须放在类中。
  • 语句结束的标志不是回车而是;,有必要的情况下可以使用回车编写一个多行的语句。
  • system.out.println()方法会在打印之后自动换行,相比之下system.out.print()方法会把新的内容打印在同一行中。

作为初学者,或许会对main方法的参数String[] args感到疑惑,这里做出解释:

当我们运行Java程序时,可以在命令行(或终端)后面附加参数,这些参数会被传递给main方法,如java HelloWorld arg1 arg2 arg3,这里的agr1 arg2 arg3会作为字符串数组String[]传进main方法。Java规范要求main方法的签名必须如此,作为程序的统一入口点。即使我们不使用它,也需要声明,否则JVM无法识别main方法。

如何运行这个程序?

或许有同学会觉得“这不是明摆着吗,点一下VS Code或者IDEA右上角的按钮就可以了啊,有啥好说的”,但实际上"点一下按钮"的背后,是IDE自动为我们执行了一些编译和启动的命令。为了让我们更好地理解Java程序编译运行的过程,深化对上面JDK与JVM的理解,探讨一下如何通过这些命令的方式来运行是很有必要的。

Java源码本质上是一个文本文件,我们需要先用 javacHelloWorld.java 编译成字节码文件 HelloWorld.class ,然后,用 java 命令执行这个字节码文件:

首先进入源代码所在的目录,执行这一个命令将Java程序编译成字节码:

1
javac HelloWorld.java

可以看到源代码所在的目录下出现了一个同名文件HelloWorld.class,这就是编译之后的字节码文件。执行这一句命令来通过JVM运行字节码程序:

1
java HelloWorld

注意:给虚拟机传递的参数 HelloWorld 是我们定义的类名而不是文件名(不需要.class的后缀),虚拟机自动查找对应的class文件并执行。

三、注释

与C/C++类似,Java中的注释也有这两种方式:

1
2
3
4
5
6
7
// 单行注释

/*
多行注释
多行注释
多行注释
*/

四、基本数据类型

Java是一种强类型语言,即必须为每一个变量声明一个类型。在Java中,一共有8种数据类型(4整型+2浮点型+1字符型+1布尔型)。

1.整型

表示没有小数的数字,可以为负。

类型 大小 取值范围
int 4 bit -2 147 483 648 ~ 2 147 483 647
short 2 bit -32 768 ~ 32 767
long 8 bit -9 223 372 036 854 775 808 ~ 9 223 372 036 854 775 807
byte 1 bit -128 ~ 127

在Java中,整型的取值范围与运行平台无关,这使得Java有较好的可移植性;一定程度上避免了C/C++在不同的机器上可能存在的溢出问题。

长整型的数据末尾有一个L或者l,十六进制前面有0x或0x,八进制的前缀是0(如8 -> 010),这种写法容易混淆所以使用得比较少。二进制数的前缀是0b或0B。数字很大时,可以加上下划线使其更易读,如2_147_483_647。

2.浮点型

表示有小数部分的数值,与C/C++类似有两种类型:

类型 大小 取值范围
long 4 bit ±3.40282347×1038\pm3.402 823 47\times10^{38}(6~7位有效数字)
double 8 bit ±1.79769313486231570×10308\pm1.79769313486231570\times10^{308}(15位有效数字)

double表示的数值精度是float的两倍,因此也有“双精度浮点数”的说法。大多数情况下,都会使用double类型存储浮点数。在一个数后面加上d、D或者f、F,可以分别标识为单精度或双精度。

在浮点数中,有三个特殊数值表示溢出或错误:

  • POSITIVE_INFINITY:正无穷大
  • NEGATIVE_INFINITY:负无穷大
  • NaN:非数字

例如,一个非零数除以零会得到无穷大的结果,而0除以0或者负数开偶次方根会得到NaN。需要注意的是,NaN不等于任何值,包括它自己,因此无法通过与NaN比较来判断结果不为NaN,而是应该使用Double.isNaN(x)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class Demo {
public static void main(String[] args) {
// 打印3 / 0的结果
System.out.println(3 / 0);
// 输出Exception in thread "main" java.lang.ArithmeticException: / by zero

// 打印3.5 / 0的结果
System.out.println(3.5 / 0);
// 输出Infinity

// 打印-3.5 / 0的结果
System.out.println(-3.5 / 0);
// 输出-Infinity

// 运算 -2 的平方根
System.out.println(Math.sqrt(-2));
// 输出NaN
}
}

3.字符型

字符型(char类型)原本用于表示单个字符,但现在有些Unicode字符需要两个char值表示。char类型使用单引号'表示,如char c = 'A';。char类型的值可以表示为十六进制值,取值范围是\u0000 ~ \uFFFF

除了\u这样的转义,还有一些常用的转义符号:

转义符号 名称 转义符号 名称
\b 退格 \f 换页
\t 制表符 \" 双引号
\n 换行 \\ 反斜线
\r 回车 \s 空格

注意这里的\u可能会与注释产生冲突,如:

1
2
// C:\users
// \u00A 是换行的转义表示

上面两个注释在Java中都会报错,分别是因为\u后面没有接十六进制,不是可识别的转义表示;\u00A被直接解析成换行,导致后续内容识别成位定义的代码语句。想要解决这个问题其实也很简单,使用/* */这样的注释就可以了。

UTF-16编码采用不同长度的代码表示所有Unicode码点(code point,指某个字符对应的代码值)。在基本多语言平面中,每个字符用16位表示,通常称为代码单元(codeunit);而辅助字符编码为一对连续的代码单元。采用这种编码对表示的每个值都属于基本多语言平面中未用的2048个值范围,通常称为替代区域(surrogatearea)(U+D800~U+DBFF用于第一个代码单元,U+DC00~U+DFFF用于第二个代码单元)。这样设计十分巧妙,因为我们可以很快知道一个代码单元是一个字符的编码,还是一个辅助字符的第一或第二部分。例如,𝕆是八元数集的数学符号,码点为U+1D546,编码为两个代码单元U+D835和U+DD46。(关于编码算法的具体描述见 https://tools.ietf.org/html/rfc27810 )。
在Java中,char类型描述了采用UTF-16编码的一个代码单元。强烈建议不要在程序中使用char类型,除非确实需要处理UTF-16代码单元。最好将字符串作为抽象数据类型来处理。
——《Java核心技术(原书第12版,机械工业出版社)》P32

4.布尔类型

布尔类型(boolean)有两个值:falsetrue,用于逻辑判断。整型值与布尔值之间不能转换。

因此在C/C++中常用的while(n--)在Java中就行不通了,因为Java中的n--作为整型数据无法转换成布尔值。不过也有好处,至少避免了不小心写出if (n = 0)而使得这段代码永远为false的情况了(我本人经常不小心写错…)

5.枚举类型

在编写程序时,我们通常需要给一些成组的数据编号,如一周的七天编号为17、方向的上下左右编号为14、尺寸的小中大号编号为1~3等。这样做有两个缺点:

  • 对不熟悉程序的人来说,看到突然出现的编号(magic number,魔法数字)会造成困扰,如if (direction == 3),这样意义不明的表达会降低代码的可读性,在协作开发时造成一些麻烦。
  • 有时编号可能会由于一些错误原因超出范围,使得程序出现意料之外的情况,难以进行管理和限制。

为了解决这些麻烦,人们提出了 枚举(enum) 这样的概念,通过给予一组编号有意义的名称来进行管理,有效缓解了代码可读性与范围限制的问题:

1
2
3
enum size { small, medium, large, extra_large};

size s = size.medium;

size类型的变量s只能存储上面声明的一些枚举值,或者是null,表示这个变量没有设置任何值。

五、整型与常量

1.声明变量

变量的声明通常需要先指定类型,然后是这个变量的名称。如:

1
2
3
int a;
double money;
boolean is_done;

2. 变量的赋值

可以在变量声明后为其赋值,也可以在声明的同时直接赋予初始值,这一点与大多数编程语言相同。

1
2
3
int a;
a = 1;
int a = 1;

从Java 10开始,局部变量可以使用var声明,这样编译器能够通过初始值自动判断变量的类型,如:

1
2
var month = 7;
var name = "Xiao Ming";

赋值语句也是一个表达式,其值等于被赋予的数值,如int a = 3这个表达式的值为3。

3.常量

在Java中,使用final来声明一个常量。尽管const作为Java的保留字(目前没有使用为关键字,但保留以后使用的可能性),但是目前仍然只能使用final来定义常量。常量只能被赋值一次,后面无法进行修改。通常对常量采取全大写的命名方式。如:

1
final PI = 3.1415926;

如果想定义一个常量以在同一个类的多个方法中使用,可以使用关键字static final将其设置为一个类常量。

1
2
3
4
5
6
7
8
9
10
11
public class ConstDemo
{
public static final double CM_PER_INCH = 2.54; // 英寸 - 厘米 换算关系

public static void main (String[] args) {
double inchs = 3.25;
System.out.println(inchs + "英寸等于" + inchs * CM_PER_INCH + "厘米");
}
}

// 输出:3.25英寸等于8.255厘米

类常量的定义通常位于main方法的外部。当其被声明为public时,其他类也可以访问到这个常量。

六、算数逻辑运算

1.算术运算符

与很多编程语言相同,Java也使用+ - * /来完成四则运算,其中两个整数相除会进行整除操作,得到的是结果的整数部分;有浮点数参与除法运算时,得到的是完整的商值。整数的求余(也称为取模)运算使用%

需要注意的是,前面提到Java中整数被0除会产生一个除零异常;浮点数被0除会得到无穷大或者NaN的结果。

2. Math类

我们经常需要进行一些数学运算,如求幂、计算三角函数、使用圆周率pi、自然对数e等。Math类很好地完成了这些工作,我们只需调用相关的属性或方法即可:

属性/方法 描述
Math.sqrt(x) 求x的平方根
Math.pow(x, a) 求x的a次幂
Math.sin(x) 计算 x(弧度)的正弦值(sine),返回值范围 [-1, 1]
Math.cos(x) 计算 x(弧度)的余弦值(cosine),返回值范围 [-1, 1]
Math.tan(x) 计算 x(弧度)的正切值(tangent),返回值范围是全体实数。
Math.atan(x) 计算 x 的反正切值(arctangent),返回弧度值,范围 [-π/2, π/2]
Math.atan2(y, x) 计算 y/x 的反正切值(arctangent),返回弧度值,范围 [-π, π] ,能正确处理象限问题。
Math.toRadians(d) 将角度制的d转换成弧度制
Math.exp(x) 计算 exe^{x},当 x 很大或很小时会返回 Infinity-Infinity
Math.log(x) 计算 ln(x)ln(x),当 x <= 0 时会返回 NaN
Math.log10(x) 计算 log10xlog_{10}x,当 x <= 0 时会返回 NaN
Math.PI 接近圆周率π的常量,值约为3.1415926
Math.E 接近自然常数e的常量,值约为2.71828

如果在一段程序中需要大量使用Math类的属性或方法,可以在程序的头部添加引入Math类的声明即可,需要使用时直接写对应的属性或方法即可:

1
2
3
4
import static java.lang.Math.*;

final π = PI;
System.out.println("sin(20)的值为:" + sin(20));

另外,Math类中的floorMod方法保证了余数不为负,这在一些进位处理中有很大的帮助,例如我们处理一个分钟的迭加逻辑时,会采用 int currentMin = (oldMin + addMin) % 60; 这样的写法,但是当 addMin 为负时,currentMin 也可能会减少到负值,需要额外的逻辑来规范处理,但是Java中可以这样解决:

1
int currentMin = floorMod(oldMin + addMin, 60);

这样就会保证 currentMin 的值始终在0 ~ 59 之间。

3.数据类型转换

上图示意了Java中的数据类型转换关系,其中实线箭头表示转换时无精度损失;虚线箭头则表示可能会发生精度损失。与大多数编程语言相同,将两个不同类型的数据用于算术运算时,Java会自动转换成较大数据类型,如 int + double -> doubleint + long -> long

需要手动转换数据类型时,可以使用强制类型转换(cast)。例如 int a = (int) 3.76 。需要注意以下两点:

  • 当前数值超过目标类型的表示范围时会发生截断,即会得到一个不同的数值。例如 (byte) 300 == 44
  • 不要试图直接将布尔类型的值转换成数值,应该使用三元表达式 b ? 1 : 0 进行转换。

Java支持二元运算符,可以使用+=-=这样的符号。如果运算符右侧的数值与左值的类型不同,会发生强制类型转换,即如果对int类型的x执行x+=3.5的操作,得到的结果是(int) x + 3.5,即结果为3。

4.自增与自减运算符

与C/C++类似,Java支持 ++-- 这两个自增/减运算符,并且同样可以前置和后置。

1
2
3
int a = 3;
int b = a++; // 先取值再++,此时 b == a == 3, a == 4;
int c = ++a; // 先++再取值,此时 c == a+1 == 5, a == 5;

5.逻辑运算符

Java采用了C++的做法,使用 == 判断两边是否相等;分别使用 &&|| 表示逻辑与、逻辑或和逻辑非,!= 表示不等于,><<=>=这样司空见惯的比较符号更是无需多言了。

需要注意的是,Java中同样存在逻辑短路的机制,即不会计算逻辑与在一端为 false 情况下、逻辑或在一端为 true 情况下,多余的表达式结果。例如:

1
2
3
if (x!=0 && 1 / x > x + y) {
// 省略
}

这个判断中,如果 x 为0时,逻辑与运算的左边为0,无论右边是否为真都不会使得整个表达式为真,所以不会计算右边的表达式,也正好就避免了发生除零异常。

6.位运算符

指定两个整数进行位运算时,实现的是两个数字的按位运算(先转换成二进制表达,然后从低位开始逐位运算)。类似地,Java也是用下面几个符号来定义按位运算:

  • &:按位与(and)
  • |:按位或(or)
  • ^:按位异或(xor)
  • ~:按位非(not)
  • <<:左移操作
  • :右移操作,高位以符号位补充,又称算数移位

  • :右移操作,高位以0补充,又称逻辑移位

7.条件运算符(三元表达式)

1
条件 ? 条件为真的操作 : 条件为假的操作;

(有人用x == y ? x : y描述自己作为x的"家庭地位",感觉挺有意思的,大家可以仔细揣摩一下…)

8.运算符优先级

一般情况下,为了避免出现优先级问题,在复杂逻辑中添加括号明确定义运算顺序即可,不过要注意以下几点:

  • 括号的优先级是最高的,会最先处理最内层括号的内容
  • +=系列的优先级是从右向左,即a += b += c的处理顺序是a += (b += c),先处理b += c,即 b = b + c,然后把现在 b 完成加法操作之后的值加到 a 上。除此之外,+= 这个系列的符号也是从右往左处理的。
  • &&的优先级高于 ||

参考资料

  1. 廖雪峰的官方网站.Java教程[EB/OL].(2025-06-07)[2025-08-20]. https://www.cnblogs.com/echolun/p/12709761.html

Java学习笔记(一):Java概述及程序设计起步
http://blog.morely.top/2025/08/18/Java学习笔记(一):Java概述及程序设计起步/
作者
陌离
发布于
2025年8月18日
许可协议