Java中的128陷阱和new String(“xxx“)创建了几个对象问题

简介: Java中的128陷阱和new String(“xxx“)创建了几个对象问题

1、String str = new String("abc") 创建了几个字符串对象?

答案:1个或者2个


Ⅰ 如果字符串常量池中已经有"abc"存在,这种情况只需要新建 1 个对象,即 new 一个对象放在堆中。

Ⅱ 当字符串常量池中没有 "abc",此时会创建如下 2 个对象:

一个是字符串字变量 "abc" 所对应的、驻留(intern)在字符串常量池中的实例,字符串常量池只存放实例对象的引用。

另一个是通过 new String() 创建并初始化的,内容与"abc"相同的实例,在堆中。

总结: new String("xxx"); 如果字符串常量池intern中没有对应的xxx 那么就需要在字符串常量池新建,然后再在堆上new 一个对象。


2、String str1 = “abc” 和 String str2 = new String(“abc”)的区别?

两者看似都是创建了一个字符串对象,但在内存中却是不一样的:


String str1= “abc” 在编译期,JVM 会去字符串常量池来查找是否存在“abc”,如果不存在,就在字符串常量池中开辟一个空间来存储“abc”;如果存在,就不用新开辟空间。然后在栈内存中开辟一个名字为 str1 的空间,来存储 “abc” 在常量池中的地址值。

String str2 = new String("abc")在编译阶段 JVM 先去字符串常量池中查找是否存在 “abc”,如果过不存在,则在常量池中开辟一个空间存储 “abc”。在运行时期,通过 String 类的构造器在堆内存中 new 了一个空间,然后字符串常量池中的 “abc”复制一份存放到堆空间中,在栈中为 str2 开辟空间,存放堆中 new 出来的这个 String 对象的地址值。

也就是说,前者在初始化的时候可能创建了一个对象,也可能一个对象也没有创建;后者因为 new 关键字,至少在内存中创建了一个对象,也有可能是两个对象。

3、Java中的128陷阱?

先来看一个例子:

public static void main(String[] args) {
        Integer a=127,b=127;
        Integer c=128,d=128;
        System.out.println(a==b);// true
        System.out.println(c==d);// false
}

为什么出现这种情况呢?


我们都知道Integer 是 基本类型int 的包装类型。

在Java设计之初,设计者认为,开发者可能经常用到的数字范围都在100以内,而每次使用这些数字的包装类型都要开辟新空间的话,可能会占用大量的资源。

因此他们规定在-128~127之间的Integer类型的变量,直接指向常量池中的缓存地址,不再使用new去开辟出新的空间。

执行 Integer c = 128,相当于执行:Integer c = Integer.valueOf(128),基本类型自动转换为包装类的过程称为,自动装箱(autoboxing)。

这也是出现上述代码两次比较结果不同的原因!下面我们看下valueOf() 源码去体会下:

public static Integer valueOf(int i) {
    if (i >= IntegerCache.low && i <= IntegerCache.high)
        return IntegerCache.cache[i + (-IntegerCache.low)];
    return new Integer(i);
}

在Integer 中引入了IntegerCache 来缓存一定范围的值,默认情况下范围为:-128~127。

因此:上述代码中的 127 命中了 IntegerCache,所以 a 和 b 是相同对象,而 128 则没有命中,所以 c 和 d 是不同对象。


那么,如果想要正确比较的话c与d的话,就需要拆箱比较,即在变量后加intValue()方法:

public static void main(String[] args) {
        Integer a=127,b=127;
        Integer c=128,d=128;
        System.out.println(a==b);// true
        System.out.println(c.intValue()==d.intValue());// true
}

可以直接记住下面的表格,道理都是一样的:

image.png


相关文章
|
2月前
|
设计模式 网络协议 数据可视化
Java 设计模式之状态模式:让对象的行为随状态优雅变化
状态模式通过封装对象的状态,使行为随状态变化而改变。以订单为例,将待支付、已支付等状态独立成类,消除冗长条件判断,提升代码可维护性与扩展性,适用于状态多、转换复杂的场景。
321 0
|
2月前
|
编解码 Java 开发者
Java String类的关键方法总结
以上总结了Java `String` 类最常见和重要功能性方法。每种操作都对应着日常编程任务,并且理解每种操作如何影响及处理 `Strings` 对于任何使用 Java 的开发者来说都至关重要。
309 5
|
4月前
|
缓存 安全 Java
Java反射机制:动态操作类与对象
Java反射机制是运行时动态操作类与对象的强大工具,支持获取类信息、动态创建实例、调用方法、访问字段等。它在框架开发、依赖注入、动态代理等方面有广泛应用,但也存在性能开销和安全风险。本文详解反射核心API、实战案例及性能优化策略,助你掌握Java动态编程精髓。
|
4月前
|
存储 SQL 缓存
Java字符串处理:String、StringBuilder与StringBuffer
本文深入解析Java中String、StringBuilder和StringBuffer的核心区别与使用场景。涵盖字符串不可变性、常量池、intern方法、可变字符串构建器的扩容机制及线程安全实现。通过性能测试对比三者差异,并提供最佳实践与高频面试问题解析,助你掌握Java字符串处理精髓。
|
5月前
|
自然语言处理 Java Apache
在Java中将String字符串转换为算术表达式并计算
具体的实现逻辑需要填写在 `Tokenizer`和 `ExpressionParser`类中,这里只提供了大概的框架。在实际实现时 `Tokenizer`应该提供分词逻辑,把输入的字符串转换成Token序列。而 `ExpressionParser`应当通过递归下降的方式依次解析
349 14
|
4月前
|
存储 人工智能 JavaScript
Java从作用域到对象高级应用​
本内容详细讲解了JavaScript中的作用域类型(函数作用域、块作用域、全局作用域)、作用域链、垃圾回收机制、闭包、变量提升、函数参数、数组方法、内置构造函数、对象高级知识、原型链、对象赋值、深浅拷贝、递归、异常处理及this指向等内容,全面覆盖JS核心概念与编程技巧。
59 0
|
5月前
|
存储 Java
Java对象的内存布局
在HotSpot虚拟机中,Java对象的内存布局分为三部分:对象头(Header)、实例数据(Instance Data)和对齐填充(Padding)。对象头包含Mark Word、Class对象指针及数组长度;实例数据存储对象的实际字段内容;对齐填充用于确保对象大小为8字节的整数倍。
117 0
|
6月前
|
Java 数据库连接 API
Java 对象模型现代化实践 基于 Spring Boot 与 MyBatis Plus 的实现方案深度解析
本文介绍了基于Spring Boot与MyBatis-Plus的Java对象模型现代化实践方案。采用Spring Boot 3.1.2作为基础框架,结合MyBatis-Plus 3.5.3.1进行数据访问层实现,使用Lombok简化PO对象,MapStruct处理对象转换。文章详细讲解了数据库设计、PO对象实现、DAO层构建、业务逻辑封装以及DTO/VO转换等核心环节,提供了一个完整的现代化Java对象模型实现案例。通过分层设计和对象转换,实现了业务逻辑与数据访问的解耦,提高了代码的可维护性和扩展性。
248 1
|
6月前
|
前端开发 Java 数据库连接
java bo 对象详解_全面解析 java 中 PO,VO,DAO,BO,POJO 及 DTO 等几种对象类型
Java开发中常见的六大对象模型(PO、VO、DAO、BO、POJO、DTO)各有侧重,共同构建企业级应用架构。PO对应数据库表结构,VO专为前端展示设计,DAO封装数据访问逻辑,BO处理业务逻辑,POJO是简单的Java对象,DTO用于层间数据传输。它们在三层架构中协作:表现层使用VO,业务层通过BO调用DAO处理PO,DTO作为数据传输媒介。通过在线商城的用户管理模块示例,展示了各对象的具体应用。最佳实践包括保持分层清晰、使用工具类转换对象,并避免过度设计带来的类膨胀。理解这些对象模型的区别与联系。
457 1