如何解决使用新运算符 用例
使用 new 运算符创建了多少字符串。
假设我正在使用 new 运算符创建一个字符串。
String str = new String("Cat")
它是否会创建 2 个字符串,一个在堆中,另一个在字符串池中?
如果它也在 string poll
中创建字符串,那么字符串 intern
方法的目的是什么?
解决方法
有多少对象?
它是否会创建 2 个字符串,一个在堆中,另一个在字符串池中?
当您编写 "Cat"
时,您最终会用 Cat
填充池,并且 "Cat"
调用从池中加载此对象。这通常在编译时就已经发生了。然后 new String(...)
将创建一个新的字符串对象,完全忽略池。
所以这个片段导致了两个对象的创建。为了消除您的困惑,请考虑以下示例:
String first = "Cat";
String second = "Cat";
String third = "Cat";
String fourth = new String("Cat");
这里也创建了两个对象。所有 "Cat"
调用都会从池中加载字符串,因此 first == second == third
和 fourth
将是它自己的对象,因为它使用了 new
,这总是导致创建一个新对象,绕过任何类型的缓存机制。
对象是在堆上还是在栈上创建并没有真正定义。内存管理完全取决于 JVM。
字符串池详细信息
对于大多数 Java 实现,字符串池已在编译期间创建和填充。当您编写 "Cat"
时,编译器会将表示 Cat
的字符串对象放入此池中,并且代码中的 "Cat"
将通过从池中加载此对象来替换。当您反汇编编译的程序时,您可以很容易地看到这一点。例如源代码:
public class Test {
public static void main(String[] args) {
String foo = "Hello World";
}
}
反汇编(javap -v
):
Classfile /C:/Users/Zabuza/Desktop/Test.class
Last modified 30.03.2021; size 277 bytes
SHA-256 checksum 83de8a7326af14fc95fb499af090f9b3377c56f79f2e78b34e447d66b645a285
Compiled from "Test.java"
public class Test
minor version: 0
major version: 59
flags: (0x0021) ACC_PUBLIC,ACC_SUPER
this_class: #9 // Test
super_class: #2 // java/lang/Object
interfaces: 0,fields: 0,methods: 2,attributes: 1
Constant pool:
#1 = Methodref #2.#3 // java/lang/Object."<init>":()V
#2 = Class #4 // java/lang/Object
#3 = NameAndType #5:#6 // "<init>":()V
#4 = Utf8 java/lang/Object
#5 = Utf8 <init>
#6 = Utf8 ()V
#7 = String #8 // Hello World
#8 = Utf8 Hello World
#9 = Class #10 // Test
#10 = Utf8 Test
#11 = Utf8 Code
#12 = Utf8 LineNumberTable
#13 = Utf8 main
#14 = Utf8 ([Ljava/lang/String;)V
#15 = Utf8 SourceFile
#16 = Utf8 Test.java
{
public Test();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
Code:
stack=1,locals=1,args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 1: 0
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: (0x0009) ACC_PUBLIC,ACC_STATIC
Code:
stack=1,locals=2,args_size=1
0: ldc #7 // String Hello World
2: astore_1
3: return
LineNumberTable:
line 3: 0
line 4: 3
}
SourceFile: "Test.java"
如你所见,有
#7 = String #8 // Hello World
#8 = Utf8 Hello World
并且方法中的部分被替换为
0: ldc #7
从字符串池中加载 Hello World
。
字符串实习
字符串实习生方法的目的是什么?
好吧,它使您可以根据池中的版本交换字符串。并用您的字符串填充池,以防它之前不存在。例如:
String first = "hello"; // from pool
String second = new String("hello"); // new object
String third = second.intern(); // from pool,same as first
System.out.println(first == second); // false
System.out.println(first == third); // true
用例
不过,我还没有看到这个功能的真实世界应用程序。
但是,我可以想到一个用例,您可以在应用程序中动态创建长字符串,并且您知道它们稍后会再次出现。然后,您可以将字符串放入池中,以便在稍后再次出现时减少内存占用。
假设您从 HTTP 响应中收到一些长字符串,并且您知道这些响应在大多数情况下完全相同,并且您还想将它们收集在 List
中:
private List<String> responses = new ArrayList<>();
...
public void receiveResponse(String response) {
...
responses.add(response);
}
如果没有实习,您最终会在您的记忆中保留每个字符串实例,包括重复项。但是,如果您实习,则内存中不会有重复的字符串对象:
public void receiveResponse(String response) {
...
String responseFromPool = response.intern();
responses.add(responseFromPool);
}
当然,这有点做作,因为您也可以在这里使用 Set
代替。
<asp:Panel ID="pnlOrders" runat="server">
<asp:Image ID="LiveOrders" alt="Live Gif" runat="server" class="extrasButton" ImageUrl="~/files/images/liveOrders.gif" />
<asp:SqlDataSource ID="DSOrders" runat="server" ConnectionString="<%$ ConnectionStrings:DBConnectionString %>" SelectCommand="SELECT TOP (100) PERCENT tblOrders.OrderID,tblOrders.stallmessage,tblOrders.price,tblAccounts.city,tblAccounts.postcode,tblOrders.phoneNo,tblOrders.tblNo,tblOrders.info,tblOrders.orderDate,tblOrders.orderStatus,tblOrders.type,tblOrders.timeFor,tblOrders.paid,tblOrders.tblNo
FROM tblOrders INNER JOIN tblAccounts ON tblOrders.accountID = tblAccounts.AccountID
WHERE tblOrders.orderStatus='Completed'
ORDER BY tblOrders.timeFor ASC">
</asp:SqlDataSource>
<asp:GridView ID="gdvOrders" width="100%" runat="server" style="font-size:1.5em" ShowHeaderWhenEmpty="True" EmptyDataText="No orders" AllowPaging="True" AutoGenerateColumns="False" CssClass="mGrid" DataKeyNames="orderID" DataSourceID="DSOrders" PageSize="20" AllowSorting="True">
<AlternatingRowStyle CssClass="alt" />
<Columns>
<asp:TemplateField HeaderText="Order">
<ItemTemplate>
<asp:Label ID="Label1" runat="server" Text='<%# Eval("stallMessage") %>' />
</ItemTemplate>
</asp:TemplateField>
<asp:BoundField DataField="price" HeaderText="Price" />
<asp:BoundField DataField="phoneNo" HeaderText="Phone No" />
<asp:TemplateField HeaderText="Address/Table No.">
<ItemTemplate>
<asp:Label ID="Label2" runat="server" Text='<%# Eval("tblNo") %>' />
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField ItemStyle-ForeColor="Red" HeaderText="Note">
<ItemTemplate>
<asp:HyperLink ID="hypNote" style="Font-Size:20px; color:Red;" runat="server" NavigateUrl='<%# "~/editNote.aspx?note=" & Eval("info").ToString & "&orderID=" & Eval("orderID").ToString %>' ><%# Eval("info") %></asp:HyperLink>
</ItemTemplate>
</asp:TemplateField>
<asp:BoundField DataField="type" HeaderText="Type" />
<asp:BoundField DataField="orderDate" DataFormatString="{0: H:mm:ss}" HeaderText="Order Time" SortExpression="orderDate" />
<asp:BoundField DataField="timeFor" DataFormatString="{0: H:mm:ss}" HeaderText="Time For" SortExpression="timeFor" />
<asp:BoundField DataField="paid" HeaderText="Paid" />
<asp:TemplateField ShowHeader="True">
<ItemTemplate>
<asp:ImageButton visible="true" ID="lnkSent" runat="server" CausesValidation="False"
onclientclick="return confirm('Mark As Sent?');"
ImageUrl="~/files/images/icons/sendIcon.png" onclick="lnkSent_Click"></asp:ImageButton>
<asp:HiddenField ID="hidnOrderID" runat="server" Value='<%# Eval("orderID") %>' />
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
</asp:Panel>
Protected Sub gdvOrders_RowDataBound(ByVal sender As Object,ByVal e As System.Web.UI.WebControls.GridViewRowEventArgs) Handles gdvOrders.RowDataBound
For Each r As GridViewRow in gdvOrders.Rows
If r.RowType = DataControlRowType.DataRow Then
Dim hypNote As Hyperlink
hypNote = r.Cells(4).FindControl("hypNote")
If hypNote.text = "" Then
hypNote.text = "Add Note"
End If
End If
Next r
End Sub
运算符只创建一个 new
实例。
在某个时间点创建了第二个 String
实例,以表示传递给 String
构造函数的字符串文字参数。但是我们不能肯定地说它是在我们执行该语句时创建的。 (它可能已经被创建。)
我们可以肯定地说:
- 在调用
String(String)
运算符之前创建表示文字的对象。 - 在应用程序的生命周期内创建一次1。
有趣的是,我们不能绝对肯定地说涉及“字符串池”,或者在任何时候都使用了 new
。 Java 15 JLS 在 section 3.10.5 中说明了这一点:
“此外,字符串字面量始终指代 String 类的同一个实例。这是因为字符串字面量 - 或者更一般地说,作为常量表达式(第 15.29 节)的值的字符串 - 是“内嵌的”以便共享唯一实例,就好像通过执行方法 String.intern(第 12.5 节)一样。”
“好像”的用语表明运行时系统必须出现以特定方式运行......但它不需要实际上 以这种方式实现。如果有一个可行的替代方法来在字符串文字上调用 String.intern
使 String.intern
对象具有正确的唯一性属性,那么这也是一个可以接受的实现。根据 JLS!
(实际上,所有 Hotspot JVMdo 使用字符串池,do 使用 String
。但这是一个“实现细节”。)
1 - 除非多次加载包含该语句的类。
,当我们创建一个字符串对象 String s1=new String("hello");
时,字符串字面量 "hello"
存储在字符串常量池中,引用存储在堆内存中,s1
指向该引用。
String s2="hello";
在这种情况下,字符串文字存储在字符串常量池中,s2
被直接引用到该地址。String s3=s1.intern();
如果您尝试这样做,您将获得 s3
点到存储在字符串常量池中的字符串文字的相同地址。
String s1=new String("hello");
String s2="hello";
String s3=s1.intern();//returns string from pool,now it will be same as s2
System.out.println(s1==s2);//false because reference variables are pointing to different instance
System.out.println(s2==s3);//true because reference variables are pointing to same instance
希望对你有帮助,如有问题请留言。
,我们可以使用 intern() 方法将其放入池中或从字符串池中引用另一个具有相同值的 String 对象。
如上一行,将创建 1 或 2 个字符串。
如果池中已经存在字符串字面量“Cat”,则池中只会创建一个字符串“str”。如果池中没有字符串字面量“Cat”,那么会先在池中创建,然后在堆空间中创建,所以一共创建了2个字符串对象。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。