Java Strings:“ String s = new String (” silly“);”


86

ฉันเป็นคน C ++ ที่เรียน Java ฉันกำลังอ่าน Effective Java และมีบางอย่างทำให้ฉันสับสน มันบอกว่าอย่าเขียนโค้ดแบบนี้:

String s = new String("silly");

เพราะมันสร้างStringวัตถุที่ไม่จำเป็น แต่ควรเขียนแบบนี้แทน:

String s = "No longer silly";

โอเคดี ... อย่างไรก็ตามจากคลาสนี้:

public final class CaseInsensitiveString {
    private String s;
    public CaseInsensitiveString(String s) {
        if (s == null) {
            throw new NullPointerException();
        }
        this.s = s;
    }
    :
    :
}

CaseInsensitiveString cis = new CaseInsensitiveString("Polish");
String s = "polish";
  1. ทำไมคำสั่งแรกถึงโอเค? มันไม่ควร

    CaseInsensitiveString cis = "Polish";

  2. ฉันจะCaseInsensitiveStringทำตัวเช่นไรStringเพื่อให้ข้อความข้างต้นนั้นใช้ได้ (มีและไม่มีการขยายString)? มันเกี่ยวอะไรกับ String ที่ทำให้สามารถส่งผ่านตัวอักษรแบบนั้นได้? จากความเข้าใจของฉันไม่มีแนวคิด "copy constructor" ใน Java?


2
สตริง str1 = "foo"; สตริง str2 = "foo"; ทั้ง str1 และ str2 เป็นของวัตถุ String เดียวกัน, "foo", b'coz สำหรับ Java จัดการ Strings ใน StringPool ดังนั้นตัวแปรใหม่ที่อ้างถึง String เดียวกันจะไม่สร้างอันอื่นแทนที่จะกำหนด alerady เดียวกันที่มีอยู่ใน StringPool แต่เมื่อเราทำสิ่งนี้: String str1 = new String ("foo"); สตริง str2 = สตริงใหม่ ("foo"); ที่นี่ทั้ง str1 และ str2 เป็นของ Objects ที่แตกต่างกัน b'coz new String () สร้าง String Object ใหม่อย่างมีประสิทธิภาพ
Akash5288

คำตอบ:


111

Stringเป็นคลาสพิเศษในตัวของภาษา มีไว้สำหรับStringชั้นเรียนเท่านั้นที่คุณควรหลีกเลี่ยงการพูด

String s = new String("Polish");

เนื่องจากลิเทอรัล"Polish"เป็นประเภทอยู่แล้วStringและคุณกำลังสร้างออบเจ็กต์พิเศษที่ไม่จำเป็น สำหรับชั้นเรียนอื่น ๆ พูดว่า

CaseInsensitiveString cis = new CaseInsensitiveString("Polish");

เป็นสิ่งที่ถูกต้อง (และในกรณีนี้เท่านั้น) ที่ต้องทำ


8
จุดที่สองคือคอมไพเลอร์สามารถจินตนาการสิ่งต่าง ๆ ในระหว่าง / แพร่กระจายด้วยตัวอักษรสตริงซึ่งไม่จำเป็นต้องเป็นไปได้ด้วยฟังก์ชันแปลก ๆ เช่น "String (literal)"
Tetha

5
เนื่องจากคุณไม่ควรโทรหาnew String("foo")คุณอาจถามตัวเองว่าเหตุใดตัวสร้างจึงnew String(String)มีอยู่ คำตอบคือบางครั้งก็มีประโยชน์สำหรับมัน: stackoverflow.com/a/390854/1442870
Enwired

FYI แสดงความคิดเห็น Tetha เหนือสะกดคำว่า "ฝึกงาน" ในขณะที่สตริง interning
Basil Bourque

57

ฉันเชื่อว่าประโยชน์หลักของการใช้รูปแบบลิเทอรัล (เช่น "foo" แทนที่จะเป็นสตริงใหม่ ("foo")) ก็คือตัวอักษร String ทั้งหมดถูก 'กักขัง' โดย VM กล่าวอีกนัยหนึ่งคือเพิ่มลงในพูลเพื่อให้โค้ดอื่น ๆ ที่สร้างสตริงเดียวกันจะใช้สตริงรวมกันแทนที่จะสร้างอินสแตนซ์ใหม่

เพื่อเป็นตัวอย่างรหัสต่อไปนี้จะพิมพ์เป็นจริงสำหรับบรรทัดแรก แต่เป็นเท็จสำหรับบรรทัดที่สอง:

System.out.println("foo" == "foo");
System.out.println(new String("bar") == new String("bar"));

15
นั่นคือเหตุผลที่ FindBugs บอกให้คุณแทนที่ "จำนวนเต็มใหม่ (N)" ด้วย "Integer.valueOf (N)" - เนื่องจากการฝึกงานนั้น
Paul Tomblin

6
คุณควรเพิ่ม "foo" == new String ("foo") ฝึกงาน ()
James Schek

5
การแก้ไข: ตัวอักษรสตริงถูกสร้างขึ้นเพื่อชี้ไปยังการอ้างอิงเดียวกันโดยคอมไพเลอร์ไม่ใช่ VM VM อาจภายในออบเจ็กต์ String ที่รันไทม์ดังนั้นบรรทัดที่สองอาจส่งคืนจริงหรือเท็จ!
Craig P.Motlin

1
@Motlin: ฉันไม่แน่ใจว่าถูกต้อง javadoc สำหรับคลาส String กำหนดว่า "สตริงตามตัวอักษรทั้งหมดและนิพจน์ค่าคงที่ที่มีค่าสตริงทั้งหมดอยู่ภายใน" ดังนั้นเราจึงสามารถพึ่งพาตัวอักษรที่ถูกกักขังหมายความว่า "foo" == "foo" ควรคืนค่าจริงเสมอ
Leigh

4
@ Leigh ใช่ลิเทอรัลอยู่ภายใน แต่โดยคอมไพเลอร์ไม่ใช่ VM สิ่งที่ Motlin ได้รับคือ VM อาจเพิ่มสตริงภายในดังนั้นไม่ว่าจะเป็นสตริงใหม่ ("bar") == new String ("bar") -> false จะขึ้นอยู่กับการใช้งานหรือไม่
Aaron Maenpaa

30

สตริงได้รับการปฏิบัติเป็นพิเศษเล็กน้อยใน java ซึ่งไม่เปลี่ยนรูปดังนั้นจึงปลอดภัยสำหรับการจัดการโดยการนับอ้างอิง

ถ้าคุณเขียน

String s = "Polish";
String t = "Polish";

จากนั้น s และ t อ้างถึงวัตถุเดียวกันจริง ๆ และ s == t จะคืนค่าจริงสำหรับ "==" สำหรับวัตถุที่อ่าน "เป็นวัตถุเดียวกัน" (หรือทำได้อย่างไรก็ตามฉันไม่แน่ใจว่านี่เป็นส่วนหนึ่งของ ข้อมูลจำเพาะภาษาจริงหรือเพียงแค่รายละเอียดของการใช้งานคอมไพเลอร์ดังนั้นอาจไม่ปลอดภัยที่จะพึ่งพาสิ่งนี้)

ถ้าคุณเขียน

String s = new String("Polish");
String t = new String("Polish");

จากนั้น s! = t (เนื่องจากคุณได้สร้างสตริงใหม่อย่างชัดเจน) แม้ว่า s.equals (t) จะคืนค่าจริง (เนื่องจากสตริงเพิ่มลักษณะการทำงานนี้ให้เท่ากับ)

สิ่งที่คุณต้องการเขียน

CaseInsensitiveString cis = "Polish";

ไม่สามารถทำงานได้เนื่องจากคุณคิดว่าใบเสนอราคาเป็นตัวสร้างการลัดวงจรสำหรับวัตถุของคุณในความเป็นจริงมันใช้ได้กับ java.lang.Strings แบบเก่าเท่านั้น


+1 สำหรับการกล่าวถึงความไม่เปลี่ยนรูปซึ่งสำหรับฉันคือเหตุผลที่แท้จริงใน java one เขียนstrA = strBแทนstrA = new String(strB). มันไม่จำเป็นต้องทำอะไรมากกับการฝึกงานสตริง
kritzikratzi

ไม่ได้รับการจัดการโดยการนับอ้างอิง JLS ต้องการการรวมสตริง
user207421

20
String s1="foo";

ลิเทอรัลจะอยู่ในพูลและ s1 จะอ้างถึง

String s2="foo";

คราวนี้จะตรวจสอบว่าตัวอักษร "foo" มีอยู่แล้วใน StringPool หรือไม่เนื่องจากตอนนี้มีอยู่ดังนั้น s2 จะอ้างถึงตัวอักษรเดียวกัน

String s3=new String("foo");

ตัวอักษร "foo" จะถูกสร้างขึ้นใน StringPool ก่อนจากนั้นผ่านตัวสร้างสตริง arg constructor String Object จะถูกสร้างขึ้นเช่น "foo" ในฮีปเนื่องจากการสร้างวัตถุผ่านตัวดำเนินการใหม่จากนั้น s3 จะอ้างถึง

String s4=new String("foo");

เช่นเดียวกับ s3

ดังนั้น System.out.println(s1==s2);// **true** due to literal comparison.

และ System.out.println(s3==s4);// **false** due to object

การเปรียบเทียบ (s3 และ s4 ถูกสร้างขึ้นที่ตำแหน่งต่างๆในฮีป)


1
อย่าใช้การจัดรูปแบบใบเสนอราคาสำหรับข้อความที่ไม่ได้ยกมาและใช้การจัดรูปแบบรหัสสำหรับรหัส
user207421

12

Strings มีความพิเศษใน Java - ไม่เปลี่ยนรูปและค่าคงที่สตริงจะเปลี่ยนเป็นStringวัตถุโดยอัตโนมัติ

ไม่มีทางที่SomeStringClass cis = "value"ตัวอย่างของคุณจะนำไปใช้กับคลาสอื่นได้

คุณไม่สามารถขยายStringได้เนื่องจากประกาศเป็นfinalหมายความว่าไม่อนุญาตให้มีการแบ่งประเภทย่อย


7

สตริง Java นั้นน่าสนใจ ดูเหมือนคำตอบจะครอบคลุมประเด็นที่น่าสนใจบางประการ นี่คือสองเซ็นต์ของฉัน

สตริงไม่เปลี่ยนรูป (คุณไม่สามารถเปลี่ยนแปลงได้)

String x = "x";
x = "Y"; 
  • บรรทัดแรกจะสร้างตัวแปร x ซึ่งจะมีค่าสตริง "x" JVM จะดูในพูลของค่าสตริงและดูว่ามี "x" อยู่หรือไม่มันจะชี้ตัวแปร x ไปที่ตัวแปรนั้นหากไม่มีอยู่ก็จะสร้างมันขึ้นมาจากนั้นทำการกำหนด
  • บรรทัดที่สองจะลบการอ้างอิงถึง "x" และดูว่า "Y" มีอยู่ในพูลของค่าสตริงหรือไม่ หากมีอยู่ก็จะกำหนดให้หากไม่มีก็จะสร้างขึ้นก่อนจากนั้นจึงมอบหมาย เนื่องจากมีการใช้ค่าสตริงหรือไม่พื้นที่หน่วยความจำในพูลของค่าสตริงจะถูกเรียกคืน

การเปรียบเทียบสตริงขึ้นอยู่กับสิ่งที่คุณกำลังเปรียบเทียบ

String a1 = new String("A");

String a2 = new String("A");
  • a1 ไม่เท่ากัน a2
  • a1และa2เป็นการอ้างอิงวัตถุ
  • เมื่อมีการประกาศสตริงอย่างชัดเจนอินสแตนซ์ใหม่จะถูกสร้างขึ้นและการอ้างอิงจะไม่เหมือนกัน

ฉันคิดว่าคุณมาผิดทางกับการพยายามใช้คลาสที่ไวต่อตัวอักษร ปล่อยให้สตริงอยู่คนเดียว สิ่งที่คุณสนใจจริงๆคือการแสดงหรือเปรียบเทียบค่าต่างๆ ใช้คลาสอื่นเพื่อจัดรูปแบบสตริงหรือทำการเปรียบเทียบ

กล่าวคือ

TextUtility.compare(string 1, string 2) 
TextUtility.compareIgnoreCase(string 1, string 2)
TextUtility.camelHump(string 1)

เนื่องจากคุณกำลังสร้างชั้นเรียนขึ้นคุณสามารถทำการเปรียบเทียบทำในสิ่งที่คุณต้องการ - เปรียบเทียบค่าข้อความ


คอมไพเลอร์สร้างพูลสตริงไม่ใช่ JVM ตัวแปรไม่มีวัตถุพวกเขาอ้างถึง พื้นที่สตริงพูลสำหรับตัวอักษรสตริงจะไม่ถูกเรียกคืน
user207421

6

คุณทำไม่ได้ สิ่งที่อยู่ในเครื่องหมายคำพูดคู่ใน Java ได้รับการยอมรับเป็นพิเศษจากคอมไพเลอร์ว่าเป็น Strings และน่าเสียดายที่คุณไม่สามารถแทนที่สิ่งนี้ (หรือขยายjava.lang.String- ได้รับการประกาศfinal)


สุดท้ายคือปลาชนิดหนึ่งสีแดงที่นี่ แม้ว่า String จะยังไม่สิ้นสุดการขยาย String จะไม่ช่วยเขาในกรณีนี้
Darron

1
ฉันคิดว่าคุณคงโชคดีที่ไม่สามารถลบล้างสิ่งนี้ได้ :)
Craig P. Motlin

@Motlin: ฮะ! คุณอาจจะพูดถูก ฉันคิดว่าฉันอ่านที่ไหนสักแห่งที่ Java ถูกออกแบบมาเพื่อหยุดทุกคนที่เคยทำอะไรโง่ ๆ โดยจงใจยกเว้นสิ่งที่น่าสนใจเช่นนี้ ...
Dan Vinton

6

วิธีที่ดีที่สุดในการตอบคำถามของคุณคือทำให้คุณคุ้นเคยกับ "String constant pool" ในวัตถุสตริง java นั้นไม่เปลี่ยนรูป (กล่าวคือไม่สามารถเปลี่ยนค่าได้เมื่อเริ่มต้นแล้ว) ดังนั้นเมื่อแก้ไขอ็อบเจ็กต์สตริงคุณจะต้องสร้างอ็อบเจ็กต์สตริงที่แก้ไขใหม่ในขณะที่อ็อบเจ็กต์เก่าลอยอยู่รอบ ๆ ในหน่วยความจำพิเศษเรียกว่าสตริง " สระคง ". สร้างวัตถุสตริงใหม่โดย

String s = "Hello";

จะสร้างอ็อบเจ็กต์สตริงในพูลเท่านั้นและการอ้างอิง s จะอ้างถึง แต่โดยใช้

String s = new String("Hello");

คุณสร้างอ็อบเจ็กต์สตริงสองรายการ: หนึ่งในพูลและอีกรายการในฮีป การอ้างอิงจะอ้างถึงวัตถุในฮีป


4

- ฉันจะทำให้ CaseInsensitiveString ทำงานเหมือน String ได้อย่างไรดังนั้นข้อความข้างต้นจึงใช้ได้ (มีและ w / out ขยาย String) มันเกี่ยวอะไรกับ String ที่ทำให้มันสามารถส่งผ่านตัวอักษรแบบนั้นได้? จากความเข้าใจของฉันไม่มีแนวคิด "copy constructor" ใน Java ใช่ไหม

ได้รับการกล่าวถึงตั้งแต่จุดแรก "Polish" เป็นสตริงลิเทอรัลและไม่สามารถกำหนดให้กับคลาส CaseInsentiviveString

ตอนนี้เกี่ยวกับจุดที่สอง

แม้ว่าคุณจะไม่สามารถสร้างตัวอักษรใหม่ได้ แต่คุณสามารถทำตามรายการแรกของหนังสือเล่มนั้นสำหรับแนวทางที่ "คล้ายกัน" ดังนั้นข้อความต่อไปนี้จึงเป็นจริง:

    // Lets test the insensitiveness
    CaseInsensitiveString cis5 = CaseInsensitiveString.valueOf("sOmEtHiNg");
    CaseInsensitiveString cis6 = CaseInsensitiveString.valueOf("SoMeThInG");

    assert cis5 == cis6;
    assert cis5.equals(cis6);

นี่คือรหัส

C:\oreyes\samples\java\insensitive>type CaseInsensitiveString.java
import java.util.Map;
import java.util.HashMap;

public final class CaseInsensitiveString  {


    private static final Map<String,CaseInsensitiveString> innerPool 
                                = new HashMap<String,CaseInsensitiveString>();

    private final String s;


    // Effective Java Item 1: Consider providing static factory methods instead of constructors
    public static CaseInsensitiveString valueOf( String s ) {

        if ( s == null ) {
            return null;
        }
        String value = s.toLowerCase();

        if ( !CaseInsensitiveString.innerPool.containsKey( value ) ) {
             CaseInsensitiveString.innerPool.put( value , new CaseInsensitiveString( value ) );
         }

         return CaseInsensitiveString.innerPool.get( value );   
    }

    // Class constructor: This creates a new instance each time it is invoked.
    public CaseInsensitiveString(String s){
        if (s == null) {
            throw new NullPointerException();
         }         
         this.s = s.toLowerCase();
    }

    public boolean equals( Object other ) {
         if ( other instanceof CaseInsensitiveString ) {
              CaseInsensitiveString otherInstance = ( CaseInsensitiveString ) other;
             return this.s.equals( otherInstance.s );
         }

         return false;
    }


    public int hashCode(){
         return this.s.hashCode();
    }

// ทดสอบคลาสโดยใช้คีย์เวิร์ด "assert"

    public static void main( String [] args ) {

        // Creating two different objects as in new String("Polish") == new String("Polish") is false
        CaseInsensitiveString cis1 = new CaseInsensitiveString("Polish");
        CaseInsensitiveString cis2 = new CaseInsensitiveString("Polish");

        // references cis1 and cis2 points to differents objects.
        // so the following is true
        assert cis1 !=  cis2;      // Yes they're different
        assert cis1.equals(cis2);  // Yes they're equals thanks to the equals method

        // Now let's try the valueOf idiom
        CaseInsensitiveString cis3 = CaseInsensitiveString.valueOf("Polish");
        CaseInsensitiveString cis4 = CaseInsensitiveString.valueOf("Polish");

        // References cis3 and cis4 points to same  object.
        // so the following is true
        assert cis3 == cis4;      // Yes they point to the same object
        assert cis3.equals(cis4); // and still equals.

        // Lets test the insensitiveness
        CaseInsensitiveString cis5 = CaseInsensitiveString.valueOf("sOmEtHiNg");
        CaseInsensitiveString cis6 = CaseInsensitiveString.valueOf("SoMeThInG");

        assert cis5 == cis6;
        assert cis5.equals(cis6);

        // Futhermore
        CaseInsensitiveString cis7 = CaseInsensitiveString.valueOf("SomethinG");
        CaseInsensitiveString cis8 = CaseInsensitiveString.valueOf("someThing");

        assert cis8 == cis5 && cis7 == cis6;
        assert cis7.equals(cis5) && cis6.equals(cis8);
    }

}

C:\oreyes\samples\java\insensitive>javac CaseInsensitiveString.java


C:\oreyes\samples\java\insensitive>java -ea CaseInsensitiveString

C:\oreyes\samples\java\insensitive>

นั่นคือสร้างพูลภายในของอ็อบเจ็กต์ CaseInsensitiveString และส่งคืนอินสแตนซ์ corrensponding จากที่นั่น

วิธีนี้ "==" ผู้ประกอบการผลตอบแทนที่แท้จริงสำหรับสองวัตถุอ้างอิงที่เป็นตัวแทนของค่าเดียวกัน

สิ่งนี้มีประโยชน์เมื่อมีการใช้วัตถุที่คล้ายกันบ่อยมากและต้นทุนการสร้างมีราคาแพง

เอกสารประกอบคลาสสตริงระบุว่าคลาสใช้พูลภายใน

ชั้นเรียนยังไม่สมบูรณ์ปัญหาที่น่าสนใจบางอย่างเกิดขึ้นเมื่อเราพยายามเดินเนื้อหาของวัตถุในการใช้งานอินเทอร์เฟซ CharSequence แต่รหัสนี้ดีพอที่จะแสดงให้เห็นว่าสามารถนำรายการนั้นไปใช้อย่างไรในหนังสือ

สิ่งสำคัญคือต้องสังเกตว่าการใช้ออบเจ็กต์internalPoolโดยการใช้การอ้างอิงจะไม่ถูกปล่อยออกมาดังนั้นจึงไม่ใช่ขยะที่รวบรวมได้และอาจกลายเป็นปัญหาหากมีการสร้างวัตถุจำนวนมาก

ใช้งานได้กับคลาส String เนื่องจากมีการใช้งานอย่างเข้มข้นและพูลประกอบด้วยอ็อบเจ็กต์ "interned" เท่านั้น

มันทำงานได้ดีสำหรับคลาสบูลีนเช่นกันเนื่องจากมีเพียงสองค่าที่เป็นไปได้

และในที่สุดนั่นก็เป็นเหตุผลว่าทำไมvalueOf (int)ในคลาสจำนวนเต็มจึงถูก จำกัด ไว้ที่ -128 ถึง 127 ค่า int


3

ในตัวอย่างแรกของคุณคุณกำลังสร้าง String "โง่" จากนั้นส่งต่อเป็นพารามิเตอร์ไปยังตัวสร้างการคัดลอกของสตริงอื่นซึ่งทำให้สตริงที่สองซึ่งเหมือนกับสตริงแรก เนื่องจาก Java Strings ไม่เปลี่ยนรูป (สิ่งที่มักดึงดูดผู้ที่คุ้นเคยกับสตริง C) จึงเป็นการสิ้นเปลืองทรัพยากรโดยไม่จำเป็น คุณควรใช้ตัวอย่างที่สองแทนเพราะข้ามขั้นตอนที่ไม่จำเป็นไปหลายขั้นตอน

อย่างไรก็ตาม String literal ไม่ใช่ CaseInsensitiveString ดังนั้นคุณจึงไม่สามารถทำสิ่งที่คุณต้องการในตัวอย่างสุดท้ายได้ นอกจากนี้ไม่มีวิธีใดที่จะโอเวอร์โหลดตัวดำเนินการแคสต์อย่างที่คุณทำได้ใน C ++ ดังนั้นจึงไม่มีวิธีใดที่จะทำสิ่งที่คุณต้องการ คุณต้องส่งเป็นพารามิเตอร์ไปยังตัวสร้างของคลาสของคุณแทน แน่นอนฉันอาจจะใช้ String.toLowerCase () และทำได้ด้วย

นอกจากนี้ CaseInsensitiveString ของคุณควรใช้อินเทอร์เฟซ CharSequence เช่นเดียวกับอินเทอร์เฟซที่ต่อเนื่องและเปรียบเทียบได้ แน่นอนว่าหากคุณใช้การเปรียบเทียบคุณควรลบล้างเท่ากับ () และ hashCode () ด้วย


3

เพียงเพราะคุณมีคำStringในชั้นเรียนไม่ได้หมายความว่าคุณจะได้รับคุณสมบัติพิเศษทั้งหมดของStringคลาสในตัว


3

CaseInsensitiveStringไม่ใช่Stringแม้ว่าจะมีไฟล์String. Stringตัวอักษรเช่น "เช่น" Stringสามารถกำหนดเฉพาะกับ


2

CaseInsensitiveString และ String เป็นวัตถุที่แตกต่างกัน คุณทำไม่ได้:

CaseInsensitiveString cis = "Polish";

เนื่องจาก "Polish" เป็น String ไม่ใช่ CaseInsensitiveString หาก String ขยาย CaseInsensitiveString String คุณก็จะโอเค แต่เห็นได้ชัดว่ามันไม่ได้

และไม่ต้องกังวลกับการก่อสร้างที่นี่คุณจะไม่ได้สร้างวัตถุที่ไม่จำเป็น หากคุณดูรหัสของตัวสร้างสิ่งที่ทำก็คือการจัดเก็บการอ้างอิงไปยังสตริงที่คุณส่งผ่านไม่มีการสร้างอะไรเพิ่มเติม

ในกรณี String s = new String ("foobar") มันทำอะไรบางอย่างที่แตกต่างออกไป คุณต้องสร้างสตริงตัวอักษร "foobar" ก่อนจากนั้นสร้างสำเนาโดยสร้างสตริงใหม่จากนั้น ไม่จำเป็นต้องสร้างสำเนานั้น


แม้ว่าคุณจะขยาย String แต่ก็ใช้ไม่ได้ คุณต้องใช้ String เพื่อขยาย CaseInsensitiveString
Darron

ในทั้งสองกรณีมันเป็นไปไม่ได้เพราะทั้งสตริงถูกสร้างขึ้นในหรือเพราะการประกาศครั้งสุดท้าย
ลุค

2

เมื่อพวกเขาบอกว่าจะเขียน

String s = "Silly";

แทน

String s = new String("Silly");

พวกเขาหมายถึงเมื่อสร้างอ็อบเจ็กต์ String เนื่องจากทั้งสองคำสั่งข้างต้นสร้างอ็อบเจ็กต์ String แต่เวอร์ชัน String () ใหม่สร้างอ็อบเจ็กต์ String สองอ็อบเจกต์: หนึ่งในฮีปและอีกอันในพูลค่าคงที่สตริง จึงใช้หน่วยความจำมากขึ้น

แต่เมื่อคุณเขียน

CaseInsensitiveString cis = new CaseInsensitiveString("Polish");

คุณไม่ได้สร้าง String แต่คุณกำลังสร้างอ็อบเจ็กต์ของคลาส CaseInsensitiveString ดังนั้นคุณต้องใช้ตัวดำเนินการใหม่


1

ถ้าฉันเข้าใจถูกต้องคำถามของคุณหมายความว่าทำไมเราไม่สามารถสร้างออบเจ็กต์โดยกำหนดค่าโดยตรงได้อย่าให้ จำกัด เฉพาะคลาส Wrapper of String ใน java

เพื่อตอบว่าฉันจะบอกว่าภาษาการเขียนโปรแกรมเชิงวัตถุล้วนมีโครงสร้างบางอย่างและกล่าวว่าตัวอักษรทั้งหมดเมื่อเขียนเพียงอย่างเดียวสามารถเปลี่ยนเป็นวัตถุประเภทที่กำหนดได้โดยตรง

นั่นหมายความว่าหากล่ามเห็น 3 มันจะถูกแปลงเป็นวัตถุจำนวนเต็มเนื่องจากจำนวนเต็มเป็นประเภทที่กำหนดไว้สำหรับตัวอักษรดังกล่าว

หากล่ามเห็นสิ่งใด ๆ ในเครื่องหมายคำพูดเดี่ยวเช่น 'a' มันจะสร้างออบเจ็กต์ของอักขระประเภทโดยตรงคุณไม่จำเป็นต้องระบุเนื่องจากภาษากำหนดอ็อบเจ็กต์เริ่มต้นของอักขระประเภทสำหรับมัน

ในทำนองเดียวกันถ้าล่ามเห็นบางสิ่งใน "" จะถือว่าเป็นอ็อบเจ็กต์ของสตริงประเภทเริ่มต้นเช่น นี่คือโค้ดเนทีฟบางส่วนที่ทำงานอยู่เบื้องหลัง

ขอบคุณวิดีโอการบรรยายหลักสูตร MIT 6.00 ที่ฉันได้รับคำแนะนำสำหรับคำตอบนี้


0

ใน Java ไวยากรณ์ "text" จะสร้างอินสแตนซ์ของคลาส java.lang.String งานที่ได้รับมอบหมาย:

String foo = "text";

เป็นการมอบหมายงานง่ายๆโดยไม่จำเป็นต้องมีตัวสร้างสำเนา

MyString bar = "text";

ไม่ว่าคุณจะทำอะไรผิดกฎหมายเนื่องจากคลาส MyString ไม่ใช่ java.lang.String หรือ superclass ของ java.lang.String


0

ขั้นแรกคุณไม่สามารถสร้างคลาสที่ขยายจาก String ได้เนื่องจาก String เป็นคลาสสุดท้าย และ java จัดการ Strings แตกต่างจากคลาสอื่น ๆ ดังนั้นคุณสามารถทำได้ด้วย String เท่านั้น

String s = "Polish";

แต่ชั้นเรียนของคุณเล็กน้อยคุณต้องเรียกใช้ตัวสร้าง ดังนั้นรหัสนั้นก็ใช้ได้


0

ฉันจะเพิ่มว่า Java มีตัวสร้างการคัดลอก ...

นั่นคือตัวสร้างธรรมดาที่มีวัตถุประเภทเดียวกับอาร์กิวเมนต์


2
เป็นรูปแบบการออกแบบไม่ใช่โครงสร้างภาษา ด้วย Java มีการใช้งานน้อยมากที่ตัวสร้างการคัดลอกจะน่าสนใจเนื่องจากทุกอย่างมักจะ "อ้างอิง" และแต่ละออบเจ็กต์มีเพียงสำเนาเดียว ในความเป็นจริงการทำสำเนาจะทำให้เกิดปัญหามากมาย
Bill K

0

ใน JDK ส่วนใหญ่ทั้งสองเวอร์ชันจะเหมือนกัน:

String s = สตริงใหม่ ("โง่");

String s = "ไม่โง่อีกต่อไป";

เนื่องจากสตริงไม่เปลี่ยนรูปคอมไพลเลอร์จึงเก็บรักษารายการค่าคงที่ของสตริงไว้และหากคุณพยายามสร้างใหม่จะต้องตรวจสอบก่อนว่ามีการกำหนดสตริงหรือไม่ หากเป็นเช่นนั้นการอ้างอิงถึงสตริงที่ไม่เปลี่ยนรูปที่มีอยู่จะถูกส่งกลับ

เพื่อชี้แจง - เมื่อคุณพูดว่า "String s =" คุณกำลังกำหนดตัวแปรใหม่ซึ่งจะใช้พื้นที่บนสแต็ก - ไม่ว่าคุณจะพูดว่า "ไม่โง่อีกต่อไป" หรือสตริงใหม่ ("โง่") สิ่งเดียวกันก็เกิดขึ้น - ใหม่ สตริงคงที่ถูกรวบรวมไว้ในแอปพลิเคชันของคุณและการอ้างอิงชี้ไปที่

ฉันไม่เห็นความแตกต่างที่นี่ อย่างไรก็ตามสำหรับคลาสของคุณเองซึ่งไม่เปลี่ยนรูปพฤติกรรมนี้ไม่เกี่ยวข้องและคุณต้องเรียกตัวสร้างของคุณ

UPDATE: คิดผิด! จากการโหวตลงและความคิดเห็นที่แนบมาฉันได้ทดสอบสิ่งนี้และตระหนักว่าความเข้าใจของฉันไม่ถูกต้อง - สตริงใหม่ ("โง่") สร้างสตริงใหม่แทนที่จะใช้สตริงที่มีอยู่ซ้ำ ฉันไม่ชัดเจนว่าทำไมถึงเป็นเช่นนี้ (ประโยชน์คืออะไร) แต่โค้ดพูดดังกว่าคำพูด!


0

String เป็นหนึ่งในคลาสพิเศษที่คุณสามารถสร้างได้โดยไม่ต้องมีส่วน Sring ใหม่

มันเหมือนกับ

int x = y;

หรือ

ถ่าน c;



0
 String str1 = "foo"; 
 String str2 = "foo"; 

ทั้ง str1 และ str2 เป็นของวัตถุ String เดียวกัน, "foo", b'coz Java จัดการ Strings ใน StringPool ดังนั้นหากตัวแปรใหม่อ้างถึง String เดียวกันก็จะไม่สร้างอันใหม่แทนที่จะกำหนด alerady เดียวกันที่มีอยู่ใน StringPool .

 String str1 = new String("foo"); 
 String str2 = new String("foo");

ที่นี่ทั้ง str1 และ str2 เป็นของ Objects ต่างกัน b'coz new String () สร้าง String Object ใหม่อย่างมีประสิทธิภาพ


-1

Java สร้างอ็อบเจ็กต์ String สำหรับลิเทอรัลสตริงแต่ละตัวที่คุณใช้ในโค้ดของคุณ เมื่อใดก็ตามที่ใช้ก็เป็นเช่นเดียวกับการเรียก""new String()

สตริงคือข้อมูลที่ซับซ้อนซึ่งแค่ "ทำหน้าที่" เหมือนข้อมูลดั้งเดิม ตัวอักษรของสตริงเป็นจริงวัตถุแม้ว่าเราจะแกล้งพวกเขากำลังอักษรดั้งเดิมเช่น6, 6.0, 'c',ฯลฯ ดังนั้นสตริง "ตัวอักษร" ผลตอบแทนวัตถุสตริงใหม่ที่มีค่า"text" char[] value = {'t','e','x','t}ดังนั้นการโทร

new String("text"); 

คล้ายกับการโทรจริงๆ

new String(new String(new char[]{'t','e','x','t'}));

หวังว่าจากที่นี่คุณจะเห็นว่าเหตุใดตำราของคุณจึงเห็นว่าซ้ำซ้อน

สำหรับการอ้างอิงนี่คือการใช้งาน String: http://www.docjar.com/html/api/java/lang/String.java.html

เป็นการอ่านที่สนุกและอาจสร้างแรงบันดาลใจให้เกิดความเข้าใจ นอกจากนี้ยังเหมาะสำหรับผู้เริ่มต้นอ่านและพยายามทำความเข้าใจเนื่องจากโค้ดนี้แสดงให้เห็นถึงโค้ดที่เป็นมืออาชีพและเป็นไปตามแบบแผน

ข้อมูลอ้างอิงที่ดีอีกอย่างคือบทช่วยสอน Java บน Strings: http://docs.oracle.com/javase/tutorial/java/data/strings.html


เมื่อใดก็ตามที่ใช้ "" เป็นการอ้างอิงถึงสตริงเดียวกันในพูลค่าคงที่
user207421
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.