在Java中, String
类封装了char
数组。 简而言之, String
是用于组成单词,句子或所需任何其他数据的字符数组。
封装是面向对象编程中最强大的概念之一。 由于封装的原因,您不需要知道String类是如何工作的。 您只需要知道在其接口上使用什么方法即可。
当您查看Java中的String
类时,可以看到如何封装char
数组:
public String(char value[]) {
this(value, 0, value.length, null);
}
为了更好地了解封装,请考虑一个物理对象:汽车。 您是否需要知道汽车在引擎盖下的运转方式? 当然不是,但是您确实需要知道汽车的接口在做什么:油门,制动器和方向盘之类的东西。 这些接口均支持某些操作:加速,制动,左转,右转。 面向对象编程中是相同的。
我在Java Challengers系列中的第一个博客介绍了方法重载 ,这是String
类广泛使用的一种技术。 重载可以使您的类真正灵活,包括String
:
public String(String original) {}
public String(char value[], int offset, int count) {}
public String(int[] codePoints, int offset, int count) {}
public String(byte bytes[], int offset, int length, String charsetName) {}
// And so on…...
而不是试图了解该String
类作品,该Java挑战者将帮助您了解它做什么 ,以及如何使用它在你的代码。
什么是字符串池?
String
可能是Java中最常用的类。 如果每次使用String
在内存堆中创建一个新对象,我们将浪费大量内存。 String
池通过为每个String
值仅存储一个对象来解决此问题,如下所示。
![该图显示了如何在字符串池中存储字符串。](https://images.idgesg.net/images/article/2018/05/challenger2-strings-100759486-large.jpg)
图1.字符串池中的字符串
尽管我们为Duke
和Juggy
String
创建了一个String
变量,但是只有两个对象被创建并存储在内存堆中。 为了证明这一点,请看下面的代码示例。 (回想一下Java中的“ ==
”运算符用于比较两个对象并确定它们是否相同。)
String juggy = "Juggy";
String anotherJuggy = "Juggy";
System.out.println(juggy == anotherJuggy);
该代码将返回true
因为两个String
指向String
池中的同一对象。 它们的值是相同的。
例外:“新”运算符
现在看一下这段代码,它看起来与前面的示例相似,但是有所不同。
String duke = new String("duke");
String anotherDuke = new String("duke");
System.out.println(duke == anotherDuke);
根据前面的示例,您可能认为此代码将返回true
,但实际上为false
。 添加new
运算符会强制在内存堆中创建新的String
。 因此,JVM将创建两个不同的对象。
本机方法
Java中的本机方法是一种将使用C语言编译的方法,通常用于操纵内存和优化性能。
字符串池和intern()方法
要将String
存储在String
池中,我们使用一种称为String
interning的技术。 这是Javadoc告诉我们有关intern()
方法的内容:
/**
* Returns a canonical representation for the string object.
*
* A pool of strings, initially empty, is maintained privately by the
* class {@code String}.
*
* When the intern method is invoked, if the pool already contains a
* string equal to this {@code String} object as determined by
* the {@link #equals(Object)} method, then the string from the pool is
* returned. Otherwise, this {@code String} object is added to the
* pool and a reference to this {@code String} object is returned.
*
* It follows that for any two strings {@code s} and {@code t},
* {@code s.intern() == t.intern()} is {@code true}
* if and only if {@code s.equals(t)} is {@code true}.
*
* All literal strings and string-valued constant expressions are
* interned. String literals are defined in section 3.10.5 of the
* The Java™ Language Specification.
*
* @returns a string that has the same contents as this string, but is
* guaranteed to be from a pool of unique strings.
* @jls 3.10.5 String Literals
*/ public native String intern();
intern()
方法用于在String
池中存储String
。 首先,它验证您创建的String
是否已经存在于池中。 如果没有,它将在池中创建一个新的String
。 在后台, String
池化的逻辑基于Flyweight模式 。
现在,请注意当我们使用new
关键字强制创建两个String
时会发生什么:
String duke = new String("duke");
String duke2 = new String("duke");
System.out.println(duke == duke2); // The result will be false here
System.out.println(duke.intern() == duke2.intern()); // The result will be true here
与前面带有new
关键字的示例不同,在这种情况下,比较结果为true。 这是因为使用intern()
方法可确保将String
存储在池中。
与String类相等的方法
equals()
方法用于验证两个Java类的状态是否相同。 因为equals()
来自Object
类,所以每个Java类都继承它。 但是必须重写equals()
方法以使其正常工作。 当然, String
会覆盖equals()
。
看一看:
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String aString = (String)anObject;
if (coder() == aString.coder()) {
return isLatin1() ? StringLatin1.equals(value, aString.value)
: StringUTF16.equals(value, aString.value);
}
}
return false;
}
如您所见, String
类值的状态必须为equals()
而不是对象引用。 对象引用是否不同并不重要。 将比较String
的状态。
最常见的String方法
在接受String
比较挑战之前,您需要了解的最后一件事。 考虑一下String
类的以下常见方法:
// Removes spaces from the borders
trim()
// Gets a substring by indexes
substring(int beginIndex, int endIndex)
// Returns the characters length of the String
length()
// Replaces String, regex can be used.
replaceAll(String regex, String replacement)
// Verifies if there is a specified CharSequence in the String
contains(CharSequences)
接受字符串比较挑战!
让我们尝试快速挑战一下您对String
类的了解。
对于这个挑战,您将使用我们探索的概念比较多个String
。 查看下面的代码,您可以确定每个结果变量的最终值吗?
public class ComparisonStringChallenge {
public static void main(String... doYourBest) {
String result = "";
result += " powerfulCode ".trim() == "powerfulCode"
? "0" : "1";
result += "flexibleCode" == "flexibleCode" ? "2" : "3";
result += new String("doYourBest")
== new String("doYourBest") ? "4" : "5";
result += new String("noBugsProject")
.equals("noBugsProject") ? "6" : "7";
result += new String("breakYourLimits").intern()
== new String("breakYourLimits").intern() ? "8" : "9";
System.out.println(result);
}
}
哪个输出代表结果变量的最终值?
答 :02468
乙 :12469
电话 :12579
电话 :12568
在这里检查您的答案。
刚才发生了什么? 了解字符串行为
在代码的第一行,我们看到:
result += " powerfulCode ".trim() == "powerfulCode"
? "0" : "1";
尽管在调用trim()
方法之后, String
将是相同的,但是String
“ powerfulcode “
在开始时是不同的。 在这种情况下,比较是false
,因为当trim()
方法从边界中删除空格时,它将使用new运算符强制创建新的String
。
接下来,我们看到:
result += "flexibleCode" == "flexibleCode" ? "2" : "3";
毫无疑问, String
在String
池中是相同的。 此比较返回true
。
接下来,我们有:
result += new String("doYourBest")
== new String("doYourBest") ? "4" : "5";
使用new
保留关键字强制创建两个新的String
,无论它们是否相等。 在这种情况下,即使String
值相同,比较也将为false
。
接下来是:
result += new String("noBugsProject")
.equals("noBugsProject") ? "6" : "7";
因为我们使用过equals()
方法,所以将比较String
的值而不是对象实例的值。 在那种情况下,对象是否不同并不重要,因为正在比较值。 此比较返回true
。
最后,我们有:
result += new String("breakYourLimits").intern()
== new String("breakYourLimits").intern() ? "8" : "9";
如您之前所见, intern()
方法将String
放入String
池中。 两个String
指向同一个对象,因此在这种情况下,比较为true
。
视频挑战! 调试字符串比较
调试是完全吸收编程概念并改善代码的最简单方法之一。 在此视频中,您可以在调试和解释Java Strings挑战的同时进行以下操作:
字符串常见错误
很难知道两个String
是否指向同一个对象,尤其是当String
包含相同的值时。 记住使用保留关键字new
总是会导致在内存中创建一个新对象,即使值相同。
使用String
方法比较Object
引用也很棘手。 关键是,如果方法更改String
某些内容,则对象引用将不同。
一些示例可以帮助阐明:
System.out.println("duke".trim() == "duke".trim());;
这种比较将是正确的,因为trim()
方法不会生成新的String
。
System.out.println(" duke".trim() == "duke".trim());
在这种情况下,第一个trim()
方法将生成一个新的String
因为该方法将执行其操作,因此引用将有所不同。
最后,当trim()
执行其操作时,它将创建一个新的String
:
// Implementation of the trim method in the String class
new String(Arrays.copyOfRange(val, index, index + len),
LATIN1);
关于字符串要记住的事情
-
String
是不可变的,因此不能更改String
的状态。 - 为了节省内存,JVM将
String
保留在String
池中。 创建新的String
,JVM会检查其值并将其指向现有对象。 如果池中没有具有该值的String
,那么JVM将创建一个新的String
。 - 使用
==
运算符比较对象引用。 使用equals()
方法比较String
的值。 相同的规则将应用于所有对象。 - 当使用
new
运营商,新的String
将在被创建String
池,即使有一个String
具有相同值。
答案键
该Java挑战者的答案是选项D。输出为12568
。
这个故事“ Java中的字符串比较”最初是由JavaWorld发布的 。
翻译自: https://www.infoworld.com/article/3276354/string-comparisons-in-java.html