1. Java 简介

Java 是由 Sun Microsystems 公司于 1995 年推出的一门面向对象程序设计语言。2010 年 Oracle 公司收购 Sun Microsystems,之后由 Oracle 公司负责 Java 的维护和版本升级。Java 也是一个平台,由 Java 虚拟机(JVM)和 Java 应用编程接口(API)构成,提供了独立于操作系统的标准接口,实现了"一次编写,到处运行"的跨平台特性。

2. Java 分类

按应用范围,Java 可分为 3 个体系:

  • Java SE(标准版):Java 技术的核心和基础,包含 Java 基础语法、面向对象、常用 API、集合、IO 流、多线程、反射等。
  • Java EE(企业版):企业级应用开发解决方案,在 Java SE 基础上构建,提供 Web 服务、组件模型、管理和通信 API,如 Spring、JSP、Servlet、JavaBean 等。
  • Java ME(微型版):针对移动设备和嵌入式设备的解决方案,如手机、PDA、电视机顶盒等。(现在基本不再使用)

3. Java 语言的特点

Java 语言的风格很像 C 语言和 C++ 语言,是一种纯粹的面向对象语言,它继承了 C++ 面向对象的技术核心,但抛弃了容易引起错误的指针以及多继承等缺点,增加了垃圾回收机制。主要特点包括:

  1. 面向对象:支持类、对象、继承、封装、多态、接口、包等,只支持单继承但可通过接口实现多继承。
  2. 平台无关性:通过 JVM 机制屏蔽具体平台信息,实现"一次编写,到处运行"。
  3. 简单性:舍弃了操作符重载、多继承等复杂特性,不使用指针,加入垃圾回收机制。
  4. 解释执行:Java 程序编译成字节码文件,由 JVM 解释执行。
  5. 多线程:支持多个线程同时执行,并提供多线程同步机制。
  6. 健壮性:强类型机制、异常处理、垃圾回收机制保证程序健壮性。
  7. 安全性:提供安全机制防止恶意代码攻击,包含安全管理机制。

4. JDK、JRE、JVM 关系

JDK(Java Development Kit)是 Java 标准开发工具包,包含 Java 程序开发的工具和资源,面向程序员。JRE(Java Runtime Environment)是 Java 运行时环境,包含 JVM 所需要的类库。JVM(Java 虚拟机)用于运行字节码文件,是 Java 实现跨平台的核心。三者关系为:JDK 包含 JRE,JRE 包含 JVM。代码通过 javac 编译器编译成字节码文件,交由 JVM 运行。

5. 变量与数据类型

变量是向内存申请空间用于存储数据的容器。定义格式为:数据类型 变量名 = 变量值。变量命名需符合标识符语法要求,大小写敏感,不能使用保留字,建议采用驼峰命名法。

Java 有 8 种基本数据类型:

类型 大小 范围 示例
byte 1 字节 -128~127 byte b = 10
short 2 字节 -32768~32767 short s = 100
int 4 字节 -21亿~21亿 int age = 20
long 8 字节 较大整数 long num = 100L
float 4 字节 小数 float price = 3.14f
double 8 字节 小数 double pi = 3.14
char 2 字节 单字符 'A'
boolean 1 字节 true/false true

类型转换规则:byte、short、char 进行计算默认结果为 int 类型;小范围类型赋值给大范围类型自动转换,大范围类型赋值给小范围类型需要强制类型转换。

6. 字符串与注释

String 是引用类型,不属于八大基本类型,使用双引号定义。字符串拼接使用 +。Java 注释分为单行注释(//)和多行注释(/* */),快捷键为 Ctrl+/Ctrl+Shift+/

7. 运算符

Java 中的运算符用于对变量和常量执行运算操作,主要包括以下几类:

7.1 算术运算符

算术运算符包括 +-*/%(取余)。其中 % 常用于判断奇偶性或倍数关系。

int x = 5, y = 5;
int result = x + y;   // 10
result = x - y;       // 0
result = x * y;       // 25
result = x / y;       // 1
result = x % y;       // 0(取余数)
System.out.println(result);

7.2 自增自减运算符

++-- 用于变量自增 1 或自减 1。运算符在变量之前时先自增再参与运算在变量之后时先参与运算再自增

int n = 2;
int y = n++;  // y=2, n=3(先赋值再自增)
int m = 5;
int z = ++m;  // z=6, m=6(先自增再赋值)
System.out.println("y=" + y + ",n=" + n);
System.out.println("z=" + z + ",m=" + m);

7.3 扩展赋值运算符

扩展赋值运算符包括 +=-=*=/=%=,用于简化赋值操作。

int id = 123;
id -= 10;  // 等价于 id = id - 10;
id += 10;  // 等价于 id = id + 10;
id *= 10;  // 等价于 id = id * 10;
id %= 10;  // 等价于 id = id % 10;

7.4 关系运算符

关系运算符包括 ><>=<===!=,用于比较两个数的关系,结果为 boolean 类型(true 或 false)。

boolean boo;
boo = 4 < 3;    // false
boo = 3 >= 3;   // true
boo = 5 != 9;   // true
boo = 21 == 21; // true
System.out.println(boo);

7.5 逻辑运算符与逻辑短路

逻辑运算符 &&(与)和 ||(或)具有短路特性:&& 左侧为 false 时右侧不再执行;|| 左侧为 true 时右侧不再执行。而 &| 没有短路特性。

int i = 100, j = 200;
boolean b1 = (i > j) && (i++ > 100);  // 短路,i++ 不执行
System.out.println("b1==" + b1 + ", i==" + i);
boolean b2 = i > 0 || j++ > 200;       // 短路,j++ 不执行
System.out.println("b2==" + b2 + ", j==" + j);

7.6 字符串连接运算符

+ 作为连接符时,可以让字符串和任意类型的数据相连接,组成一个新的字符串。

int id = 1001;
int age = 20;
System.out.println("学号:" + id + ",年龄:" + age);
System.out.println("年龄:" + (age + 10));
System.out.println("[学号=" + id + ",年龄=" + age + "]");

7.7 三目运算符

三目运算符格式为 boolean 表达式 ? 表达式1 : 表达式2。如果 boolean 表达式成立,结果为表达式1,否则为表达式2。

int n = 2 < 1 ? 10 : 20;  // 结果为 20
System.out.println(n);
// 判断闰年
int year = 2008;
String str = year % 4 == 0 && year % 100 != 0 || year % 400 == 0
? year + "是闰年" : year + "是平年";
System.out.println(str);

8. Scanner 键盘输入

Scanner 是 Java 提供的键盘输入函数,允许用户通过键盘输入值,变量可以接收这些输入的值。

8.1 使用步骤

  1. 导入 Scanner 类:import java.util.Scanner;
  2. 创建 Scanner 对象:Scanner scanner = new Scanner(System.in);
  3. 调用相关方法读取输入。

8.2 常用方法

方法名 说明
nextInt() 输入整数
nextDouble() 输入小数
next() 输入字符串
Scanner scanner = new Scanner(System.in);
// 输入整数
System.out.println("请输入一个整数:");
int a = scanner.nextInt();
// 输入小数
System.out.println("请输入分数:");
double score = scanner.nextDouble();
// 输入字符串
System.out.println("请输入字符串:");
String info = scanner.next();

9. 分支结构

9.1 if 单分支

格式:if (boolean 表达式) { 语句 }。当 boolean 表达式成立时执行 if 语句中的代码,否则不执行。

if (4 < 2) {
    System.out.println("我是 if 语句");
}
// 输入成绩判断等级
Scanner scanner = new Scanner(System.in);
System.out.println("请输入一个分数:");
double score = scanner.nextDouble();
if (score >= 90) { System.out.println("A"); }
if (score >= 80) { System.out.println("B"); }
if (score >= 70) { System.out.println("C"); }
if (score >= 60) { System.out.println("D"); }
if (score < 60)  { System.out.println("不及格"); }

9.2 if-else 分支

格式:if (boolean 表达式) { 语句1 } else { 语句2 }。成立时执行语句1,否则执行语句2。

// 判断两个数的大小关系
int m = 5, n = 5;
if (m > n) {
    System.out.println("m > n");
} else if (m < n) {
    System.out.println("m < n");
} else {
    System.out.println("m == n");
}
// 成绩等级判断(多分支)
if (score >= 90) {
System.out.println("优秀");
} else if (score >= 80) {
System.out.println("良好");
} else if (score >= 70) {
System.out.println("中等");
} else if (score >= 60) {
System.out.println("及格");
} else {
System.out.println("不及格");
}

9.3 嵌套 if

if 语句内部可以嵌套另一个 if 语句,用于多条件判断。

// 登录验证
int id = 1001, password = 123456;
System.out.println("请输入学号:");
int stu_id = scanner.nextInt();
System.out.println("请输入密码:");
int stu_password = scanner.nextInt();
if (stu_id == id) {           // 学号正确
    if (stu_password == password) {  // 密码正确
        System.out.println("登录成功");
    } else {
        System.out.println("密码错误");
    }
} else {
    System.out.println("学号错误");
}

9.4 switch 分支

switch-case 是一种特殊的分支结构,通过表达式的值与每个 case 的值进行比较,匹配后执行对应语句。表达式支持 byte、short、int、char、String 类型。

int select = 1;
switch (select) {
    case 1:
        System.out.println("开始游戏"); break;
    case 2:
        System.out.println("设置"); break;
    case 3:
        System.out.println("存档"); break;
    case 4:
        System.out.println("退出"); break;
    default:
        System.out.println("输入有误!");
}
// 显示月份对应天数
System.out.println("请输入月份:");
int month = scanner.nextInt();
switch (month) {
case 1: case 3: case 5: case 7:
case 8: case 10: case 12:
System.out.println(month + "有31天"); break;
case 2:
if (year % 4 == 0 && year % 100 != 0 || year % 400 == 0) {
System.out.println(month + "有29天");
} else {
System.out.println(month + "有28天");
}
break;
case 4: case 6: case 9: case 11:
System.out.println(month + "有30天"); break;
default:
System.out.println("月份输入错误");
}

9.5 字符串比较

字符串比较使用 equals() 方法,不能直接使用 ==

String str = "hello";
str.equals("world");  // 比较字符串内容是否相同
// 用户登录示例
String username = "admin";
String password = "123456";
System.out.println("请输入用户名:");
String inputUsername = scanner.next();
System.out.println("请输入密码:");
String inputPassword = scanner.next();
if (username.equals(inputUsername)) {
if (password.equals(inputPassword)) {
System.out.println("登录成功");
} else {
System.out.println("密码错误");
}
} else {
System.out.println("用户名不存在");
}

10. 转义字符

转义字符用于表示不方便直接输出的特殊字符,以反斜杠 \ 开头。常用转义字符如下:

转义字符 说明
\n 换行符
\r 回车符
\t 制表位,实现对齐功能
\\ 反斜杠 \
\' 单引号
\" 双引号
String str = " 你好\r吗 ";
System.out.println(str);
System.out.println("学号\t\t姓名\t年龄\t性别");
System.out.println("2023\07\19");
System.out.println("2023-07-19");
// 姓名:'张三'
String name = "张三";
System.out.println("姓名:'张三'");
System.out.println("姓名:'" + name + "'");
System.out.println("姓名:'" + name + "'");
// 姓名:"张三"
System.out.println("姓名:"" + name + """);

11. Random 随机数

Random 是 java.util 包下提供的工具类,用于生成随机数。

使用格式:

Random random = new Random();          // 定义随机数对象
int rand = random.nextInt(10);         // 生成 0-9 之间的随机数

12. 循环结构

循环结构用于重复执行一段代码,Java 提供了 whiledo-whilefor 三种循环语句。

12.1 while 循环

格式:while (boolean 表达式) { 循环体 }。只要 boolean 表达式为 true,循环中的语句就会执行,直到条件不成立时循环结束。

循环三要素:

  1. 循环初始值:记录循环的次数
  2. 循环体:要重复执行的代码
  3. 循环结束条件:循环次数达到要求时结束
// 输出 1~10
int n = 1;           // 1. 循环初始值
while (n <= 10) {    // 3. 循环结束条件
    System.out.println(n);  // 2. 循环体
    n++;
}
// 计算 1+2+3+...+10
int sum = 0;
int num = 1;
while (num <= 10) {
sum = sum + num;
num++;
}
System.out.println(sum);  // 55
// 1-2+3-4+5-6+...+99-100
int sum2 = 0;
int i = 1;
while (i <= 100) {
if (i % 2 == 0) {
sum2 = sum2 - i;
} else {
sum2 = sum2 + i;
}
i++;
}
System.out.println(sum2);  // -50
// 1/1 + 1/3 + 1/5 + ... + 1/99
double s = 0;
int j = 1;
while (j <= 50) {
s = s + 1.0 / (2 * j - 1);
j++;
}
System.out.println(s);

12.2 死循环

while(true) 会形成死循环,条件永远成立,循环中的代码会一直执行。

while (true) {  // 死循环,条件永远成立
    // 代码
}

12.3 break 关键字

break 用于提前结束整个循环。

// 定义随机数,允许用户无限次数猜,直到猜对或输入 -1 终止
Scanner scanner = new Scanner(System.in);
Random random = new Random();
int rand = random.nextInt(50) + 10;
System.out.println("随机数:" + rand);
while (true) {
System.out.println("请输入猜的数字,输入 -1 终止游戏:");
int guess = scanner.nextInt();
if (guess == -1) {
System.out.println("游戏终止!");
break;
}
if (guess > rand) {
System.out.println("猜大了");
} else if (guess < rand) {
System.out.println("猜小了");
} else {
System.out.println("猜对了");
break;
}
}

12.4 continue 关键字

continue 用于结束本次循环,进入下一次循环。

// 不输出 3 的倍数
int m = 1;
while (m <= 10) {
    m++;
    if (m % 3 == 0) {
        continue;  // 结束本轮循环,不再执行后面的代码,开始下一轮循环
    }
    System.out.println(m);
}

break 和 continue 总结:

关键字 作用
break 结束整个循环
continue 结束本次循环,进入下一次循环

12.5 do-while 循环

do-whilewhile 的区别:do-while 先执行后判断,while 先判断后执行。仅仅在第一次条件不满足时,do-whilewhile 多执行一次。如果第一次条件满足,两者执行次数和结果一模一样。

int n = 1;
do {
    System.out.println(n);
    n++;
} while (n <= 5);

12.6 for 循环

格式:for (表达式1; 表达式2; 表达式3) { 循环体 }

// 输出 1~10
for (int i = 1; i <= 10; i++) {
    System.out.println(i);
}
// 死循环写法
for (;;) {
// 死循环
}

12.7 嵌套 for 循环

嵌套循环执行规则:内循环执行结束后,才会执行下一次外循环。

for (int i = 1; i <= 5; i++) {      // 外循环
    System.out.println("i=" + i);
    for (int j = 1; j <= 6; j++) {  // 内循环
        System.out.println("j=" + j);
    }
}

12.8 嵌套循环结束

break 关键字只能结束当前循环,并不能结束多个循环。可以使用标记的方式结束多层循环。

out:  // 标记,使用 break + 标记终止多层循环
for (int i = 1; i <= 3; i++) {
    System.out.println("i:" + i);
    for (int j = 1; j <= 5; j++) {
        if (j == 3) {
            break out;  // 终止外层循环
        }
        System.out.println("j:" + j);
    }
}

12.9 嵌套 for 应用

使用嵌套 for 可以打印相关图形以及遍历复杂的数据。

矩形图形:

for (int i = 1; i <= 5; i++) {       // 外循环控制行数
    for (int j = 1; j <= 11; j++) {  // 内循环控制列数
        System.out.print("*");
    }
    System.out.println();
}

直角三角形:

for (int i = 1; i <= 6; i++) {
    for (int j = 1; j <= 2 * i - 1; j++) {
        System.out.print("*");
    }
    System.out.println();
}

九九乘法表:

for (int i = 1; i <= 9; i++) {       // 9 行
    for (int j = 1; j <= i; j++) {
        System.out.print(j + "*" + i + "=" + (i * j) + "\t");
    }
    System.out.println();
}

等腰三角形:

Scanner scanner = new Scanner(System.in);
System.out.println("请输入打印行数:");
int line = scanner.nextInt();
for (int i = 1; i <= line; i++) {
    for (int j = 1; j <= line - i; j++) {  // 打印空格
        System.out.print(" ");
    }
    for (int k = 1; k <= 2 * i - 1; k++) {
        System.out.print("*");
    }
    System.out.println();
}

13. 数组

数组是一种数据结构,用于存储相同类型数据集合。数组中的元素通过下标(索引)访问,下标从0开始。

13.1 数组定义与初始化

数组定义格式:数据类型[] 数组名 = new 数据类型[长度];

// 定义整型数组,存储3个数据
int[] arr = new int[3];
// 定义char数组
char[] arr2 = new char[10];
// 数组初始化方式一:定义时直接赋值
int[] arr2 = {1, 2, 3, 4, 5};
// 数组初始化方式二:先声明后赋值
int[] arr3;
arr3 = new int[5];
arr3[0] = 2;
// 另一种声明方式
int arr4[];
arr4 = new int[]{1, 3, 5, 7, 9};

13.2 数组在内存中的存储

数组元素是顺序排列的,在内存中的地址是连续的。通过下标访问数组元素:数组名[下标],下标范围从0到长度-1。

package day04;
public class BasicArr {
    public static void main(String[] args) {
        int[] arr = new int[3]; // 定义一个长度为3的整型数组
        System.out.println(arr); // 打印数组地址
    // 通过下标访问数组
    System.out.println(arr[2]); // 访问第三个元素
// 给数组元素赋值
arr[0] = 5; // 给第一个元素赋值为5
arr[1] = 3;
arr[2] = 10;
// arr[3] = 6; // 错误:不能超过下标的最大范围(长度-1)

System.out.println(arr[2]); // 输出:10
}
}

13.3 数组长度与遍历

数组长度通过length属性获取,遍历数组可以使用普通for循环或增强for循环。

int[] arr = {10, 4, 2, 6, 8, 9, 3, 1};
// 获取数组长度
System.out.println(arr.length);
// 获取数组首元素
System.out.println(arr[0]);
// 获取数组尾元素
System.out.println(arr[arr.length - 1]);
// 普通for循环遍历
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + " ");
}
// 增强for循环遍历
for (int num : arr) {
System.out.print(num + " ");
}

13.4 数组默认值

不同类型的数组元素有默认初始值:

类型 默认值
整型(byte, short, int, long) 0
浮点型(float, double) 0.0
字符型(char) 空字符(NUL)
布尔类型(boolean) false
字符串(String) null

13.5 数组最值查找

查找数组最值的思想:先假设数组首位是最大或最小值,然后与数组后面的所有元素进行比较,找到更大或更小的值则更新。

package day04;
public class FindMax {
    public static void main(String[] args) {
        int[] arr = {20, 27, 5, 3, 18, 32, 16, 55, -10};
        int max = arr[0]; // 假设第一个元素是最大值
    for (int i = 1; i < arr.length; i++) {
        if (max < arr[i]) {
            max = arr[i]; // 找到更大的值,更新max
        }
    }
    System.out.println("最大值:" + max);
}
}

13.6 数组元素交换

数组元素可以使用下标方式进行值交换,通常借助临时变量

package day04;
public class Swap {
    public static void main(String[] args) {
        int[] arr = {1, 2, 3, 4, 5, 6};
    // 交换第二个和第四个元素
    int temp = arr[1];
    arr[1] = arr[3];
    arr[3] = temp;
for (int n : arr) {
    System.out.print(n + " "); // 输出:1 4 3 2 5 6
}
}
}

13.7 数组复制

使用System.arraycopy()方法可以复制数组元素。

// 数组复制
int[] arr = {2, 3, 4, 1, 6, 5, 8}; // 源数组
int[] arr2 = new int[arr.length];   // 目标数组
// 从arr数组第2个元素开始复制,到arr2中起始位置开始粘贴,共复制5个长度
System.arraycopy(arr, 2, arr2, 0, 5);
for (int n : arr2) {
System.out.print(n + " "); // 输出:4 1 6 5 8 0 0
}

13.8 数组扩容

使用Arrays.copyOf()方法可以实现数组扩容。

package day04;
import java.util.Arrays;
public class GrowArr {
public static void main(String[] args) {
// 数组扩容
int[] arr = {1, 2, 3, 4, 5, 6, 7, 8};
    // 将arr数组中的元素复制到新数组,复制的个数取决于新数组的长度
    int[] arr2 = Arrays.copyOf(arr, 5);
    for (int n : arr2) {
        System.out.print(n + " "); // 输出:1 2 3 4 5
    }
System.out.println();

// 动态扩容:原数组增加5个长度
arr = Arrays.copyOf(arr, arr.length + 5);
arr[8] = 9; // 给新扩容的位置赋值
for (int n : arr) {
    System.out.print(n + " "); // 输出:1 2 3 4 5 6 7 8 9 0 0 0 0 0
}
}
}

13.9 数组排序

13.9.1 冒泡排序

冒泡排序通过相邻元素比较和交换实现排序。

package day05;
public class PopSort {
    public static void main(String[] args) {
        // 冒泡排序(从小到大)
        int[] arr = {20, 4, 16, 32, 12, 6, 18, 69, 34};
    for (int i = 0; i < arr.length - 1; i++) { // 外循环:找最大值的次数
        for (int j = 0; j < arr.length - 1 - i; j++) { // 内循环:每一轮比较的次数
            if (arr[j] > arr[j + 1]) { // 前面比后面大,交换位置
                int temp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = temp;
            }
        }
    }
for (int n : arr) {
    System.out.print(n + " "); // 输出排序后的数组
}
}
}
13.9.2 工具类排序

使用Arrays.sort()方法可以快速对数组排序。

int[] arr = {20, 4, 16, 32, 12, 6, 18, 69, 34};
Arrays.sort(arr); // 对数组从小到大排序
for (int n : arr) {
System.out.print(n + " ");
}

14. 方法

方法是 Java 程序中最基本的代码组织单元,用于封装一段具备独立功能、可重复调用的代码块。合理使用方法可以提升代码的复用性、可读性和可维护性。

14.1 方法的核心概念

方法的作用主要体现在以下四个方面:

  • 代码复用:相同逻辑不用重复抄写。
  • 简化代码结构:拆分复杂业务,可读性更强。
  • 便于维护:修改功能只改一处,所有调用处同步生效。
  • 实现逻辑解耦:一段代码只负责一件事。

14.2 方法完整语法定义

标准格式如下:

修饰符 返回值类型 方法名(形参列表) {
    // 方法体:实现功能的代码
    return 返回值; // 有返回值方法必须写
}

各部分详解:

  • 修饰符:初学统一使用 public static,表示公共静态方法,可直接在 main 中调用。
  • 返回值类型:有结果返回时写对应数据类型(byte、short、int、long、float、double、char、boolean、String、数组、自定义类等);无任何数据返回时固定写 void
  • 方法名:采用小驼峰命名,首字母小写,多个单词从第二个单词首字母大写;建议使用动词或动名词,见名知意,如 sumprintInfogetUserName;不能数字开头、不能用关键字、不建议使用拼音。
  • 形参列表:方法运行需要外部传入数据时,在括号内定义形参,格式为 数据类型 变量名,多个参数用逗号分隔;不需要外部数据时括号保留为空 ()
  • 方法体:方法的核心逻辑,循环、判断、运算都写在这里。
  • return 返回值:有返回值方法必须写 return 数据;,数据类型必须和定义的返回值一致;void 无返回值方法可写单独的 return;,作用是终止方法,不能带数据。

14.3 四种方法分类

按返回值与参数划分,方法可分为四种类型:

类型 格式 示例 适用场景
无返回值、无参数 public static void printHello(){} 固定执行一段固定逻辑,不需要传参、不需要结果 打印固定提示信息
无返回值、有参数 public static void printNum(int n){} 外部传数据,方法内部直接打印或操作,不返回结果 打印指定次数的内容
有返回值、无参数 public static int getRandom(){} 方法内部自行计算,只需要对外返回一个结果 生成随机数
有返回值、有参数 public static int add(int a, int b){} 外部传入数据,运算后返回最终结果 两数求和
public class MethodDemo {
    public static void main(String[] args) {
        // 1. 无返回无参
        printHello();
        // 2. 无返回有参
        printCount(10);
        // 3. 有返回无参
        int num = getOneNum();
        System.out.println(num);
        // 4. 有返回有参
        int res = add(3, 5);
        System.out.println(res);
    }

    // 1. 无返回 无参数
    public static void printHello() {
        System.out.println("Hello Java 方法");
    }

    // 2. 无返回 有参数
    public static void printCount(int count) {
        for (int i = 0; i < count; i++) {
            System.out.print(i + " ");
        }
    }

    // 3. 有返回 无参数
    public static int getOneNum() {
        return 666;
    }

    // 4. 有返回 有参数
    public static int add(int a, int b) {
        return a + b;
    }
}

14.4 方法调用规则

方法只有被调用才会执行,写在类中与 main 同级的方法不会自动运行,必须主动调用。

静态方法(public static)调用方式:

方式一:同个类内直接调用。

public static void main(String[] args) {
    // 无返回值:直接写方法名(实参);
    print();
    printName("小明");
    // 有返回值:两种写法
    // ① 用对应类型变量接收(推荐,保留结果后续使用)
    int sum = add(1, 2);
    // ② 直接打印(临时使用,不保存结果)
    System.out.println(add(10, 20));
}

方式二:不同类调用,使用 类名.静态方法()

// TestA.java
public class TestA {
    public static void test() {
        System.out.println("跨类调用");
    }
}

// TestB.java
public class TestB {
    public static void main(String[] args) {
        TestA.test(); // 类名.静态方法()
    }
}

调用关键限制:

  • void 无返回方法不能用变量接收,编译直接报错。
  • 有返回值方法不接收不会报错,但返回结果直接丢弃,失去调用意义。
  • 调用时实参个数、类型、顺序必须和形参完全匹配,否则编译报错。

14.5 形参与实参

形参:定义方法时写在小括号里的变量,仅形式占位,没有真实数据。作用是指定调用时必须传入什么类型、多少个数据。作用域仅限当前方法内部,方法执行结束自动销毁。

实参:调用方法时括号中传入真实的值或变量,用来给形参赋值。可以是常量、变量、表达式、方法返回值。匹配规则为个数相同、类型兼容、顺序一一对应。实参和形参变量名不需要一致,只看类型和顺序。

// int a、int b 是形参
public static int add(int a, int b) {
    return a + b;
}

public static void main(String[] args) {
    int x = 10;
    int y = 20;
    // x、y 是实参,依次赋值给形参 a、b
    add(x, y);
    // 直接传常量实参
    add(100, 200);
    // 传表达式实参
    add(2 + 3, 5 * 6);
}

形参与局部变量:

  • 形参:方法括号内,属于方法局部,方法结束销毁。
  • 局部变量:方法体、for/if 内部定义,无默认值,使用前必须赋值。

14.6 return 关键字两种用法

用法一:有返回值方法 return 数据;

  • 返回的数据类型必须和方法声明的返回值完全匹配。
  • 执行 return 后,立刻终止当前方法,后面代码不会执行。
  • void 方法不能写带数据的 return,编译报错。
public static int max(int a, int b) {
    if (a > b) {
        return a;
    }
    return b;
}

用法二:void 方法单独 return;

不带任何数据,仅用于提前终止方法运行,后续代码不再执行。

public static void printSum() {
    int sum = 0;
    for (int i = 1; i <= 100; i++) {
        if (i == 90) {
            System.out.println("加到 90 停止,总和:" + sum);
            return; // 直接结束方法,循环剩余数字不再执行
        }
        sum += i;
    }
    System.out.println("循环全部执行完毕");
}

return 补充细节:

  • 有返回值方法所有逻辑分支必须保证一定能走到 return,否则编译报错。
  • return 之后不能写代码(不可达代码)。

14.7 方法参数传递机制(值传递与地址传递)

Java 只有值传递,分为两种表现形式:基本类型副本传递、引用类型地址值传递。

基本数据类型 + String:传递副本(值传递)

基本类型(byte、short、int、long、float、double、char、boolean)传递的是变量拷贝副本,方法内部修改形参,不会影响外部实参原始数据。String 虽然是引用类型,但字符串不可变,修改会创建新对象,表现和基本类型一致。

public class ParamTest {
    public static void changeNum(int num) {
        num = 999; // 修改副本,外部不变
    }

    public static void main(String[] args) {
        int a = 10;
        changeNum(a);
        System.out.println(a); // 输出 10,原始值没变化
    }
}

除 String 外其他引用类型:传递地址值

数组、自定义类对象、集合等,传递的是对象内存地址副本,形参和实参指向同一个堆内存对象。方法内通过地址修改对象内部数据,外部原始变量会同步变化。

public class ArrayParam {
    public static void changeArr(int[] arr) {
        arr[0] = 888; // 修改堆中数组元素,外部同步变
    }

    public static void main(String[] args) {
        int[] nums = {1, 2, 3};
        changeArr(nums);
        System.out.println(nums[0]); // 输出 888
    }
}

14.8 可变参数(不定长参数)

当不确定调用时会传入多少个同类型参数时(求和、批量打印、批量判断),可以使用可变参数。语法格式为 数据类型... 参数名,底层本质是数组。

// 可变参数 nums 等价于 int[] nums
public static int getSum(int... nums) {
    int sum = 0;
    for (int n : nums) {
        sum += n;
    }
    return sum;
}

public static void main(String[] args) {
    System.out.println(getSum(1));
    System.out.println(getSum(1, 2));
    System.out.println(getSum(1, 2, 3, 4, 5));
}

语法规则:

  • 一个方法最多只能有 1 个可变参数。
  • 可变参数必须放在所有形参最后面。
// 正确:普通参数在前,可变参数最后
public static void test(String name, int... arr) {}

// 错误:可变参数不能放前面
// public static void test(int... arr, String name){}

// 错误:多个可变参数
// public static void test(int... a, double... b){}

调用时可以不传可变参数、传多个常量、直接传同类型数组。

14.9 方法重载 Overload

同一个类中,方法名完全相同、参数列表不同的多个方法,互为重载。

判断重载的核心标准:

  • 方法名必须一模一样。
  • 参数列表必须不同(满足任意一条即可):参数个数不同、参数类型不同、参数顺序不同(类型组合不同)。

无关条件:

  • 返回值类型(仅返回值不同,参数完全一致不算重载,直接报错)。
  • 修饰符、形参变量名。
public class OverLoadDemo {
    public static void main(String[] args) {
        test();
        test(10);
        test("张三");
        test(20, "李四");
    }

    // 无参
    public static void test() {}

    // 1 个 int 参数
    public static void test(int a) {}

    // 1 个 String 参数
    public static void test(String name) {}

    // int + String
    public static double test(int num, String name) {
        return 1.1;
    }
}

重载调用匹配规则:调用时传入实参,编译器自动匹配参数最贴合的重载方法:精确类型匹配优先,自动类型转换匹配次之,无匹配则编译报错。

重载实际开发意义:同一功能多种传参方式,对外只暴露同一个方法名,调用简单、可读性高。例如打印方法,可以传数字、字符串、数组,统一叫 print。

14.10 方法嵌套调用与递归

嵌套调用:方法内部可以调用其他方法,支持多层嵌套。执行流程为 A 暂停、执行 B、B 执行完回到 A 继续运行剩余代码。

public static void main(String[] args) {
    printAll();
}

public static void printAll() {
    printHeader();
    printBody();
    printFooter();
}

public static void printHeader() {
    System.out.println("==== 头部 ====");
}

public static void printBody() {
    System.out.println("内容正文");
}

public static void printFooter() {
    System.out.println("==== 底部 ====");
}

递归调用(特殊嵌套):方法自己调用自己,用于解决重复性拆分问题(阶乘、斐波那契、遍历目录)。递归必备两个条件,缺一不可:

  • 递归出口(终止条件):满足条件不再调用自己,否则死递归栈溢出。
  • 递归递进公式:每次调用缩小问题规模。
public class RecursionDemo {
    public static void main(String[] args) {
        int res = factorial(5);
        System.out.println(res);
    }

    public static int factorial(int n) {
        // 递归出口
        if (n == 1) {
            return 1;
        }
        // 递进:自己调用自己,规模缩小
        return n * factorial(n - 1); // 3*factorial(2)*factorial(1)
    }
}

递归缺点:层数过多会出现 StackOverflowError 栈内存溢出,复杂循环场景优先使用 for 循环。

14.11 方法常见易错点总结

  • void 方法写 return 10; 编译报错,无返回值不能返回数据。
  • 有返回值所有分支没有 return,编译报错。
  • 调用方法实参类型、个数、顺序和形参不匹配,报错。
  • 可变参数放在参数列表前面、多个可变参数,语法报错。
  • 仅返回值不同、参数完全相同,不构成重载,编译冲突。
  • 基本类型传参修改形参,外部实参不变;数组或对象传参会同步修改。
  • 递归不写终止条件,运行时报栈溢出异常。
  • 方法内部定义局部变量无默认值,使用前必须赋值;成员变量自带默认值。
  • return 后书写代码,提示不可达代码,编译失败。
Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐