0
点赞
收藏
分享

微信扫一扫

「Java语言基础」方法的使用

DYBOY 07-15 21:00 阅读 14

Java中的方法

方法作为Java程序的基本构建块,掌握它们的使用是成为合格程序员的关键一步。现在开始探讨方法的各个方面,通过案例理解其运作机制。

方法的定义

方法是一段封装了特定功能的代码块,可以重复调用。定义方法时需要考虑五个要素:访问修饰符、返回类型、方法名、参数列表和方法体。例如:

public class MethodDemo {
    // 定义一个计算两个数之和的方法
    public static int addNumbers(int num1, int num2) {
        int sum = num1 + num2;
        return sum;
    }
}

在这个简单的加法方法中,public是访问修饰符表示公开访问,static表示属于类而非对象,int是返回类型,addNumbers是方法名,括号内的int num1, int num2是参数列表,大括号内是方法体。

方法的调用

定义了方法后,需要调用它才能执行其中的代码。对于静态方法,可以通过类名直接调用;对于实例方法,则需要通过对象实例调用。例如:

# 源文件保存为“MethodCallExample.java”
public class MethodCallExample {
    public static void main(String[] args) {
        // 调用静态方法
        int result = MethodDemo.addNumbers(5, 3);
        System.out.println("5 + 3 = " + result);
        
        // 实例方法调用示例
        Calculator calc = new Calculator();
        double area = calc.calculateCircleArea(2.5);
        System.out.println("半径为2.5的圆面积: " + area);
    }
}

class MethodDemo {
    // 定义一个计算两个数之和的方法
    public static int addNumbers(int num1, int num2) {
        int sum = num1 + num2;
        return sum;
    }
}

class Calculator {
    // 实例方法计算圆面积
    public double calculateCircleArea(double radius) {
        return Math.PI * radius * radius;
    }
}

运行结果

5 + 3 = 8
半径为2.5的圆面积: 19.634954084936208

方法重载

Java允许在同一个类中定义多个同名方法,要求它们的参数列表不同。这称为方法重载,是Java多态性的一种表现。

# 源文件保存为“OverloadDemo.java”
public class OverloadDemo {
    // 重载方法示例:不同类型的参数
    public void print(int num) {
        System.out.println("整数: " + num);
    }
    
    public void print(double num) {
        System.out.println("浮点数: " + num);
    }
    
    // 重载方法示例:不同数量的参数
    public void print(String str1, String str2) {
        System.out.println("字符串1: " + str1);
        System.out.println("字符串2: " + str2);
    }
    
    public static void main(String[] args) {
        OverloadDemo demo = new OverloadDemo();
        demo.print(10);       // 调用print(int)
        demo.print(3.14);     // 调用print(double)
        demo.print("Hello", "World"); // 调用print(String, String)
    }
}

运行结果

整数: 10
浮点数: 3.14
字符串1: Hello
字符串2: World

注意事项:重载方法时,仅返回类型不同不足以构成重载。比如同时定义int process()String process()会导致编译错误。方法重载应该基于参数差异,而不是返回值。

变量作用域

Java中的变量作用域决定了变量的可见性和生命周期。理解作用域对编写健壮代码至关重要。

# 源文件保存为“ScopeExample.java”
public class ScopeExample {
    // 类变量/静态变量 - 整个类可见
    static int classVar = 100;
    
    // 实例变量 - 每个实例独有
    int instanceVar = 200;
    
    public void demoMethod(int param) { // 参数作用域是整个方法
        int localVar = 300; // 局部变量 - 仅在方法内可见
        
        if(true) {
            int blockVar = 400; // 块级变量 - 仅在if块内可见
            System.out.println("块内访问所有变量:");
            System.out.println(blockVar + localVar + param + instanceVar + classVar);
        }
        
        // System.out.println(blockVar); // 错误!blockVar不可见
    }
    
    public static void main(String[] args) {
        ScopeExample example = new ScopeExample();
        example.demoMethod(50);
        
        // System.out.println(localVar); // 错误!localVar不可见
        System.out.println("类变量: " + ScopeExample.classVar);
        System.out.println("实例变量: " + example.instanceVar);
    }
}

运行结果

1050
类变量: 100
实例变量: 200

注意事项:变量遮蔽是常见问题,当局部变量与实例变量同名时,局部变量会遮蔽实例变量。可以使用this关键字访问被遮蔽的实例变量。另一个常见错误是试图在变量作用域外访问它,比如在if块外访问块内定义的变量。

案例解析

简易计算器

编写一个程序,构建一个简易计算器.

# 源文件保存为“SimpleCalculator.java”
public class SimpleCalculator {
    // 类变量记录使用次数
    private static int usageCount = 0;
    
    // 实例变量存储计算结果
    private double lastResult;
    
    // 方法重载:整数加法
    public int add(int a, int b) {
        usageCount++;
        lastResult = a + b;
        return (int)lastResult;
    }
    
    // 方法重载:浮点数加法
    public double add(double a, double b) {
        usageCount++;
        lastResult = a + b;
        return lastResult;
    }
    
    // 方法重载:三个数相加
    public int add(int a, int b, int c) {
        return add(add(a, b), c);
    }
    
    // 获取使用次数
    public static int getUsageCount() {
        return usageCount;
    }
    
    // 获取最后结果
    public double getLastResult() {
        return lastResult;
    }
    
    public static void main(String[] args) {
        SimpleCalculator calc = new SimpleCalculator();
        
        System.out.println("5 + 7 = " + calc.add(5, 7));
        System.out.println("3.2 + 4.8 = " + calc.add(3.2, 4.8));
        System.out.println("2 + 3 + 4 = " + calc.add(2, 3, 4));
        
        System.out.println("计算器使用次数: " + SimpleCalculator.getUsageCount());
        System.out.println("最后计算结果: " + calc.getLastResult());
    }
}

运行结果

5 + 7 = 12
3.2 + 4.8 = 8.0
2 + 3 + 4 = 9
计算器使用次数: 4
最后计算结果: 9.0

这个案例展示了类变量与实例变量的区别,方法重载的实际应用,以及如何通过方法组织代码逻辑。usageCount被所有实例共享,而lastResult是每个实例独有的。三个add方法展示了基于参数类型和数量的重载。

常见错误及解决

  • 方法签名冲突:定义了两个仅返回类型不同的方法。解决方法确保重载方法的参数列表不同。
  • 变量作用域混淆:在方法外使用局部变量。检查变量声明位置,确保在正确的作用域内使用。
  • 静态上下文错误:在静态方法中访问实例成员。要么将方法改为实例方法,要么通过对象实例访问实例成员。
  • 参数传递误解:以为修改参数会影响原始变量。Java是值传递,对于基本类型,方法内修改不会影响原始值。
  • 忽略返回值:调用有返回值的方法却没有使用结果。如果不需要返回值,考虑将方法改为void返回类型。

练习题

理论题

  1. 下面代码有什么问题?如何修正?

    public class Test {
        public void print(int x) {
            System.out.println(x);
        }
        
        public int print(int y) {
            return y * 2;
        }
    }
    

    参考答案 两个print方法具有相同的参数列表(int),仅返回类型不同,这不是有效的重载。修正方法是修改参数列表,例如将第二个方法改为public int print(int y, int z)

  2. 分析变量作用域:类变量、实例变量、局部变量和块级变量在内存分配和生命周期上有何不同? 参考答案 在Java中,变量作用域的关键差异总结如下

    变量类型 声明位置 内存区域 生命周期 作用域
    类变量<br>(静态变量) 类中 + static修饰 方法区 类加载时创建 → 程序结束销毁 整个类(所有实例共享)
    实例变量<br>(成员变量) 类中(无static 堆内存 对象创建时产生 → 对象被GC回收时销毁 对象实例内部
    局部变量 方法/构造函数内部 栈内存 方法开始执行 → 方法执行结束 声明位置到方法结束
    块级变量 {}代码块内部<br>(如if/for) 栈内存 进入代码块 → 退出代码块 声明位置到代码块结束

    类变量:所有实例共享同一内存空间; 不受对象实例生命周期影响 。 实例变量:个对象拥有独立副本;生命周期绑定对象,new MyClass() → GC回收
    局部变量 & 块级变量:共同点:存储在栈内存 + 自动销毁;局部变量生命周期 > 块级变量(方法包含多个代码块时) 本质规律

    • 生命周期:类变量 > 实例变量 > 局部变量 > 块级变量
    • 内存开销:实例变量(堆) > 类变量(方法区) > 局部/块级(栈)
    • 共享性:类变量(共享) > 实例变量(对象独享) > 局部/块级(不可共享)
  3. 解释方法调用时参数传递的机制,基本类型和对象引用在传递时有何区别? 参考答案 在Java中,方法调用采值传递(Pass by Value) 机制。基本类型和对象引用的区别如下:

    • 基本类型(Primitive Types):传递值的副本;方法内操作的是原始数据的独立拷贝;方法内的修改不影响原始变量
  • 对象引用(Object References):传递**引用的副本**;通过副本引用修改对象状态(影响原始对象)
    

    基本类型和对象引用在传递时区别总结表

    特性 基本类型 对象引用
    传递内容 实际值的拷贝(如 10 内存地址的拷贝(如 0x1001
    内存影响 完全隔离 共享同一对象
    修改效果 不影响原始数据 可修改对象状态
    重新赋值效果 形参修改不影响实参(安全) 形参重定向不影响实参指向
    类比 复印文件后修改复印件 给朋友家门钥匙(能改家具陈设)

实践题

  1. 编写一个工具类,包含以下重载方法:

    • 将一个整数数组转换为字符串,用指定分隔符连接
    • 将一个字符串数组转换为字符串,用默认逗号连接
    • 将三个整数用"a-b-c"格式连接

    参考答案

    # 源文件保存为“StringUtils.java”
    public class StringUtils {
        // 方法1: 整型数组 + 自定义分隔符
        public static String join(int[] array, String delimiter) {
            if (array == null) return "";
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < array.length; i++) {
                sb.append(array[i]);
                if (i < array.length - 1) {
                    sb.append(delimiter);
                }
            }
            return sb.toString();
        }
    
        // 方法2: 字符串数组 + 默认逗号分隔符
        public static String join(String[] array) {
            return join(array, ",");
        }
    
        // 字符串数组通用方法(支持任意分隔符)
        public static String join(String[] array, String delimiter) {
            if (array == null) return "";
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < array.length; i++) {
                if (array[i] != null) {
                    sb.append(array[i]);
                    if (i < array.length - 1) {
                        sb.append(delimiter);
                    }
                }
            }
            return sb.toString();
        }
    
        // 方法3: 三个整数 + 固定格式连接
        public static String join(int a, int b, int c) {
            return a + "-" + b + "-" + c;
        }
    
        // 测试用例
        public static void main(String[] args) {
            // 测试整数数组转换
            int[] numbers = {1, 2, 3, 4};
            System.out.println("整数数组测试:");
            System.out.println("默认分隔符: " + join(numbers, ", "));  // 1, 2, 3, 4
            System.out.println("自定义分隔符: " + join(numbers, " | ")); // 1 | 2 | 3 | 4
            System.out.println("空数组: " + join(new int[0], "-"));     // 空行
    
            // 测试字符串数组转换
            String[] languages = {"Java", "Python", "C++", null};
            System.out.println("\n字符串数组测试:");
            System.out.println("默认逗号连接: " + join(languages));       // Java,Python,C++
            System.out.println("自定义分隔符: " + join(languages, " => ")); // Java => Python => C++
            System.out.println("空数组: " + join(new String[0]));          // 空行
            System.out.println("Null数组: " + join(null));                 // 空行
    
            // 测试三整数连接
            System.out.println("\n三整数连接测试:");
            System.out.println("结果: " + join(10, 20, 30));  // 10-20-30
            System.out.println("零值: " + join(0, -5, 100));  // 0--5-100
        }
    }
    

    运行结果

    整数数组测试:
    默认分隔符: 1, 2, 3, 4
    自定义分隔符: 1 | 2 | 3 | 4
    空数组: 
    
    字符串数组测试:
    默认逗号连接: Java,Python,C++,
    自定义分隔符: Java => Python => C++ => 
    空数组: 
    Null数组: 
    
    三整数连接测试:
    结果: 10-20-30
    零值: 0--5-100
    
  2. 创建一个银行账户类,包含:

    • 类变量记录总账户数
    • 实例变量存储余额
    • 存款和取款方法(考虑余额不足情况)
    • 静态方法获取总账户数

    参考答案

    # 源文件保存为“BankAccount.java”
    public class BankAccount {
        // 类变量:记录总账户数
        private static int totalAccounts = 0;
    
        // 实例变量:账户属性和余额
        private final String accountNumber;
        private double balance;
        private final String accountHolder;
    
        // 构造函数:创建新账户
        public BankAccount(String accountNumber, String accountHolder, double initialBalance) {
            this.accountNumber = accountNumber;
            this.accountHolder = accountHolder;
            this.balance = initialBalance;
    
            // 增加总账户数(类变量)
            totalAccounts++;
        }
    
        // 存款方法(带异常检查)
        public void deposit(double amount) {
            if (amount <= 0) {
                throw new IllegalArgumentException("存款金额必须大于零");
            }
            balance += amount;
        }
    
        // 取款方法(带余额不足检查)
        public void withdraw(double amount) throws InsufficientFundsException {
            if (amount <= 0) {
                throw new IllegalArgumentException("取款金额必须大于零");
            }
            if (amount > balance) {
                throw new InsufficientFundsException("余额不足,可用余额: " + balance);
            }
            balance -= amount;
        }
    
        // 获取余额
        public double getBalance() {
            return balance;
        }
    
        // 获取账户信息
        public String getAccountInfo() {
            return String.format("账号: %s | 户主: %s | 余额: $%.2f",
                    accountNumber, accountHolder, balance);
        }
    
        // 静态方法:获取总账户数
        public static int getTotalAccounts() {
            return totalAccounts;
        }
    
        // 自定义异常:余额不足
        public static class InsufficientFundsException extends Exception {
            public InsufficientFundsException(String message) {
                super(message);
            }
        }
    
        // 测试代码
        public static void main(String[] args) {
            try {
                // 创建账户
                BankAccount account1 = new BankAccount("ACC001", "张三", 1000.0);
                BankAccount account2 = new BankAccount("ACC002", "李四", 500.0);
    
                System.out.println("--- 操作前信息 ---");
                System.out.println(account1.getAccountInfo());
                System.out.println(account2.getAccountInfo());
                System.out.println("总账户数: " + BankAccount.getTotalAccounts());
    
                // 执行操作
                account1.deposit(200.0);
                account2.withdraw(150.0);
    
                System.out.println("\n--- 操作后信息 ---");
                System.out.println(account1.getAccountInfo());
                System.out.println(account2.getAccountInfo());
    
                // 测试异常情况
                System.out.println("\n--- 测试异常 ---");
                account2.withdraw(1000.0); // 余额不足异常
            } catch (InsufficientFundsException e) {
                System.out.println("取款失败: " + e.getMessage());
            } catch (IllegalArgumentException e) {
                System.out.println("参数错误: " + e.getMessage());
            }
        }
    }
    

    运行结果

    --- 操作前信息 ---
    账号: ACC001 | 户主: 张三 | 余额: $1000.00
    账号: ACC002 | 户主: 李四 | 余额: $500.00
    总账户数: 2
    
    --- 操作后信息 ---
    账号: ACC001 | 户主: 张三 | 余额: $1200.00
    账号: ACC002 | 户主: 李四 | 余额: $350.00
    
    --- 测试异常 ---
    取款失败: 余额不足,可用余额: 350.0
    
  3. 调试以下代码,找出作用域问题并修正:

    public class ScopeProblem {
        int value = 10;
        
        public void print() {
            int value = 20;
            System.out.println(value);
        }
        
        public void printValue() {
            System.out.println(value);
        }
        
        public static void main(String[] args) {
            ScopeProblem sp = new ScopeProblem();
            sp.print();
            sp.printValue();
        }
    }
    

    参考答案 代码中存在变量遮蔽问题,实例变量value被局部变量value遮蔽。print()方法中的value指局部变量(输出20),printValue()中的value指实例变量(输出10)。若想访问被遮蔽的实例变量,可修改print()为:

    public void print() {
        int value = 20;
        System.out.println(this.value); // 明确指定实例变量
    }
    
举报

相关推荐

0 条评论