java中BigDecimal的介绍及使用,BigDecimal格式化,BigDecimal常见问题

这篇具有很好参考价值的文章主要介绍了java中BigDecimal的介绍及使用,BigDecimal格式化,BigDecimal常见问题。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

系列文章目录

  • java中BigDecimal的介绍及使用,BigDecimal格式化,BigDecimal常见问题
  • java中BigDecimal的介绍及使用(二)

一、BigDecimal概述

BigDecimal是Java在java.math包中提供的线程安全的API类。BigDecimal是Java中用于表示任意精度数字的类,它可以表示无限长度的小数,BigDecimal 通常支持任意位数的小数部分,用来对超过16位有效位的数进行精确的运算。双精度浮点型变量double可以处理16位有效数,但在实际应用中,可能需要对更大或者更小的数进行运算和处理。一般情况下,对于那些不需要准确计算精度的数字,我们可以直接使用Float和Double处理,但是Double.valueOf(String) 和Float.valueOf(String)会丢失精度。所以开发中,如果我们需要精确计算的结果,则必须使用BigDecimal类来操作。

​ BigDecimal所创建的是对象,故我们不能使用传统的+、-、*、/等算术运算符直接对其对象进行数学运算,而必须调用其相对应的方法。方法中的参数也必须是BigDecimal的对象。构造器是类的特殊方法,专门用来创建对象,特别是带有参数的对象。

二、BigDecimal常用构造方法

方法 含义
BigDecimal(int val) 创建一个具有参数所指定整数值的对象。
BigDecimal(double val) 创建一个具有参数所指定双精度值的对象。不推荐使用,因为存在精度丢失问题
BigDecimal(long val) 创建一个具有参数所指定长整数值的对象。
BigDecimal(String val) 创建一个具有参数所指定以字符串表示的数值的对象。 推荐使用

三、BigDecimal常用方法

注意:BigDecimal进行运算时必须要保证对象本身不能是null,否则就会抛空指针异常。

方法 含义
add(BigDecimal) BigDecimal对象中的值相加,返回BigDecimal对象
subtract(BigDecimal) BigDecimal对象中的值相减,返回BigDecimal对象
multiply(BigDecimal) BigDecimal对象中的值相乘,返回BigDecimal对象
divide(BigDecimal) BigDecimal对象中的值相除,返回BigDecimal对象。该方法可能会遇到无限精度问题,会抛出异常,使用时需注意。详细见下方的无限精度的坑
abs() 将BigDecimal对象中的值转换成绝对值
doubleValue() 将BigDecimal对象中的值转换成双精度数
floatValue() 将BigDecimal对象中的值转换成单精度数
longValue() 将BigDecimal对象中的值转换成长整数
intValue() 将BigDecimal对象中的值转换成整数
compareTo(BigDecimal val) 比较大小,返回int类型。0(相等) 1(大于) -1(小于)
toString() 有必要时使用科学计数法。
toPlainString() 不使用任何指数。推荐使用
toEngineeringString() 有必要时使用工程计数法。 工程记数法是一种工程计算中经常使用的记录数字的方法,与科学技术法类似,但要求10的幂必须是3的倍数
max(BigDecimal val) 两值比较,返回最大值
negate() 求相反数,正变负,负变正
pow(int n) 求乘方,如BigDecimal.valueOf(2).pow(3)的值为8

代码示例

import java.math.BigDecimal;

public class Test {
    public static void main(String[] args){
        BigDecimal b1 = new BigDecimal("1");
        BigDecimal b2 = new BigDecimal("2");
        BigDecimal b3 = new BigDecimal("4");
        System.out.println("相加:"+b1.add(b2));
        System.out.println("相减:"+b1.subtract(b2));
        System.out.println("相乘:"+b2.multiply(b3));
        System.out.println("相除:"+b2.divide(b3));
    }
}

四、BigDecimal进阶

4.1、BigDecimal的八种舍入模式

BigDecimal.setScale()方法用于格式化小数点
setScale(1)表示保留一位小数,默认用四舍五入方式
setScale(1,BigDecimal.ROUND_DOWN)直接删除多余的小数位,如2.35会变成2.3
setScale(1,BigDecimal.ROUND_UP)进位处理,2.35变成2.4
setScale(1,BigDecimal.ROUND_HALF_UP)四舍五入,2.35变成2.4
setScaler(1,BigDecimal.ROUND_HALF_DOWN)四舍五入,2.35变成2.3,如果是5则向下舍
setScaler(1,BigDecimal.ROUND_CEILING)接近正无穷大的舍入
setScaler(1,BigDecimal.ROUND_FLOOR)接近负无穷大的舍入,数字>0和ROUND_UP作用一样,数字<0和ROUND_DOWN作用一样
setScaler(1,BigDecimal.ROUND_HALF_EVEN)向最接近的数字舍入,如果与两个相邻数字的距离相等,则向相邻的偶数舍入。

1、ROUND_UP,向远离0的方向舍入,在丢弃非零部分之前始终增加数字(始终对非零舍弃部分前面的数字加1)。
注意,此舍入模式始终不会减少计算值的大小。
eg: 保留1位小数 1.60->1.6 1.61->1.7 1.66->1.7 , -1.62->-1.7

2、ROUND_DOWN,向接近0的方向舍入,在丢弃某部分之前,始终不增加数据(即,截断),该方式是只减不加。
eg: 保留1位小数 1.60->1.6 1.61->1.6 1.66->1.6 , -1.62->-1.6

3、ROUND_CEILING,向正无穷方向舍入,如果数值为正,舍入方式与ROUND_UP一致,如果为负,舍入方式与ROUND_DOWN一致,该模式始终不会减少计算数值。
eg: 保留1位小数 1.60->1.6 1.61->1.7 1.66->1.7 , -1.62->-1.6

4、ROUND_FLOOR,向负无穷方向舍入,如果数值为正,舍入行为与 ROUND_DOWN 相同;如果为负,则舍入行为与 ROUND_UP 相同。该模式始终不会增加计算数值。
eg: 保留1位小数 1.60->1.6 1.61->1.6 1.66->1.6 , -1.62->-1.7

5、ROUND_HALF_UP,向“最接近的”数字舍入,也就是四舍五入。
eg: 保留1位小数 1.61->1.6 1.65->1.7 1.66->1.7 , -1.62->-1.6

6、ROUND_HALF_DOWN,向“最接近的”数字舍入,如果与两个相邻数字的距离相等,则为上舍入的舍入模式,也就是五舍六入。
eg: 保留1位小数 1.61->1.6 1.65->1.6 1.66->1.7 , -1.62->-1.6

7、ROUND_HALF_EVEN,向“最接近的”数字舍入,如果与两个相邻数字的距离相等,则向相邻的偶数舍入。如果舍弃部分左边的数字为奇数,则舍入行为与 ROUND_HALF_UP 相同;如果为偶数,则舍入行为与 ROUND_HALF_DOWN 相同。
注意,在重复进行一系列计算时,此舍入模式可以将累加错误减到最小。
此舍入模式也称为“银行家舍入法”,主要在美国使用。四舍六入,五分两种情况。如果前一位为奇数,则入位,否则舍去。以下例子为保留小数点1位,那么这种舍入方式下的结果。
eg. 1.15->1.2, 1.25->1.2

8、ROUND_UNNECESSARY,计算结果是精确的,不需要舍入模式。如果对获得精确结果的操作指定此舍入模式,则抛出ArithmeticException。

代码示例

BigDecimal b = new BigDecimal("1.6666");
System.out.println("result b:" + b.setScale(2, BigDecimal.ROUND_HALF_UP)); // 1.67
System.out.println("result b:" + b.setScale(2)); // 精度错误

执行结果:

result b:1.67
Exception in thread "main" java.lang.ArithmeticException: Rounding necessary

原因分析:

setScale方法默认使用的roundingMode是ROUND_UNNECESSARY,不需要使用舍入模式,设置精度2位,但是小数点后有4位肯定会抛异常。

4.2、BigDecimal格式化、小数点转换

BigDecimal可以与DecimalFormat结合使用,从而对金额格式化,如小数点后面统一保留两位,不够两位的补零,多余两位的舍入。

import java.math.BigDecimal;
import java.text.DecimalFormat;

public class Test {
    public static void main(String[] s){
        System.out.println(formatToNumber(new BigDecimal("12333.435")));
        System.out.println(formatToNumber(new BigDecimal(0)));
        System.out.println(formatToNumber(new BigDecimal("0.00")));
        System.out.println(formatToNumber(new BigDecimal("0.001")));
        System.out.println(formatToNumber(new BigDecimal("0.006")));
        System.out.println(formatToNumber(new BigDecimal("0.206")));
        System.out.println(formatToNumber(new BigDecimal("1.22")));
    }
    /**
     * @desc
     * @param obj 传入的小数
     * @return
     */
    public static String formatToNumber(BigDecimal obj) {
        // DecimalFormat默认使用的是进位方式是RoundingMode.HALF_EVEN,此舍入模式也称为“银行家算法”,主要在美国使用。
        //银行家算法:四舍六入五考虑,五后非零就进一,五后为零看奇偶,五前为偶应舍去,五前为奇要进一
        DecimalFormat df = new DecimalFormat("###,##0.00"); 
        return df.format(obj);
    }
}

执行结果:

12,333.44
0.00
0.00
0.00
0.01
0.21
1.22

需注意:

  1. DecimalFormat的默认进位方式不是四舍五入,所以当小数点后面需要舍去的时候,肯能跟预想的不一样,具体可参考《关于DecimalFormat的取舍问题,DecimalFormat四舍五入的坑》
  2. new DecimalFormat(“###,##0.00”)小数点前面需要有个0,这样0-1之间的数字才会正常格式化;若##0.00的小数点前面没有0,则0-1之间的数字会被丢失掉小数点前的0,代码如下:
import java.math.BigDecimal;
import java.text.DecimalFormat;

public class Test {
    public static void main(String[] s){
        System.out.println(formatToNumber(new BigDecimal("12333.435")));
        System.out.println(formatToNumber(new BigDecimal(0)));
        System.out.println(formatToNumber(new BigDecimal("0.00")));
        System.out.println(formatToNumber(new BigDecimal("0.001")));
        System.out.println(formatToNumber(new BigDecimal("0.006")));
        System.out.println(formatToNumber(new BigDecimal("0.206")));
        System.out.println(formatToNumber(new BigDecimal("1.22")));
    }
    /**
     * @desc
     * @param obj 传入的小数
     * @return
     */
    public static String formatToNumber(BigDecimal obj) {
        // DecimalFormat默认使用的是进位方式是RoundingMode.HALF_EVEN,此舍入模式也称为“银行家算法”,主要在美国使用。
        //银行家算法:四舍六入五考虑,五后非零就进一,五后为零看奇偶,五前为偶应舍去,五前为奇要进一
        DecimalFormat df = new DecimalFormat("###,##.00");
        return df.format(obj);
    }
}

执行结果:

1,23,33.44
.00
.00
.00
.01
.21
1.22

4.3、货币格式化与百分比格式化

经常能看到金额用¥120.00表示,利率用0.8%表示,这里扩展一下BigDecimal的货币格式化与百分比格式化

NumberFormat类的format()方法可以使用BigDecimal对象作为其参数,可以利用BigDecimal对超出16位有效数字的货币值,百分值,以及一般数值进行格式化控制。

NumberFormat对象:
getCompactNumberInstance();返回FORMAT带有"SHORT"格式样式的默认语言环境 的紧凑数字格式 。
getCurrencyInstance​(Locale inLocale);返回指定语言环境的货币格式。若是不指定参数,则以默认语言为参数。
getInstance​(Locale inLocale);返回指定语言环境的通用数字格式。若是不指定参数,则以默认语言为参数。
getPercentInstance​(Locale inLocale);返回指定语言环境的百分比格式。若是不指定参数,则以默认语言为参数。

代码示例:

import java.math.BigDecimal;
import java.text.NumberFormat;

public class Test {
    public static void main(String[] args){
        NumberFormat currency = NumberFormat.getCurrencyInstance(); //建立货币格式化引用
        NumberFormat percent = NumberFormat.getPercentInstance();  //建立百分比格式化引用
        percent.setMinimumFractionDigits(2);//设置数的小数部分所允许的最小位数(如果不足后面补0)
        percent.setMaximumFractionDigits(3);//设置数的小数部分所允许的最大位数(如果超过会四舍五入)

        BigDecimal amount = new BigDecimal("250600.42"); //金额
        BigDecimal interestRate = new BigDecimal("0.0004"); //利率
        BigDecimal interest = amount .multiply(interestRate); //相乘

        System.out.println("金额: " + currency.format(loanAmount));
        System.out.println("利率: " + percent.format(interestRate));
        System.out.println("利息: " + currency.format(interest));
    }
}

执行结果:

金额: ¥250,600.42
利率: 0.04%
利息: ¥100.24

NumberFormat提供了多种货币格式的引用,如¥(人民币),$(美元、英元)等等,具体的后续将再写一篇博文介绍。

五、BigDecimal常见问题

5.1、踩坑一:创建 BigDecimal精度丢失的坑

在BigDecimal 中提供了多种创建方式,可以通过new 直接创建,也可以通过 BigDecimal#valueOf 创建。这两种方式使用不当,也会导致精度问题。如下:

public static void main(String[] args) throws Exception {
   BigDecimal b1= new BigDecimal(0.1);
   System.out.println(b1);
   BigDecimal b2= BigDecimal.valueOf(0.1);
   System.out.println(b2);
   BigDecimal b3= BigDecimal.valueOf(0.111111111111111111111111111234);
   System.out.println(b3);
}

执行结果:

0.1000000000000000055511151231257827021181583404541015625
0.1
0.1111111111111111

上面示例中两个方法都传入了double类型的参数0.1但是 b1 还是出现了精度的问题。造成这种问题的原因是 0.1 这个数字计算机是无法精确表示的,送给 BigDecimal 的时候就已经丢精度了,而 BigDecimal#valueOf 的实现却完全不同。如下源码所示,BigDecimal#valueOf 中是把浮点数转换成了字符串来构造的BigDecimal,因此避免了问题。

public static BigDecimal valueOf(double val) {
   return new BigDecimal(Double.toString(val));
}

结论:

  • 第一,在使用BigDecimal构造函数时,尽量传递字符串而非浮点类型;
  • 第二,如果无法满足第一条,则可采用BigDecimal#valueOf方法来构造初始化值。但是valueOf受double类型精度影响,当传入参数小数点后的位数超过double允许的16位精度还是可能会出现问题的

5.2、踩坑二:等值比较的坑

一般在比较两个值是否相等时,都是用equals 方法,但是,在BigDecimal 中使用equals可能会导致结果错误,BigDecimal 中提供了 compareTo 方法,在很多时候需要使用compareTo 比较两个值。如下所示:

public static void main(String[] args){
    BigDecimal b1 = new BigDecimal("1.0");
    BigDecimal b2 = new BigDecimal("1.00");
    System.out.println(b1.equals(b2));
    System.out.println(b1.compareTo(b2));
}

执行结果:

false
0

出现此种结果的原因是,equals不仅比较了值是否相等,还比较了精度是否相同。示例中,由于两个值的精度不同,所有结果也就不相同。而 compareTo 是只比较值的大小。返回的值为-1(小于),0(等于),1(大于)。

结论

  • 如果比较两个BigDecimal值的大小,采用其实现的compareTo方法;
  • 如果严格限制精度的比较,那么则可考虑使用equals方法。

5.3、踩坑三:无限精度的坑

BigDecimal 并不代表无限精度,当在两个数除不尽的时候,就会出现无限精度的坑,如下所示:

 public static void main(String[] args){
   BigDecimal b1 = new BigDecimal("1.0");
    BigDecimal b2 = new BigDecimal("3.0");
    b1.divide(b2);
}

执行结果:

Exception in thread "main" java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result.
	at java.math.BigDecimal.divide(BigDecimal.java:1693)
	at com.demo.controller.Test.main(Test.java:29)

在官方文档中对该异常有如下说明:

If the quotient has a nonterminating decimal expansion and the operation is specified to return an exact result, an ArithmeticException is thrown. Otherwise, the exact result of the division is returned, as done for other operations.

大致意思就是,如果在除法(divide)运算过程中,如果商是一个无限小数(如 0.333…),而操作的结果预期是一个精确的数字,那么将会抛出ArithmeticException异常。

此种情况,只需要在使用 divide方法时指定结果的精度即可:

public static void main(String[] args){
   BigDecimal b1 = new BigDecimal("1.0");
   BigDecimal b2 = new BigDecimal("3.0");
   System.out.println(b1.divide(b2,2, RoundingMode.HALF_UP));//0.33
}

结论:

  • 在使用BigDecimal进行(所有)运算时,尽量指定精度和舍入模式。

5.4、踩坑四:BigDecimal三种字符串输出的坑

在BigDecimal 转换成字符串时,有可能输出非你预期的结果。如下所示:

public static void main(String[] args){
   BigDecimal bg = new BigDecimal("1E11");
    System.out.println(bg.toString()); // 1E+11
    System.out.println(bg.toPlainString()); // 100000000000
    System.out.println(bg.toEngineeringString()); // 100E+9
}

执行结果:

1E+11
100000000000
100E+9

可以看到三种方式输出的结果可能都不相同,可能这个并不是预期的结果 ,BigDecimal 有三个方法可以转为相应的字符串类型,切记不要用错:

以下内容介绍java.math.BigDecimal下的三个toString方法的区别及用法

toPlainString() : 不使用任何指数。
toString() :有必要时使用科学计数法。
toEngineeringString():有必要时使用工程计数法。 工程记数法是一种工程计算中经常使用的记录数字的方法,与科学技术法类似,但要求10的幂必须是3的倍数

具体三种方法的区别可参见另一篇博文《BigDecimal的toString()、toPlainString()和toEngineeringString()区别及用法》

5.5、踩坑五:使用BigDecimal进行计算时参数不能为NULL

在使用BigDecimal类型进行计算时,进行加、减、乘、除、比较大小时,一定要保证参与计算的两个值不能为空,否则会抛出java.lang.NullPointerException异常。

代码示例:

BigDecimal b1 = new BigDecimal("1");
BigDecimal b2 = null;
System.out.println("相加:"+b2.add(b1));

结果:

Exception in thread "main" java.lang.NullPointerException
	at com.demo.controller.Test.main(Test.java:14)

5.6、踩坑六:使用BigDecimal进行除法计算时被除数不能为0

代码示例:

BigDecimal b1 = new BigDecimal("1");
BigDecimal b2 = new BigDecimal("0");
System.out.println(b1.divide(b2));

执行结果:

Exception in thread "main" java.lang.ArithmeticException: Division by zero

踩坑七:执行顺序不能调换(乘法交换律失效)

乘法满足交换律是一个常识,但是在计算机的世界里,会出现不满足乘法交换律的情况;

代码示例:

BigDecimal b1 = BigDecimal.valueOf(1.0);
BigDecimal b2 = BigDecimal.valueOf(3.0);
BigDecimal b3 = BigDecimal.valueOf(3.0);
System.out.println(b1.divide(b2, 2, RoundingMode.HALF_UP).multiply(b3)); // 0.990
System.out.println(b1.multiply(b3).divide(b2, 2, RoundingMode.HALF_UP)); // 1.00

执行结果:

0.990
1.00

执行顺序交换后,产生的结果可能不同,会导致一定的问题,使用顺序建议先乘后除。文章来源地址https://www.toymoban.com/news/detail-601463.html

到了这里,关于java中BigDecimal的介绍及使用,BigDecimal格式化,BigDecimal常见问题的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包赞助服务器费用

相关文章

  • 日期格式化的最佳实践:如何在Java中处理日期格式化

    日期格式化的最佳实践:如何在Java中处理日期格式化

    当涉及到日期格式化时,了解正确的方式和最佳实践是至关重要的。 日期格式化是将日期转换为特定格式的过程,以便在应用程序开发中更好地展示、存储或交互。 以下内容展示常用的三种方式 在Java中,你可以使用java.time.format.DateTimeFormatter类来格式化日期,并将格式化后

    2024年02月07日
    浏览(35)
  • 【Java】SimpleDateFormat格式化日期

    SimpleDateFormat 是用于以区域设置敏感的方法格式化和解析日期。它允许格式化(日期文本),解析(文本日期) 对时间日期进行格式化处理 把Date对象转换为年月日时分秒格式字符串 把字符串转换为Date对象 常用的模式字母及对应关系如下:yyyy年MM月dd日HH时mm分ss秒 y 年,M 月

    2024年04月16日
    浏览(8)
  • java实现当前系统时间格式化

    java实现当前系统时间格式化

    运行结果:

    2024年02月13日
    浏览(6)
  • idea Java代码格式化规范

    idea Java代码格式化规范

    最近在公司开发中,遇到了一点小问题,组内各同事的格式化规范不一致。一来导致代码样式并不统一,二来有时候合并代码会出现冲突,因此做一套统一代码格式化规范很有必要。 在idea中,可以通过快捷键Ctrl+Alt+L来格式化代码,如果没有做任何配置,idea本身也有一套默认

    2024年02月03日
    浏览(13)
  • 使用vscode格式化文档无效(vue代码格式化文档无效)

    使用vscode格式化文档无效(vue代码格式化文档无效)

    问题: 最近在写代码的时候,vscode使用格式化文档不管用。 原因: 单页面使用大量element组件和html代码导致,vscode识别不了。 解决方案: 1、 打开设置,点击右侧的图标打开settings.json文件,在文件中注入代码。 settings.json文件位置,vscode左下角:  设置页面右上角:  在

    2024年02月16日
    浏览(29)
  • DBeaver中使用外部格式化程序对进行sql格式化

    DBeaver中使用外部格式化程序对进行sql格式化

    本文介绍了如何在DBeaver中使用pgFormatter、sqlprase、sqlformatter等外部格式化程序对sql进行格式化。 目录 一、pgFormatter 1.准备工作 2.DBeaver中进行配置 二、sqlprase 1.准备工作 2.在DBeaver中配置 三、sql-formatter 1.准备工作 2.在DBeaver中配置 pgFormatter本质是perl脚本,所以需要perl运行环境支

    2024年01月25日
    浏览(52)
  • Java格式化日期,时间(三种方法)

    Java格式化日期,时间(三种方法)

    在java中String类格式化的方法,是静态format()用于创建格式化的字符串。 format(String format, Object... args) 新字符串使用本地语言环境,制定字符串格式和参数生成格式化的新字符串。 format(Locale locale, String format, Object... args) 使用指定的语言环境,制定字符串格式和参数生成格式化

    2024年02月13日
    浏览(9)
  • vue3 codemirror关于 sql 和 json格式化的使用以及深入了解codemirror 使用json格式化提示错误的关键代码。包含了json格式化没有效果和json格式化提示错误

    vue3 codemirror关于 sql 和 json格式化的使用以及深入了解codemirror 使用json格式化提示错误的关键代码。包含了json格式化没有效果和json格式化提示错误

    后端返回一个json字符串,里面有value1、value2指定字段渲染sql语句,其他渲染json语句。 jsonlint 和 jsonlint-mod 的区别 简单理解:jsonlint校验json格式化是否有效,jsonlint-mod用来格式化后同时校验json格式化是否有效。 (1)JSONLint是一种可以验证JSON格式是否有效的工具。 它可以检查

    2024年04月28日
    浏览(32)
  • java中的数字的格式化(超详细)

    数字的格式化在解决实际问题时使用非常普遍,如显示某超市的商品价格,需要保留两位小数。Java 主要对浮点型数据进行数字格式化操作,其中浮点型数据包括 double 和 float 型数据,在 java 中常用以下方法: 目录        // 方法一 :DecimalFormat转换最简便 #.00 表示两位小数

    2024年02月16日
    浏览(6)
  • java格式化数字 NumberFormat及DecimalFormat

    java格式化数字 NumberFormat及DecimalFormat

    NumberFormat帮助您格式化和解析任何区域设置的数字。您的代码可以完全独立于小数点,千位分隔符的区域设置约定,甚至是使用的特定十进制数字,或者数字格式是否为十进制。 DecimalFormat是NumberFormat十进制数字格式的具体子类 。它具有多种功能,旨在解析和格式化任何语言

    2024年02月02日
    浏览(3)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包