方法是一组执行特定任务的语句集合,它把代码封装起来,能被重复调用,以此提升代码的复用性和可维护性。在 Java 里,方法是类的一部分,定义在类内部。Java 方法需要通过类的对象或者类名(静态方法)来调用。
方法的定义
Java 方法的基本定义格式如下:
修饰符 返回值类型 方法名(参数列表) {
// 方法体
return 返回值; // 如果返回值类型不为 void,则必须有返回语句
}
- 修饰符:像
public
、private
、protected
等访问修饰符,还有static
、final
等非访问修饰符,用来控制方法的访问权限和行为。 - 返回值类型:表明方法执行后返回结果的类型,若方法不返回任何值,返回值类型为
void
。 - 方法名:是用于标识方法的名称,要遵循 Java 的命名规范。
- 参数列表:是传递给方法的参数集合,多个参数之间用逗号分隔。
- 方法体:包含了实现方法功能的具体代码。
- 返回值:如果返回值类型不是
void
,方法必须使用return
语句返回一个与返回值类型相符的值。
方法的调用
在 Java 中,调用方法就是执行该方法的代码。若方法是静态方法,可通过类名直接调用;若为非静态方法,则需先创建类的对象,再通过对象调用。
方法的参数
参数分为形式参数(形参)和实际参数(实参)。形参是方法定义时的参数,用于接收调用方法时传递的值;实参则是调用方法时实际传递给方法的值。
方法的返回值
返回值是方法执行完毕后返回给调用者的结果。返回值类型必须与方法定义时指定的返回值类型一致。
示例代码
public class MethodExample {
// 静态方法,用于计算两个整数的和
public static int add(int a, int b) {
return a + b;
}
// 非静态方法,用于打印消息
public void printMessage(String message) {
System.out.println(message);
}
public static void main(String[] args) {
// 调用静态方法
int result = add(3, 5);
System.out.println("3 + 5 = " + result);
/*
在同一个类中调用类方法
当类方法在定义它的类内部被调用时,类前缀能够省略。
这是由于在类的内部,编译器能够明确知晓该方法属于哪个类。
*/
// 创建对象
MethodExample example = new MethodExample();
// 调用非静态方法
example.printMessage("Hello, Java!");
}
}
代码解释
add
方法属于静态方法,可直接通过类名调用。它接收两个整数参数,返回它们的和。printMessage
方法是非静态方法,要先创建MethodExample
类的对象,再通过对象调用。它接收一个字符串参数,把该字符串打印输出。- 在
main
方法里,先调用add
方法计算两个整数的和并输出结果,接着创建MethodExample
类的对象,调用printMessage
方法打印消息。
- 属于类:类方法属于类本身,而不属于类的某个实例。无论创建多少个类的实例,类方法都只有一份。
- 可直接访问:可以直接通过类名调用,无需创建类的实例。
- 访问限制:类方法只能访问类的静态成员(静态变量和静态方法),不能直接访问实例变量和实例方法。这是因为实例变量和实例方法是依赖于具体的对象实例的,而类方法在调用时可能还没有创建对象。
静态方法使用场景
- 工具类方法:当方法不需要访问对象的状态,只需要进行一些通用的计算或操作时,可以将其定义为静态方法。例如,Java 中的
Math
类,它包含了很多静态方法,如Math.sqrt()
用于计算平方根,Math.random()
用于生成随机数等。 - 工厂方法:用于创建类的对象,特别是在创建对象的过程比较复杂,或者需要根据不同的条件创建不同类型的对象时。
对象方法的使用场景
对象方法可以用来实现对象所具有的特定行为,这些行为与对象的属性和功能相关。对象方法可以将相关的业务逻辑封装在对象内部,提高代码的可维护性和可复用性。
方法的重载
在 Java 中,方法重载指的是在同一个类里可以定义多个同名方法,但这些方法的参数列表不同(参数的类型、个数或顺序不同)。方法重载能让代码更具可读性和易用性。示例如下:
public class MethodOverloadingExample {
// 计算两个整数的和
public static int add(int a, int b) {
return a + b;
}
// 计算三个整数的和
public static int add(int a, int b, int c) {
return a + b + c;
}
// 计算两个双精度浮点数的和
public static double add(double a, double b) {
return a + b;
}
public static void main(String[] args) {
System.out.println("2 + 3 = " + add(2, 3));
System.out.println("2 + 3 + 4 = " + add(2, 3, 4));
System.out.println("2.5 + 3.5 = " + add(2.5, 3.5));
}
}
在这个示例中,add
方法被重载了三次,分别用于计算两个整数的和、三个整数的和以及两个双精度浮点数的和。在调用 add
方法时,Java 编译器会依据传递的参数类型和个数自动选择合适的方法。
Java 只有值传递。所有参数传递的本质都是传递值的副本
- 当传递引用类型时,传递的是引用的副本,而非原始引用本身。
- 方法内无法直接修改外部的基本类型变量。
- 方法内可以通过引用的副本修改对象的内容,但无法改变外部引用指向的对象。
-
示例代码:
public class ValuePassingDemo { public static void main(String[] args) { int num = 10; modifyPrimitive(num); System.out.println(num); // 输出 10(未被修改) } public static void modifyPrimitive(int x) { x = 20; // 修改的是副本,不影响原始变量 } }
num
的值10
被复制到参数x
中。方法内修改x
不会影响外部的num
。 -
传递引用的副本
示例 1:修改数组内容(生效)
public class ReferencePassingDemo { public static void main(String[] args) { int[] numbers = {10, 20, 30}; modifyNumber(numbers); System.out.println( Arrays.toString(numbers)); } public static void modifyNumber(int[] n) { // Modify the first element of the array n[0] = 99; } }
numbers
的引用值(内存地址)被复制到参数n
中。numbers
和 n 指向同一个对象,因此修改n[0]
会影响原对象。 - 示例 2:试图修改引用指向的对象(无效)
public class ReferencePassingDemo {
public static void main(String[] args) {
int[] numbers = {10, 20, 30};
modifyNumber(numbers);
System.out.println( Arrays.toString(numbers));
}
public static void modifyNumber(int[] n) {
// Modify the n to a new array
n = new int[]{1,2,4};
}
}
- 参数 n 是
numbers
引用值的的副本。 - 在方法内让
n
指向新对象,但外部的numbers
仍指向原对象。
方法设计的技巧
单一职责原则
一个方法应该只负责一项明确的任务。这样可以使方法的功能更加清晰,易于理解和维护。如果一个方法承担了过多的职责,当其中一个职责需要修改时,可能会影响到其他职责,增加代码出错的风险。
- 参数数量:尽量减少方法的参数数量,过多的参数会使方法调用变得复杂,也增加了出错的可能性。如果需要传递多个参数,可以考虑将相关的参数封装成一个对象。
- 明确返回值类型:方法的返回值类型应该与方法的功能相匹配。如果方法不返回任何有意义的值,应该使用
void
类型。 - 不依赖对象状态:当方法不需要访问对象的实例变量,只进行一些通用的计算或操作时,适合使用静态方法。
- 实现对象行为:如果方法是实现对象所具有的特定行为,这些行为与对象的属性和功能相关,通常使用实例方法。
综合案例:
根据输入的数字打印出对应的等腰三角形。在这个程序中,输入的数字表示等腰三角形的行数。
import java.util.Scanner;
public class TrianglePrint {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.print("请输入一个正整数,表示三角形的行数: ");
int n = scanner.nextInt();
scanner.close();
printTriangle(n);
}
// 打印等腰三角形的方法
public static void printTriangle(int n) {
for (int i = 1; i <= n; i++) {
// 打印空格
for (int j = n - i; j > 0; j--) {
System.out.print(" ");
}
// 打印星号
for (int k = 0; k < 2 * i - 1; k++) {
System.out.print("*");
}
System.out.println();
}
}
}
打印乘法表
public static void multiplicationTabl {
for (int i = 1; i <= 9; i++) {
for (int j = 1; j <= i; j++) {
System.out.print(j + "×" + i + "=" + i * j + "\t");
}
System.out.println();
}
}
生成双色球
import java.util.*;
public class Main {
public static void main(String[] args) {
Random r = new Random();
int[] reds = getRedBalls();
bubbleSort(reds);
// 输出红色球号码
System.out.print("红色球号码: ");
for (int redBall : reds) {
System.out.printf("%02d ", redBall);
}
// 输出蓝色球号码
System.out.print("\n蓝色球号码: ");
System.out.printf("%02d\n", r.nextInt(16) + 1);
}
// 生成 6 个不重复的红色球号码
public static int[] getRedBalls() {
int[] reds = new int[33];
for (int i = 0; i < reds.length; i++) {
reds[i] = i+1;
}
Random r = new Random();
//洗牌算法,把数组打乱
for (int i = reds.length -1 ; i > 0; i--) {
//i 从32.也随时数组的最后一位。开始和随机生成的位置交换。
int tmp = r.nextInt(i+1); //随机数 从0 到 32(需要包含32)所以要加1
if (tmp == i){
//如果随机数和要交换的数字相等。则不换位置
}else{
int tmpInt = reds[i];
reds[i] = reds[tmp];
reds[tmp] = tmpInt;
}
}
//返回乱序数组的前六个
return Arrays.copyOf(reds,6);
}
// 冒泡排序,对int数组进行排序
public static void bubbleSort(int[] arr) {
int n = arr.length;
for (int i = 0; i < n - 1; i++) {
for (int j = 0; j < n - i - 1; j++) {
if (arr[j] > arr[j + 1]) {
// 交换 arr[j+1] 和 arr[j]
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
}