01方法课后作业

Java 第二讲《方法》:动手动脑问题与课后实验整理


目录

  1. 动手动脑 1:手写线性同余随机数发生器

  2. 动手动脑 2:MethodOverLoad 到底特殊在哪?(方法重载)

  3. 课堂练习:观察 System.out.println ()

  4. 思考:n! 的阶乘结果怎么会是负数?(int 溢出)

  5. 思考:为什么 i == j 输出 true?(浮点数比较)

  6. 课后实验:自动四则运算出题程序


1. 动手动脑 1:手写线性同余随机数发生器

题目

课件给出了一种完全 "手写代码" 实现随机数的算法 —— 线性同余法(LCG),参数如下:

  • Modulus(模数):M = 2³¹ − 1 = Integer.MAX_VALUE = 2147483647

  • Multiplier(乘数):A = 7⁵ = 16807

  • Increment(增量):C = 0

要求:编写一个方法,使用以上算法生成指定数目(比如 1000 个)的随机整数。

原理

线性同余公式:

next = (A × seed + C) mod M

每次以上一次的结果作为新的种子(seed)代入,即可产生一个伪随机序列。该序列要经过约 2³¹−1 个数之后才会重复。

代码

public class LCGRandom {
    private long seed;
    private static final long M = 2147483647L; // 2^31 - 1
    private static final long A = 16807L;       // 7^5
    private static final long C = 0L;

    public LCGRandom(long seed) {
        this.seed = seed;
    }

    /** 生成下一个伪随机整数 */
    public int nextInt() {
        seed = (A * seed + C) % M;
        return (int) seed;
    }

    /** 一次生成 count 个随机整数 */
    public int[] generate(int count) {
        int[] arr = new int[count];
        for (int i = 0; i < count; i++) {
            arr[i] = nextInt();
        }
        return arr;
    }

    public static void main(String[] args) {
        // 以当前时间为种子,避免每次运行结果相同
        LCGRandom rand = new LCGRandom(System.currentTimeMillis());
        int[] data = rand.generate(10); // 试生成 10 个
        for (int x : data) {
            System.out.print(x + "  ");
        }
    }
}

2. 动手动脑 2:MethodOverLoad 到底特殊在哪?(方法重载)

代码

public class MethodOverLoad {
    // 三个方法都叫 add,但参数列表不同
    public static int add(int a, int b) {
        System.out.println("调用的是 add(int, int)");
        return a + b;
    }

    public static double add(double a, double b) {
        System.out.println("调用的是 add(double, double)");
        return a + b;
    }

    public static int add(int a, int b, int c) {
        System.out.println("调用的是 add(int, int, int)");
        return a + b + c;
    }

    public static void main(String[] args) {
        System.out.println(add(1, 2));          // 两个 int
        System.out.println(add(1.5, 2.5));     // 两个 double
        System.out.println(add(1, 2, 3));       // 三个 int
    }
}

特殊之处在于:方法名相同,但参数列表不同,这就是 Java 的方法重载(Overload)。

方法重载的判断要点

两个或多个方法构成重载,必须同时满足:

  1. 方法名相同;

  2. 参数列表不同:参数类型不同、参数个数不同,或者参数类型的顺序不同。

注意:

方法的返回值类型不作为重载的判断条件

仅返回值不同而参数列表相同,编译器会直接报错。

调用时编译器会根据你传入的实参类型和个数,自动匹配到最合适的那个重载版本。


3. 课堂练习:观察 System.out.println ()

题目

查看一下 JDK 中 System.out.println() 方法,你发现了什么?

答案

打开 java.io.PrintStream 的源码就会发现,println 被重载了很多次,例如:

public void println()                 // 只换行
public void println(int x)
public void println(long x)
public void println(double x)
public void println(float x)
public void println(boolean x)
public void println(char x)
public void println(char[] x)
public void println(String x)
public void println(Object x)

也就是说,无论你传给它的是整数、浮点数、字符串还是一个对象,都能正确打印 —— 这正是方法重载在 JDK 中的典型应用:用同一个方法名,适配不同类型的参数。


4. 思考:n! 的阶乘结果怎么会是负数?(int 溢出)

现象

课件演示 CalculateN 用递归求 n!,结果阶乘算着算着竟然出现了负数:

public class CalculateN {
    public static int f(int n) {
        if (n == 1) return 1;
        return n * f(n - 1);
    }

    public static void main(String[] args) {
        for (int i = 1; i <= 21; i++) {
            System.out.println(i + "! = " + f(i));
        }
    }
}

原因

Java 中 int 占 32 位,是有符号整数:

  • Integer.MAX_VALUE = 2³¹ − 1 = 2,147,483,647

  • Integer.MIN_VALUE = −2³¹ = −2,147,483,648

阶乘增长极快:13! = 6,227,020,800 已经超过了 int 的上限。计算机用固定位数保存数值,超出范围时会自动截断高位二进制,从补码角度看,符号位被翻成 1,结果就变成了负数。

这就是课件说的:"由于计算机使用固定的位数来保存数值,能处理的数值大小是有限的,超出范围就会得到错误结果。"

解决:用 BigInteger

对大整数运算,应使用 java.math.BigInteger:

import java.math.BigInteger;

public class BigFactorial {
    public static BigInteger calculateN(int n) {
        if (n == 1 || n == 0) {
            return BigInteger.valueOf(1);
        }
        return BigInteger.valueOf(n).multiply(calculateN(n - 1));
    }

    public static void main(String[] args) {
        System.out.println("50! = " + calculateN(50));
    }
}

BigInteger 支持任意精度整数的加减乘除,不再受 32 位限制。


5. 思考:为什么 i == j 输出 true?(浮点数比较)

现象

double i = 0.0001;
double j = 0.00010000000000000001;
System.out.println(i == j); // 输出:true

肉眼看这两个数明显不同,但 == 却判断它们相等。

原因

计算机遵循 IEEE 754 标准存储浮点数,double 只有 53 位有效二进制精度。在这个精度下,0.0001 和 0.00010000000000000001 的差别已经落在 "最后几位" 之外,被舍入成了完全相同的二进制表示,所以 == 返回 true。

结论:不能用 == 直接比较两个浮点数是否相等。

正确做法

比较它们差的绝对值是否落在一个可接受的误差范围(epsilon)内:

double i = 0.0001;
double j = 0.00010000000000000001;

if (Math.abs(i - j) < 1e-10) {
    System.out.println("true(在允许误差范围内相等)");
} else {
    System.out.println("false");
}

具体容差取多少,要根据业务对精度的要求来定。


6. 课后实验:自动四则运算出题程序

6.1 背景与阶段 1

故事:二柱的小孩上二年级,老师要求家长每天出 30 道四则运算题。写个小程序自动生成。

阶段 1(课堂测试一):花约 20 分钟写一个能自动生成 30 道小学四则运算题的 "软件"。

6.2 阶段 2(课堂测试二)要求

  1. 题目避免重复;

  2. 减法结果不允许出现负数;

  3. 乘法结果不允许出现四位数;

  4. 除法必须整除,不允许出现小数;

  5. 实现在线实时答题,交卷后统计错题数和正确率;

  6. 增加倒计时功能,指定时间内完成,超时未做完的题算错;

  7. 上述内容要求用方法(method)实现。

6.3 实现

import java.util.*;

public class ArithmeticQuiz {
    private static final int TOTAL = 30;            // 题目总数
    private static final int TIME_LIMIT_SEC = 300;  // 限时 5 分钟
    private static Set<String> used = new HashSet<>(); // 题目去重
    private static Random rand = new Random();

    /** 一道题:题干 + 正确答案 */
    static class Question {
        String text;
        int answer;
        Question(String t, int a) { text = t; answer = a; }
    }

    /** 按约束随机生成一道不重复的题目 */
    private static Question generateQuestion() {
        String text;
        int answer;
        int a, b;
        do {
            int op = rand.nextInt(4); // 0:+  1:-  2:×  3:÷
            switch (op) {
                case 0: // 加法
                    a = rand.nextInt(100) + 1;
                    b = rand.nextInt(100) + 1;
                    text = a + " + " + b;
                    answer = a + b;
                    break;
                case 1: // 减法:保证不出现负数
                    a = rand.nextInt(100) + 1;
                    b = rand.nextInt(100) + 1;
                    if (a < b) { int t = a; a = b; b = t; }
                    text = a + " - " + b;
                    answer = a - b;
                    break;
                case 2: // 乘法:结果不超过三位数
                    a = rand.nextInt(9) + 1;
                    b = rand.nextInt(9) + 1;
                    text = a + " × " + b;
                    answer = a * b;
                    break;
                default: // 除法:保证整除
                    b = rand.nextInt(9) + 1;
                    answer = rand.nextInt(9) + 1;
                    a = b * answer;
                    text = a + " ÷ " + b;
            }
        } while (used.contains(text)); // 去重
        used.add(text);
        return new Question(text, answer);
    }

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int correct = 0;
        long start = System.currentTimeMillis();

        System.out.println("开始答题,共 " + TOTAL + " 题,限时 "
                + TIME_LIMIT_SEC + " 秒!");

        for (int i = 1; i <= TOTAL; i++) {
            // 倒计时:检查是否超时
            long elapsed = (System.currentTimeMillis() - start) / 1000;
            if (elapsed >= TIME_LIMIT_SEC) {
                System.out.println("\n时间到!剩余题目按错题计。");
                break;
            }

            Question q = generateQuestion();
            System.out.printf("[剩余 %d 秒] 第 %d 题:%s = ",
                    TIME_LIMIT_SEC - elapsed, i, q.text);

            int userAns = sc.nextInt();
            if (userAns == q.answer) {
                correct++;
            }
        }

        int wrong = TOTAL - correct;
        double rate = correct * 100.0 / TOTAL;
        System.out.println("===== 答题结束 =====");
        System.out.println("答对:" + correct + " 题");
        System.out.println("答错:" + wrong + " 题");
        System.out.printf("正确率:%.2f%%%n", rate);
    }
}

核心点:出题、去重、判分、统计都各自封装成方法,正体现了本讲 "方法" 的主题。阶段 1 的需求(自动出 30 道题)只需要 generateQuestion() 部分即可完成。


小结

知识点 对应问题
手写随机数 / 伪随机算法 动手动脑 1(线性同余法)
方法重载(Overload) 动手动脑 2、课堂练习 println
整数溢出、BigInteger n! 算出负数
浮点数精度、epsilon 比较 i == j 为何为 true
方法封装、流程设计 自动四则运算出题程序

通过这几个问题可以看到:"方法" 本质上就是把一段重复出现、有独立功能的代码封装起来,起名字、给参数、给返回值,从而让程序更清晰、更好维护—— 也就是课件开头说的 "是懒人造就了方法"。

posted @ 2026-10-08 12:27  maglevpotato  阅读(4)  评论(0)    收藏  举报