Java表达式的陷阱——关于字符串的陷阱

core_analyzer分析c++程序内存占用 环境:centos; 进程:单进程多线程c++程序; 问题:1. 分析系统内存占用; 2. 分析ptmalloc内存空洞(bins上的空闲chunk的大小); 包括:1. 占用了很大内存的c++进程; 2. gcore(在进程不core dump的情况下生成core文件); 3. core_analyzer(分析gcor... 阅读详情

1、关于字符串的陷阱

       字符串是Java程序中使用最广泛的一种对象,虽然它具有简单易用的特征,但是实际使用字符串时也会有一些潜在的陷阱,这些陷阱往往会给实际开发带来潜在的困扰。

1、1 JVM对字符串的处理

     String str = new String("Java对象");
       对于上面常见的Java语句,常见的问题是,上面语句创建了几个字符串?答案是:上面的语句实际上创建了两个字符串对象,其中一个是“Java对象”这个直接量对应的字符串对象,另一个是有new String()构造器返回的字符串对象。
提示:Java程序中创建对象的常见方式有如下四种。
  1. 通过new调用构造器创建Java对象
  2. 通过Class对象的newInstance()方法调用构造器创建Java对象
  3. 通过Java的反序列化机制从IO流中恢复Java对象
  4. 通过Java对象提供的clone()方法复制一个Java对象
       对于java程序中的字符串直接量,JVM会使用一个字符串池来保存它们:当第一次使用某个字符串直接量时,JVM会将它放入字符串池进行缓存。在一般情况下,字符串池中的字符串对象不会被垃圾回收,当程序再次需要使用该字符串时,无须重新创建一个新的字符串,而是直接让引用变量指向字符串池中已有的字符串。
public class StringTest {
	public static void main(String[] args) {
		String str1 = "Java对象";
		String str2 = "Java对象";
		System.out.println(str1 = str2);
	}
}
输出结果:
true
       从输出的结果可以看出,str1和str2都指向JVM字符串池中的"Java对象",同时也可以知道两个变量都是指向同一块内存空间。

       在之前的学习中可以知道,除了通过字符串直接量创建字符串对象之外,还可以通过字符串连接表达式来创建字符串对象。如果这个字符串连接表达式的值可以在编译时确定下来,那么JVM会在编译时计算该字符串变量的值,并让它指向字符串池中对应的字符串。
public class StringJoinTest2 {
	public static void main(String[] args) {
		String str1 = "Java对象的长度:6";
		String str2 = "Java" + "对象" + "的长度:" + 6;
		System.out.println(str1 == str2);
	}
}
输出结果:
true       
       上面程序中的定义的str2是一个字符串连接表达式,但是由于字符串连接表达式可以在编译时就确定下来,因此JVM将在编译时计算str2的值,并让str2指向字符串池中对应的字符串。因此,上面程序最终输出的是true。
注意:在创建str2字符串变量时,它们都是字符串直接量、整数直接量,没有变量参与,没有方法调用。因此,JVM可以在编译时就确定该字符串连接表达式的值,可以让该字符串变量指向字符串池中对应的字符串。但是如果程序使用了变量或者调用了方法,那就只能等到运行时才能确定字符串连接表达式的值,也就无法再编译时就确定该字符串比那辆的值,因此无法利用JVM的字符串池。
public class StringJoinTest3 {
	public static void main(String[] args) {
		String str1 = "Java对象的长度:6";
		String str2 = "Java" + "对象" + "的长度:" + "Java对象".length();
		System.out.println(str1 == str2);
		
		int len = 6;
		String str3 = "Java" + "对象" + "的长度:" + len;
		System.out.println(str1 == str3);
	}
}
输出结果为:
false
false
       上面程序中str2变量对应的连接表达式中包含了一个方法的调用,因此程序无法再编译时确定str2变量的值,也就是不会让str2指向JVM字符串池中对应的字符串。类似地,str3的值也是字符串连接表达式,但由于这个字符串连接表达式中包含了一个len变量,因此str3变量也不会指向JVM字符串池中对应的字符串。
       有一种情况例外,如果字符串连接运算中的所有变量都可以执行“宏替换”,那么JVM一样可以在编译时确定字符串连接表达式的值,一样会让字符串变量指向JVM字符串池中的对应字符串。
public class StringJoinTest4 {
	public static void main(String[] args) {
		String str1 = "Java对象的长度:6";
		final String s1 = "Java";
		String str2 = s1 + "对象" + "的长度:6";
		System.out.println(str1 == str2);
		
		final int len = 6;
		String str3 = "Java" + "对象" +"的长度:" + len;
		System.out.println(str1 == str3);
	}
}
输出结果为:
true
true
总结:当程序中需要使用该字符串、基本类型包装类实例时,应该尽量使用字符串直接量、基本类型值的直接量,避免通过new String()、new Integer()的形式来创建,这样能保证有更好的性能。

1、2 不可变的字符串

       String类是一个典型的不可变类。当一个String对象创建完成后,该String类包含的字符序列就被固定下来,以后永远不能改变。
public class ImmutableString {
	public static void main(String[] args) {
		String str = "Hello";  //①
		System.out.println(System.identityHashCode(str));
		str = str + "Java对象";  //②
		System.out.println(System.identityHashCode(str));
	}
}
输出结果为:
134683287
174240671

       从上面两幅图中可以看出,str变量原来指向的字符串对象并没有发生任何改变,所包含的字符串序列依然是"Hello",只是str变量不再指向它而已。换句话来说,发生改变的不是String对象,而是str变量本身,它改变了指向,指向了一个新的String对象。
注意:System提供identityHashCode()静态方法用于获取某个对象的唯一的hashCode值,这个identityHashCode()方法返回值与该类是否重写了hashCode()方法无关。只有当两个对象相同时,它们的identityHashCode值才会相等。
       对于String类而言,它代表字符串序列不可改变的字符串,因此如果程序需要一个字符序列会发生改变的字符串,应该考虑使用StringBuilder和StringBuffer。实际上通常应该优先考虑使用StringBuilder。StringBuilder和StringBuffer区别在于,StringBuilder是线程不安全的,但StringBuffer是线程安全的,也就是说,其中大部分方法都增加了synchronized修饰符。对方法增加了synchronized修饰符可以保证该线程安全,但是会降低方法执行效率。
public class MutableString {
	public static void main(String[] args) {
		StringBuilder str = new StringBuilder("Hello");
		System.out.println(str);
		System.out.println(System.identityHashCode(str));
		
		str.append("Java对象");
		System.out.println(str);
		System.out.println(System.identityHashCode(str));
	}
}
输出结果为:
Hello
134683287
HelloJava对象
134683287

1、3 字符串比较

       如果程序需要比较两个字符串是否相同,用==进行判断就可以了;但如果要判断两个字符串所包含的字符序列是否相同,则应该使用String重写过的equals()方法进行比较。
//String源代码
public boolean equals(Object anObject) {
    if (this == anObject) {
        return true;
    }
    if (anObject instanceof String) {
        String anotherString = (String) anObject;
        int n = value.length;
        if (n == anotherString.value.length) {
            char v1[] = value;
            char v2[] = anotherString.value;
            int i = 0;
            while (n-- != 0) {
                if (v1[i] != v2[i])
                        return false;
                i++;
            }
            return true;
        }
    }
    return false;
}
       从String类源代码来看,字符串底层实际上采用一个字符数组来保存该字符串所包含的字符序列,如果当前面字符串和被比较字符串底层的字符数组所包含的字符序列完全相同,程序通过equals()方法判断两个字符串是否相等就会返回true。
       除此之外,由于String类还实现了Comparable接口,因此程序还可通过String提供的compareTo()方法来判断两个字符串之间的大小。当两个字符串所包含的字符序列相同时,程序通过compareTo()方法比较,将返回0。
public class StringCompare {
	public static void main(String[] args) {
		String s1 = new String("abc");
		String s2 = new String("def");
		String s3 = new String("abc");
		if ( s1.compareTo(s3) == 0 ) {
			System.out.println("s1和s3包含的字符序列相同");
		}
		if( s1.compareTo(s2) < 0 ) {
			System.out.println("s1小于s2");
		}
		System.out.println("s1和s3包含的字符序列相同:" + s1.equals(s3));
		System.out.println("s1和s3包含的字符序列相同:" + (s1 == s3));
	}
}
输出结果为:
s1和s3包含的字符序列相同
s1小于s2
s1和s3包含的字符序列相同:true
s1和s3包含的字符序列相同:false





智能制造——解读AI+工业设备预测性维护解决方案【附全文阅读】 摘要:本方案面向设备密集型制造企业,提供基于AI的预测性维护解决方案。通过工业物联网、大数据和AI算法,整合传感器数据与专家知识库,构建异常检测、故障诊断等模型,实现设备台账管理、智能点检等核心功能。方案配套软硬件工具矩阵,已在锂电子等行业落地应用,可降低维护成本30%以上,减少非计划停机50%,助力企业智能化转型。 阅读详情

相关推荐

VS2022跨平台开发实战:SSH远程调试Linux程序全攻略

本文详细介绍了如何使用Visual Studio 2022进行跨平台开发,通过SSH协议远程调试部署在Linux服务器上的C/C++程序。文章提供了从环境搭建、连接配置、项目创建到编译调试的完整实战指南,帮助Windows开发者无缝衔接Linux环境,显著提升问题定位与开发效率。

weixin_28826521的博客 430

C陷阱与缺陷 第1章 词法“陷阱” 1.5 字符与字符串

C陷阱与缺陷 第1章 词法“陷阱” 1.5 字符与字符串

weixin_客子光阴的博客 171

5分钟搞定!用FunASR在本地快速部署达摩院Paraformer语音识别模型(附常见问题解决)

本文详细介绍了如何在本地快速部署阿里达摩院开源的Paraformer语音识别模型。通过FunASR工具包,用户可在5分钟内完成环境配置、模型下载与基础识别,并掌握批量处理、性能调优及构建API服务等进阶技巧。文章重点解析了Paraformer的非自回归架构带来的速度优势,并提供了音频格式转换、依赖冲突、显存不足等常见问题的实战解决方案,助力开发者实现高效、离线的语音转写应用。

p1q2r的博客 705

c语言字符串输入的陷阱

scanf函数的陷阱

qq_42279154的博客 629

利用OTSU阈值算法在ZYNQ上实现浒苔识别

利用OTSU阈值算法进行图像二值化,结果用HDMI显示

qq_38075564的博客 1275

(保姆式教程:从下数据到画图)python如何利用EOF分析SSTA海温异常现象并画图

保姆式教程,教你如何利用python分析1979-2004年的太平洋地区的海温异常SSTA,并且绘制出空间模态和时间序列的图片。

shayuxing的博客 2万+

C陷阱与缺陷(一)词法“陷阱”、语法“陷阱”

编译器中负责将程序分解为一个一个符号的部分,一般称为“词法分析器”。

Exp.Joker 500

第47节:cesium 热力图效果(含源码+视频)

cesium 热力图效果

qq_29384639的博客 1676

【学习笔记】C缺陷与陷阱

优先级比单目运算符要低的,接下来就是双目运算符,在双目运算符中,算术运算符的优先级最高,移位运算符次之,关系运算符再次之,接着是逻辑运算符,赋值运算符,最后是条件运算符;在所有的运算符中,逗号运算符的优先级最低;典型的链接器把编译器或汇编器生成的若干个目标模块,整合成一个被称为载入模块或可执行文件的实体,该实体能够被操作系统直接执行,其中某些目标模块是直接作为输入提供给链接器的;答:一个异常终止的程序可能没有机会来清空其输出缓冲区,因此,该程序生成的输出可能位于内存的某个位置,但却永远不会被写出了,

368

【第1章>第9节】基于FPGA的图像开运算处理算法的Verilog实现

本文介绍了基于FPGA的开运算图像处理实现方法。主要内容包括:1)输入图像二值化处理,通过阈值将灰度图像转换为二值图像;2)数据缓存与窗口提取,使用寄存器数组缓存5行图像数据并构建5×5处理窗口;3)腐蚀运算逻辑,判断窗口内是否全为白色像素;4)膨胀运算逻辑,检测窗口内是否存在白色像素。最后给出了Verilog实现方案,通过级联腐蚀和膨胀模块完成开运算处理。该方法可用于FPGA图像处理算法开发。

MATLAB,verilog,python,opencv,tensorflow,caffe,C,C++ 315

Fortify代码扫描:Privacy Violation:Heap Inspection漏洞解决方案

该漏洞引发情况: 将敏感数据存储在 String 对象中使系统无法从内存中可靠地清除数据。 如果在使用敏感数据(例如密码、社会保障号码、信用卡号等)后不清除内存,则存储在内存中的这些数据可能会泄漏。通常而言,String是所用的存储敏感数据,然而,由于String对象不可改变,因此用户只能使用 JVM 垃圾收集器来从内存中清除String的值。除非 JVM 内存不足,否则系统不要求运...

初阶农民工的博客 2万+

GEE APP:基于Sentinel-2遥感影像的植被指数(NDVI)和水体指数(NDWI)可视化应用

本文介绍了利用Google Earth Engine(GEE)平台处理Sentinel-2卫星数据的方法,重点讲解NDVI(归一化植被指数)和NDWI(归一化水体指数)的计算与应用。内容包括环境准备、数据加载与处理、指数计算原理、可视化方法以及实际应用案例。教程详细展示了如何定义研究区域、加载影像数据、进行指数运算,并生成专业统计报告和图例。文章还提供了常见问题解答和最佳实践建议,为生态环境监测、农业评估和水资源管理等场景提供了实用工具。通过JavaScript代码示例,读者可以快速掌握GEE平台的遥感影像

此星光明博客 273

java 字符串,表达式,等陷阱

1. 关于字符串的陷阱 java程序中创建对象的常规方式有如下4种: 通过new()调用构造器创建java对象 通过class对象的newInstance()方法调用构造器创建java对象 通过java的反序列化机制从IO流中恢复java对象 通过java对象提供的clone()方法复制一个新的java对象 除此之外,还允许直接量创建java对象,如 String str="a

yuppies_coldplay的博客 516

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

迈瑞BS系列全自动生化分析仪LIS协议接口手册(内含HL7和ASTM)

迈瑞BS系列全自动生化分析仪LIS协议接口手册,内含HL7和ASTM)

Java字符串直接量实参_疯狂Java笔记之表达式中的陷阱

关于字符串的陷阱JVM对字符串的处理首先看如下代码:String java=new String("HelloJack");上面创建了两个字符串对象,其中一个是“HelloJack”这个直接量对应的字符串对象,另一个是由new String()构造器返回的字符串对象。对于Java程序中的字符串直接量,JVM会使用一个字符串池来保存它们:当第一次使用某个字符串直接量是,JVM会将它放入字符串池进行缓...

weixin_36146057的博客 169

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

JAVA面试讲义-表达式中的陷阱

String str1 = "Hello" + "Java" + "长度:" + "Hello Java".length();,当程序再次需要使用的时候,无需重新创建一个新的字符串,而是直接让引用变量指向字符串池中已有的字符串。如果使用final修饰,会进行宏替换,此时在编译时就可以确定下来,也就可以使用JVM字符串池中的值。String str2 = "Hello" + "Java" + "长度:" + len;String str2 = "Hello" + "Java" + "长度:" + len;

weixin_43317933的博客 279

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

JAVA中字符串的split方法有两个陷阱需要注意

JAVA中字符串的split方法有两个陷阱需要注意 在开发过程中,我们会碰到一种场景,需要对字符串进行分割,从而得到更具体的信息。 比如下面这个字符串: String str = "123455#JAVA编程思想#这是一本JAVA圣经级别的书籍!"; 该字符串包含书籍ID、书籍名称以及简介,三条信息用#号连接在一起。 想要得到这三条信息,我们通常使用split方法: String[] strArray = str.split("#"); for (int i = 0; i < strArray.le

ant0209的博客 4752

java 中的陷阱

java中的陷阱 1.字符串陷阱

Jensen 590

表达式中的陷阱

对于大部分Java程序员来说,总有些难以绕过的错误,也许你第一次犯了这个错误后,第二次还会再犯,因为这些错误具有隐蔽性,我们把这种错误称为陷阱。为了避免错误一犯再犯,我们就需要把这些陷阱记录下来,以免在未来的开发之中踩坑。     表达式是Java里最基本的组成单元,但在简单的表达式背后,依然有一些很容易让我们犯错的陷阱。

小林同学的daily 495

关于Java表达式中的陷阱

一、字符串陷阱 1.对于对象的创建方式 (1)通过new关键字调用构造器创建Java对象:String a=new String("ltt")->此语句创建了两个Java对象即“ltt”这个直接量对应的字符串对象和new String构造器返回的字符串对象。 (2)通过Class对象的的newInstance方法调用构造器创建Java对象。 (3)通过Java的反序列化机制从IO流中恢复J

NEU_tt的博客 432

No.5 表达式中的陷阱

1. 关于字符串的陷阱 JVM对字符串的处理 String java = new String("Java");创建了几个对象? 2个。“Java”直接量对应的字符串对象;new String()构造器返回的字符串对象 java.intern()可以字符串池中的直接量对象。 字符串及基本类型的包装类,Java允许通过直接量的方式来创建对应的Java对象;...

146

表达式陷阱(字符串,表达式类型,输入法,注释字符,转义字符,正则表达式)

1.    表达式中的陷阱1.1  关于字符串的陷阱1. 在创建一个String s = new String("java");的时候,JVM会到常量池中去检查看是否有一个"java"对象了,如果没有则在常量池中创建一个。之后会在堆内存中分配了一个空间,放置这个new出来的String对象,形式如下:java.lang.String@123b, 常量池是在编译期生成的,而new一个对象是在运行时进

BornForIT 2516

《java解惑》读书笔记3——更多字符串之谜

1.字符串替换: 问题: 下面这段程序把类全路经名中的"."替换为"/",代码如下: package com.javapuzzlers; public class Test { public static void main(String[] args){ System.out.println(Test.class.getName().replaceAll(".", "/") +

Tony Chen的专栏 2331

25、表达式中的陷阱

1、关于

YGingko的专栏 535

5 表达式中的陷阱整理

本节要点: 表达式类型自动提升的陷阱 复合赋值运算符隐含的类型转换 原始类型带来的泛型擦除 Java 不支持泛型数组 正则表达式中点号(.)匹配任意字符 不要调用线程对象的 run() 方法 静态同步方法的同步监视器是类 多线程执行环境的线程安全问题 1 JVM 对字符串的处理 先看一条创建字符串对象的代码,如下: String str = new String("hell

zhouxukun123的专栏 356

2.表达式中的陷阱

写在前面表达式是Java程序的基本组成单元,这里有一些很容易让人出错的陷阱,例如:》当在程序中使用算术表达式时,表达式类型的自动提升,复合赋值运算符所隐含的类型转换,会给程序带来一I些潜在陷阱》jDK1.5新增的泛型支持也有些陷阱,Java为了兼容以前不用泛型的程序,因此引入了原始类型的概念,原始类型在泛型编程中很容易致错1.JVM对字符串的处理Java程序中创建对象的常见方式有如下四种:&gt;...

Hyo_yew的博客 530
上一篇: Java的内存回收——内存管理小技巧
下一篇: Java表达式的陷阱——表达式类型的陷阱
Never_Blue
博客等级 码龄10年 5粉丝 49原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值