สิ่งที่ไม่เปลี่ยนแปลงหมายถึงอะไร?


400

นี่อาจเป็นคำถามที่โง่ที่สุดที่เคยถาม แต่ฉันคิดว่ามันค่อนข้างสับสนสำหรับมือใหม่จาวา

  1. ใครสามารถอธิบายสิ่งที่มีความหมายโดยไม่เปลี่ยนรูป ?
  2. ทำไมถึงStringไม่เปลี่ยนรูป
  3. อะไรคือข้อดี / ข้อเสียของวัตถุที่ไม่เปลี่ยนรูป?
  4. ทำไมวัตถุที่ไม่แน่นอนเช่นStringBuilderที่ต้องการมากกว่าสตริงและข้อรอง?

ตัวอย่างที่ดี (ใน Java) จะได้รับการชื่นชมจริงๆ


73
ดูสิมันไม่ใช่คำถามที่น่าเบื่อ ดีใจที่คุณถาม!
DOK

2
โดยวิธีการที่ผมไม่คิดว่ามันเป็นคำถามโง่เคย :) ฉันคิดว่ามันเป็นแนวคิดที่สำคัญสวยที่จะเข้าใจ
เจสันโคโค่

1
เมื่อคุณพูด StringBuilder คุณไม่ได้หมายถึง StringBuffer คลาสที่ไม่แน่นอนหรือ String และ StringBuffer มีฟังก์ชั่นคล้ายกันมากกว่า String และ StringBuilder StringBuffer เป็นสตริงที่ไม่แน่นอน
Derek Mahar

3
ฉันขอแนะนำให้เราเพิ่มแท็ก "เริ่มต้น" คำถามนี้เพื่อให้โปรแกรมเมอร์ใหม่ใน Java อาจพบในการค้นหาคำถามเบื้องต้นอื่น ๆ ?
Derek Mahar

คำตอบ:


268

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

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

เช่น

class Foo
{
     private final String myvar;

     public Foo(final String initialValue)
     {
         this.myvar = initialValue;
     }

     public String getValue()
     {
         return this.myvar;
     }
}

Fooไม่ต้องกังวลว่าผู้โทรgetValue()อาจเปลี่ยนข้อความในสตริง

หากคุณนึกภาพคลาสที่คล้ายกันFooแต่มีStringBuilderมากกว่าที่จะStringเป็นสมาชิกคุณจะเห็นว่าผู้ที่โทรเข้าgetValue()จะสามารถเปลี่ยนStringBuilderคุณสมบัติของFooอินสแตนซ์ได้

นอกจากนี้ระวังประเภทที่เปลี่ยนไม่ได้ที่คุณอาจพบ: Eric Lippert เขียนบทความบล็อกเกี่ยวกับเรื่องนี้ โดยทั่วไปคุณสามารถมีวัตถุที่มีส่วนต่อประสานที่ไม่เปลี่ยนรูป แต่อยู่เบื้องหลังของสถานะที่ไม่แน่นอนที่เป็นส่วนตัว (และไม่สามารถแชร์อย่างปลอดภัยระหว่างเธรด)


3
ฉันคิดว่าคุณควรเพิ่มตัวสร้าง arg หนึ่งตัวเพื่อกำหนดค่าอย่างน้อยหนึ่งครั้ง จุดของรหัสปัจจุบันไม่ชัดเจนเนื่องจากไม่มีค่าที่จะเปลี่ยนแปลงจริงๆ :)
Georgy Bolyuba

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

7
myVar สมาชิกควรถือเป็นที่สุดสำหรับสิ่งนี้ที่ไม่เปลี่ยนแปลงอย่างแท้จริง
laz

13
คุณถูกต้องที่ myVar ไม่สามารถเข้าถึงได้นอก Foo อย่างไรก็ตามการปรากฏตัวของขั้นสุดท้ายบ่งชี้ถึงทุกคนที่อาจจะปรับเปลี่ยนชั้นในอนาคตที่ค่าของมันไม่ได้มีไว้เพื่อเปลี่ยนแปลง ฉันมักจะชอบที่จะเป็นชัดเจนที่สุดในสถานการณ์เช่นนี้
laz

2
วิธีการเกี่ยวกับ "ประเภทการอ้างอิงไม่สามารถทำให้ไม่เปลี่ยนรูปได้เพียงแค่ใช้คำหลักสุดท้ายเท่านั้นขั้นสุดท้ายจะป้องกันการมอบหมายใหม่" จากen.wikipedia.org/wiki/Immutable_object
Yousha Aleayoub

81

วัตถุที่ไม่เปลี่ยนรูปเป็นวัตถุที่เขตข้อมูลภายใน (หรืออย่างน้อยที่สุดเขตข้อมูลภายในทั้งหมดที่มีผลต่อพฤติกรรมภายนอก) ไม่สามารถเปลี่ยนแปลงได้

มีประโยชน์มากมายกับสตริงที่ไม่เปลี่ยนรูปแบบ:

ประสิทธิภาพการทำงาน:ใช้การดำเนินการต่อไปนี้:

String substring = fullstring.substring(x,y);

C ต้นแบบสำหรับเมธอดย่อย () อาจเป็นดังนี้:

// Assume string is stored like this:
struct String { char* characters; unsigned int length; };

// Passing pointers because Java is pass-by-reference
struct String* substring(struct String* in, unsigned int begin, unsigned int end)
{
    struct String* out = malloc(sizeof(struct String));
    out->characters = in->characters + begin;
    out->length = end - begin;
    return out;
}

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

การเกิดขึ้นพร้อมกัน:หากโครงสร้างภายในของวัตถุที่เปลี่ยนรูปไม่ถูกต้องก็จะถูกต้องเสมอ ไม่มีโอกาสที่เธรดที่ต่างกันสามารถสร้างสถานะที่ไม่ถูกต้องภายในวัตถุนั้นได้ ดังนั้นวัตถุที่ไม่เปลี่ยนรูปเป็นด้ายปลอดภัย

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

อย่างไรก็ตามยังมีข้อเสียต่อการเปลี่ยนแปลงไม่ได้:

ประสิทธิภาพ:เดี๋ยวก่อนฉันคิดว่าคุณพูดว่าการแสดงนั้นเป็นสิ่งที่ไม่สามารถเปลี่ยนแปลงได้! บางครั้งก็เป็น แต่ก็ไม่เสมอไป ใช้รหัสต่อไปนี้:

foo = foo.substring(0,4) + "a" + foo.substring(5);  // foo is a String
bar.replace(4,5,"a"); // bar is a StringBuilder

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

struct String* concatenate(struct String* first, struct String* second)
{
    struct String* new = malloc(sizeof(struct String));
    new->length = first->length + second->length;

    new->characters = malloc(new->length);

    int i;

    for(i = 0; i < first->length; i++)
        new->characters[i] = first->characters[i];

    for(; i - first->length < second->length; i++)
        new->characters[i] = second->characters[i - first->length];

    return new;
}

// The code that executes
struct String* astring;
char a = 'a';
astring->characters = &a;
astring->length = 1;
foo = concatenate(concatenate(slice(foo,0,4),astring),slice(foo,5,foo->length));

โปรดทราบว่า concatenate จะได้รับการเรียกสองครั้งซึ่งหมายความว่าสตริงทั้งหมดจะต้องวนลูปผ่าน! เปรียบเทียบสิ่งนี้กับรหัส C สำหรับbarดำเนินการ:

bar->characters[4] = 'a';

การดำเนินการกับสตริงที่ไม่แน่นอนนั้นเร็วกว่ามาก

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

// This will have awful performance if you don't use mutable strings
String join(String[] strings, String separator)
{
    StringBuilder mutable;
    boolean first = true;

    for(int i = 0; i < strings.length; i++)
    {
        if(!first) first = false;
        else mutable.append(separator);

        mutable.append(strings[i]);
    }

    return mutable.toString();
}

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


4
ยอดเยี่ยมอ่าน! สิ่งหนึ่งที่ฉันคิดว่ามันควรจะเป็นถ้า (ก่อน) และไม่ถ้า (! ก่อน)
Siddhartha

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

7
Passing pointers because Java is pass-by-referenceไม่ใช่จาวา "pass-by-value หรือไม่"
Cristian Gutu

@CristianGutu ใช่คุณพูดถูก JAVA คือ "Pass by Value" ไม่ใช่ "Pass by REFERENCE"
Arsh Kaushal

การอ้างอิงถูกส่งผ่านเป็นค่า !!
devv

31

จริง ๆ แล้วสตริงไม่เปลี่ยนรูปถ้าคุณใช้คำจำกัดความวิกิพีเดียแนะนำข้างต้น

สถานะของสตริงจะเปลี่ยนโครงสร้างการโพสต์ ดูวิธีแฮชโค้ด () สตริงแคชค่าแฮชโค้ดในฟิลด์โลคัล แต่ไม่คำนวณจนกระทั่งการแฮชโค้ดครั้งแรก () การประเมินค่าแฮชโค้ดที่ขี้เกียจนี้วาง String ในตำแหน่งที่น่าสนใจในฐานะวัตถุที่ไม่เปลี่ยนรูปซึ่งมีการเปลี่ยนแปลงสถานะ แต่ไม่สามารถสังเกตได้ว่าจะเปลี่ยนแปลงโดยไม่ใช้การสะท้อน

ดังนั้นความหมายของคำว่าเปลี่ยนรูปไม่ได้ควรเป็นวัตถุที่ไม่สามารถสังเกตได้ว่ามีการเปลี่ยนแปลง

หากสถานะการเปลี่ยนแปลงในวัตถุที่เปลี่ยนไม่ได้หลังจากที่มันถูกสร้างขึ้น แต่ไม่มีใครสามารถมองเห็นมัน (ไม่มีการสะท้อน) วัตถุยังคงไม่เปลี่ยนรูป?


1
ความคิดที่ดี - วัตถุที่ไม่สามารถสังเกตได้ว่ามีการเปลี่ยนแปลงรวมถึงไม่มีวิธีการเปลี่ยนจากภายนอก เขตข้อมูลส่วนตัวสำหรับ hashCode () เป็นการเปลี่ยนแปลงภายในที่ไม่สำคัญกับสถานะของวัตถุที่มองเห็นจากภายนอก
mparaz

2
จริงๆแล้วมันสามารถสังเกตได้ว่ามีการเปลี่ยนแปลงถ้าคุณใช้การสะท้อน ดูเพิ่มเติมได้ที่เซดจ์วิกของStrings ไม่แน่นอนถ้าคุณให้สะท้อน
Miguel

24

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

ชี้แจงเพียงเพื่อชี้แจง StringBuilder เป็นวัตถุที่ไม่แน่นอนไม่ได้เปลี่ยนไม่ได้ สตริง java ปกติไม่เปลี่ยนรูป (หมายความว่าเมื่อมันถูกสร้างขึ้นแล้วคุณจะไม่สามารถเปลี่ยนสตริงพื้นฐานได้โดยไม่ต้องเปลี่ยนวัตถุ)

ตัวอย่างเช่นสมมติว่าฉันมีคลาสที่ชื่อว่า ColoredString ซึ่งมีค่าสตริงและสีสตริง:

public class ColoredString {

    private String color;
    private String string;

    public ColoredString(String color, String string) {
        this.color  = color;
        this.string = string;
    }

    public String getColor()  { return this.color;  }
    public String getString() { return this.string; }

    public void setColor(String newColor) {
        this.color = newColor;
    }

}

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

new ColoredString("Blue", "This is a blue string!");

จากนั้นคุณคาดว่าสตริงนั้นจะเป็น "Blue" เสมอ อย่างไรก็ตามถ้าเธรดอื่นได้รับ ahold ของอินสแตนซ์นี้และเรียกใช้

blueString.setColor("Red");

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

เพื่อสรุปใน Java, java.lang.String เป็นวัตถุที่ไม่เปลี่ยนรูป (มันไม่สามารถเปลี่ยนแปลงได้เมื่อมันถูกสร้างขึ้น) และ java.lang.StringBuilder เป็นวัตถุที่ไม่แน่นอนเพราะมันสามารถเปลี่ยนแปลงได้โดยไม่ต้องสร้างอินสแตนซ์ใหม่


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

@ JaredPar - จริง ๆ แล้วคลาสนั้นไม่เปลี่ยนรูปเลย ... มันเป็นตัวอย่างของคลาสที่ไม่แน่นอนเพื่อแสดงให้เห็นว่าทำไมมันถึงเป็นปัญหา
Jason Coco

1
@ JaredPar - โอ้ไม่เป็นไร :) ฉันจะเขียนใหม่อีกซักนิดเพื่อให้ชัดเจนขึ้น แต่ Douglas ก็เขียนได้ดีและดูเหมือนจะเป็นรายการโปรดดังนั้นฉันจะทิ้งฉันไว้เป็นอีกตัวอย่างหนึ่ง; แต่ใครบางคนไม่จริงแก้ไขมันเพื่อให้คุณสมบัติสุดท้ายซึ่งผมคิดว่าเป็นสนุกสนาน :)
เจสันโคโค่

24
  1. ในแอปพลิเคชันขนาดใหญ่เป็นเรื่องปกติสำหรับตัวอักษรสตริงที่จะใช้หน่วยความจำขนาดใหญ่ ดังนั้นเพื่อจัดการกับหน่วยความจำได้อย่างมีประสิทธิภาพ JVM จะจัดสรรพื้นที่ที่เรียกว่า "String constant pool" ( โปรดทราบว่าในหน่วยความจำแม้สตริงที่ไม่มีการอ้างอิงจะดำเนินการรอบอักขระ [], int สำหรับความยาวและอื่น ๆ สำหรับ hashCode ในทางตรงกันข้ามจำเป็นต้องมีไบต์ทันทีสูงสุดแปดไบต์ )
  2. เมื่อ complier เจอตัวอักษร String มันจะตรวจสอบ pool เพื่อดูว่ามีตัวอักษรเหมือนกันอยู่แล้ว และหากพบสิ่งใดสิ่งหนึ่งการอ้างอิงไปที่ตัวอักษรใหม่จะถูกนำไปยังสตริงที่มีอยู่และไม่มีการสร้าง 'วัตถุตัวอักษรสตริงใหม่' (สตริงที่มีอยู่นั้นจะได้รับการอ้างอิงเพิ่มเติม)
  3. ดังนั้น: ความผันแปรของสตริงจะบันทึกหน่วยความจำ ...
  4. แต่เมื่อตัวแปรใด ๆ เปลี่ยนค่าจริง ๆ - เป็นเพียงการอ้างอิงของพวกเขาที่เปลี่ยนไปไม่ใช่ค่าในหน่วยความจำ (ดังนั้นมันจะไม่ส่งผลกระทบต่อตัวแปรอื่น ๆ ที่อ้างอิงถึงมัน) ดังที่แสดงด้านล่าง ....

String s1 = "สตริงเก่า";

//s1 variable, refers to string in memory
        reference                 |     MEMORY       |
        variables                 |                  |

           [s1]   --------------->|   "Old String"   |

String s2 = s1;

//s2 refers to same string as s1
                                  |                  |
           [s1]   --------------->|   "Old String"   |
           [s2]   ------------------------^

s1 = "สตริงใหม่";

//s1 deletes reference to old string and points to the newly created one
           [s1]   -----|--------->|   "New String"   |
                       |          |                  |
                       |~~~~~~~~~X|   "Old String"   |
           [s2]   ------------------------^

สตริงต้นฉบับ 'ในหน่วยความจำ' ไม่เปลี่ยนแปลง แต่ตัวแปรอ้างอิงถูกเปลี่ยนเพื่อให้อ้างอิงถึงสตริงใหม่ และถ้าเราไม่มี s2 "สตริงเก่า" จะยังคงอยู่ในหน่วยความจำ แต่เราจะไม่สามารถเข้าถึงได้ ...


16

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

String foo = "Hello";
foo.substring(3);
<-- foo here still has the same value "Hello"

เพื่อรักษาการเปลี่ยนแปลงคุณควรทำอะไรเช่นนี้ foo = foo.sustring (3);

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


13

java.time

มันอาจจะช้าไปหน่อย แต่เพื่อที่จะเข้าใจว่าวัตถุที่ไม่เปลี่ยนรูปนั้นคืออะไรพิจารณาตัวอย่างต่อไปนี้จาก Java 8 Date and Time API ใหม่ ( java.time ) อย่างที่คุณอาจจะรู้ว่าวัตถุวันที่ทั้งหมดจาก Java 8 นั้นไม่เปลี่ยนรูปดังนั้นในตัวอย่างต่อไปนี้

LocalDate date = LocalDate.of(2014, 3, 18); 
date.plusYears(2);
System.out.println(date);

เอาท์พุท:

2014/03/18

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

ดังนั้นที่ตัวอย่างโค้ดควรจับและใช้วัตถุใหม่ instantiated plusYearsและกลับโดยการเรียกไปว่า

LocalDate date = LocalDate.of(2014, 3, 18); 
LocalDate dateAfterTwoYears = date.plusYears(2);

date.toString () … 2014-03-18

dateAfterTwoYears.toString () ... 2016-03-18


8

ผมชอบคำอธิบายจากSCJP อาทิตย์ที่ผ่านการรับรองสำหรับ Java Programmer 5 คู่มือการศึกษา

เพื่อให้หน่วยความจำ Java มีประสิทธิภาพมากขึ้น JVM จะจัดสรรพื้นที่พิเศษของหน่วยความจำที่เรียกว่า "String constant pool" เมื่อคอมไพเลอร์พบ String ตามตัวอักษรมันจะตรวจสอบพูลเพื่อดูว่ามีสตริงที่เหมือนกันอยู่แล้ว หากพบการจับคู่การอ้างอิงไปที่ตัวอักษรใหม่จะถูกนำไปยังสตริงที่มีอยู่และไม่มีการสร้างวัตถุตัวอักษรสตริงใหม่


มันควรจะทำสิ่งนี้กับวัตถุที่ไม่เปลี่ยนรูปแบบเหมือนกัน แต่ฉันคิดว่ามันจะใช้เวลามากเกินไป
Zan Lynx

8

วัตถุที่ไม่เปลี่ยนรูปไม่สามารถเปลี่ยนแปลงสถานะได้หลังจากสร้างขึ้นแล้ว

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

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

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


5

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


4
String s1="Hi";
String s2=s1;
s1="Bye";

System.out.println(s2); //Hi  (if String was mutable output would be: Bye)
System.out.println(s1); //Bye

s1="Hi": วัตถุs1ถูกสร้างขึ้นด้วยค่า "สูง" ในนั้น

s2=s1 : วัตถุs2ถูกสร้างขึ้นโดยอ้างอิงกับวัตถุ s1

s1="Bye": s1ค่าของวัตถุก่อนหน้าจะไม่เปลี่ยนแปลงเนื่องจากs1มีประเภทสตริงและประเภทสตริงเป็นชนิดไม่เปลี่ยนรูปคอมไพเลอร์สร้างวัตถุสตริงใหม่ที่มีค่า "Bye" และs1อ้างอิงถึงมัน ที่นี่เมื่อเราพิมพ์s2ค่าผลลัพธ์จะเป็น "Hi" ไม่ใช่ "Bye" เนื่องจากs2อ้างถึงs1วัตถุก่อนหน้าซึ่งมีค่า "Hi"


คุณช่วยอธิบายเพิ่มหน่อยได้ไหม?
minigeek

3

หมายความว่าไม่เปลี่ยนรูปเมื่อมีสร้างวัตถุไม่ใช่สมาชิกจะเปลี่ยน Stringไม่เปลี่ยนรูปเนื่องจากคุณไม่สามารถเปลี่ยนเนื้อหาได้ ตัวอย่างเช่น:

String s1 = "  abc  ";
String s2 = s1.trim();

ในโค้ดข้างต้น s1 สตริงไม่เปลี่ยนแปลงวัตถุอื่น ( s2) s1ถูกสร้างขึ้นโดยใช้


3

ไม่เปลี่ยนรูปก็หมายถึงไม่เปลี่ยนแปลงหรือ unmodifiable เมื่อวัตถุสตริงถูกสร้างข้อมูลหรือสถานะของมันไม่สามารถเปลี่ยนแปลงได้

ลองพิจารณาตัวอย่างร้อง

class Testimmutablestring{  
  public static void main(String args[]){  
    String s="Future";  
    s.concat(" World");//concat() method appends the string at the end  
    System.out.println(s);//will print Future because strings are immutable objects  
  }  
 }  

มาทำความคิดกันเรื่องแผนภาพร้อง

ป้อนคำอธิบายรูปภาพที่นี่

ในแผนภาพนี้คุณสามารถเห็นวัตถุใหม่ที่สร้างขึ้นเป็น "อนาคตโลก" แต่ไม่เปลี่ยน "อนาคต" Because String is immutable. sยังอ้างถึง "อนาคต" หากคุณต้องการโทรหา "Future World"

String s="Future";  
s=s.concat(" World");  
System.out.println(s);//print Future World

ทำไมวัตถุสตริงไม่เปลี่ยนรูปใน java?

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


2

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


0

วัตถุไม่เปลี่ยนรูป

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

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

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

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

แหล่ง


0

ในฐานะที่เป็นคำตอบที่ยอมรับไม่ได้ตอบคำถามทุกข้อ ฉันถูกบังคับให้ตอบหลังจาก 11 ปี 6 เดือน

ใครสามารถอธิบายสิ่งที่มีความหมายโดยไม่เปลี่ยนรูป?

หวังว่าคุณหมายถึงวัตถุที่ไม่เปลี่ยนแปลง (เพราะเราสามารถคิดถึงการอ้างอิงที่ไม่เปลี่ยนรูปได้ )

วัตถุไม่สามารถเปลี่ยนแปลงได้ : ถ้าสร้างขึ้นครั้งเดียววัตถุเหล่านั้นจะแสดงค่าเดียวกันเสมอ (ไม่มีวิธีการใดที่เปลี่ยนค่า)

ทำไมถึงStringไม่เปลี่ยนรูป

เคารพข้อกำหนดข้างต้นซึ่งสามารถตรวจสอบได้โดยดูที่ซอร์สโค้ด Sting.java

อะไรคือข้อดี / ข้อเสียของวัตถุที่ไม่เปลี่ยนรูป? ประเภทไม่เปลี่ยนรูปคือ:

  • ปลอดภัยยิ่งขึ้นจากข้อบกพร่อง

  • ง่ายต่อการเข้าใจ

  • และพร้อมสำหรับการเปลี่ยนแปลงมากขึ้น

เหตุใดจึงควรเลือกวัตถุที่ไม่แน่นอนเช่น StringBuilder เหนือ String และ Vice-verse

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

String s = "";
for (int i = 0; i < n; ++i) {
    s = s + n;
}

การใช้สตริงที่ไม่เปลี่ยนรูปแบบทำให้สำเนาชั่วคราวจำนวนมาก - จำนวนแรกของสตริง ("0") ถูกคัดลอก n ครั้งในระหว่างการสร้างสตริงสุดท้ายจำนวนที่สองจะถูกคัดลอก n-1 ครั้งและอื่น ๆ บน. จริงๆแล้วมันใช้เวลา O (n2) ในการคัดลอกทุกอย่างถึงแม้ว่าเราจะต่อกันแค่องค์ประกอบ n เท่านั้น

StringBuilder ถูกออกแบบมาเพื่อลดการคัดลอกนี้ มันใช้โครงสร้างข้อมูลภายในที่เรียบง่าย แต่ชาญฉลาดเพื่อหลีกเลี่ยงการคัดลอกใด ๆ จนกว่าจะถึงจุดสิ้นสุดเมื่อคุณขอใช้สตริงสุดท้ายด้วยการเรียก toString ():

StringBuilder sb = new StringBuilder();
for (int i = 0; i < n; ++i) {
  sb.append(String.valueOf(n));
}
String s = sb.toString();

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

ดูเพิ่มเติมได้ที่นี่: https://web.mit.edu/6.005/www/fa15/classes/09-immutability/#useful_immutable_types


-1

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

ภาษาโปรแกรมโฆษณาซึ่งได้รับความนิยมเพิ่มขึ้นมีความคิดเรื่อง "immutability" ผ่านคำสำคัญ "ไม่เปลี่ยนแปลง" ตรวจสอบบทความ Dr.Dobb นี้เกี่ยวกับมัน - http://dobbscodetalk.com/index.php?option=com_myblog&show=Invariant-Strings.html&Itemid=29 มันอธิบายปัญหาได้อย่างสมบูรณ์แบบ


ฉันเชื่อว่าตั้งแต่ D 2.020 คำหลักถูกเปลี่ยนจากค่าคงที่เป็นไม่เปลี่ยนรูป ฉันไม่เห็นจุด แต่มันบอกว่า "ไม่เปลี่ยนรูปตอนนี้มีการใช้งาน" digitalmars.com/d/2.0/changelog.html#new2_020
he_the_great
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.