本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

简介:用纯Java实现一个轻量级Fraction分数类,内部用两个int字段分别保存分子和分母,不依赖任何外部库或浮点运算。支持标准构造方式(含默认分母为1、带符号处理)、获取原始分子分母值、自动约分(基于欧几里得算法求最大公约数)、大小比较(compareTo)、字符串格式化输出(如‘3/4’或’-5/2’)。Main.java提供完整运行示例,演示如何创建分数、执行加减乘除、链式调用及结果打印;readme.txt说明设计思路、使用步骤和常见注意事项,比如分母不能为零、负号统一归到分子等。代码结构扁平清晰,所有方法封装在单个类中,变量私有、方法公有,符合基础面向对象实践要求,适合教学演示、课后练习或小型工具类嵌入。

1. 为什么需要手写一个分数类?——从“0.1 + 0.2 ≠ 0.3”说起

你有没有在Java里写过 System.out.println(0.1 + 0.2);?结果不是 0.3,而是 0.30000000000000004。这不是Bug,是IEEE 754浮点数表示法的固有局限:十进制小数在二进制中往往无法精确表达。对金融计算、数学教学、算法验证这类要求绝对精度的场景,浮点数就是一把悬在头顶的达摩克利斯之剑。

我带过三届Java入门实训,每次讲到“封装”和“对象设计”,总有学生问:“老师,我们能不能自己做一个‘真正准确’的分数?”——不是用 BigDecimal(它本质仍是十进制浮点模拟),也不是靠字符串拼接糊弄,而是回归数学本源:用两个整数,明确表达分子与分母的关系。这正是这个 Fraction 类诞生的起点:它不追求性能极致,也不堆砌设计模式,而是用最朴素的 int 字段、最扎实的欧几里得算法、最直白的面向对象实践,把“分数”这个概念,从数学课本里完整搬进Java内存。

关键词里的“Java分数类”“自动约分”“分子分母运算”,说的不是炫技,而是三个刚性需求:第一,类型安全——你不能把 3/4 当成 double 随意丢进 Math.sqrt();第二,数学正确——6/8 必须等于 3/4,且 compareTo 返回值必须严格符合数学序关系;第三,行为可控——加减乘除每一步都由你定义,没有黑箱,没有隐式转换。它适合谁?不是高并发交易系统,而是正在调试辗转相除法的学生、需要生成精确比例图表的前端同学、或是想给孩子写个“分数计算器”App的家长。它轻量,因为只依赖JDK基础库;它可靠,因为所有逻辑都在你眼皮底下;它教学友好,因为每一个 private int numerator 的声明,都在无声地告诉你:什么是数据封装,什么是不变量约束。

我试过直接用 double 做小学奥数题验证,一道“1/3 + 1/6 = ?”的题,输出 0.49999999999999994,学生当场懵了。而用这个 Fractionnew Fraction(1,3).add(new Fraction(1,6)) 直接返回 Fraction(1,2),toString() 输出 "1/2"——干净,确定,可预期。这才是编程该有的样子:工具服务于人的思维,而不是让人去迁就工具的缺陷。

2. 整体设计思路拆解:为什么是两个int?为什么约分必须在构造时做?

这个 Fraction 类的设计,表面看只是“两个int字段+一堆方法”,但每个决策背后都有明确的工程权衡。我们来一层层剥开它的设计肌理。

2.1 核心字段:为什么坚持用 int,而非 long 或泛型?

初学者常会疑惑:“万一分子分母很大怎么办?”比如 123456789/987654321,用 int 会不会溢出?答案是:会,但这恰恰是设计意图的一部分。int 的取值范围(-2³¹ ~ 2³¹-1)对绝大多数教学、演示、小型工具场景绰绰有余。强行上 long 会带来两个问题:一是 gcd 算法的递归深度可能增加(虽然影响微乎其微),二是模糊了设计焦点——我们教的是“分数建模思想”,不是“大数运算库”。如果真遇到超限场景,正确的做法是重构为 BigInteger 版本,而不是在基础版里埋下“可能溢出但没人提醒”的隐患。所以,private final int numerator; private final int denominator; 这两行代码,既是约束,也是教学提示:任何抽象都有边界,明确边界比盲目扩大边界更重要

2.2 不变量(Invariant)设计:分母恒正、符号归一、最简形式

这是整个类的灵魂所在。一个 Fraction 对象一旦创建,就必须永远满足三条铁律:

  1. 分母永不为零:这是数学定义,也是程序底线。构造函数里 if (denominator == 0) throw new IllegalArgumentException("Denominator cannot be zero"); 不是可选项,是强制守门员。
  2. 符号统一归于分子-3/43/-4 在数学上等价,但在对象内部,我们必须选择唯一表示法。这里采用“分母恒为正,符号由分子携带”的约定。实现很简单:if (denominator < 0) { numerator = -numerator; denominator = -denominator; }。这避免了后续比较、输出时反复判断符号的混乱。
  3. 永远保持最简形式4/8 在内存里必须存储为 1/2。这步约分不是“锦上添花”,而是维护不变量的关键动作。它发生在构造函数末尾,调用 reduce() 方法完成。好处是:所有后续运算(如 add)的输入都是已约分状态,算法逻辑更清晰;equals()hashCode() 可以直接比较约分后的值,无需每次都临时约分;toString() 输出天然简洁。

提示:reduce() 方法内部使用欧几里得算法求最大公约数(GCD)。核心逻辑是 while (b != 0) { int temp = b; b = a % b; a = temp; }。这里 ab 是分子分母的绝对值。为什么用迭代而非递归?因为递归深度在极端情况下(如 Integer.MAX_VALUE1)可能导致栈溢出,而迭代版本空间复杂度为 O(1),更稳健。

2.3 方法设计哲学:不可变性(Immutability)与链式调用

所有运算方法(add, subtract, multiply, divide)都返回全新的 Fraction 对象,而非修改自身。这是不可变对象的核心特征。好处显而易见:线程安全(无需同步)、易于推理(调用 a.add(b) 不会影响 a 的原始值)、支持函数式风格。Main.java 里的 f1.add(f2).multiply(f3) 就是典型链式调用,读起来像数学公式本身。实现上,每个运算都先计算新分子新分母,再通过 new Fraction(newNum, newDen) 构造新实例——这个构造过程又自动触发约分,保证新对象也满足全部不变量。

3. 核心细节解析与实操要点:从构造函数到字符串输出

现在我们深入到代码的毛细血管,看看那些看似简单的 public 方法,背后藏着哪些容易被忽略的细节和实战技巧。

3.1 构造函数:不止一种创建方式,但只有一种正确姿势

Fraction 提供了三种构造方式,覆盖常见使用场景:

// 1. 最常用:分子/分母
public Fraction(int numerator, int denominator)

// 2. 整数转分数:分母默认为1
public Fraction(int wholeNumber)

// 3. 字符串解析:如 "3/4", "-5/2", "7"(视为7/1)
public Fraction(String str)

重点看第三个。字符串解析是用户友好的关键,但也最容易出错。Fraction("3/4") 要能成功,Fraction("-5/2") 要能识别负号,Fraction("7") 要能补全分母。实现逻辑是:先用 str.trim() 去空格;再用正则 ^(-?\\d+)(?:/(\\d+))?$ 匹配((?:...) 是非捕获组,? 表示分母可选);然后分别提取分子、分母字符串,再 Integer.parseInt() 转换。关键陷阱在于:parseInt 可能抛 NumberFormatException,而构造函数不能声明抛出检查异常(Checked Exception),所以必须用 try-catch 捕获并包装为 IllegalArgumentException。这是新手常犯的错误——忘了异常处理,导致程序崩溃而非优雅报错。

3.2 获取原始值:getNumerator()getDenominator() 的意义

这两个 getter 方法看似简单,但它们的存在本身就是一种设计宣言。它们返回的是经过符号归一和约分后的原始整数值。例如 new Fraction(6, -8),调用 getNumerator() 返回 -3getDenominator() 返回 4。这意味着,如果你需要将分数用于其他计算(比如作为数组索引、参与另一个算法),你拿到的就是最简、最规范的整数对。它们不是“原始输入值”,而是“规范输出值”。这体现了面向对象中“封装”的精髓:外部只关心对象能提供什么服务(获取规范值),不关心内部如何存储或转换。

3.3 toString():不只是格式化,更是用户界面

toString() 方法决定了 System.out.println(frac) 的最终呈现。它必须满足:清晰、无歧义、符合数学惯例。实现逻辑是:
- 如果分母为 1,只输出分子(如 5/1"5");
- 否则输出 "分子/分母"(如 -3/4"-3/4");
- 分子为 0 时,固定输出 "0"(避免 "0/5" 这种冗余)。

这里有个易错点:字符串拼接时,"" + numerator + "/" + denominator 是低效的,应使用 String.format("%d/%d", numerator, denominator)StringBuilder。但在教学代码中,前者更直观,牺牲一点性能换取可读性,是合理取舍。

3.4 equals()hashCode():一致性是生命线

这两个方法必须成对重写,且逻辑必须严格一致,否则放入 HashSet 或作为 HashMap 的 key 时会出大问题。equals() 的逻辑是:先检查是否为同一对象(this == obj),再检查是否为 Fraction 实例,最后比较约分后的分子分母是否完全相等。注意,这里不是比较原始输入值,而是比较 getNumerator()getDenominator() 的返回值。hashCode() 则基于这两个值计算:Objects.hash(getNumerator(), getDenominator())为什么不能用原始输入值? 因为 new Fraction(2,4)new Fraction(1,2) 是同一个数学对象,equals() 必须返回 truehashCode() 也必须相同。如果用原始值,它们的 hash 会不同,违反契约。

4. 实操过程与核心环节实现:四则运算的数学推导与代码落地

现在进入最硬核的部分:如何把小学数学课本里的分数运算法则,精准无误地翻译成Java代码。每一步运算,我们都从数学原理出发,再落到代码实现,并解释关键参数的选择依据。

4.1 加法:通分是核心,但不必真的“通分”

数学法则:a/b + c/d = (a*d + b*c) / (b*d)。注意,这里不是 (a+c)/(b+d)!这是初学者最大误区。代码实现如下:

public Fraction add(Fraction other) {
    int newNum = this.numerator * other.denominator + this.denominator * other.numerator;
    int newDen = this.denominator * other.denominator;
    return new Fraction(newNum, newDen); // 构造时自动约分
}

为什么是 a*d + b*c 因为要通分,公分母是 b*d,那么 a/b 变成 (a*d)/(b*d)c/d 变成 (c*b)/(b*d),分子相加即 a*d + c*b。代码里 this.denominator * other.numerator 就是 c*b,顺序不影响结果。

关键考量:溢出风险newNumnewDen 是两个 int 相乘的结果,极易超出 int 范围。例如 Integer.MAX_VALUE/1 + Integer.MAX_VALUE/1,结果远超 int 上限。教学版的处理策略是:不主动防御,但通过文档和 readme.txt 明确警示。真实项目中,此处应引入 BigInteger 或增加溢出检查(如 Math.multiplyExact),但教学代码优先保证逻辑纯粹和易懂。

4.2 减法:加法的镜像,符号是灵魂

减法是加法的自然延伸:a/b - c/d = a/b + (-c)/d。因此,最简洁的实现是复用加法:

public Fraction subtract(Fraction other) {
    return this.add(new Fraction(-other.numerator, other.denominator));
}

这里 new Fraction(-other.numerator, other.denominator) 创建了一个新分数,其值等于 -other为什么不用 new Fraction(other.numerator, -other.denominator) 因为后者会触发构造函数内的符号归一逻辑,最终效果一样,但前者更直接,语义更清晰:“取负”。

4.3 乘法:最简洁的运算,也是最易错的

数学法则:a/b * c/d = (a*c) / (b*d)。代码实现极简:

public Fraction multiply(Fraction other) {
    int newNum = this.numerator * other.numerator;
    int newDen = this.denominator * other.denominator;
    return new Fraction(newNum, newDen);
}

易错点警示:乘法没有“通分”概念,但学生常误以为要先约分再相乘。实际上,先算后约分(由构造函数保证)和先约分再算,结果一致,但后者代码更复杂(需找分子分母间的公因子)。教学版选择“先算后约”,逻辑更线性,也便于调试观察中间值。

4.4 除法:倒数是桥梁,零检查是底线

数学法则:a/b ÷ c/d = a/b * d/c。核心是“乘以除数的倒数”。代码实现:

public Fraction divide(Fraction other) {
    if (other.numerator == 0) {
        throw new ArithmeticException("Division by zero fraction");
    }
    return this.multiply(new Fraction(other.denominator, other.numerator));
}

为什么检查 other.numerator == 0 因为 other 已经是约分后的状态,分母恒为正,所以 other 为零当且仅当其分子为零。这是最准确的零值判断。如果错误地检查 other.denominator == 0,永远为 false(构造时已禁止),反而漏掉真正的零除。

倒数构造new Fraction(other.denominator, other.numerator) 直接交换分子分母。由于 other 是约分后的,其倒数也自动满足分母为正(因为 other.denominator > 0),无需额外处理符号。

4.5 compareTo():定义数学上的“大小”关系

Comparable 接口要求实现 compareTo,用于排序和 TreeSet。数学上比较 a/bc/d,等价于比较 a*dc*b(交叉相乘,避免除法和浮点)。但需小心溢出和符号:

@Override
public int compareTo(Fraction other) {
    // 计算 a*d - c*b,但避免溢出的稳健方式:使用 long 临时计算
    long left = (long) this.numerator * other.denominator;
    long right = (long) other.numerator * this.denominator;
    return Long.compare(left, right);
}

为什么用 long 因为 int 乘积可能溢出,导致比较结果错误(如 2147483647 * 2 溢出为负数)。long 范围更大,能容纳绝大多数 int 乘积。Long.compare() 安全地比较两个 long 值,返回 -1, 0, 1。这是教学代码中为数不多的“性能妥协换正确性”的地方。

5. Main.java 完整示例解析:从创建到链式运算的全流程

Main.java 不是零散的测试片段,而是一条精心设计的学习路径。它展示了如何将 Fraction 类像乐高积木一样组合起来,解决一个完整的、有现实感的问题。我们逐行拆解这个“黄金示例”。

5.1 示例目标:验证“三分之一加六分之一等于二分之一”

这是小学数学的经典等式,也是检验 Fraction 类精度的试金石。Main.java 开篇就直奔主题:

public class Main {
    public static void main(String[] args) {
        Fraction oneThird = new Fraction(1, 3);
        Fraction oneSixth = new Fraction(1, 6);
        Fraction half = new Fraction(1, 2);

        System.out.println("1/3 = " + oneThird);
        System.out.println("1/6 = " + oneSixth);
        System.out.println("1/2 = " + half);

这里创建了三个基本分数对象。new Fraction(1, 3) 触发构造函数:检查分母非零 → 符号归一(已是正)→ reduce() 计算 GCD(1,3)=1 → 存储为 (1,3)。同理,oneSixth 存储为 (1,6)half(1,2)toString() 输出 "1/3", "1/6", "1/2",清晰明了。

5.2 执行运算与验证:add()equals() 的协同

        Fraction sum = oneThird.add(oneSixth);
        System.out.println("1/3 + 1/6 = " + sum);
        System.out.println("Is result equal to 1/2? " + sum.equals(half));

oneThird.add(oneSixth) 计算:newNum = 1*6 + 3*1 = 9, newDen = 3*6 = 18, 构造 new Fraction(9, 18)。在 Fraction(9,18) 的构造中,reduce() 计算 GCD(9,18)=9,得到 (1,2)。因此 sum.toString() 输出 "1/2"sum.equals(half) 返回 true这行 equals 调用,完美验证了类的数学正确性和 equals/hashCode 实现的可靠性

5.3 链式调用与复杂表达式:展示API的流畅性

        // 复杂表达式: (1/3 + 1/6) * 2/5
        Fraction complex = oneThird.add(oneSixth).multiply(new Fraction(2, 5));
        System.out.println("(1/3 + 1/6) * 2/5 = " + complex);

oneThird.add(oneSixth) 返回一个新的 Fraction(即 (1,2)),再调用 .multiply(new Fraction(2, 5))newNum = 1*2 = 2, newDen = 2*5 = 10, 构造 new Fraction(2, 10)reduce() 后为 (1,5)。输出 "1/5"。链式调用让代码高度接近数学表达式,降低了认知负荷。

5.4 边界情况测试:负数、零、字符串解析

        // 测试负数
        Fraction negative = new Fraction(-3, 4);
        System.out.println("-3/4 = " + negative); // 输出 "-3/4"

        // 测试零
        Fraction zero = new Fraction(0, 100);
        System.out.println("0/100 = " + zero); // 输出 "0"

        // 测试字符串解析
        Fraction fromString = new Fraction("7/2");
        System.out.println("Parsed '7/2' = " + fromString); // 输出 "7/2"

这些测试覆盖了核心边界:
- negative 验证了符号归一逻辑(-3/4 不会变成 3/-4);
- zero 验证了 toString() 对零的特殊处理(输出 "0" 而非 "0/100");
- fromString 验证了字符串解析器的健壮性。

5.5 readme.txt 的价值:不是文档,是协作契约

readme.txt 的内容远不止“怎么用”。它是一份隐性的协作契约,告诉任何未来的使用者(包括三个月后的你自己):
- 设计前提:“本类假设所有输入均为有效整数,不处理 null 或非数字字符串”——这明确了责任边界;
- 使用步骤:“1. 将 Fraction.java 加入项目;2. 在 Main.java 中导入 Fraction;3. 调用构造函数…”——步骤清晰,小白可照做;
- 注意事项:“⚠️ 分母为零将抛出 IllegalArgumentException”、“⚠️ 大数运算可能导致 int 溢出,请自行评估范围”——用 ⚠️ 符号醒目提示风险点,比藏在代码注释里更有效。

我见过太多学生写的“完美代码”,却因为没写 readme,导致助教花半小时才搞懂怎么运行。一份好的 readme,是代码可维护性的第一道防线。

6. 常见问题与排查技巧实录:那些只有亲手敲过才会踩的坑

在带学生实现这个 Fraction 类的十几轮实践中,以下问题是出现频率最高、最让人抓狂的。我把它们整理成一张“避坑速查表”,并附上我当时是如何一步步定位和解决的。

问题现象 可能原因 排查技巧 我的实操心得
new Fraction(2, 4) 输出 "2/4" 而非 "1/2" reduce() 方法未被调用,或 gcd 计算错误 reduce() 方法首行加 System.out.println("Reducing: " + numerator + "/" + denominator);,观察是否执行;手动计算 gcd(2,4) 应为 2 心得:约分必须在构造函数最后调用,且 reduce() 必须是 private 并修改 this.numerator/denominator。我曾把 reduce() 写成 return new Fraction(...),结果对象永远不约分。
f1.add(f2) 后,f1 的值变了 运算方法错误地修改了 this 的字段,而非返回新对象 add() 方法开头打印 System.out.println("Before add: " + this);,结尾再打印一次;或用调试器单步,观察 this.numerator 是否被修改 心得Fraction 是不可变类,所有运算方法签名必须是 public Fraction xxx(Fraction other),绝不能是 public void xxx(Fraction other)。这是面向对象的基石。
System.out.println(frac) 输出 Fraction@1b6d3586 忘记重写 toString() 方法 编译时IDE通常会警告“Class ‘Fraction’ does not override ‘toString()’”,留意黄色波浪线;或运行时报错后,立刻检查 Fraction 类是否有 toString() 心得toString()Object 类的方法,不重写就会用父类默认实现(输出哈希码)。把它当成和 main 方法一样重要的入口点。
f1.equals(f2) 返回 false,但数学上相等 equals() 方法未重写,或重写逻辑错误(如比较了原始输入值而非约分后值) 用调试器查看 f1.getNumerator()f2.getNumerator() 是否相等;检查 equals() 方法内是否调用了 getNumerator() 心得equals() 必须与 hashCode() 保持一致。我第一次写时,equals() 比较了原始值,hashCode() 却用了 getNumerator(),导致 HashSet 里存了两个“相同”的分数。
new Fraction("3/4")NumberFormatException 字符串解析正则错误,或 parseInt 传入了空字符串 Fraction(String str) 构造函数中,System.out.println("Parsing: '" + str + "'");;检查正则匹配结果,确保 group(1)group(2) 不为 null 心得:字符串解析是“脏活”,必须用 try-catch 包裹 parseInt,并给出清晰的错误信息,如 throw new IllegalArgumentException("Invalid fraction string: " + str, e);

独家避坑技巧分享
- “断点三连”法:遇到任何运算结果不对,立刻在 add() 方法的 newNum 计算后、newDen 计算后、return new Fraction(...) 前,各设一个断点。观察这三个变量的值,90% 的问题(如符号错、乘反了)一眼就能揪出来。
- “最小复现”原则:不要一上来就跑 Main.java 全部代码。先写一个最简测试:new Fraction(1,2).add(new Fraction(1,2)),预期 "1"。如果这个都错,说明核心逻辑有硬伤,不必往下看复杂例子。
- “纸笔演算”习惯:在写 multiply() 前,先在纸上算一遍 2/3 * 4/5 = ?,得到 8/15,再对照代码看 newNum = 2*4, newDen = 3*5 是否匹配。这是程序员最朴素、最有效的验证方式。

7. 这个分数类还能怎么玩?——从教学工具到实用扩展

写完这个 Fraction 类,我并没有停在这里。它像一块干净的画布,后续可以根据不同需求,平滑地添加新功能,而不会破坏原有的简洁性。这里分享几个我实际做过、效果很好的扩展方向,它们不是“炫技”,而是解决真实痛点。

7.1 扩展1:支持混合数(带分数)输出

toString() 当前输出 "7/2",但数学课上更习惯写成 "3 1/2"。只需在 toString() 里加几行逻辑:

public String toString() {
    if (numerator == 0) return "0";
    if (denominator == 1) return String.valueOf(numerator);

    int whole = Math.abs(numerator) / denominator; // 整数部分
    int remainder = Math.abs(numerator) % denominator; // 余数

    if (remainder == 0) {
        return String.valueOf(numerator < 0 ? -whole : whole);
    } else {
        String fracPart = remainder + "/" + denominator;
        return (numerator < 0 ? "-" : "") + (whole > 0 ? whole + " " + fracPart : fracPart);
    }
}

这样 new Fraction(7, 2) 就输出 "3 1/2"new Fraction(-7, 2) 输出 "-3 1/2"。对学生理解“假分数”和“带分数”的转换,帮助巨大。

7.2 扩展2:添加 toDecimal(double precision) 方法

虽然核心是整数运算,但有时需要一个近似的小数用于对比或绘图。可以添加一个非精确的转换方法:

public double toDecimal() {
    return (double) numerator / denominator;
}

注意:这个方法必须明确标注为“近似值”,并在 readme.txt 里强调其局限性。它存在的意义,是让 Fraction 类能与其他使用 double 的库(如某些图表库)进行有限度的交互,而不是替代它。

7.3 扩展3:集成到JUnit测试框架

Main.java 里的验证逻辑,升级为正式的单元测试,是工程化的必经之路。用 JUnit 5 写一个测试类:

class FractionTest {
    @Test
    void testAddition() {
        Fraction oneThird = new Fraction(1, 3);
        Fraction oneSixth = new Fraction(1, 6);
        Fraction expected = new Fraction(1, 2);
        assertEquals(expected, oneThird.add(oneSixth));
    }

    @Test
    void testDivisionByZero() {
        Fraction f = new Fraction(1, 2);
        assertThrows(ArithmeticException.class, () -> f.divide(new Fraction(0, 1)));
    }
}

运行 mvn test,所有测试通过,才是真正的“可交付”。这一步,把教学代码变成了生产就绪的组件。

7.4 我的个人体会是…

这个 Fraction 类,我写了不下二十遍。第一次是在大学数据结构课上,用C++实现;后来在教Java时,为了让学生理解,又用最朴素的Java重写;再后来,为了给一个教育App做后台计算,我把它升级为 BigInteger 版本。每一次重写,都不是重复劳动,而是对“抽象”和“封装”这两个词更深一层的理解。

它教会我的,远不止是欧几里得算法。它让我明白,一个好类,不在于它有多“高级”,而在于它的契约是否清晰Fraction(2,4) 必须等于 Fraction(1,2))、边界是否坚固(分母为零立刻爆炸)、行为是否可预测add 永远不改变原对象)。这些品质,和任何框架、任何语言无关,它们是编程的底层操作系统。

所以,当你下次看到一个“简单”的需求,比如“做个分数计算器”,别急着搜开源库。试着自己手写一个 Fraction。在 int numeratorint denominator 这两行代码之间,在 gcd 的循环里,在 add() 的乘法中,你会触摸到编程最本真的质地:用确定的规则,去描述不确定的世界。

本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

简介:用纯Java实现一个轻量级Fraction分数类,内部用两个int字段分别保存分子和分母,不依赖任何外部库或浮点运算。支持标准构造方式(含默认分母为1、带符号处理)、获取原始分子分母值、自动约分(基于欧几里得算法求最大公约数)、大小比较(compareTo)、字符串格式化输出(如‘3/4’或’-5/2’)。Main.java提供完整运行示例,演示如何创建分数、执行加减乘除、链式调用及结果打印;readme.txt说明设计思路、使用步骤和常见注意事项,比如分母不能为零、负号统一归到分子等。代码结构扁平清晰,所有方法封装在单个类中,变量私有、方法公有,符合基础面向对象实践要求,适合教学演示、课后练习或小型工具类嵌入。


本文还有配套的精品资源,点击获取
menu-r.4af5f7ec.gif

更多推荐