JAVA学习-基础部分【2】

本文对基础部分【1】进行补充。

教程来源为:

8种基本数据类型 (gitee.io)

正文:

1、为什么不能用浮点型表示金额

由于计算机中保存的小数其实是十进制的小数的近似值,并不是准确值,所以,千万不要在代码中使用浮点数来表示金额等重要的指标。

建议使用BigDecimal或者Long(单位为分)来表示金额。

2、自动拆装箱

为什么需要包装类:

  • 因为 Java 是一种面向对象语言,很多地方都需要使用对象而不是基本数据类型。比如,在集合类中,我们是无法将 int 、double 等类型放进去的。因为集合的容器要求元素是 Object 类型。

自动装箱都是通过包装类的 valueOf() 方法来实现的.自动拆箱都是通过包装类对的 xxxValue() 来实现的。

举例:

    public static void main(String... strings) {

        Integer integer1 = 3;
        Integer integer2 = 3;

        if (integer1 == integer2)
            System.out.println("integer1 == integer2");
        else
            System.out.println("integer1 != integer2");

        Integer integer3 = 300;
        Integer integer4 = 300;

        if (integer3 == integer4)
            System.out.println("integer3 == integer4");
        else
            System.out.println("integer3 != integer4");
    }

在 Java 中,== 比较的是对象引用,而 equals 比较的是值。但是这段代码的输出如下:

integer1 == integer2
integer3 != integer4

和 Integer 中的缓存机制有关。当需要进行自动装箱时,如果数字在 -128 至 127 之间时,会直接使用缓存中的对象,而不是重新创建一个对象。

3、Integer的缓存机制

实际上这个功能在Java 5中引入的时候,范围是固定的-128 至 +127。后来在Java 6中,可以通过java.lang.Integer.IntegerCache.high设置最大值。这使我们可以根据应用程序的实际情况灵活地调整来提高性能。

Integer的缓存机制 (gitee.io)

4、关于接口的返回值类型及命名(在POJO中和RPC中)

如何评价阿里近期发布的Java编码规范? - 知乎 (zhihu.com)

在定义POJO中的布尔类型的变量时,不要使用isSuccess这种形式,而要直接使用success!

关于Boolean和boolean的使用:

  • 即对象的默认值是null,boolean基本数据类型的默认值是false

建议使用的是包装类型。原文:

如何正确定义接口的返回值(boolean/Boolean)类型及命名(success/isSuccess) (gitee.io)

  • 举一个扣费的例子,我们做一个扣费系统,扣费时需要从外部的定价系统中读取一个费率的值,我们预期该接口的返回值中会包含一个浮点型的费率字段。当我们取到这个值得时候就使用公式:金额*费率=费用 进行计算,计算结果进行划扣。
  • 如果由于计费系统异常,他可能会返回个默认值,如果这个字段是Double类型的话,该默认值为null,如果该字段是double类型的话,该默认值为0.0
  • 如果扣费系统对于该费率返回值没做特殊处理的话,拿到null值进行计算会直接报错,阻断程序。拿到0.0可能就直接进行计算,得出接口为0后进行扣费了。这种异常情况就无法被感知。
  • 这种使用包装类型定义变量的方式,通过异常来阻断程序,进而可以被识别到这种线上问题。如果使用基本数据类型的话,系统可能不会报错,进而认为无异常。
Boolean success;

5、String

字符串的不变性:一旦一个string对象在内存(堆)中被创建出来,他就无法被修改。而且,String类的所有方法都没有改变字符串本身的值,都是返回了一个新的对象。

如果想要创建一个可以被修改的字符串,可以选择StringBuffer 或者 StringBuilder这两个代替String。

JVM中专门开辟了一部分空间来存储Java字符串,那就是字符串池。

通过字符串池,两个内容相同的字符串变量,可以从池中指向同一个字符串对象,从而节省了关键的内存资源。

由于字符串是应用最广泛的数据结构,提高字符串的性能对提高整个应用程序的总体性能有相当大的影响。

substring

截取字符串并返回其[beginIndex,endIndex-1]范围内的内容。

JDK 6和JDK 7中substring的原理及区别 (gitee.io)

String中对“+”的重载

Java中的+对字符串的拼接,其实现原理是使用StringBuilder.append。当然有些情况下编译器会进行常量折叠。

在循环体内,字符串的连接方式,使用StringBuilder的append方法进行扩展。

如果使用“+”,反编译出的字节码显示每次循环都会new出一个StringBuilder的对象,然后进行append操作,最后使用toString()方法返回String对象,造成内存浪费。

StringJoiner的使用:

Java 8中的StringJoiner (gitee.io)

StringJoiner其实就是依赖StringBuilder实现的。所以他的性能和StringBuilder差不多,他也是非线程安全的。链接中的作者做了很好的总结:

  1. 如果只是简单的字符串拼接,考虑直接使用"+"即可。
  2. 如果是在for循环中进行字符串拼接,考虑使用StringBuilderStringBuffer
  3. 如果是通过一个List进行字符串拼接,则考虑使用StringJoiner

switch对String的支持

到目前为止switch支持这样几种数据类型:byte short int char String 。

  • switch对int的判断是直接比较整数的值。
  • 对char类型进行比较的时候,实际上比较的是ascii码,编译器会把char型变量转换成对应的int型变量。

例子:
原代码

public class switchDemoString {
    public static void main(String[] args) {
        String str = "world";
        switch (str) {
        case "hello":
            System.out.println("hello");
            break;
        case "world":
            System.out.println("world");
            break;
        default:
            break;
        }
    }
}

反编译后的:

public class switchDemoString {
    public static void main(String[] args) {
        String str = "world";
        switch (str) {
        case "hello":
            System.out.println("hello");
            break;
        case "world":
            System.out.println("world");
            break;
        default:
            break;
        }
    }
}

总结一下,其实switch只支持一种数据类型,那就是整型,其他数据类型都是转换成整型之后再使用switch的。

3、Class常量池

Class常量池 (gitee.io)

在Java体系中,共用三种常量池。分别是字符串常量池Class常量池运行时常量池

这里先介绍Class常量池,Class文件中包含了Java虚拟机指令集和符号表以及若干其他辅助信息。

可以使用16进制打开class文件,关于”魔数“:

Java中的”魔数”-HollisChuang's Blog

在代码中使用魔数,不仅使代码的可读性大大降低,还可能导致各种问题。所以在代码中,我们要尽量避免产生魔数。

例子:

public int getSalary(String title, int grade) {
    if ("Programmer".equals(title)){
        return grade * 500 + 700;
    }

    else if ("Tester".equals(title)){
        return grade * 500 + 800;
    }

    else if ("Analyst".equals(title)){
        return grade * 800 + 1000;
    }
}

所有需要使用魔数的地方,都可以使用枚举或者静态变量来代替。

public int getSalary(String title, int grade) {
    if (Constants.TITLE_PROGRAMMER.equals(title)){
        return grade * Constants.BASE_SALARY_LOW + Constants.ALLOWANCE_LOW;
    }
    else if (Constants.TITLE_TESTER.equals(title)){
        return grade * Constants.BASE_SALARY_LOW + Constants.ALLOWANCE_MEDIUM;
    }
    else if (Constants.TITLE_ANALYST.equals(title)){
        return grade * Constants.BASE_SALARY_HIGH + Constants.ALLOWANCE_HIGH;
    }

}

Class文件中除了包含类的版本、字段、方法、接口等描述信息外,还有一项信息就是常量池(constant pool table),用于存放编译器生成的各种字面量(Literal)和符号引用(Symbolic References)。

可以通过以下命令,查看常量池(Constant pool):详细说明可以见原文章。

javap -v  HelloWorld.class

常量池中主要存放两大类常量:字面量(literal)和符号引用(symbolic references)。

  • 字面量(literal)是用于表达源代码中一个固定值的表示法。翻译:字面量就是指由字母、数字等构成的字符串或者数值。
  • 符号引用包括了以下三类常量: * 类和接口的全限定名 * 字段的名称和描述符 * 方法的名称和描述符

关于用处:Class是用来保存常量的一个媒介场所,并且是一个中间场所。在JVM真的运行时,需要把常量池中的常量加载到内存中。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值