因重载方法使用不当导致报错

因重载而容易导致错误的例子

在Java中,方法重载可能会导致一些容易出错的情况,特别是当参数不匹配时。以下是一些类似于setScale因重载而容易导致错误的例子:

  1. String.substring(int) 和 String.substring(int, int)

    这两个方法都返回字符串的子串。但是,如果错误地传递了一个参数,而本意是传递两个参数,编译器不会报错(因为一个参数的方法存在),但运行时结果可能不符合预期。

    示例:

    java

    复制代码
    String str = "Hello, World!";
    // 本意可能是获取从索引7开始到结尾的子串,但错误地使用了两个参数的方法
    String sub1 = str.substring(7); // 正确,返回 "World!"
    String sub2 = str.substring(7, 12); // 返回 "World"

    注意:这里虽然不会报错,但可能因为理解错误而使用错误的方法。

  2. Thread.sleep(long) 和 TimeUnit.sleep(long)
    Thread.sleep(long millis) 接受毫秒数,而TimeUnitsleep方法接受对应时间单位的数值。如果混淆了单位,可能会导致休眠时间错误。

    示例:

    java

    复制代码
    // 休眠1秒
    Thread.sleep(1000); // 1000毫秒
    TimeUnit.SECONDS.sleep(1); // 1秒,这里如果传递1000,就会休眠1000秒

    但是,注意TimeUnit.sleep实际上内部调用了Thread.sleep,只是转换了单位。如果错误地传递了一个没有单位的值,可能会得到意想不到的结果。

  3. ArrayList的构造方法
    ArrayList有两个构造方法:ArrayList(int initialCapacity)ArrayList(Collection<? extends E> c)。如果传递一个整数,本意是创建一个具有指定容量的列表,但错误地传递了一个整数,而该整数被解释为初始容量,而不是集合中的元素。

    示例:

    java

    复制代码
    // 本意可能是创建一个包含数字1的列表,但实际上创建了一个初始容量为1的空列表
    List<Integer> list1 = new ArrayList<>(1); // 初始容量为1的空列表
    List<Integer> list2 = new ArrayList<>(Arrays.asList(1)); // 包含一个元素1的列表
  4. BigDecimal.divide(BigDecimal) 和 BigDecimal.divide(BigDecimal, int, RoundingMode)

    类似于setScaledivide方法也有多个重载。如果不指定舍入模式,当除法结果是无限小数(非终止小数)时,会抛出ArithmeticException

    示例:

    java

    复制代码
    BigDecimal a = new BigDecimal("1");
    BigDecimal b = new BigDecimal("3");
    // 错误:没有指定舍入模式,结果为无限小数,抛出ArithmeticException
    // BigDecimal result = a.divide(b); 
    // 正确:指定舍入模式
    BigDecimal result = a.divide(b, 2, RoundingMode.HALF_UP); // 0.33
  5. String.format(String, Object...) 和 String.format(Locale, String, Object...)

    如果本意是想使用特定的区域设置,但错误地使用了只有一个字符串参数的方法,那么将使用默认区域设置,可能导致格式化结果不符合预期(如日期、数字格式)。

    示例:

    java

    复制代码
    // 使用默认区域设置(可能不是期望的)
    String s1 = String.format("%.2f", 123.456); // 123.46
    // 使用特定的区域设置(例如,德国使用逗号作为小数点)
    String s2 = String.format(Locale.GERMANY, "%.2f", 123.456); // 123,46
  6. Arrays.asList(T... a)

    这个方法返回一个固定大小的列表,但是要注意,如果传递一个基本类型的数组,它会被当作一个对象,而不是多个元素。

    示例:

    java

    复制代码
    int[] intArray = {1, 2, 3};
    // 错误:这里Arrays.asList(intArray) 会返回一个List<int[]>,而不是List<Integer>
    List<int[]> list1 = Arrays.asList(intArray);
    // 正确:使用包装类型数组
    Integer[] integerArray = {1, 2, 3};
    List<Integer> list2 = Arrays.asList(integerArray);
  7. Integer.valueOf(String) 和 Integer.valueOf(String, int)

    第一个方法使用十进制解析字符串,第二个方法可以指定基数。如果混淆了,可能导致解析错误。

    示例:

    java

    复制代码
    // 错误:本意是想解析二进制字符串,但使用了十进制解析,导致NumberFormatException
    // Integer num1 = Integer.valueOf("1010"); // 解析为1010,但如果是二进制字符串,本意是10
    // 正确:指定基数为2
    Integer num2 = Integer.valueOf("1010", 2); // 10
  8. MessageFormat.format(String, Object...) 和 String.format(String, Object...)

    这两个方法很容易混淆,因为它们都用于格式化,但模式字符串的语法不同。如果错误地使用了另一个的语法,会导致格式化错误或异常。

    示例:

    java

    复制代码
    // 使用MessageFormat
    String result1 = MessageFormat.format("Hello {0}", "World"); // Hello World
    // 使用String.format
    String result2 = String.format("Hello %s", "World"); // Hello World
    // 如果混淆了占位符,就会出错
    // String result3 = String.format("Hello {0}", "World"); // 不会替换,输出 Hello {0}

这些例子都展示了由于方法重载,在调用时如果选择了错误的方法或参数,可能会导致运行时错误或不符合预期的结果。因此,在调用重载方法时,要仔细检查参数类型和数量,确保调用了正确的方法。

重载方法导致错误的原因

我们以BigDecimal.setScale为例,详细讲解重载方法导致错误的原因,并类比其他常见例子。

首先,理解重载(Overload)是指同一个类中可以有多个方法名相同,但参数列表不同(参数类型、个数、顺序不同)的方法。

当调用一个重载方法时,编译器会根据传入的参数来决定具体调用哪个方法。如果传入的参数与任何一个重载方法的参数列表不匹配,就会编译错误。但是,有时候参数类型匹配上了,却因为重载方法的不同行为导致运行时错误。

例子1:BigDecimal.setScale

BigDecimal.setScale(int newScale) 和 BigDecimal.setScale(int newScale, int roundingMode) 是两个重载方法。

第一个方法:setScale(int newScale)

这个方法要求新设置的小数位数必须和原来的小数位数一致,或者通过补零可以达到,否则会抛出ArithmeticException(如果精度损失,即需要舍入)。

例如:原来数字是1.23,调用setScale(3)会变成1.230,这是可以的。但是如果原来数字是1.234,调用setScale(3)则不会抛异常(因为小数位数已经是3)。但是原来数字是1.2345,调用setScale(3)就会抛异常,因为需要舍入(1.2345->1.235)但不知道舍入模式。

第二个方法:setScale(int newScale, int roundingMode)

这个方法允许指定舍入模式,因此可以处理需要舍入的情况。

所以,当你使用setScale减少小数位数(比如从4位小数变成3位)时,必须使用第二个方法指定舍入模式,否则就会抛出异常。

类比其他例子:

例子2:String.substring

String.substring(int beginIndex) 和 String.substring(int beginIndex, int endIndex)

第一个方法:从beginIndex开始到字符串末尾。

第二个方法:从beginIndex开始到endIndex(不包括)。

常见错误:想要截取一段字符串,但错误地使用了只有一个参数的方法,导致截取到了末尾。

例子3:List的remove方法

List接口有两个remove方法:

remove(int index) 按索引删除

remove(Object o) 按元素删除

如果你有一个List<Integer>,那么调用remove(1)会删除索引为1的元素,而不是删除元素1。如果你想删除元素1,应该调用remove(Integer.valueOf(1))。

例子4:Arrays.asList

Arrays.asList(T... a) 返回一个固定大小的列表。但是,如果传入一个基本类型的数组,比如int[],那么整个数组会被当作一个元素,因为泛型T不能是基本类型。所以,如果你想将一组基本类型转换为列表,应该使用包装类型数组。

如何避免这类错误?

  1. 仔细阅读API文档,了解每个重载方法的具体行为。

  2. 使用IDE时,注意查看方法签名,确保调用的是正确的方法。

  3. 对于可能产生混淆的方法,可以使用一些技巧,比如对于List的remove,当使用包装类型时,可以使用remove(Integer.valueOf(1))来明确意图。

  4. 在代码中,如果参数含义不明确,可以使用注释或者将参数赋值给有意义的变量名,然后再传递。

总结:重载方法虽然方便,但使用时需要谨慎,确保传入的参数和期望调用的方法匹配,并且理解每个重载方法的行为差异。

关于Java重载方法使用不当导致的常见错误

一、什么是方法重载?

方法重载 就像在同一个班级里有多个叫"小明"的学生,我们需要通过他们的特征来区分:

java

复制代码
// 举例:同样是"打印"功能,但根据不同的"特征"(参数类型)区分
public class Printer {
    // 打印整数
    public void print(int number) {
        System.out.println("整数:" + number);
    }
    
    // 打印字符串
    public void print(String text) {
        System.out.println("文本:" + text);
    }
    
    // 打印整数和字符串
    public void print(int number, String text) {
        System.out.println(number + ":" + text);
    }
}

二、BigDecimal.setScale() 的经典错误案例

错误场景重现

想象一下,你有一根长度为123.456米的绳子,现在需要把它精确到厘米(只保留两位小数)。

java

复制代码
import java.math.BigDecimal;

public class ScaleExample {
    public static void main(String[] args) {
        // 场景1:绳子长度123.456米,要精确到厘米(123.46米)
        BigDecimal rope = new BigDecimal("123.456");
        
        // ❌ 错误做法:只说"保留两位小数",没说"四舍五入"还是"直接截断"
        rope.setScale(2);  // 报错:需要指定舍入模式
        
        // ✅ 正确做法:明确告诉计算机如何处理
        rope.setScale(2, BigDecimal.ROUND_HALF_UP);  // 四舍五入:123.46
        rope.setScale(2, BigDecimal.ROUND_DOWN);     // 直接截断:123.45
    }
}

为什么必须指定舍入模式?

text

复制代码
原始数字:123.456
保留2位小数后:123.45?(最后一位不确定)

计算机不知道你的业务需求:
1. 四舍五入? → 123.46
2. 直接舍去? → 123.45  
3. 向上取整? → 123.46
4. 向下取整? → 123.45

如果不告诉它,它就会报错:"我不知道该怎么处理!"

三、其他容易出错的重载方法案例

案例1:List的remove()方法 - "按位置删"还是"按内容删"?

java

复制代码
import java.util.ArrayList;
import java.util.List;

public class RemoveExample {
    public static void main(String[] args) {
        List<Integer> scores = new ArrayList<>();
        scores.add(90);
        scores.add(100);
        scores.add(80);
        
        // ❌ 容易混淆的地方:
        scores.remove(100);  // 你以为:删除分数100
        // 实际:删除索引100位置的元素 → 报错:IndexOutOfBoundsException
        
        // ✅ 正确做法:
        // 删除索引1的元素(第二个元素)
        scores.remove(1);  // 删除100?不对!删除的是索引1的元素(100)
        
        // 删除值为100的元素
        scores.remove(Integer.valueOf(100));  // 这才是删除分数100
    }
}

通俗解释

  • remove(1) → 就像说"把第1个学生叫出来"(按位置)

  • remove(Integer.valueOf(100)) → 就像说"把考100分的学生叫出来"(按内容)

案例2:String.substring() - "从哪到哪"还是"从哪到最后"?

java

复制代码
public class SubstringExample {
    public static void main(String[] args) {
        String address = "北京市海淀区中关村";
        
        // 方法1:substring(开始位置)
        String part1 = address.substring(3);  // "海淀区中关村"
        
        // 方法2:substring(开始位置, 结束位置)
        String part2 = address.substring(3, 5);  // "海淀"
        
        // ❌ 常见错误:参数传反了
        String wrong = address.substring(5, 3);  // 报错:开始位置不能大于结束位置
        
        // ❌ 另一个错误:忘记结束位置是不包含的
        // 想要"北京市"应该是(0, 3),不是(0, 2)
        String beijing = address.substring(0, 3);  // "北京市" ✓
        String wrongBeijing = address.substring(0, 2);  // "北京" ✗
    }
}

案例3:Arrays.asList() - "多个元素"还是"一个数组"?

java

复制代码
import java.util.Arrays;
import java.util.List;

public class AsListExample {
    public static void main(String[] args) {
        // ❌ 错误理解:以为把数组变成了列表
        int[] numbers = {1, 2, 3};
        List<int[]> wrongList = Arrays.asList(numbers);  
        // 实际上得到的是:List<int[]>,只有一个元素(整个数组)
        
        System.out.println(wrongList.size());  // 输出:1,不是3!
        System.out.println(wrongList.get(0));  // 输出:[I@...(数组地址)
        
        // ✅ 正确做法1:使用包装类型
        Integer[] scores = {90, 95, 100};
        List<Integer> scoreList = Arrays.asList(scores);  // 正确:3个元素
        
        // ✅ 正确做法2:直接列出元素
        List<String> names = Arrays.asList("张三", "李四", "王五");
    }
}

四、如何避免这类错误?

方法1:使用IDE的提示功能

java

复制代码
// 在IDE中,当你输入"rope.setScale("时:
// IDE会显示:
// 1. setScale(int newScale)
// 2. setScale(int newScale, int roundingMode)
// 3. setScale(int newScale, RoundingMode roundingMode)

// 把鼠标悬停在方法上,会显示文档说明

方法2:写注释明确意图

java

复制代码
// ❌ 不明确的代码
list.remove(1);

// ✅ 明确的代码
// 删除索引为1的元素
list.remove(1);

// 或者
int indexToRemove = 1;
list.remove(indexToRemove);

方法3:使用有意义的变量名

java

复制代码
// ❌ 不清晰的代码
BigDecimal value = getPrice();
value.setScale(2, 4);  // 2是什么?4是什么?

// ✅ 清晰的代码
BigDecimal price = getPrice();
int decimalPlaces = 2;
RoundingMode roundingMode = RoundingMode.HALF_UP;
price.setScale(decimalPlaces, roundingMode);

方法4:查看官方文档

遇到不确定的方法时:

  1. 按F1查看文档

  2. 或访问:https://docs.oracle.com/javase/8/docs/api/

五、快速记忆口诀

text

复制代码
重载方法看参数,类型顺序要记住。
整数小数易混淆,明确意图是出路。
IDE提示多利用,变量命名要清楚。
遇到报错莫慌张,查查文档有好处。

六、练习:找出错误

java

复制代码
import java.util.*;

public class Quiz {
    public static void main(String[] args) {
        // 题目1:下面代码有什么问题?
        BigDecimal money = new BigDecimal("99.995");
        money.setScale(2);  // 问题:______________
        
        // 题目2:下面代码会输出什么?
        List<Integer> list = new ArrayList<>();
        list.add(1);
        list.add(2);
        list.add(3);
        list.remove(2);  // 删除的是:______
        System.out.println(list);  // 输出:______
        
        // 题目3:下面代码会报错吗?
        String text = "HelloWorld";
        String result = text.substring(5, 5);  // 结果:______
    }
}

答案

  1. 需要指定舍入模式,否则会抛异常(99.995四舍五入后是100.00,但计算机不知道要不要进位)

  2. 删除的是索引2的元素(3),输出:[1, 2]

  3. 不会报错,返回空字符串(从位置5开始,到位置5结束,没有字符)

记住:Java很严谨,需要你明确告诉它每一步要做什么,它不会"猜"你的意图!

相关推荐
zore_c6 小时前
【C语言】Win 32 API——一部分内容详解!!!
c语言·开发语言·c++·经验分享·笔记
郑州光合科技余经理6 小时前
定制开发实战:海外版外卖系统PHP全栈解决方案
java·服务器·开发语言·javascript·git·uni-app·php
郝学胜-神的一滴6 小时前
Linux线程编程:从原理到实践
linux·服务器·开发语言·c++·程序人生·设计模式·软件工程
XiaoHu02076 小时前
C++的IO流
开发语言·c++
m0_471199636 小时前
【场景】笛卡尔积
开发语言·前端·javascript
Brookty7 小时前
Java并发编程核心的基础知识
java·开发语言·java-ee·多线程·线程安全
hellotutu7 小时前
Java 读取 Excel 文件
java·开发语言·excel
胡萝卜3.07 小时前
构建安全的C++内存管理体系:从RAII到智能指针的完整解决方案
运维·开发语言·c++·人工智能·安全·智能指针·raii
MSTcheng.7 小时前
【C++】如何快速实现一棵支持key或key-value的二叉搜索树?关键技巧一文掌握!
开发语言·c++·算法·二叉搜索树