m+n等于1求m-n等于多少,m加n的绝对值等于多少

首页 > 体育 > 作者:YD1662023-11-06 04:52:53

了解 java 中 == 运算符和 equals() 方法之间的主要区别,并了解在 Java 编程中如何有效地使用它们进行对象比较。

public class Demo { public static void main(String args[]){ String a = "a" "b" 1; String b = "ab1"; System.out.println(a == b); } }

根据上面的代码,请看下列问题:

a和b在内存中的表示是什么?

编译时有什么优化策略吗?

“==”

在Java语言中,==用于比较两个内存单元的内容是否相同。

对于诸如 byte、boolean、short、char、int、long、float 和 double 等基本数据类型,比较的是它们的值。

对于引用类型,比较的是引用的值,可以将其视为对象的逻辑地址。

当 == 运算符应用于两个引用类型时,它会比较两个对象各自的内存地址是否相同。

也就是说,如果两个引用指向同一个对象,则返回true;否则,返回 false。

public class Demo { public static void main(String args[]){ List<String> a = null; List<String> b = null; System.out.println(a == b); } } 结果是:true“equals”

下面是Object类中equals()的源代码。

public boolean equals(Object obj) { return (this == obj); }

换句话说,如果没有重写 equals() 方法,并且相应类的父类也没有重写 equals() 方法,则 equals() 默认就是比较对象的地址。

equals()方法的存在是为了鼓励子类重写它,从而实现基于值的比较。

掌握了基础知识后,现在让我们解决文章开头提出的两个问题。

a和b在内存中的表示是什么?

由于 Java 的编译优化策略,a 和 b 都引用相同的内存位置。 我们可以使用JD-GUI反编译工具来检查反编译后的代码

public class Demo { public static void main(String[] args) { String a = "ab1"; String b = "ab1"; System.out.println(a == b); } } 结果是:true

到这里,结果应该就很清楚了。(==比较的内存地址) JVM在编译时对常量折叠进行了优化,因为常量折叠的结果是一个固定值,不需要运行时计算,所以通过这种方式进行优化。

不过,不要急于下结论。 JVM 只优化它能优化的部分。它并没有优化所有方面。

例如,前面提到的字符串连接。如果字符串的连接过程中有变量,意味着编译时不知道具体值,则 JVM 不会执行这种编译时合并。

如果你已经理解了上一段的意思,我们再来看另一个例子:

public class Demo { public static void main(String args[]){ String a = "a"; final String c ="a"; String b = a "b"; String d = c "b"; String e = getA() "b"; String compare = "ab"; System.out.println( b == compare); System.out.println( d == compare); System.out.println( e == compare); } private static String getA(){ return "a"; } } 结果是: false true false

根据我们之前的解释,很容易理解为什么 b == Compare 和 e == Compare 会产生错误结果。

这是因为 a 和 getA() 不是常量,所以并且没有对此进行编译时优化。相反,c是一个final的不可变的常量,所以进行了编译优化。

我们可以通过使用 JD-GUI 检查反编译代码来验证这一点:

import java.io.PrintStream; public class Demo { public static void main(String[] args) { String a = "a"; String c = "a"; String b = a "b"; String d = "ab"; String e = getA() "b"; String compare = "ab"; System.out.println(b == compare); System.out.println(d == compare); System.out.println(e == compare); } private static String getA() { return "a"; } }

从反编译的代码中,我们可以确认b和e没有经过JVM编译优化。

看到的是变量 d 已经被 JVM 优化了。 关键的区别在于变量 c 用 Final 修饰符修饰。 该声明对 c 的不可变性施加了严格的约束,并且由于 Final 意味着不可变性,因此编译器自然地知道结果也是不可变的。

字符串在内存里怎么存储?

m+n等于1求m-n等于多少,m加n的绝对值等于多少(1)

字符串对象内部使用字符数组进行存储。

现在,让我们看一下以下示例:

String m = "hello,world"; String n = "hello,world"; String u = new String(m); String v = new String("hello,world");

这段代码在内存中怎么分配?大致是这样的:

结论是m和n是同一个对象,而m、u和v是不同的对象,但它们都共享相同的字符数组。

使用equals()方法进行比较时,也会返回true。

我们可以使用反射来修改字符数组来验证效果。

public class Demo { public static void main(String args[]) throws NoSuchFieldException, IllegalAccessException { String m = "hello,world"; String n = "hello,world"; String u = new String(m); String v = new String("hello,world"); Field f = m.getClass().getDeclaredField("value"); f.setAccessible(true); char[] cs = (char[]) f.get(m); cs[0] = 'H'; String p = "Hello,world"; System.out.println(m.equals(p)); System.out.println(n.equals(p)); System.out.println(u.equals(p)); System.out.println(v.equals(p)); } } 结果是: true true true true

从上面的例子中,我们可以看到,通常说字符串是不可变的时,这意味着对字符串的引用是不可更改的,类似于其他final类。

虽然String类没有暴露它的value字段,但是仍然可以通过反射来修改它。

关于String中的intern()方法

public class Demo { public static void main(String args[]){ String a = "a"; String b = a "b"; String c = "ab"; String d = new String(b); System.out.println(b == c); System.out.println(c == d); System.out.println(c == d.intern()); System.out.println(b.intern() == d.intern()); } } 结果是: false false true true

String引用指向的对象存储在常量池中,保证具有相同值的字符串全局唯一。

如何确保这种全局唯一性?

当调用 intern() 方法时,JVM 使用 equals() 方法检查常量池中是否存在具有相等值的 String。

如果找到,则返回常量池中 String 对象的引用。如果没有找到匹配的字符串,它会创建一个具有相同值的新字符串,并在常量池中返回对此新字符串的引用。

只要两个字符串相同,对它们调用 intern() 就会产生对常量池中相应 String 的引用。 因此,在调用intern()之后,可以使用相等运算符来匹配两个字符串

栏目热文

文档排行

本站推荐

Copyright © 2018 - 2021 www.yd166.com., All Rights Reserved.