一、概述

JSR 354 - “金钱和货币”解决了Java中货币和货币金额的标准化问题。

它的目标是为Java生态系统添加一个灵活的可扩展的API,并使货币量更简单,更安全。

JSR没有进入JDK 9,而是未来JDK版本的候选人。虽然在Java9有Currency类简单实现但是实际开发中满足不了需求

二、引入

在maven的pom.xml中做如下引入

org.javamoney

moneta

最新版本

在gradle中

compile group: 'org.javamoney', name: 'moneta', version: '最新版本'

最新版本依赖,可以查看,点击这里

三、JSR-354功能

“货币和金钱”API的目标:

1.提供处理和计算货币金额的API

2.定义货币和货币金额的类别,以及货币四舍五入

3.处理汇率

4.处理货币和货币金额的格式化和解析

四、API分析与使用

1.规范中提到的类及接口都在javax.money.*包下面。

2.先从核心的两个接口CurrencyUnit与MonetaryAmount开始剖析

3.CurrencyUnit及MonetaryAmount

CurrencyUnit

代表的是货币。它有点类似于现在的java.util.Currency类,不同之处在于它支持自定义的实现。从规范的定义来看,java.util.Currency也是可以实现该接口的。CurrencyUnit的实例可以通过Monetary.getCurrency()方法获取,如下:

//据货币代码来获取货币单位

CurrencyUnit currencyUnit = Monetary.getCurrency("USD");

//亦或根据国家及地区来获取货币单位

CurrencyUnit unit = Monetary.getCurrency(Locale.US);

CurrencyUnit模拟货币的最小属性,我们使用货币的字符串表示形式创建CurrencyUnit ,这可能会导致我们尝试使用不存在的代码创建货币的情况。使用不存在的代码创建货币会引发UnknownCurrency异常。

MonetaryAmount

MonetaryAmount是货币金额的数字表示。它始终与CurrencyUnit 关联,并定义货币的货币表示形式。

金额可以用不同的方式来实现,重点放在由每个具体用例所定义的货币表示要求的行为上。例如。Money和FastMoney是MonetaryAmount接口的实现。

FastMoney实现MonetaryAmount使用长为数字表示,并且比更快的BigDecimal在精度的成本; 它可以在我们需要性能时使用,精度不是问题。

Money与FastMoney是JavaMoney库中MonetaryAmount的两种实现。Money是默认实现,它使用BigDecimal来存储金额。FastMoney是可选的另一个实现,它用long类型来存储金额。根据文档来看,FastMoney上的操作要比Money的快10到15倍左右。然而,FastMoney的金额大小与精度都受限于long类型。

注意:这里的Money和FastMoney都是具体的实现类(它们在org.javamoney.moneta.包下面,而不是javax.money.)。如果你不希望指定具体类型的话,可以通过MonetaryAmountFactory来生成一个MonetaryAmount的实例

通用实例可以使用默认工厂创建。

e.g:

CurrencyUnit currencyUnit = Monetary.getCurrency(Locale.US);

//金额表示

MonetaryAmount fstAmtUSD = Monetary.getDefaultAmountFactory().setCurrency(currencyUnit).setNumber(200).create();

Money money = Money.of(12, currencyUnit);

FastMoney fastMoney = FastMoney.of(2, currencyUnit);

注意:当且仅当实现类,货币单位,以及数值全部相等时才认为这两个MontetaryAmount实例是相等的。

MonetaryAmount内包含丰富的方法,可以用来获取具体的货币,金额,精度等等。

//货币计算

MonetaryAmount oneDolar = Monetary.getDefaultAmountFactory().setCurrency(currencyUnit).setNumber(1).create();

Money oneEuro = Money.of(1, "EUR");

//"+"

MonetaryAmount[] monetaryAmounts = new MonetaryAmount[]{

Money.of(100, "CHF"),

Money.of(10.20, "CHF"),

Money.of(1.15, "CHF")};

Money sumAmtCHF = Money.of(0, "CHF");

for (MonetaryAmount monetaryAmount : monetaryAmounts) {

sumAmtCHF = sumAmtCHF.add(monetaryAmount);

}

//"-"

Money calcAmtUSD = Money.of(1, "USD").subtract(fstAmtUSD);

//"*"

MonetaryAmount multiplyAmount = oneDolar.multiply(0.25);

//"\"

MonetaryAmount divideAmount = oneDolar.divide(0.25);

Money moneyOf = Money.of(12, currencyUnit);

fstAmtUSD = Monetary.getDefaultAmountFactory().setCurrency(currencyUnit).setNumber(200.50).create();

oneDolar = Monetary.getDefaultAmountFactory().setCurrency("USD").setNumber(1).create();

Money subtractedAmount = Money.of(1, "USD").subtract(fstAmtUSD);

multiplyAmount = oneDolar.multiply(0.25);

divideAmount = oneDolar.divide(0.25);

//四舍五入

MonetaryAmount fstAmtEUR = Monetary.getDefaultAmountFactory().setCurrency("EUR").setNumber(1.30473908).create();

MonetaryAmount roundEUR = fstAmtEUR.with(Monetary.getDefaultRounding());

MonetaryAmount oneDollar = Monetary.getDefaultAmountFactory().setCurrency("USD").setNumber(1).create();

//货币格式化以及解析

MonetaryAmountFormat formatUSD = MonetaryFormats.getAmountFormat(Locale.US);

String usFormatted = formatUSD.format(oneDollar);

MonetaryAmount parsed = germanFormat.parse("12,4 USD");

可以通过AmountFormatQueryBuilder来生成自定义的格式。

MonetaryAmountFormat customFormat = MonetaryFormats.getAmountFormat(

AmountFormatQueryBuilder.of(Locale.US).set(CurrencyStyle.NAME).set("pattern", "00,00,00,00.00 #").build());

注意,这里的#符号在模式串中是作为货币的占位符。

在操作在操作MonetaryAmount集合时,有许多实用的工具方法可以用来进行过滤,排序以及分组。这些方法还可以与Java 8的流API一起配套使用。

example:

List amounts = new ArrayList<>();

amounts.add(Money.of(2000.00, "EUR"));

amounts.add(Money.of(4200.00, "USD"));

amounts.add(Money.of(700.00, "USD"));

amounts.add(Money.of(13.37, "JPY"));

amounts.add(Money.of(188000.80, "USD"));

根据CurrencyUnit来进行金额过滤:

CurrencyUnit yen = Monetary.getCurrency("JPY");

CurrencyUnit dollar = Monetary.getCurrency("USD");

// 根据货币过滤,只返回美金

List onlyDollar = amounts.stream().filter(MonetaryFunctions.isCurrency(dollar)).collect(Collectors.toList());

// 根据货币过滤,只返回美金和日元

List onlyDollarAndYen = amounts.stream().filter(MonetaryFunctions.isCurrency(dollar, yen)).collect(Collectors.toList());

还可以过滤出大于或小于某个阈值的金额:

MonetaryAmount tenDollar = Money.of(1000, dollar);

List greaterThanTenDollar = amounts.stream().filter(MonetaryFunctions.isCurrency(dollar))

.filter(MonetaryFunctions.isGreaterThan(tenDollar)).collect(Collectors.toList());

排序与分组

// Sorting dollar values by number value

List sortedByAmount = onlyDollar.stream().sorted(MonetaryFunctions.sortNumber()).collect(Collectors.toList());

//Sorting by CurrencyUnit

List sortedByCurrencyUnit = amounts.stream().sorted(MonetaryFunctions.sortCurrencyUnit()).collect(Collectors.toList());

//按货币单位进行分组

Map> groupedByCurrency = amounts.stream().collect(MonetaryFunctions.groupByCurrencyUnit());

// 分组并进行汇总

Map summary = amounts.stream().collect(MonetaryFunctions.groupBySummarizingMonetary()).get();

MonetarySummaryStatistics dollarSummary = summary.get(dollar);

MonetaryAmount average = dollarSummary.getAverage();

MonetaryAmount min = dollarSummary.getMin();

MonetaryAmount max = dollarSummary.getMax();

MonetaryAmount sum = dollarSummary.getSum();

long count = dollarSummary.getCount();

MonetaryFunctions还提供了归约函数,可以用来获取最大值,最小值,以及求和:

List amounts = new ArrayList<>();

amounts.add(Money.of(10, "EUR"));

amounts.add(Money.of(7.5, "EUR"));

amounts.add(Money.of(12, "EUR"));

Optional max = amounts.stream().reduce(MonetaryFunctions.max()); // "EUR 12"

Optional min = amounts.stream().reduce(MonetaryFunctions.min()); // "EUR 7.5"

Optional sum = amounts.stream().reduce(MonetaryFunctions.sum()); // "EUR 29.5"

自定义的MonetaryAmount操作

MonetaryAmount还提供了一个非常友好的扩展点叫作MonetaryOperator。MonetaryOperator是一个函数式接口,它接收一个MonetaryAmount入参并返回一个新的MonetaryAmount对象。

MonetaryOperator tenPercentOperator = (MonetaryAmount amount) -> {

BigDecimal baseAmount = amount.getNumber().numberValue(BigDecimal.class);

BigDecimal tenPercent = baseAmount.multiply(new BigDecimal("0.1"));

return Money.of(tenPercent, amount.getCurrency());

};

MonetaryAmount dollars = Money.of(12.34567, "USD");

MonetaryAmount tenPercentDollars = dollars.with(tenPercentOperator);

标准的API特性都是通过MonetaryOperator的接口来实现的。比方说,前面看到的舍入操作就是以MonetaryOperator接口的形式来提供的。

五、汇率

货币兑换率可以通过ExchangeRateProvider来获取。JavaMoney自带了多个不同的ExchangeRateProvider的实现。其中最重要的两个是ECBCurrentRateProvider与 IMFRateProvider。

ECBCurrentRateProvider查询的是欧洲中央银行(European Central Bank,ECB)的数据而IMFRateProvider查询的是国际货币基金组织(International Monetary Fund,IMF)的汇率。

e.g:

//get the default ExchangeRateProvider (CompoundRateProvider)

ExchangeRateProvider exchangeRateProvider = MonetaryConversions.getExchangeRateProvider();

// get the names of the default provider chain

// [IDENT, ECB, IMF, ECB-HIST]

List defaultProviderChain = MonetaryConversions.getDefaultProviderChain();

// get a specific ExchangeRateProvider (here ECB)

ExchangeRateProvider ecbExchangeRateProvider = MonetaryConversions.getExchangeRateProvider("ECB");

如果没有指定ExchangeRateProvider的话返回的就是CompoundRateProvider。CompoundRateProvider会将汇率转换请求委派给一个ExchangeRateProvider链并将第一个返回准确结果的提供商的数据返回。

ExchangeRate rate = exchangeRateProvider.getExchangeRate("EUR", "USD");

NumberValue factor = rate.getFactor(); // 1.2537 (at time writing)

CurrencyUnit baseCurrency = rate.getBaseCurrency(); // EUR

CurrencyUnit targetCurrency = rate.getCurrency(); // USD

六、币种转换

不同货币间的转换可以通过ExchangeRateProvider返回的CurrencyConversions来完成。

CurrencyConversion dollarConversion = MonetaryConversions.getConversion("USD");

CurrencyConversion ecbDollarConversion = ecbExchangeRateProvider.getCurrencyConversion("USD");

MonetaryAmount tenEuro = Money.of(10, "EUR");

MonetaryAmount inDollar = tenEuro.with(dollarConversion);

注意:CurrencyConversion也实现了MonetaryOperator接口。正如其它操作一样,它也能通过MonetaryAmount.with()方法来调用。

七、类结构图

java money_Java Money与Currency API浅谈相关推荐

  1. java执行jar中的main_浅谈java 执行jar包中的main方法

    浅谈java 执行jar包中的main方法 通过 OneJar 或 Maven 打包后 jar 文件,用命令: java -jar ****.jar 执行后总是运行指定的主方法,如果 jar 中有多个 ...

  2. java方法区对象类型_浅谈Java内存区域与对象创建过程

    一.java内存区域 Java虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干个不同的数据区域.这些区域都有各自的用途,以及创建和销毁的时间,有的区域随着虚拟机进程的启动而存在,有的区域则 ...

  3. jsr 269 api_研究Java 9 Money and Currency API(JSR 354)

    jsr 269 api JSR 354定义了一个用于处理货币和货币的新Java API,计划将其包含在Java 9中.在本文中,我们将研究参考实现的当前状态: JavaMoney . 就像我关于Jav ...

  4. 研究Java 9 Money and Currency API(JSR 354)

    JSR 354定义了一个用于处理货币和货币的新Java API,计划将其包含在Java 9中.在本文中,我们将研究参考实现的当前状态: JavaMoney . 就像我关于Java 8日期/时间API的 ...

  5. java中单例的应用_浅谈Java中单例模式的几种应用

    目录 浅谈Java中单例模式的几种应用 第一种:懒汉式 第二种:饿汉式 第三种:双重检索式 第四种:注册登记式 第五种:内部类形式 浅谈Java中单例模式的几种应用 日常开发中,为了提高我们系统中对象 ...

  6. java的向下转型_浅谈Java向下转型的意义

    一开始学习 Java 时不重视向下转型.一直搞不清楚向下转型的意义和用途,不清楚其实就是不会,那开发的过程肯定也想不到用向下转型. 其实向上转型和向下转型都是很重要的,可能我们平时见向上转型多一点,向 ...

  7. java socket 异步回调函数_浅谈socket同步和异步、阻塞和非阻塞、I/O模型

    原标题:浅谈socket同步和异步.阻塞和非阻塞.I/O模型 在进行网络编程时,常常见到同步(Sync)/异步(Async),阻塞(Block)/非阻塞(Unblock)四种调用方式 同步/异步主要针 ...

  8. Java hibernate假外键_浅谈hibernate急迫加载问题(多重外键关联)

    数据库结构如下 strategy中有外键member_id(关联member表)外键strategy_category(关联category表)而member表中有外键position_id(关联po ...

  9. php和java的区别菜鸟教程_浅谈Java和PHP的主要区别

    当谈到PHP与Java的差异性问题时,更多的是回答初学者的一些疑问.对于刚接触IT的同学来说,他们需要做好对未来职业的选择.所以是选择PHP还是选择Java更有利于自身的技术特点和发展前景.所以在解决 ...

最新文章

  1. php的json格式
  2. Python应用实战-从pandas的角度来对比MySQL,教你如何更快更好的学习sql
  3. 计算机硬件系统的ppt,计算机硬件系统.ppt
  4. IT基础结构-1.DC-DNS-安装
  5. android gradle proguard,Android Gradle插件2.2.0 ProGuard开始保留内部类
  6. 使用公用表表达式的递归查询
  7. Problem E: 校庆
  8. python获取视频时长方法
  9. extjs 前后端分离_为什么我不喜欢「前后端分离」(个人观点,欢迎来喷)
  10. 题目:输入某年某月某日,判断这一天是这一年的第几天?
  11. Python实验二——制作可视化标准计算器
  12. 【 PID 算法 】PID 算法基础
  13. python 埃米尔特_跨入第四维度–卡尔·埃米尔·卡尔森如何通过Unity创造艺术创作
  14. java int转byte_JAVA中怎么将int数据转换为byte数据?
  15. tomcat升级版本升级
  16. Intel Composer XE
  17. Mybatis 报The error occurred while handling results
  18. 前端入门-HTML篇
  19. 【Oracle】B-tree和函数索引
  20. Ctrl组合快捷键大全

热门文章

  1. Android双卡设备 如何正确获取上网卡运营商类型
  2. 凹凸世界搬运工机器人图片_凹凸众人观看凹凸世界(2)下
  3. 哈工大形式语言与自动机2022期末试题
  4. 写给互联网大厂员工的真心话,醍醐灌顶!
  5. python通过关键字搜索淘宝商品详细信息
  6. 使用新浪SAE开发微信公众号 验证Token却一直失败
  7. 继电器的使用及其原理
  8. 在安装了Sql2000的基础上安装Sql2005的详细过程
  9. 2009世界500强:《财富》全球五百家公司排名
  10. Unity与 SO 交互 ☀️| .so文件(动态链接库 ) 基础知识科普