Java语法基础梳理
本文面介绍了Java语言的核心基础知识。文章首先详细讲解了Java的8种基本数据类型(byte、short、int、long、float、double、char、boolean)及其包装类型的特点、范围和使用方法;接着阐述了变量的声明规则、命名规范和常量的定义方式;然后系统梳理了Java提供的各类运算符(算术、关系、逻辑、位运算等)及其用法;还介绍了流程控制语句,包括条件语句(if、switch)、循环语句(for、while、do-while)和跳转语句(break、continue、return);此外,文章还讲解了Java中的三种注释方式,以及基本数据类型和引用数据类型在JVM中的存储位置(栈内存、堆内存、方法区/元空间等)。整体内容涵盖了Java语言基础的核心知识点,结构清晰,示例丰富。
Java语言基础梳理
一、基础语法
1. 数据类型
Java是一种强类型语言,每个变量都必须声明其数据类型。Java的数据类型分为两大类:基本数据类型和引用数据类型。
1.1 基本数据类型(Primitive Data Types)
Java有8种基本数据类型,它们是Java语言内置的,不是对象,不引用堆内存。
包装类型 (如Boolean、Integer)作为引用类型,其大小 取决于具体的JVM实现 ,没有固定的字节数。这是因为包装类型是对象,会包含对象头、实例数据等额外信息
1.1.1 整数类型(基本数据类型)
byte:1字节(8位),范围:-128 到 1271
byte b = 100;
short:2字节(16位),范围:-32768 到 327671
short s = 1000;
int:4字节(32位),范围:-2^31 到 2^31-1(约±21亿)1
int i = 100000;
long:8字节(64位),范围:-2^63 到 2^63-11
long l = 100000L; // 注意后面的L或l
1.1.2 整数类型(包装数据类型)
Byte:范围:-128 到 1271
Byte b = 100;
Short:范围:-32768 到 327671
Short s = 1000;
Integer:范围:-2^31 到 2^31-1(约±21亿)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21Integer i = 100000;
Integer a = 127;
Integer b = 127;
System.out.println(a == b); // 输出true,因为使用了缓存
Integer c = 128;
Integer d = 128;
System.out.println(c == d); // 输出false,超出缓存范围
/*
这段代码会输出 false,这是由于 Java 的 Integer 缓存机制导致的。
Integer 缓存机制详解
1. 缓存范围
Java 对 Integer 类型在 -128 到 127 范围内的值进行了缓存
这个范围由 java.lang.Integer.IntegerCache 类实现
可以通过 JVM 参数 -XX:AutoBoxCacheMax=<size> 来调整上限
2. 缓存的工作原理
当使用 Integer.valueOf() 方法(包括自动装箱)创建 Integer 对象时:
如果值在缓存范围内,返回缓存中的同一个对象实例
如果值超出缓存范围,每次都创建新的 Integer 对象
*/Long:范围:-2^63 到 2^63-11
Long l = 100000L; // 注意后面的L或l
1.1.3 浮点类型(基本数据类型)
float:4字节(32位),单精度浮点数1
float f = 3.14F; // 注意后面的F或f
double:8字节(64位),双精度浮点数,是默认的浮点类型1
2double d1 = 3.14;
double d2 = Math.PI; // 约等于3.141592653589793
1.1.4 浮点类型(包装数据类型)
Float:单精度浮点数1
Float f = 3.14F; // 注意后面的F或f
Double:双精度浮点数,是默认的浮点类型1
2Double d1 = 3.14;
Double d2 = Math.PI; // 约等于3.141592653589793
1.1.5 字符类型(基本数据类型)
char:2字节(16位),用于表示单个字符,使用Unicode编码1
2
3char c1 = 'A';
char c2 = '中';
char c3 = '\u0041'; // Unicode值表示的'A'
1.1.6 字符类型(包装数据类型)
Character:用于表示单个字符,使用Unicode编码1
2
3Character c1 = 'A';
Character c2 = '中';
Character c3 = '\u0041'; // Unicode值表示的'A'
1.1.7 布尔类型(基本数据类型)
boolean:在JVM中通常占用1字节(8位),只有true和false两个值1
2boolean flag = true;
boolean isEmpty = false;
1.1.8 布尔类型(包装数据类型)
Boolean:只有true和false两个值1
2Boolean flag = true;
Boolean isEmpty = false;
1.2 引用数据类型(Reference Data Types)
引用数据类型指向对象,它们存储的是对象在堆内存中的引用地址。
类(Class):如
String、Integer、自定义类等1
2String str = "Hello Java";
Date now = new Date();接口(Interface):如
List、Map等1
List<String> list = new ArrayList<>();
数组(Array):存储相同类型元素的容器
1
2int[] numbers = {1, 2, 3, 4, 5};
String[] names = new String[3];枚举(Enum):Java 5引入的一种特殊的类
1
enum Weekday { MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY }
1.3 上面的数据类型也可由JDK17+的类型推断来处理,由JVM自动推断该字段的数据类型
1 | var a = 1; |
2. 变量与常量
2.1 变量
变量是程序中存储数据的容器,其值可以在程序运行过程中改变。
变量声明:需要指定数据类型和变量名
1
2int age; // 只声明不初始化
double salary = 5000.0; // 声明并初始化变量命名规则
- 必须以字母、下划线
_或美元符号$开头 - 后续字符可以是字母、数字、下划线或美元符号
- 不能使用Java关键字或保留字
- 区分大小写
- 建议使用驼峰命名法(如:
userName、totalAmount)
- 必须以字母、下划线
2.2 常量
常量是在程序运行过程中其值不能改变的量,使用final关键字修饰。
1 | final double PI = 3.1415926; |
常量命名通常使用全大写字母,单词之间用下划线分隔。
3. 运算符
Java提供了丰富的运算符,用于执行各种运算操作。
3.1 算术运算符
用于执行基本的数学运算。
| 运算符 | 描述 | 示例 |
|---|---|---|
+ |
加法 | a + b |
- |
减法 | a - b |
* |
乘法 | a * b |
/ |
除法(整数相除结果为整数) | a / b |
% |
取模(求余数) | a % b |
++ |
自增1 | a++ 或 ++a |
-- |
自减1 | a-- 或 --a |
1 | int a = 10, b = 3; |
3.2 关系运算符
用于比较两个值之间的关系,返回布尔值true或false。
| 运算符 | 描述 | 示例 |
|---|---|---|
== |
等于 | a == b |
!= |
不等于 | a != b |
> |
大于 | a > b |
< |
小于 | a < b |
>= |
大于等于 | a >= b |
<= |
小于等于 | a <= b |
1 | int a = 10, b = 20; |
3.3 逻辑运算符
用于连接布尔表达式,进行逻辑运算。
| 运算符 | 描述 | 示例 | 短路特性 |
|---|---|---|---|
&& |
逻辑与 | a && b |
若a为false,不计算b |
| ` | ` | 逻辑或 | |
! |
逻辑非 | !a |
- |
1 | boolean isAdult = true; |
3.4 位运算符
用于对整数类型的二进制位进行操作。
| 运算符 | 描述 | 示例 |
|---|---|---|
& |
按位与 | a & b |
| ` | ` | 按位或 |
^ |
按位异或 | a ^ b |
~ |
按位取反 | ~a |
<< |
左移 | a << b |
>> |
右移(符号位不变) | a >> b |
>>> |
无符号右移(补0) | a >>> b |
1 | int a = 6; // 二进制: 0110 |
3.5 赋值运算符
用于给变量赋值,也可以与其他运算符组合。
| 运算符 | 描述 | 示例 | 等价于 |
|---|---|---|---|
= |
简单赋值 | a = b |
- |
+= |
加后赋值 | a += b |
a = a + b |
-= |
减后赋值 | a -= b |
a = a - b |
*= |
乘后赋值 | a *= b |
a = a * b |
/= |
除后赋值 | a /= b |
a = a / b |
%= |
取模后赋值 | a %= b |
a = a % b |
&= |
按位与后赋值 | a &= b |
a = a & b |
| ` | =` | 按位或后赋值 | `a |
^= |
按位异或后赋值 | a ^= b |
a = a ^ b |
<<= |
左移后赋值 | a <<= b |
a = a << b |
>>= |
右移后赋值 | a >>= b |
a = a >> b |
>>>= |
无符号右移后赋值 | a >>>= b |
a = a >>> b |
1 | int a = 10; |
3.6 条件运算符(三元运算符)
Java中唯一的三目运算符,用于简化if-else语句。
语法:条件表达式 ? 表达式1 : 表达式2
- 如果条件表达式为true,结果为表达式1
- 如果条件表达式为false,结果为表达式2
1 | int a = 10, b = 20; |
3.7 instanceof运算符
用于检查对象是否为指定类或其子类的实例。
1 | String str = "Hello"; |
3.8 文本字符串
1 | String str1 = "JDK 21 中的 switch 表达式和语句引入了一些新特性,主要包括:\n" + |
4. 流程控制语句
流程控制语句用于控制程序的执行顺序,包括条件语句、循环语句和跳转语句。
4.1 条件语句
if语句
1
2
3if (条件表达式) {
// 条件为true时执行的代码
}if-else语句
1
2
3
4
5if (条件表达式) {
// 条件为true时执行的代码
} else {
// 条件为false时执行的代码
}if-else if-else语句
1
2
3
4
5
6
7if (条件表达式1) {
// 条件1为true时执行的代码
} else if (条件表达式2) {
// 条件2为true时执行的代码
} else {
// 所有条件都为false时执行的代码
}switch语句(Java 7+支持String类型,Java 12+支持箭头表达式)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73switch (表达式) {
case 值1:
// 表达式等于值1时执行的代码
break; // 可选,用于跳出switch语句
case 值2:
// 表达式等于值2时执行的代码
break;
// 更多case分支...
default:
// 表达式不匹配任何case值时执行的代码
}
// Java 12+的箭头表达式语法
switch (day) {
case MONDAY, FRIDAY, SUNDAY -> System.out.println(6);
case TUESDAY -> System.out.println(7);
case THURSDAY, SATURDAY -> System.out.println(8);
case WEDNESDAY -> System.out.println(9);
default -> System.out.println(0);
}
// JDK 21 引入了模式匹配 switch,允许在 switch 表达式中使用类型模式和守卫条件
static String format(Object obj) {
return switch (obj) {
case Integer i -> String.format("int %d", i);
case Long l -> String.format("long %d", l);
case Double d -> String.format("double %g", d);
case String s -> String.format("String %s", s);
default -> String.format("Object %s", obj.toString());
};
}
// 可以在模式后添加 when 子句来增加额外的条件
static String checkNumber(Number number) {
return switch (number) {
case Integer i when i > 0 -> "positive integer";
case Integer i when i < 0 -> "negative integer";
case Integer i -> "zero";
case Double d when d > 0.0 -> "positive double";
case Double d when d < 0.0 -> "negative double";
default -> "other number";
};
}
//增强的 null 处理
static void test(Object obj) {
switch (obj) {
case null -> System.out.println("It's a null");
case String s -> System.out.println("It's a string: " + s);
default -> System.out.println("It's something else");
}
}
//多个常量的 case 标签
static String processDay(int day) {
return switch (day) {
case 1, 2, 3, 4, 5 -> "Weekday";
case 6, 7 -> "Weekend";
default -> "Invalid day";
};
}
// 支持 Record 模式的解构
record Point(int x, int y) {}
static String processPoint(Object obj) {
return switch (obj) {
case Point(int x, int y) when x == y -> "Diagonal point";
case Point(int x, int y) -> "Regular point (" + x + ", " + y + ")";
default -> "Not a point";
};
}
4.2 循环语句
for循环
1
2
3
4
5
6
7
8for (初始化表达式; 条件表达式; 步进表达式) {
// 循环体,当条件表达式为true时执行
}
// 增强for循环(for-each,用于遍历数组和集合)
for (元素类型 变量名 : 数组或集合) {
// 循环体,变量名代表当前元素
}示例:
1
2
3
4
5
6
7
8
9
10// 传统for循环
for (int i = 0; i < 10; i++) {
System.out.println(i);
}
// 增强for循环遍历数组
int[] numbers = {1, 2, 3, 4, 5};
for (int num : numbers) {
System.out.println(num);
}while循环
1
2
3while (条件表达式) {
// 循环体,当条件表达式为true时执行
}示例:
1
2
3
4
5int i = 0;
while (i < 10) {
System.out.println(i);
i++;
}do-while循环(至少执行一次循环体)
1
2
3do {
// 循环体
} while (条件表达式);示例:
1
2
3
4
5int i = 0;
do {
System.out.println(i);
i++;
} while (i < 10);
4.3 跳转语句
用于在循环或switch语句中改变程序的执行流程。
break语句:用于跳出循环或switch语句
1
2
3
4
5
6for (int i = 0; i < 10; i++) {
if (i == 5) {
break; // 跳出循环
}
System.out.println(i); // 只输出0-4
}continue语句:用于跳过当前循环的剩余部分,进入下一次循环
1
2
3
4
5
6for (int i = 0; i < 10; i++) {
if (i % 2 == 0) {
continue; // 跳过偶数
}
System.out.println(i); // 只输出奇数1,3,5,7,9
}return语句:用于从方法中返回结果,或直接结束方法的执行
1
2
3
4
5
6
7
8
9
10public int add(int a, int b) {
return a + b; // 返回计算结果
}
public void checkAge(int age) {
if (age < 0) {
return; // 直接结束方法
}
// 后续代码
}
5. 注释
注释是程序中用于说明代码的文字,不会被编译器执行。Java支持三种注释方式:
单行注释:使用
//开头1
int age = 18; // 定义一个整型变量表示年龄
多行注释:使用
/*开始,*/结束1
2
3
4/*
* 这是一个多行注释
* 可以跨越多行
*/文档注释:使用
/**开始,*/结束,用于生成API文档1
2
3
4
5
6
7
8
9/**
* 计算两个数的和
* @param a 第一个整数
* @param b 第二个整数
* @return 两个数的和
*/
public int add(int a, int b) {
return a + b;
}
6. Java中基本数据类型在JVM的存储位置
Java中基本数据类型的存储位置根据其作用域和声明位置有所不同,主要分为以下几个区域:
1. 栈内存 (Stack Memory)
局部变量存储
- 方法中的局部基本数据类型变量存储在栈内存中
- 包括方法参数和在方法内部声明的基本类型变量
1 | public void method() { |
特点
- 访问速度快
- 生命周期与方法执行周期一致
- 方法执行完毕后自动释放
2. 堆内存 (Heap Memory)
对象实例中的基本类型字段
- 作为类的成员变量(非静态)存储在堆内存中,与对象实例一起
- 作为数组元素的基本类型数据存储在堆内存中
1 | public class Example { |
3. 方法区/元空间 (Metaspace)
静态变量
- 类的静态基本数据类型成员变量存储在方法区(JDK 8之前)或元空间(JDK 8及以后)
1 | public class Example { |
4. 本地方法栈 (Native Method Stack)
- 用于存储本地方法(Native Method)调用时的基本数据类型
- 不常见,主要用于JNI调用
内存分配示例
1 | public class MemoryAllocation { |
总结
| 存储位置 | 数据类型 | 说明 |
|---|---|---|
| 栈内存 | 局部基本变量、方法参数 | 自动分配和释放,速度快 |
| 堆内存 | 对象中的基本类型字段、数组元素 | 需要垃圾回收,生命周期较长 |
| 方法区/元空间 | 静态基本变量、常量 | 类级别的数据,程序运行期间存在 |
| 本地方法栈 | Native方法中的基本类型 | JNI调用使用 |
对于代码示例中的 Integer i = 129; 和 Integer i1 = 129;,虽然 Integer 是包装类(对象类型),但变量 i 和 i1 的引用本身作为局部变量存储在栈中,而实际的 Integer 对象存储在堆中。
7. Java中的引用数据类型在JVM中的存储位置
Java中的引用数据类型在JVM中的存储位置根据其类型和作用域有所不同,主要分布在以下几个内存区域:
1. 堆内存 (Heap Memory) - 主要存储区域
对象实例
- 所有通过
new关键字创建的对象实例都存储在堆内存中 - 这是引用数据类型最主要的存储位置
1 | public class Example { |
示例分析(你的代码)
1 | Integer i = 129; // 等价于 Integer.valueOf(129) - 对象存储在堆中 |
2. 栈内存 (Stack Memory)
局部引用变量
- 引用变量本身(即指向对象的引用)存储在栈内存中
- 实际对象仍然存储在堆内存中
1 | public class Example { |
3. 方法区/元空间 (Metaspace)
类的元数据
- 类的定义信息、静态变量、常量池等存储在方法区(JDK 8前)或元空间(JDK 8及以后)
- 字符串常量池也位于堆中(JDK 7及以后)或方法区(JDK 6及以前)
1 | public class Example { |
4. 本地方法栈 (Native Method Stack)
- 存储本地方法(Native Method)调用时的引用数据
- 主要用于JNI调用
内存分配详细示例
1 | public class MemoryAllocation { |
内存布局图示
1 | JVM内存结构: |
总结
| 存储位置 | 数据类型 | 说明 |
|---|---|---|
| 堆内存 | 对象实例、数组 | 主要存储区域,需要垃圾回收 |
| 栈内存 | 局部引用变量 | 存储指向堆中对象的引用 |
| 方法区/元空间 | 静态引用变量、类信息 | 存储类级别的引用和元数据 |
| 本地方法栈 | Native方法中的引用 | JNI调用使用 |
对于代码示例:
1 | Integer i = 129; |
i和i1这两个引用变量存储在栈内存中- 两个
Integer对象实例存储在堆内存中 - 由于129超出缓存范围,所以创建了两个不同的对象实例
二、面向对象编程
- 四大特性:
- 封装:通过访问控制符(
private,protected,public, 默认)隐藏实现细节,提供公共方法访问。- 继承:
extends关键字,实现代码复用。单继承,但可多层继承。- 多态:
- 编译时多态:方法重载(Overload)
- 运行时多态:方法重写(Override),基于继承和接口实现,通过父类/接口引用指向子类对象。
- 抽象:
abstract类和接口(interface)用于定义规范。- 类与对象:类的定义、对象的创建(
new)、构造方法、this关键字。- 方法:方法的定义、参数传递(值传递)、递归。
2.1 封装
封装(Encapsulation)是面向对象编程的核心概念之一,它指的是将数据(属性)和操作数据的方法(行为)组合在一个单元(类)中,并通过访问控制符来控制对这些数据和方法的访问。
封装的实现方式
Java 中通过访问控制符来实现封装:
1. 访问控制符类型
- **
private**:仅在本类中可访问 default(包私有):同一包内可访问- **
protected**:同一包内或子类可访问 - **
public**:任何地方都可访问
2. 封装的实践示例
1 | public class BankAccount { |
封装的优势
- 数据安全:防止外部代码直接修改对象内部状态
- 维护性:内部实现可以改变而不影响外部代码
- 可控制访问:可以添加验证、日志等逻辑
- 代码复用:隐藏复杂实现,提供简洁接口
封装通过合理使用访问控制符,实现了数据隐藏和接口暴露的平衡,是构建健壮、可维护代码的重要手段。
| 访问控制符 | 同一类 | 同一包 | 不同包的子类 | 不同包的非子类 |
|---|---|---|---|---|
| private | ✓ | ✗ | ✗ | ✗ |
| 默认(包访问) | ✓ | ✓ | ✗ | ✗ |
| protected | ✓ | ✓ | ✓ | ✗ |
| public | ✓ | ✓ | ✓ | ✓ |
2.2 继承
继承(Inheritance)是面向对象编程的基本特性之一,它允许一个类(子类)获得另一个类(父类)的属性和方法,实现代码复用和层次化设计。
继承的基本语法
Java 中使用 extends 关键字实现继承:
1 | // 父类(基类) |
Java 继承的特点
1. 单继承
Java 只支持单继承,一个类只能直接继承一个父类:
1 | class Animal { |
2. 多层继承
虽然 Java 不支持多继承,但支持多层继承:
1 | class Vehicle { |
继承中的关键概念
1. 方法重写(Override)
子类可以重写父类的方法以提供特定实现:
1 | class Shape { |
2. super 关键字
用于访问父类的成员:
1 | class Employee { |
继承的访问控制
| 访问修饰符 | 同一类 | 同一包 | 子类 | 不同包 |
|---|---|---|---|---|
private |
✓ | ✗ | ✗ | ✗ |
default |
✓ | ✓ | ✗ | ✗ |
protected |
✓ | ✓ | ✓ | ✗ |
public |
✓ | ✓ | ✓ | ✓ |
继承的优缺点
优点:
- 代码复用:子类可以复用父类的代码
- 扩展性:可以在父类基础上添加新功能
- 多态性基础:为多态提供支持
- 层次结构:建立清晰的类层次关系
缺点:
- 紧耦合:子类与父类紧密耦合
- 复杂性:深层继承可能导致复杂性增加
- 脆弱性:父类的修改可能影响所有子类
继承是 Java 面向对象编程的重要机制,通过 extends 关键字实现单继承但支持多层继承,为代码复用和系统设计提供了强大支持。
补充
implements不属于继承,它是实现关系。虽然两者都涉及代码复用和类型扩展,但它们在Java中有本质区别:1. implements(实现)- 接口实现
特点:
- 类实现接口必须使用
implements关键字- 实现类必须实现接口中所有抽象方法(除非是抽象类)
- 一个类可以实现多个接口
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 // 接口定义
interface Flyable {
void fly();
}
interface Swimmable {
void swim();
}
// 类实现多个接口
class Duck implements Flyable, Swimmable {
public void fly() {
System.out.println("Duck is flying");
}
public void swim() {
System.out.println("Duck is swimming");
}
}2. extends(继承)- 类继承
特点:
- 子类继承父类使用
extends关键字- 子类继承父类的属性和方法
- Java中一个类只能继承一个父类(单继承)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 // 父类
class Animal {
protected String name;
public void eat() {
System.out.println("Animal is eating");
}
}
// 子类继承父类
class Dog extends Animal {
public void bark() {
System.out.println("Dog is barking");
}
}3. 主要区别对比
特性 extends(继承) implements(实现) 关键字 extendsimplements关系类型 继承关系(is-a) 实现关系(can-do) 数量限制 一个类只能继承一个父类 一个类可以实现多个接口 方法实现 可以有具体方法实现 Java 8前只能有抽象方法 访问修饰符 继承所有非private成员 实现所有接口方法 构造器 子类构造器调用父类构造器 接口没有构造器 4. 组合使用示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32 // 接口
interface Drawable {
void draw();
}
// 父类
class Shape {
protected String color;
public Shape(String color) {
this.color = color;
}
public void display() {
System.out.println("Shape color: " + color);
}
}
// 子类既继承又实现
class Circle extends Shape implements Drawable {
private double radius;
public Circle(String color, double radius) {
super(color); // 调用父类构造器
this.radius = radius;
}
public void draw() {
System.out.println("Drawing a circle with radius: " + radius);
}
}5. 语法顺序
当同时使用继承和实现时,必须遵循特定顺序:
1
2
3 class MyClass extends ParentClass implements Interface1, Interface2, Interface3 {
// 类实现
}总结
implements是实现接口,不是继承- 继承(
extends)是 “is-a” 关系,实现(implements)是 “can-do” 关系- 两者可以同时存在于一个类中,但
extends必须在implements之前
2.3 多态
Java 多态详解
多态(Polymorphism)是面向对象编程的核心特性之一,指的是同一个接口可以有多种不同的实现方式。Java中的多态分为编译时多态和运行时多态两种。
1. 编译时多态(Compile-time Polymorphism)
定义
编译时多态也称为静态多态,是在编译阶段确定调用哪个方法的多态形式。
方法重载(Method Overloading)
- 在同一个类中,方法名相同但参数列表不同(类型、个数、顺序)
- 返回值类型可以不同,但不能仅凭返回值类型区分重载方法
- 编译器根据调用时传入的参数类型和个数来决定调用哪个方法
1 | public class Calculator { |
2. 运行时多态(Runtime Polymorphism)
定义
运行时多态也称为动态多态,是在程序运行时根据对象的实际类型来决定调用哪个方法的多态形式。
方法重写(Method Overriding)
- 子类重新定义父类中已有的方法
- 方法签名(方法名和参数列表)必须完全相同
- 返回值类型可以是原返回值类型的子类型(协变返回类型)
- 访问权限不能更低,但可以更高
1 | // 父类 |
运行时多态的关键要素
- 继承关系
1 | // Dog、Cat、Bird 都继承自 Animal |
- 方法重写
1 | // 各个子类都重写了 makeSound() 和 move() 方法 |
- 父类引用指向子类对象
1 | public class PolymorphismDemo { |
3. 接口实现的多态
接口实现也是运行时多态的重要体现:
1 | // 接口定义 |
4. 多态的内存机制
1 | public class PolymorphismMemory { |
5. 多态的优势
1. 代码可扩展性
1 | // 添加新的动物类型无需修改现有代码 |
2. 降低耦合度
1 | // 服务类使用接口编程 |
总结
| 特性 | 编译时多态 | 运行时多态 |
|---|---|---|
| 实现方式 | 方法重载 | 方法重写 |
| 决定时机 | 编译时 | 运行时 |
| 基础 | 参数列表不同 | 继承/接口实现 |
| 灵活性 | 较低 | 较高 |
| 性能 | 较高(编译时确定) | 略低(需要动态绑定) |
多态是面向对象编程的重要特性,它提高了代码的可扩展性、可维护性和复用性,是设计模式和框架开发的基础。
2.4 抽象
抽象是面向对象编程的核心概念之一,它允许我们定义规范而不提供具体实现,让子类或实现类来完成具体细节。
1. 抽象类(Abstract Class)
基本概念
抽象类是使用 abstract 关键字修饰的类,不能被直接实例化,主要用于被其他类继承。
1 | // 抽象类定义 |
抽象类的特点
- 使用
abstract关键字修饰 - 不能被直接实例化
- 可以包含抽象方法和非抽象方法
- 可以包含成员变量、构造方法、静态方法等
- 子类继承抽象类时,必须实现所有抽象方法(除非子类也是抽象类)
1 | // 具体子类必须实现所有抽象方法 |
2. 接口(Interface)
基本概念
接口是使用 interface 关键字定义的完全抽象的类型,用于定义规范和契约。
1 | // 基本接口定义 |
接口的特点
- 使用
interface关键字定义 - 所有方法默认是
public abstract(Java 8前) - 所有变量默认是
public static final - 类通过
implements关键字实现接口 - 一个类可以实现多个接口
- 接口可以继承其他接口
1 | // 实现接口的类 |
多接口实现
1 | // 多个接口 |
3. 接口的继承
1 | // 接口可以继承其他接口 |
4. 抽象类 vs 接口对比
| 特性 | 抽象类 | 接口 |
|---|---|---|
| 关键字 | abstract class |
interface |
| 实现方式 | extends |
implements |
| 继承数量 | 一个类只能继承一个抽象类 | 一个类可以实现多个接口 |
| 方法实现 | 可以有具体方法实现 | Java 8前全部抽象,之后可有默认实现 |
| 成员变量 | 可以有各种访问修饰符的变量 | 只能有 public static final 常量 |
| 构造方法 | 可以有构造方法 | 不能有构造方法 |
| 访问修饰符 | 可以有各种访问修饰符 | 方法默认 public,变量默认 public static final |
| 设计目的 | 代码复用,定义共同行为 | 定义规范和契约 |
5. 使用场景
抽象类适用场景
1 | // 当多个类有共同的属性和行为时使用抽象类 |
接口适用场景
1 | // 当需要定义行为规范时使用接口 |
6. 实际应用示例
1 | // 综合示例:图形系统 |
抽象类和接口都是定义规范的重要工具,选择使用哪个取决于具体的设计需求和场景。
三、关键字和核心概念
Java 关键字一览表
| 关键字 | 一句话解释 |
|---|---|
| abstract | 声明抽象类或抽象方法,不能被实例化 |
| assert | 断言,用于调试时检查条件是否为真 |
| boolean | 基本数据类型,表示布尔值(true/false) |
| break | 跳出循环或switch语句 |
| byte | 基本数据类型,8位有符号整数 |
| case | switch语句中的分支标签 |
| catch | 捕获并处理异常 |
| char | 基本数据类型,16位Unicode字符 |
| class | 定义类 |
| const | 保留关键字,但未被使用(用final代替) |
| continue | 跳过当前循环迭代,继续下一次循环 |
| default | switch语句的默认分支或包访问权限 |
| do | do-while循环语句的开始 |
| double | 基本数据类型,64位双精度浮点数 |
| else | if语句的否则分支 |
| enum | 定义枚举类型 |
| extends | 类继承或接口扩展 |
| final | 声明常量、不可继承的类或不可重写的方法 |
| finally | 异常处理中最终执行的代码块 |
| float | 基本数据类型,32位单精度浮点数 |
| for | for循环语句 |
| goto | 保留关键字,但未被使用 |
| if | 条件判断语句 |
| implements | 类实现接口 |
| import | 导入包或类 |
| instanceof | 检查对象是否为指定类型实例 |
| int | 基本数据类型,32位有符号整数 |
| interface | 定义接口 |
| long | 基本数据类型,64位有符号整数 |
| native | 声明本地方法(由其他语言实现) |
| new | 创建对象实例 |
| package | 定义包 |
| private | 最严格的访问权限修饰符 |
| protected | 包内和子类可访问的访问权限修饰符 |
| public | 最宽松的访问权限修饰符 |
| return | 从方法返回值或结束方法执行 |
| short | 基本数据类型,16位有符号整数 |
| static | 声明类变量、类方法或静态代码块 |
| strictfp | 严格浮点运算模式 |
| super | 引用父类成员 |
| switch | 多分支选择语句 |
| synchronized | 同步代码块或方法,用于线程安全 |
| this | 引用当前对象实例 |
| throw | 抛出异常 |
| throws | 声明方法可能抛出的异常类型 |
| transient | 标记不参与序列化的变量 |
| try | 异常处理的尝试代码块 |
| void | 方法无返回值 |
| volatile | 标记变量为易变的,用于多线程环境 |
| while | while循环语句 |
| true | 布尔字面值 |
| false | 布尔字面值 |
| null | 空引用字面值 |
Java 9+ 新增关键字
| 关键字 | 一句话解释 |
|---|---|
| module | 定义模块(Java 9+模块系统) |
| requires | 声明模块依赖(Java 9+) |
| exports | 导出包给其他模块(Java 9+) |
| opens | 开放包给其他模块进行反射访问(Java 9+) |
| uses | 声明使用的服务(Java 9+) |
| provides | 提供服务实现(Java 9+) |
Java 14+ 新增关键字
| 关键字 | 一句话解释 |
|---|---|
| yield | switch表达式中的返回值(Java 14+) |
| record | 定义不可变数据类(Java 14+预览,Java 16+正式) |
| sealed | 限制类的继承(Java 15+预览,Java 17+正式) |
| permits | 指定允许继承sealed类的子类(Java 15+预览,Java 17+正式) |
| non-sealed | 声明允许继承的sealed类子类(Java 17+) |
总计:Java 8 共有 50 个关键字,Java 9+ 增加到 57 个关键字。
四、写在最后
Java基础语法是学习Java编程的第一步,掌握好这些基础知识对于后续学习面向对象编程、集合框架、多线程等高级特性至关重要。重点需要掌握:
- 数据类型:理解基本数据类型和引用数据类型的区别,掌握它们的用法和转换规则
- 变量与常量:掌握变量的声明、初始化和命名规则,理解常量的特性
- 运算符:熟悉各种运算符的功能和优先级,能够正确使用它们进行运算
- 流程控制:掌握条件语句、循环语句和跳转语句的用法,能够编写复杂的控制逻辑
- 注释规范:养成良好的注释习惯,提高代码的可读性和可维护性
通过大量的编程练习,可以加深对这些基础知识的理解和掌握,为成为一名优秀的Java程序员打下坚实的基础。
