环境:jdk18
今天在看Java string类的equals源码,源码主要逻辑比较好理解:先判断是否是同一对象,是就直接返回true,否则判断类型是否是string类型,且每一个元素内容是否相同(先判断length,再判断内容)
在使用断点debug时发现
问题1:
return (anObject instanceof String aString) && (!COMPACT_STRINGS || this.coder == aString.coder) && StringLatin1.equals(value, aString.value);
是循环运行的,且有的时候value与aString.value的数组长度就不一样(哪怕字符相等,如"a".equals("a")
)
问题2:
"a".equals(new String("a"));
传到equals后,参数如图所示"a".equals("a");
传到equals后,参数如图所示但是上述两行代码,正常情况下传值后不应该是"a"吗?
上述两个问题,实在想不通,希望大佬可以解惑一下
在这里有完整的 String 源码(好像是 JDK13 的)。
很容易搜索到 COMPACT_STRINGS 的定义和说明:
/** * If String compaction is disabled, the bytes in {@code value} are * always encoded in UTF16. * * For methods with several possible implementation paths, when String * compaction is disabled, only one code path is taken. * * The instance field value is generally opaque to optimizing JIT * compilers. Therefore, in performance-sensitive place, an explicit * check of the static boolean {@code COMPACT_STRINGS} is done first * before checking the {@code coder} field since the static boolean * {@code COMPACT_STRINGS} would be constant folded away by an * optimizing JIT compiler. The idioms for these cases are as follows. * * For code such as: * * if (coder == LATIN1) { ... } * * can be written more optimally as * * if (coder() == LATIN1) { ... } * * or: * * if (COMPACT_STRINGS && coder == LATIN1) { ... } * * An optimizing JIT compiler can fold the above conditional as: * * COMPACT_STRINGS == true => if (coder == LATIN1) { ... } * COMPACT_STRINGS == false => if (false) { ... } * * @implNote * The actual value for this field is injected by JVM. The static * initialization block is used to set the value here to communicate * that this static final field is not statically foldable, and to * avoid any possible circular dependency during vm initialization. */ static final boolean COMPACT_STRINGS; static { COMPACT_STRINGS = true; }
这一段说明大致上可以看明白,如果 COMPACT_STRINGS
是 false
,那 value
固定是按 UTF16 进行编码的。而且,大致可以猜到跟 coder
相关。
然后关于 coder
,可以找到对应的源码
/** * The identifier of the encoding used to encode the bytes in * {@code value}. The supported values in this implementation are * * LATIN1 * UTF16 * * @implNote This field is trusted by the VM, and is a subject to * constant folding if String instance is constant. Overwriting this * field after construction will cause problems. */ private final byte coder;
其实就两个值,分别表示 LATIN1
和 UTF16
。Java 的字段和函数(方法)是可以同名的,所以除了字段 coder 外,也可以找到函数 coder()。这个就很好理解了:
byte coder() { return COMPACT_STRINGS ? coder : UTF16; }
那么这句话就好理解了:
(!COMPACT_STRINGS || this.coder == aString.coder)
如果 COMPACT_STRINGS == false
,那就是按 UTF16,继续看下一个条件。如果这个条件不成立,就要看 coder
是否相等,如果不等,那直接判“否”。这里如果不好理解,可以自己手写代码,把这个逻辑判断拆开来理解。
boolean flag = false;if (!COMPACT_STRINGS) { flag = true; // 根据 COMPACT_STRINGS 的说明,这种情况下使用 UTF16,忽略 coder 值} else if (this.coder == aString.coder) { flag = true; // 说明 coder 一致}
然后下一个条件,StringLatin1.equals(value, aString.value)
,直接使用 Latin1 编码规则来对字符串的内部数据 value
来进行比较。至于 value 是什么,代码里也很清楚的写了,就是用来存储字符的。
/** * The value is used for character storage. * * @implNote This field is trusted by the VM, and is a subject to * constant folding if String instance is constant. Overwriting this * field after construction will cause problems. * * Additionally, it is marked with {@link Stable} to trust the contents * of the array. No other facility in JDK provides this functionality (yet). * {@link Stable} is safe here, because value is never null. */ @Stable private final byte[] value;
所以整个比较的逻辑就出来了
2024-03-03 17:33:32 补充回答评论
“如果 COMPACT_STRINGS == false,那就是按 UTF16”,但是在jdk13这个源码中,只用了StringLatin1.equals,utf16是如何进行比较的?
UTF16 如何进行比较我,说实话我也没明白。不过只要确定了编码规则一样,而且 StringLatin1 是按字节进行对比的话,那其实并不需要关注它本身是什么编码规则。毕竟按字节对比是最底层的方法。当然前提是去看 StringLatin1 代码看看他的实现是不是跟假设的一样。
是循环运行了很多次,但它本质上是return,并没有什么循环语句,为啥会循环运行?
在断点debug的时候,发现"a".equals("a"),传递的Object anObject参数是GBK,请问这又是为什么
并不是循环,如果在 "a".equals("a")
的时候发现了需要比较的是 "GBK"
,那说明在比较的过程中有编码的比较。这里产生的比较似乎只有在 StringLatin1.equals
中才会发生。所以到底是怎么回事,可能还是要看看 StringLatin1
的源代码。另外,既然 DEBUG 跟踪了,那可以看看调用栈,并且在调用栈中逐级去查找调用点的代码。
在 Java 中,String
类的 equals
方法被设计用来比较两个字符串对象的内容是否相等。在 Java 8 及以后的版本中,String
类的 equals
方法的实现采用了优化,这种优化主要体现在对字符串内部编码的处理上。
首先,equals
方法会检查传入的对象是否与当前对象是同一个对象(即它们的引用是否相同),如果是,那么直接返回 true
。
if (anObject == this) { return true;}
接下来,equals
方法会检查传入的对象是否是 String
类型:
if (!(anObject instanceof String)) { return false;}
如果传入的对象是 String
类型,那么 equals
方法会进一步检查当前字符串和传入字符串的编码是否相同。这里涉及到 Java 字符串的内部表示。Java 字符串内部可能使用两种编码方式:UTF-16 或 Latin1。这个检查是为了确保在比较字符串内容时,使用的是相同的编码方式。
final String anotherString = (String)anObject;int len1 = value.length;int len2 = anotherString.value.length;if (len1 != len2) { return false;}
上面的代码段中,value
和 anotherString.value
是字符串的内部字符数组。如果两个字符串的长度不同,那么它们的内容肯定不同,因此直接返回 false
。
接下来,equals
方法会根据字符串的编码方式(UTF-16 或 Latin1)来比较字符数组的内容。如果编码方式相同,并且字符数组的内容也相同,那么返回 true
,否则返回 false
。
if (COMPACT_STRINGS) { if (coder != anotherString.coder) { return false; } return StringUTF16.equals(value, anotherString.value);} else { return StringLatin1.equals(value, anotherString.value);}
这里需要注意的是,COMPACT_STRINGS
是一个布尔值,用于指示是否使用紧凑字符串表示。紧凑字符串表示是 Java 9 中引入的一种优化,它减少了字符串对象的内存占用。
总的来说,Java String
类的 equals
方法的设计是为了高效地比较两个字符串对象的内容是否相等。它通过一系列的检查和优化,确保在比较字符串内容时能够尽可能地减少不必要的计算。
问题内容: 关于启动应用程序作为入口点,主要方法是Java应用程序中最重要的方法。使用此方法之前会发生什么尚不清楚。请有人可以通过以下方法签名来纠正我的看法,从而帮助我理解/阐明使用该方法之前发生的情况: JVM创建至少一个将访问您的主方法的对象。这个(假定的)对象尝试根据该API来访问Java应用程序,该API显然会将您绑定到已知的方法签名 您是否不能限制JVM上的(假定的)单独对象访问包含主要
问题内容: 请看下面的代码: 在上面的代码中,在方法ModifyList()中声明的匿名内部类的实例能够访问传递给该方法的参数。AFAIK Java为内部类创建一个单独的字节码文件。 谁能解释一下Java在字节码级别上如何处理这些局部变量绑定?我的意思是,Java如何精确跟踪对作为参数传递给该方法的对象的引用? 任何帮助将不胜感激! [抱歉我的英语不好! 如果您理解我的问题,请编辑这篇文章,并删除
我正在REST API中执行一个方法,您可以通过其ID获取出版物上的注释。CommentServiceImplementation中出现错误,因为它无法解析方法等于(long) 评论服务实施 我首先找到了出版物。然后我找到了评论。最后,如果与发布相关联的注释ID与发布的ID不同,则该注释不属于所述发布。如果是,我会将注释作为DTO返回。据我所知,问题一定是出版物。getId()返回一个长的和注释。
本文向大家介绍Java内部类详解,包括了Java内部类详解的使用技巧和注意事项,需要的朋友参考一下 内部类 (一) 概述 把类定义在另一个类的内部,该类就被称为内部类。 举例:把类Inner定义在类Outer中,类Inner就被称为内部类。 (二) 内部类的访问规则 A:可以直接访问外部类的成员,包括私有 B:外部类要想访问内部类成员,必须创建对象 (三) 内部类的分类 A:成员内部类
本文向大家介绍深入理解Java嵌套类和内部类,包括了深入理解Java嵌套类和内部类的使用技巧和注意事项,需要的朋友参考一下 一、什么是嵌套类及内部类 可以在一个类的内部定义另一个类,这种类称为嵌套类(nested classes),它有两种类型:静态嵌套类和非静态嵌套类。静态嵌套类使用很少,最重要的是非静态嵌套类,也即是被称作为内部类(inner)。嵌套类从JDK1.1开始引入。其中inner类
本文向大家介绍Java Float类equals()方法与示例,包括了Java Float类equals()方法与示例的使用技巧和注意事项,需要的朋友参考一下 Float类方法 equals()方法在java.lang包中可用。 equals()方法用于检查该对象与给定对象的相等性或不相等性,换句话说,可以说该方法用于比较两个对象。 equals()方法是一种非静态方法,只能通过类对象访问,如果尝