概述
java 中有很多,新手经常遇到的陷阱,本文主要包含
- 自动装箱与拆箱
- 不可变的String
- 内存泄漏
- 自增类型使用
- 使用 “==”进行对象比较
- double 类型计算
不可变的String
Java String类是不可变的(不可修改)。这是因为String对象被缓存在String池中。字符串引用的对象可以更改,但字符串对象本身不能更改。
字符串是不可变的。一旦创建了字符串,以后将无法更改该字符串对象。
Java使用按值传递,而不是按引用传递。当您在方法中为分配新值时,它只会修改本地,而不是调用代码中的原始s
自动装箱与拆箱
装箱就是自动将基本数据类型转换为包装器类型;
拆箱就是自动将包装器类型转换为基本数据类型。
装箱拆箱的类型有哪些?
通过上图,可以看出,java 基本类型可以进行拆装箱。
那拆装箱会出现什么问题呢?
通过实例,上面两个程序,计算耗时相差近10倍,在大量存在装箱行为时,会导致程序性能低下。
当封装类型进行==、+、-、*、/计算时,会自动拆箱,对基础数据类型进行运算.所以在进行计算时,使用基本数据类型。
内存泄漏
Java的核心优势之一是 Java垃圾收集器,它可以管理堆上的对象内存。每当对象不可访问时,它将自动释放。
但是,对于新手和有经验的程序员而言,常见的错误是通过允许不再使用的对象可访问来防止释放内存。这可能对项目造成很大的不利影响,因为内存泄漏会阻塞资源并降低应用程序性能。它甚至可能导致java.lang.OutOfMemoryError。
常见的情况是:
- 静态字段声明。静态字段,并在不再需要其数据后忘记将其设置为null
- 未正常关闭流。 Java虚拟机为每个打开的连接分配内存。忘记关闭连接会消耗内存。这样的连接可以是:输入流,数据库连接,会话等。
- finalize() 方法。当我们覆盖的finalize()方法,finalize()只会在对象内存回收前被调用一次,具有不确定行,只保证方法会调用,但不保证方法里的任务会被执行完。所以尽量避免使用。在Java 9 中,已经声明为过期函数,
自增类型使用
Java中运算符的计算顺序是在同等级下从左到右计算,看下自增情况
第一种情况的执行上下文如下:
1.存储操作数的先前值。
2.增加值。
3.返回上一个值
第二种情况的执行上下文如下:
1.增加值。
2.存储操作数的值(递增)
3.返回值
使用 “==”进行对象比较
许多新手程序员尝试使用“ ==”运算符比较对象,并且当代码的行为不符合预期时,就会感到困惑。需要注意的是,关系运算符“ ==”正在进行引用比较,它检查两个对象是否都指向内存中的相同位置。使用 .equals()方法将消除此问题,因为它会比较对象内部的值。
尽管有时“ ==”运算符会给出预期的答案:
这是什么原因呢?同样是字符串,创建的方式不同,差距咋这么大呢
Java语言规范的字符串文字中:同一包中不同类内的文字字符串表示对同一String 对象的引用
如果还不清楚那看下两种字符串创建过程
第一种new的方式
new一个字符串时,做了两件事。首先在堆中生成了该字符串对象,然后去看常量池中有没有该字符串,如果有就不管了,没有就往常量池中添加一个
第二种,直接赋值
这样创建字符串,首先会去常量池里找有没有这个字符串,有就直接指向常量池的该字符串,没有就先往常量池中添加一个,再指向它。
上面就是两种方式的对比情况了。
两个Integer 对比
那为什么100的时候就是相等200就不行了呢这是由于Integer 使用了缓存。
其static块中就一次性生成了-128到127直接的Integer类型变量存储在cache[]中,对于-128到127之间的int类型,返回的都是同一个Integer类型对象。
整个工作过程就是:Integer.class在装载(Java虚拟机启动)时,其内部类型IntegerCache的static块即开始执行,实例化并暂存数值在-128到127之间的Integer类型对象。
当自动装箱int型值在-128到127之间时,即直接返回IntegerCache中暂存的Integer类型对象。
为什么Java这么设计?应该是出于效率考虑,因为自动装箱经常遇到,尤其是小数值的自动装箱;而如果每次自动装箱都触发new,在堆中分配内存,就太耗时了;
其它几种基本类型包装类,也进行了缓存
Double 类型计算
Java中的double和float在内部表示为二进制分数,因此在表示十进制分数时可能不够精确(IEEE标准754)。十进制数计算需要精度,需要使用java.math.BigDecimal
总结
Java 中还有很多小陷阱,如果你有可以写出来吆。