zoukankan      html  css  js  c++  java
  • String与BigDecimal两个基础类

    来源:流浪舟的博客 https://www.maliaoblog.cn/2020/0916/

    String, StringBuffer and StringBuilder

    string有11种构造方法

    1. 可变性

    • String不可变
    • StringBuffer 和 StringBuilder 可变

    在 Java 9 之后,String 类的实现改用 byte 数组存储字符串,同时使用 coder 来标识使用了哪种编码。

    public final class String
        implements java.io.Serializable, Comparable<String>, CharSequence {
        /** The value is used for character storage. */
        private final byte[] value;
    
        /** The identifier of the encoding used to encode the bytes in {@code value}. */
        private final byte coder;
    }
    

    value 数组被声明为 final,这意味着 value 数组初始化之后就不能再引用其它数组。并且 String 内部没有改变 value 数组的方法,因此可以保证 String 不可变。

    2. 线程安全

    • String 不可变,因此是线程安全的
    • StringBuilder 不是线程安全的
    • StringBuffer 是线程安全的,内部使用 synchronized 进行同步

    String Pool

    字符串常量池(String Pool)保存着所有字符串字面量(literal strings),这些字面量在编译时期就确定。不仅如此,还可以使用 String 的 intern() 方法在运行过程将字符串添加到 String Pool 中。

    当一个字符串调用 intern() 方法时,如果 String Pool 中已经存在一个字符串和该字符串值相等(使用 equals() 方法进行确定),那么就会返回 String Pool 中字符串的引用;否则,就会在 String Pool 中添加一个新的字符串,并返回这个新字符串的引用。

    下面示例中,s1 和 s2 采用 new String() 的方式新建了两个不同字符串,而 s3 和 s4 是通过 s1.intern() 方法取得同一个字符串引用。intern() 首先把 s1 引用的字符串放到 String Pool 中,然后返回这个字符串引用。因此 s3 和 s4 引用的是同一个字符串。

    String s1 = new String("aaa");
    String s2 = new String("aaa");
    System.out.println(s1 == s2);           // false
    String s3 = s1.intern();
    String s4 = s1.intern();
    System.out.println(s3 == s4);           // true
    

    如果是采用 "bbb" 这种字面量的形式创建字符串,会自动地将字符串放入 String Pool 中。

    String s5 = "bbb";
    String s6 = "bbb";
    System.out.println(s5 == s6);  // true
    

    在 Java 7 之前,String Pool 被放在运行时常量池中,它属于永久代。而在 Java 7,String Pool 被移到堆中。这是因为永久代的空间有限,在大量使用字符串的场景下会导致 OutOfMemoryError 错误。

    new String("abc")

    使用这种方式一共会创建两个字符串对象(前提是 String Pool 中还没有 "abc" 字符串对象)。

    • "abc" 属于字符串字面量,因此编译时期会在 String Pool 中创建一个字符串对象,指向这个 "abc" 字符串字面量;
    • 而使用 new 的方式会在堆中创建一个字符串对象。

    创建一个测试类,其 main 方法中使用这种方式来创建字符串对象。

    public class NewStringTest {
        public static void main(String[] args) {
            String s = new String("abc");
        }
    }
    

    使用 javap -verbose 进行反编译,得到以下内容:

    // ...
    Constant pool:
    // ...
       #2 = Class              #18            // java/lang/String
       #3 = String             #19            // abc
    // ...
      #18 = Utf8               java/lang/String
      #19 = Utf8               abc
    // ...
    
      public static void main(java.lang.String[]);
        descriptor: ([Ljava/lang/String;)V
        flags: ACC_PUBLIC, ACC_STATIC
        Code:
          stack=3, locals=2, args_size=1
             0: new           #2                  // class java/lang/String
             3: dup
             4: ldc           #3                  // String abc
             6: invokespecial #4                  // Method java/lang/String."<init>":(Ljava/lang/String;)V
             9: astore_1
    // ...
    

    在 Constant Pool 中,#19 存储这字符串字面量 "abc",#3 是 String Pool 的字符串对象,它指向 #19 这个字符串字面量。在 main 方法中,0: 行使用 new #2 在堆中创建一个字符串对象,并且使用 ldc #3 将 String Pool 中的字符串对象作为 String 构造函数的参数。

    以下是 String 构造函数的源码,可以看到,在将一个字符串对象作为另一个字符串对象的构造函数参数时,并不会完全复制 value 数组内容,而是都会指向同一个 value 数组。

    public String(String original) {
        this.value = original.value;
        this.hash = original.hash;
    }
    

    BigDecimal

    Java在java.math包中提供的API类BigDecimal,用来对超过16位有效位的数进行精确的运算。双精度浮点型变量double可以处理16位有效数。在实际应用中,需要对更大或者更小的数进行运算和处理。float和double只能用来做科学计算或者是工程计算,在商业计算中要用java.math.BigDecimal。BigDecimal所创建的是对象,我们不能使用传统的+、-、*、/等算术运算符直接对其对象进行数学运算,而必须调用其相对应的方法。方法中的参数也必须是BigDecimal的对象。构造器是类的特殊方法,专门用来创建对象,特别是带有参数的对象。

    BigDecimal一共有4个构造方法:

    BigDecimal(int) 创建一个具有参数所指定整数值的对象。

    BigDecimal(double) 创建一个具有参数所指定双精度值的对象。(不建议采用)

    BigDecimal(long) 创建一个具有参数所指定长整数值的对象。

    BigDecimal(String) 创建一个具有参数所指定以字符串表示的数值的对象

    这里不建议采用第二种,原因:

    1、参数类型为double的构造方法的结果有一定的不可预知性。有人可能认为在Java中写入 newBigDecimal(0.1)所创建的BigDecimal正好等于 0.1,但是它实际上等于0.1000000000000000055511151。这是因为0.1无法准确地表示为 double(或者说对于该情况,不能表示为任何有限长度的二进制小数)。这样,传入到构造方法的值不会正好等于 0.1(虽然表面上等于该值)。

    2、另一方面,String 构造方法是完全可预知的:写入 newBigDecimal("0.1") 将创建一个 BigDecimal,它正好等于预期的 0.1。因此,比较而言,通常建议优先使用String构造方法 。

    BigDecimal a = new BigDecimal("2.3");
    

    当double必须用作BigDecimal的源时,请使用Double.toString(double)转成String,然后使用String构造方法,或使用BigDecimal的静态方法valueOf,如下:

    BigDecimal a = new BigDecimal.valueOf(Double.toString(2.3));
    

    对于常用的加,减,乘,除,BigDecimal类提供了相应的成员方法

    public BigDecimal add(BigDecimal value); //加法
    public BigDecimal subtract(BigDecimal value); //减法 
    public BigDecimal multiply(BigDecimal value); //乘法
    public BigDecimal divide(BigDecimal value); //除法
    

    总结:

    1. 在需要精确的小数计算时再使用BigDecimal,BigDecimal的性能比double和float差,在处理庞大,复杂的运算时尤为明显。故一般精度的计算没必要使用BigDecimal。
    2. 尽量使用参数类型为String的构造函数。
    3. BigDecimal都是不可变的(immutable)的, 在进行每一次四则运算时,都会产生一个新的对象 ,所以在做加减乘除运算时要记得要保存操作后的值。
  • 相关阅读:
    1301班 github安装及账户注册
    对于软件工程课程的疑问
    LeetCode50:Pow
    LeetCode49:字母异位词分组
    LeetCode46:全排列
    LeetCode38:外观数列
    LeetCode:有效的数独
    LeetCode34:在排序数组中查找元素的第一个位置和最后一个位置
    LeetCode33:搜索旋转排序数组
    LeetCode29:两数相除
  • 原文地址:https://www.cnblogs.com/coderma/p/13681363.html
Copyright © 2011-2022 走看看