Java的方法详解
作者:天炜coding
5.1 方法的定义和调用
5.1.1 方法的基本概念和作用
方法类似于C语言中的函数,是包含特定功能的代码块,必须定义在类中、main 方法外部,本质上是一个造轮子的过程—— 将重复使用的逻辑包装成方法,需要时直接调用,无需重复编写。
5.1.2 方法的语法结构
// 完整格式:修饰符 → 返回值类型 → 方法名 → 参数列表 → 方法体
修饰符 返回值类型 方法名(参数列表){
// 方法体:实现功能的代码
return 返回值;// 如果返回值类型为 void,可以省略return或只写return
}方法的关键构成:
- 修饰符:用于控制方法的访问权限和特性,比如我们常见的
public static - 返回值类型:方法执行完毕后返回的数据类型,无返回值时使用 void
- 方法名:采用小驼峰命名法,要能只管表达其功能
- 参数列表:传递给方法的参数,可以看作是方法的输入,多个参数之间用逗号隔开
- 方法体:实现方法功能的代码块
- 方法签名:由方法名和参数列表共同组成的唯一标识,与返回值,修饰符无关
5.1.3 方法的调用
方法定义后是不会执行的,需要通过调用触发,不同类型的方法调用方式不同。当方法被调用后执行过程如下:
调用方法--->传递参数--->找到方法地址--->执行被调方法的方法体--->被调方法结束返回--->回到主调方法继续往下执行
- 调用静态方法(static修饰的方法)
静态方法属于类,不属于对象,调用时不需要创建对象,直接通过 类名.方法名调用
public class Dog {
// 定义静态方法
public static void eating(String food) {
System.out.println("小狗正在吃" + food);
}
}
class Test {
public static void main(String[] args) {
// 调用静态方法:类名.方法名(实参)
Dog.eating("狗粮");
}
}就如上面代码,在Dog类中定义了一个静态方法eating,包含了一个形参用于接收传来的实参数据,没有返回值。在Test类的main方法中调用静态方法eating,可以直接通过类名.方法名的方式调用,不需要创建对象
- 调用实例方法(无static修饰)
实例方法属于对象,必须先创建对象,再通过对象名.方法名调用
public class Sum {
// 定义实例方法(无 static)
public int add(int a, int b) {
return a + b;
}
}
class Test {
public static void main(String[] args) {
// 步骤1:创建对象
Sum sum = new Sum();
// 步骤2:调用实例方法:对象.方法名(实参)
int result = sum.add(3, 5);
System.out.println("两数之和:" + result); // 8
}
}就如上面代码,在Sum类中定义了一个实例方法add,然后在Test类中的main方法中调用add方法,需要先创建Sum的实例化对象,然后通过对象名.方法名调用
- 方法内部调用当前类的其他方法
本类中的方法可直接调用,不需要类名和对象
public class Sum {
public static int add(int a, int b) {
return a + b;
}
// 调用本类的 add 方法
public static int calculateTotal(int x, int y, int z) {
return add(x, y) + z; // 直接调用本类静态方法
}
public static void main(String[] args) {
System.out.println(calculateTotal(1, 2, 3)); // 6
}
}就如上面代码所示,在Sum类中定义了add方法和calculateTotal方法,并且calculateTotal方法中调用了add方法,由于这两个方法定义在同一个类中,所以可以通过方法名直接调用
5.1.4 方法的文档注释
一个规范的方法需添加文档注释,便于他人理解和工具生成文档。在定义好方法后在方法的上面一行使用文档注释,一般通过输入/**敲回车,就会生成参数模板,然后填写对应的说明即可。在添加文档注释后鼠标悬停在方法名上时会显示注释内容,大大提高代码的可读性

5.2 方法的参数传递
在 Java 中,参数传递只有值传递这一种方式,无论传递基本类型还是引用类型,传递的都是 “值的副本”,核心区别在于副本的含义不同。
5.2.1 基本数据类型的参数传递
基本数据类型的参数传递,传递的是变量值的副本,形参的修改不会影响实参。形参是指方法定义的参数,实参是指调用时传入的参数,就如下面案例中:Test类中定义了一个changeValue方法,无论传入的参数是什么最终都会改为10,在main方法中调用了changeValue方法并将实参a的值赋值给了形参num,最终输出a的值还是5。
public class Test {
public static void changeValue(int num) {
num = 10; // 修改的是副本的值,与实参无关
}
public static void main(String[] args) {
int a = 5;
changeValue(a); // 传入 a 的副本(5)
System.out.println("实参 a 的值:" + a); // 5(未改变)
}
}5.2.2 引用类型的参数传递
引用类型的参数传递,传递的是对象引用(地址)的副本,副本与原引用指向同一个对象,因此修改对象内容会影响原对象,但修改副本的地址不会影响原引用:
public class Test {
// 定义引用类型(数组)
public static void changeArray(int[] arr) {
System.out.println("arr地址: "+arr); // 实参的地址副本,与实参相同
arr[0] = 10; // 修改对象内容,会影响原数组
arr = new int[]{4,5,6}; // 修改副本地址:与原数组无关
System.out.println("修改后arr地址: "+arr); // 副本地址已改变
}
public static void main(String[] args) {
int[] array = {1,2,3};
System.out.println("array地址:"+array); // 数组的地址
changeArray(array); // 传入数组引用的副本
System.out.println("原数组第一个元素:" + array[0]); // 10(内容被修改)
System.out.println("最终array的地址:"+array); // 输出数组的地址
}
}
直接打印 array 输出的值是 [I@682a0b20[ 代表这是一个数组,I代表 int ,@ 为分隔符,6d03e736 为哈希值,可以理解为地址。
5.2.3 可变参数
当一个方法的参数个数不确定时,可以使用可变参数简化代码,语法格式为形参类型... 形参名,本质上就是一个数组,必须写在参数参数列表的最后面,因此一个方法只能有一个可变参数
public class VarargsExample {
// 可变参数语法:类型... 变量名(必须在参数列表最后)
public static int sum(int... nums) {
int total = 0;
for (int num : nums) { // 可变参数可遍历(本质是数组)
total += num;
}
return total;
}
public static void main(String[] args) {
System.out.println(sum(1,2)); // 3(传入2个参数)
System.out.println(sum(1,2,3,4)); // 10(传入4个参数)
System.out.println(sum()); // 0(传入0个参数)
System.out.println(sum(new int[]{5,6,7})); // 18(直接传入数组)
}
}5.3 方法的分类
方法可以从多个角度进行分类:是否有返回值、是否带参数、所属类别(方法与类,对象的关系)、访问权限、功能用途。便于理解不同场景的用法
5.3.1 按是否有返回值分类
- 带返回值的方法
在定义方法的时候指定方法的返回值类型,方法执行完毕后用return语句返回对应类型的数据
// 返回两个数的最大值
public static int getMax(int a, int b) {
return a > b ? a : b;
}- 无返回值的方法
使用 void 关键字表明方法不返回任何数据。在方法体中可以省略return 语句,或仅写return,用于方法的提前结束
// 打印欢迎信息(无返回值)
public static void printWelcome() {
System.out.println("欢迎学习 Java 方法!");
// return; // 可选,无实际意义
}
// 提前结束方法示例
public static void printNum(int num) {
if (num < 0) {
System.out.println("数字不能为负数!");
return; // 提前结束方法,后续代码不执行
}
System.out.println("数字:" + num);
}5.3.2 按是否带参数分类
- 带参数方法
方法定义时包含参数列表,调用时需传入对应类型和数量的实际参数,是参与形参必须一一对应
// 带参数:根据姓名打招呼
public static void greet(String name) {
System.out.println("Hello, " + name + "!");
}- 无参数方法
方法定义时没有参数,调用时无需传入任何数据
// 无参数:打印当前时间
public static void printCurrentTime() {
System.out.println("当前时间:" + System.currentTimeMillis());
}5.3.3 按所属类别分类
- 实例方法
没有使用static修饰的方法即为实例方法,调用其他类中的实例方法,或是静态方法中调用实例方法需要先创建对象,然后通过对象名来调用实例方法,如果是在同一个类中,实例方法调用实例方法可以通过方法名直接调用。每个对象都拥有自己的一份实例方法。实例方法可以访问实例成员变量和其他实例方法,也可以访问静态成员变量和静态方法。因为实例方法是在对象创建后才能调用,此时对象的实例成员已经存在。
public class InstanceMethodExample {
public static int staticVar = 10;
public int instanceVar = 20;
// 实例方法
public void instanceMethod() {
System.out.println("静态变量:" + staticVar); // 合法:访问静态变量
System.out.println("实例变量:" + instanceVar); // 合法:访问实例变量
staticMethod(); // 合法:访问静态方法
}
public static void staticMethod() {}
public static void main(String[] args) {
// 步骤1:创建对象
InstanceMethodExample obj = new InstanceMethodExample();
// 步骤2:调用实例方法
obj.instanceMethod();
}
}- 静态方法
静态方法是指由static修饰的方法,其归属于类而非实例,在访问权限允许的条件下可直接用类名.方法名调用,无需创建对象。静态方法中只能访问静态成员变量和其他静态方法,不能直接访问实例成员变量和实例方法,因为实例成员依赖于对象的创建,而静态方法调用时可能还没有对象存在。在类加载时就被加载到内存中,并且在整个程序运行期间一直存在。

public class StaticMethodExample {
// 静态变量
public static int staticVar = 10;
// 实例变量
public int instanceVar = 20;
// 实例方法
public void instanceMethod() {}
// 静态方法
public static void staticMethod() {
System.out.println("静态变量:" + staticVar); // 合法:访问静态变量
// System.out.println(instanceVar); // 编译错误:不能直接访问实例变量
// instanceMethod(); // 编译错误:不能直接访问实例方法
}
public static void main(String[] args) {
StaticMethodExample.staticMethod(); // 直接调用静态方法
}
}- 构造方法
构造方法的方法名和类名一样,并且没有返回值类型,主要用途是在创建对象时对成员变量进行初始化操作。当创建对象时,会根据参数列表自动调用相应的构造方法。当用户没有写构造方法时编译器会生成一个没有带参数的构造方法,而写了构造方法后不再生成。
public class Person {
private String name;
private int age;
// 1. 无参构造方法(手动添加,初始化默认值)
public Person() {
this.name = "未知"; // this 指代当前对象
this.age = 0;
}
// 2. 带参构造方法(初始化自定义值)
public Person(String name, int age) {
this.name = name; // 区分形参和实例变量(同名时用 this)
this.age = age;
}
// 3. 构造方法重载(参数列表不同)
public Person(String name) {
this.name = name;
this.age = 18; // 默认年龄18
}
// 打印个人信息
public void showInfo() {
System.out.println("姓名:" + name + ",年龄:" + age);
}
public static void main(String[] args) {
// 调用无参构造
Person p1 = new Person();
p1.showInfo(); // 姓名:未知,年龄:0
// 调用带参构造
Person p2 = new Person("张三", 20);
p2.showInfo(); // 姓名:张三,年龄:20
// 调用单参构造
Person p3 = new Person("李四");
p3.showInfo(); // 姓名:李四,年龄:18
}
}5.3.4 按访问权限分类
- 公共方法
使用 public 修饰,可以在任何类、任何包中被访问,通常用于定义类的对外接口,允许其他类自由调用,以实现类与类之间的交互和协作。
// 定义一个公共类
public class PublicMethodExample {
// 定义一个公共的静态方法
public static void publicStaticMethod() {
System.out.println("这是一个公共的静态方法。");
}
// 定义一个公共的实例方法
public void publicInstanceMethod() {
System.out.println("这是一个公共的实例方法。");
}
}
// 在另一个类中调用公共方法
class AnotherClass {
public static void main(String[] args) {
// 调用公共的静态方法
PublicMethodExample.publicStaticMethod();
// 创建对象
PublicMethodExample obj = new PublicMethodExample();
// 调用公共的实例方法
obj.publicInstanceMethod();
}
}上述代码中,PublicMethodExample 类中的 publicStaticMethodpublicInstanceMethod 都是公共方法,在 AnotherClass 中可以直接调用它们。
- 私有方法
使用 private 关键字修饰的方法是私有方法,只能在定义该方法的类内部被访问,其他类(包括子类)无法访问。主要用于封装类内部的实现细节,将一些不希望外部直接调用的逻辑隐藏起来,提高类的安全性和可维护性。
public class PrivateMethodExample {
// 定义一个私有方法
private void privateMethod() {
System.out.println("这是一个私有方法。");
}
// 定义一个公共方法,在公共方法中调用私有方法
public void callPrivateMethod() {
privateMethod();
}
}
class AntherClass{
public static void main(String[] args) {
PrivateMethodExample obj = new PrivateMethodExample();
// 通过公共方法间接调用私有方法
obj.callPrivateMethod();
// 以下代码会报错,因为外部无法直接调用私有方法
// obj.privateMethod();
}
}在这个例子中,privateMethod 是私有方法,只能在 PrivateMethodExample 类内部被调用,在其他类的 main 方法中不能直接调用该方法,只能通过公共方法 callPrivateMethod 间接调用。
- 受保护的方法
使用 protected 关键字修饰的方法是受保护的方法,可以在同一个包内的任何类中被访问,也可以在不同包的子类中被访问。用于在一定范围内共享方法,既允许同一个包内的类访问,又允许不同包的子类对其进行扩展和重写。
// 定义一个父类,包含受保护的方法
package com.example.parent;
public class ParentClass {
// 定义一个受保护的方法
protected void protectedMethod() {
System.out.println("这是一个受保护的方法。");
}
}
// 同一个包内的类调用受保护的方法
package com.example.parent;
class SamePackageClass {
public static void main(String[] args) {
ParentClass obj = new ParentClass();
// 同一个包内可以直接调用受保护的方法
obj.protectedMethod();
}
}
// 不同包的子类调用受保护的方法
package com.example.child;
import com.example.parent.ParentClass;
class ChildClass extends ParentClass {
public void callProtectedMethod() {
// 子类中可以调用受保护的方法
protectedMethod();
}
public static void main(String[] args) {
ChildClass child = new ChildClass();
child.callProtectedMethod();
}
}在上述代码中,ParentClass 中的 protectedMethod 是受保护的方法,SamePackageClass 和 ChildClass 都可以访问该方法,前者是因为在同一个包内,后者是因为它是 ParentClass 的子类。
- 默认方法
不使用任何访问修饰符的方法具有默认访问权限,也称为包访问权限。只能在定义该方法的的类所在的包的不同类中访问,不同包的类不能访问。用于在包内部实现类之间的交互和协作,将相关的功能封装在同一个包内。
// 定义一个类,包含默认访问权限的方法
package com.example.package1;
class DefaultMethodExample {
// 定义一个默认访问权限的方法
void defaultMethod() {
System.out.println("这是一个默认访问权限的方法。");
}
}
// 同一个包内的类调用默认访问权限的方法
package com.example.package1;
class SamePackageCaller {
public static void main(String[] args) {
DefaultMethodExample obj = new DefaultMethodExample();
// 同一个包内可以调用默认访问权限的方法
obj.defaultMethod();
}
}
// 不同包的类尝试调用默认访问权限的方法(会报错)
package com.example.package2;
// 以下代码无法通过编译,因为不同包无法访问默认访问权限的方法
// import com.example.package1.DefaultMethodExample;
// class DifferentPackageCaller {
// public static void main(String[] args) {
// DefaultMethodExample obj = new DefaultMethodExample();
// obj.defaultMethod();
// }
// }在这个例子中,DefaultMethodExample 类中的 defaultMethod 是默认访问权限的方法,SamePackageCaller 类可以调用该方法,因为它们在同一个包内,而不同包的类无法调用该方法。
5.3.5 按功能用途分类
5.3.5.1 抽象方法
使用 abstract 关键字修饰的方法,抽象方法是定义在抽象类或接口当中的方法,他只有方法的声明,没有方法体。如果一个类继承了包含抽象方法的抽象类,那么这个子类必须实现抽象类中的所有抽象方法,否则子类也必须被声明为抽象类。
// 定义抽象类
abstract class Animal {
// 抽象方法,描述动物发出声音的行为
public abstract void makeSound();
// 普通方法
public void sleep() {
System.out.println("动物在睡觉");
}
}
// 子类 Dog 继承自抽象类 Animal
class Dog extends Animal {
// 实现抽象方法
@Override
public void makeSound() {
System.out.println("狗汪汪叫");
}
}
// 子类 Cat 继承自抽象类 Animal
class Cat extends Animal {
// 实现抽象方法
@Override
public void makeSound() {
System.out.println("猫喵喵叫");
}
}
public class AbstractMethodExample {
public static void main(String[] args) {
// 父类引用指向子类对象
Animal dog = new Dog();
Animal cat = new Cat();
// 调用抽象方法,根据实际对象类型执行不同实现
dog.makeSound();
cat.makeSound();
// 调用普通方法
dog.sleep();
cat.sleep();
}
}抽象类 Animal:包含一个抽象方法 makeSound 和一个普通方法 sleep。由于 Animal 类包含抽象方法,所以它必须被声明为抽象类。子类 Dog 和 Cat:继承自 Animal 类,必须实现 makeSound 抽象方法,分别给出狗和猫发出声音的具体实现。main 方法:创建 Dog 和 Cat 对象,并使用 Animal 类型的引用指向它们。调用 makeSound 方法时,会根据实际对象的类型执行不同的实现,体现了多态性。同时,也可以调用普通方法 sleep。
5.3.5.2 重写方法
重写方法指的是在子类中定义一个与父类中方法具有相同方法名、参数列表和返回值类型的方法。通过重写,子类可以修改或扩展父类方法的行为。并且子类重写方法的访问权限不能比父类被重写方法的访问权限更严格,不能抛出比父类被重写方法更多或更宽泛的异常。静态方法不能被重写,只能构成隐藏
// 父类 Animal
class Animal {
// 父类方法
public void makeSound() {
System.out.println("动物发出声音");
}
// 带有返回值的父类方法
public Animal getSelf() {
return this;
}
}
// 子类 Dog 继承自 Animal
class Dog extends Animal {
// 重写父类的 makeSound 方法
@Override
public void makeSound() {
System.out.println("狗汪汪叫");
}
// 重写带有返回值的方法,使用协变返回类型
@Override
public Dog getSelf() {
return this;
}
}
// 测试类
public class MethodOverrideExample {
public static void main(String[] args) {
// 创建 Dog 对象
Dog dog = new Dog();
// 调用重写后的方法
dog.makeSound();
// 测试协变返回类型
Dog self = dog.getSelf();
System.out.println("返回的对象类型: " + self.getClass().getName());
}
}Animal 类:包含 makeSound 方法和 getSelf 方法。makeSound 方法用于输出动物发出的声音,getSelf 方法返回 Animal 类型的对象。Dog 类:继承自 Animal 类,重写了 makeSound 方法,输出狗的叫声。同时,重写了 getSelf 方法,使用协变返回类型,返回 Dog 类型的对象。MethodOverrideExample 类:在 main 方法中创建 Dog 对象,调用重写后的 makeSound 方法,并测试 getSelf 方法的协变返回类型。
5.3.5.3 重载方法
同一个类中,方法名相同但是参数列表不同的方法,参数列表的不同体现在参数类型,数量,顺序的不同,返回值类型和修饰符可以任意,仅靠返回值不同不能构成重载,核心作用是用同一方法名处理不同参数的场景,本质上就是使用方法签名来区分不同方法,回顾方法签名的概念:由方法名和参数列表共同组成的唯一标识,与返回值,修饰符无关
public class MethodOverloadingExample {
// 计算两个整数的和
public int add(int a, int b) {
return a + b;
}
// 计算三个整数的和
public int add(int a, int b, int c) {
return a + b + c;
}
// 计算两个双精度浮点数的和
public double add(double a, double b) {
return a + b;
}
public static void main(String[] args) {
MethodOverloadingExample example = new MethodOverloadingExample();
// 调用两个整数相加的方法
int result1 = example.add(1, 2);
System.out.println("两个整数的和: " + result1);
// 调用三个整数相加的方法
int result2 = example.add(1, 2, 3);
System.out.println("三个整数的和: " + result2);
// 调用两个双精度浮点数相加的方法
double result3 = example.add(1.5, 2.5);
System.out.println("两个双精度浮点数的和: " + result3);
}
}add(int a, int b):用于计算两个整数的和。
add(int a, int b, int c):用于计算三个整数的和,参数数量与第一个 add 方法不同。
add(double a, double b):用于计算两个双精度浮点数的和,参数类型与前两个 add 方法不同
5.5 递归
5.5.1 概念
递归是指在方法的内部调用自身的方法,递归包含两部分:递和归。相当于数学上的 "数学归纳法", 有一个结束条件, 然后有一个递推公式。
递:首先要递出去,找到递归条件,也就是推导公式,将大问题拆分为更小的同类问题,如 f(n) = f(n-1) + f(n-2)
归:最后要归回来,也就是结束条件。
就以计算 n 的阶层为例,n!= n * (n-1)*(n-2)...*1最终的结束条件就是n = 1
public static int faction(int n) {
// 结束条件 0!= 1,1!= 1
if (n == 0 || n == 1) {
return 1;
}
// 递推公式:n! = n * (n-1)!
return n * (n - 1);
}5.5.2 经典案例
- 斐波那契数列
斐波那契数列的规则是 f(1) = 1, f(2) = 1, f(n) = f(n-1) + f(n-2),由此我们可以得出结束条件 n ==1 || n==2时 f(n) = 1,递归公式为f(n) = f(n-1) + f(n-2)
public static int fibonacci(int n) {
if (n == 1 || n == 2) {
return 1; // 结束条件
}
return fibonacci(n - 1) + fibonacci(n - 2); // 递推公式
}到此这篇关于Java的方法详解的文章就介绍到这了,更多相关Java方法详解内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
