在Java中,String
该类封装了一个数组char
。简而言之,String
是一个字符数组,用于组合您想要的单词,句子或任何其他数据。
封装是面向对象编程中最强大的概念之一。由于封装的,你不需要知道如何将字符串类的工作; 你只需要知道在它的界面上使用什么方法。
当您String
在Java中查看该类时,您可以看到如何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中最常用的类。如果每次使用a都在内存堆中创建了一个新对象String
,我们就会浪费大量内存。该String
池通过存储仅仅一个对象的每个解决了这个问题String
值,如下所示。
[ 在这个全面的12部分课程中,从初学概念到高级设计模式学习Java!]
图1.字符串池中的字符串
虽然我们String
为Duke
和Juggy
String
s 创建了一个变量,但只创建了两个对象并将其存储在内存堆中。有关证明,请查看以下代码示例。(回想一下,==
Java 中的“ ”运算符用于比较两个对象并确定它们是否相同。)
String juggy = "Juggy";
String anotherJuggy = "Juggy";
System.out.println(juggy == anotherJuggy);
此代码将返回,true
因为两个String
s指向String
池中的同一对象。他们的价值是一样的。
现在看看这段代码 - 它看起来与前面的示例类似,但是有区别。
String duke = new String("duke");
String anotherDuke = new String("duke");
System.out.println(duke == anotherDuke);
根据前面的示例,您可能会认为此代码会返回true
,但实际上是这样false
。添加new
运算符会强制String
在内存堆中创建新的。因此,JVM将创建两个不同的对象。
甲本地方法在Java中是将利用C语言进行编译,通常用于操纵存储器和优化性能的目的的方法。
要String
在String
池中存储,我们使用一种称为String
实习的技术。以下是Javadoc告诉我们的intern()
方法:
/ **
*返回字符串对象的规范表示。
*
*一个字符串池,最初是空的,由私人维护
* class {@code String}。
*
*调用实习方法时,如果池已包含
*字符串等于此{@code String}对象,由...确定
* {@link #equals(Object)}方法,然后是池中的字符串
* returned. Otherwise,将此{@code String}对象添加到
*池和对此{@code String}对象的引用被返回。
*
*对于任何两个字符串{@code s}和{@code t},
* {@code s.intern()== t.intern()}是{@code true}
*当且仅当{@code s.equals(t)}为{@code true}时。
*
*所有文字字符串和字符串值常量表达式
*实习。字符串文字在3.10.5节中定义
* Java™语言规范。
*
* @returns一个与该字符串具有相同内容的字符串,但是
*保证来自一串独特的字符串。
* @jls 3.10.5字符串文字
* / public native String intern();
该intern()
方法用于String
在String
池中存储s 。首先,它验证String
您创建的池中是否已存在。如果没有,它会String
在池中创建一个新的。在幕后,String
汇集的逻辑基于Flyweight模式。
现在,请注意当我们使用new
关键字强制创建两个String
s 时会发生什么:
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
关键字示例不同,在这种情况下,比较结果是正确的。那是因为使用该intern()
方法可确保String
s将存储在池中。
该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
类的常用方法:
// 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
使用我们探索过的概念来比较一些s。查看下面的代码,您可以确定每个结果变量的最终值吗?
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);
}
}
哪个输出代表结果变量的最终值?
A:02468
B:12469
C:12579
D:12568
在这里检查你的答案。
在代码的第一行,我们看到:
result += " powerfulCode ".trim() == "powerfulCode"
? "0" : "1";
尽管在调用方法String
后它们将是相同的trim()
,但String
“ powerfulcode “
在开始时它是不同的。在这种情况下进行比较false
,因为当trim()
方法从边界移除空格时,它会强制String
使用new运算符创建new。
接下来,我们看到:
result += "flexibleCode" == "flexibleCode" ? "2" : "3";
这里没什么神秘之处,游泳池里的String
s也是一样的String
。这种比较返回true
。
接下来,我们有:
result += new String("doYourBest")
== new String("doYourBest") ? "4" : "5";
使用new
保留关键字强制创建两个新的String
s,无论它们是否相等。在这种情况下,false
即使String
值相同,也将进行比较。
接下来是:
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
。
可能很难知道两个String
s是否指向同一个对象,尤其是当String
s包含相同的值时。有助于记住,使用保留关键字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
s是不可变的,所以String
不能改变状态。String
s 保留在String
池中。String
创建new时,JVM会检查其值并将其指向现有对象。如果String
池中没有该值,则JVM会创建一个新值String
。==
运算符比较对象引用。使用该equals()
方法比较的值String
。相同的规则将应用于所有对象。new
运算符时,即使存在具有相同值的值,String
也会在String
池中创建新的运算符String
。这个Java Challengers的答案是选项D.输出将是12568
。