Java手写分数类:整数分子分母+自动约分+四则运算示例
简介:用纯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,学生当场懵了。而用这个 Fraction,new 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 对象一旦创建,就必须永远满足三条铁律:
- 分母永不为零:这是数学定义,也是程序底线。构造函数里
if (denominator == 0) throw new IllegalArgumentException("Denominator cannot be zero");不是可选项,是强制守门员。 - 符号统一归于分子:
-3/4和3/-4在数学上等价,但在对象内部,我们必须选择唯一表示法。这里采用“分母恒为正,符号由分子携带”的约定。实现很简单:if (denominator < 0) { numerator = -numerator; denominator = -denominator; }。这避免了后续比较、输出时反复判断符号的混乱。 - 永远保持最简形式:
4/8在内存里必须存储为1/2。这步约分不是“锦上添花”,而是维护不变量的关键动作。它发生在构造函数末尾,调用reduce()方法完成。好处是:所有后续运算(如add)的输入都是已约分状态,算法逻辑更清晰;equals()和hashCode()可以直接比较约分后的值,无需每次都临时约分;toString()输出天然简洁。
提示:
reduce()方法内部使用欧几里得算法求最大公约数(GCD)。核心逻辑是while (b != 0) { int temp = b; b = a % b; a = temp; }。这里a和b是分子分母的绝对值。为什么用迭代而非递归?因为递归深度在极端情况下(如Integer.MAX_VALUE和1)可能导致栈溢出,而迭代版本空间复杂度为 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() 返回 -3,getDenominator() 返回 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() 必须返回 true,hashCode() 也必须相同。如果用原始值,它们的 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,顺序不影响结果。
关键考量:溢出风险。newNum 和 newDen 是两个 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/b 和 c/d,等价于比较 a*d 和 c*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 numerator 和 int denominator 这两行代码之间,在 gcd 的循环里,在 add() 的乘法中,你会触摸到编程最本真的质地:用确定的规则,去描述不确定的世界。
简介:用纯Java实现一个轻量级Fraction分数类,内部用两个int字段分别保存分子和分母,不依赖任何外部库或浮点运算。支持标准构造方式(含默认分母为1、带符号处理)、获取原始分子分母值、自动约分(基于欧几里得算法求最大公约数)、大小比较(compareTo)、字符串格式化输出(如‘3/4’或’-5/2’)。Main.java提供完整运行示例,演示如何创建分数、执行加减乘除、链式调用及结果打印;readme.txt说明设计思路、使用步骤和常见注意事项,比如分母不能为零、负号统一归到分子等。代码结构扁平清晰,所有方法封装在单个类中,变量私有、方法公有,符合基础面向对象实践要求,适合教学演示、课后练习或小型工具类嵌入。
更多推荐


所有评论(0)