首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java 不可變分數類設計:支持四則運算與自動約分

     編輯:Java 相關知識

設計目標

實現一個線程安全的分數類,支持加減乘除運算與自動約分。採用不可變模式:所有運算返回新實例,原對象不變,天然線程安全。

核心算法

  1. 最大公約數 (GCD):輾轉相除法
  2. 最小公倍數 (LCM):a * b / GCD(a, b)
  3. 通分 (RCD):以 LCM 為新分母,調整分子
  4. 約分:分子分母同除以 GCD

完整實現代碼

package net.zj.fraction;

import java.io.IOException;

public final class Fraction {
    private final int numerator;     // 分子
    private final int denominator;   // 分母

    // 構造函數:自動約分並規範化符號
    public Fraction(int numerator, int denominator) {
        if (denominator == 0) throw new ArithmeticException("分母不能為零");
        
        // 符號統一到分子
        if (denominator < 0) {
            numerator = -numerator;
            denominator = -denominator;
        }
        
        int gcd = gcd(Math.abs(numerator), denominator);
        this.numerator = numerator / gcd;
        this.denominator = denominator / gcd;
    }

    public Fraction(int numerator) {
        this(numerator, 1);
    }

    // 靜態工廠方法:從字符串解析
    public static Fraction parse(String s) {
        String[] parts = s.split("/");
        try {
            if (parts.length == 2) {
                return new Fraction(Integer.parseInt(parts[0]), Integer.parseInt(parts[1]));
            } else if (parts.length == 1) {
                return new Fraction(Integer.parseInt(parts[0]));
            }
        } catch (NumberFormatException e) {
            throw new IllegalArgumentException("無效分數格式: " + s);
        }
        throw new IllegalArgumentException("無效分數格式: " + s);
    }

    // 四則運算:返回新實例
    public Fraction add(Fraction other) {
        int lcm = lcm(this.denominator, other.denominator);
        int n1 = this.numerator * (lcm / this.denominator);
        int n2 = other.numerator * (lcm / other.denominator);
        return new Fraction(n1 + n2, lcm);
    }

    public Fraction subtract(Fraction other) {
        int lcm = lcm(this.denominator, other.denominator);
        int n1 = this.numerator * (lcm / this.denominator);
        int n2 = other.numerator * (lcm / other.denominator);
        return new Fraction(n1 - n2, lcm);
    }

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

    public Fraction divide(Fraction other) {
        if (other.numerator == 0) throw new ArithmeticException("除數不能為零");
        return new Fraction(this.numerator * other.denominator, this.denominator * other.numerator);
    }

    // 靜態輔助方法
    private static int gcd(int a, int b) {
        while (b != 0) {
            int temp = a % b;
            a = b;
            b = temp;
        }
        return a;
    }

    private static int lcm(int a, int b) {
        return a / gcd(a, b) * b;  // 避免溢出
    }

    // 輸出格式化
    @Override
    public String toString() {
        return denominator == 1 ? String.valueOf(numerator) : numerator + "/" + denominator;
    }

    // 交互式主程序
    public static void main(String[] args) throws IOException {
        System.out.println("輸入格式:");
        System.out.println("  單個分數(自動約分):3/6");
        System.out.println("  運算表達式:1/2 + 1/3");
        System.out.println("支持運算符:+ - * /");
        System.out.println("輸入 'exit' 退出\n");

        java.util.Scanner scanner = new java.util.Scanner(System.in);
        while (true) {
            System.out.print("> ");
            String line = scanner.nextLine().trim();
            if ("exit".equalsIgnoreCase(line)) break;
            if (line.isEmpty()) continue;

            try {
                String[] tokens = line.split("\\s+");
                if (tokens.length == 1) {
                    System.out.println(Fraction.parse(tokens[0]));
                } else if (tokens.length == 3) {
                    Fraction f1 = Fraction.parse(tokens[0]);
                    Fraction f2 = Fraction.parse(tokens[2]);
                    Fraction result = switch (tokens[1]) {
                        case "+" -> f1.add(f2);
                        case "-" -> f1.subtract(f2);
                        case "*" -> f1.multiply(f2);
                        case "/" -> f1.divide(f2);
                        default -> throw new IllegalArgumentException("未知運算符: " + tokens[1]);
                    };
                    System.out.println(result);
                } else {
                    System.out.println("格式錯誤,請重新輸入");
                }
            } catch (Exception e) {
                System.out.println("錯誤: " + e.getMessage());
            }
        }
    }
}

使用示例

> 3/6
1/2
> 1/2 + 1/3
5/6
> 5/4 - 1/2
3/4
> 2/3 * 3/4
1/2
> 3/4 / 2/3
9/8
> -2/4
-1/2
> 3/-6
-1/2

設計亮點解析

特性實現方式優勢
不可變性final 欄位、無 setter、運算返回新對象線程安全、可安全共享、適合作為 Map 鍵
自動約分構造函數中調用 GCD保證唯一標準形式、避免分子分母溢出
符號規範化負號統一移至分子消除 -1/-2 vs 1/2 等等價表示差異
防溢出 LCMa / gcd * b 而非 a * b / gcd減少中間結果溢出風險
豐富靜態工廠parse() 支持整數與分數字符串API 友好、易於集成解析邏輯

擴展建議

  • 實現 Comparable<Fraction> 支持排序
  • 添加 BigInteger 版本支持任意精度
  • 提供 doubleValue()、floatValue() 轉換方法
  • 實現 Serializable 支持持久化
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved