문제

이것은 지금까지 물어본 것 중 가장 멍청한 질문일 수 있지만 Java 초보자에게는 상당히 혼란스러운 질문이라고 생각합니다.

  1. 누군가가 무엇을 의미하는지 명확히 할 수 있습니까? 불변?
  2. String 불변?
  3. 불변 객체의 장점/단점은 무엇입니까?
  4. 왜 다음과 같은 변경 가능한 객체를 사용해야 합니까? StringBuilder String과 그 반대의 경우보다 선호됩니까?

좋은 예(Java)를 보여주시면 정말 감사하겠습니다.

도움이 되었습니까?

해결책

불변성은 객체의 생성자가 실행을 완료하면 인스턴스를 변경할 수 없다는 것을 의미합니다.

이것은 다른 사람이 내용을 바꿀 것이라고 걱정하지 않고 주변의 물체에 대한 참조를 전달할 수 있다는 의미입니다. 특히 동시성을 다룰 때는 절대 변하지 않는 객체에 잠금 문제가 없습니다.

예를 들어

class Foo
{
     private final String myvar;

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

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

Foo 발신자가 걱정할 필요가 없습니다 getValue() 문자열의 텍스트를 변경할 수 있습니다.

비슷한 수업을 상상한다면 Foo, 그러나 a StringBuilder a보다는 String 회원으로서 발신자가 getValue() 변경할 수 있습니다 StringBuilder a의 속성 Foo 사례.

또한 당신이 찾을 수있는 다른 종류의 불변성을 조심합니다 : Eric Lippert는 블로그 기사 이것에 관해서. 기본적으로 인터페이스가 불변이지만 실제 Mutables Private State 뒤에있는 객체를 가질 수 있습니다 (따라서 스레드간에 안전하게 공유 할 수 없습니다).

다른 팁

불변의 물체는 내부 필드 (또는 적어도 외부 동작에 영향을 미치는 모든 내부 필드)를 변경할 수없는 객체입니다.

불변의 현에는 많은 장점이 있습니다.

성능: 다음 작업을 수행하십시오.

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

substring () 메소드의 기본 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;
}

주목하십시오 캐릭터를 복사 할 필요가 없습니다! 문자열 객체가 음소거 된 경우 (캐릭터가 나중에 변경 될 수 있음) 모든 문자를 복사해야합니다. 그렇지 않으면 하위 문자의 문자 변경이 나중에 다른 문자열에 반영됩니다.

동시성 : 불변의 객체의 내부 구조가 유효하면 항상 유효합니다. 다른 스레드가 해당 객체 내에서 유효하지 않은 상태를 만들 수있는 가능성은 없습니다. 따라서 불변의 대상은 있습니다 스레드 안전합니다.

쓰레기 수집 : 쓰레기 수집가가 불변의 물체에 대한 논리적 결정을 내리는 것이 훨씬 쉽습니다.

그러나 불변성의 단점도 있습니다.

성능: 잠깐, 나는 당신이 불변성의 상승이라고 말했다고 생각했습니다! 글쎄, 그것은 때때로하지만 항상 그런 것은 아닙니다. 다음 코드를 선택하십시오.

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

두 줄은 모두 네 번째 문자를 문자 "a"로 바꿉니다. 두 번째 코드는 더 읽기 쉬운 일뿐만 아니라 더 빠릅니다. Foo에 대한 기본 코드를 어떻게 해야하는지 살펴보십시오. 하위 문자열은 쉽지만 이제는 공간 5에 이미 캐릭터가 있고 다른 것이 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));

연결이 호출됩니다 두 배 전체 문자열을 루프해야한다는 것을 의미합니다! 이것을 C의 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 객체는 로컬 참조이며 동시성 안전에 대해 걱정할 필요가 없습니다 (하나의 스레드 만 접촉합니다). 그리고 다른 곳에서는 참조되지 않기 때문에 스택에만 할당되므로 기능 호출이 완료 되 자마자 거래됩니다 (쓰레기 수집에 대해 걱정할 필요가 없습니다). 그리고 당신은 돌연변이와 불변성의 모든 성능 이점을 얻습니다.

실제로 위에서 제안한 위키피디아 정의를 사용하면 문자열은 변경할 수 없습니다.

문자열의 상태는 생성 후 변경됩니다.hashcode() 메소드를 살펴보세요.String은 로컬 필드에 해시코드 값을 캐시하지만 hashcode()를 처음 호출할 때까지 이를 계산하지 않습니다.해시코드에 대한 이러한 지연 평가는 String을 상태가 변경되는 불변 객체로서 흥미로운 위치에 배치하지만 리플렉션을 사용하지 않고는 변경된 것으로 관찰할 수 없습니다.

따라서 불변의 정의는 변경된 것으로 관찰될 수 없는 객체여야 합니다.

불변 객체가 생성된 후 상태가 변경되었지만 아무도 그것을 볼 수 없다면(리플렉션 없이) 객체는 여전히 불변인가요?

불변의 물체는 프로그래밍 방식으로 변경할 수없는 객체입니다. 특히 여러 스레드 환경이나 둘 이상의 프로세스가 객체의 값을 변경 (돌연변이) 할 수있는 다른 환경에 적합합니다.

그러나 명확히하기 위해 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;
    }

}

이 예에서, 색상 정식은 새로운 컬러링 스트링 클래스를 만들지 않고 핵심 속성 중 하나를 변경 (돌연변이) 할 수 있기 때문에 변이 가능하다고합니다. 예를 들어, 이것이 나쁜 이유는 여러 스레드가있는 GUI 응용 프로그램이 있고 컬러링 스트링을 사용하여 데이터를 창에 인쇄한다고 가정 해 봅시다. 당신이

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

그러면 문자열이 항상 "파란색"이 될 것으로 기대합니다. 그러나 다른 스레드 가이 인스턴스를 보유하고

blueString.setColor("Red");

당신은 갑자기, 아마도 예기치 않게 아마 "파란색"을 원했을 때 "빨간색"줄을 가질 것입니다. 이로 인해 불변의 물체는 주변의 인스턴스를 통과 할 때 거의 항상 선호됩니다. 변이 가능한 물체가 실제로 필요한 경우가있는 경우 일반적으로 특정 제어 분야에서 사본을 전달하여 OBJET을 보호합니다.

Java에서 Java.lang.string은 불변의 대상입니다. 할 수 없습니다 생성되면 변경됩니다) 및 Java.lang.stringBuilder는 새 인스턴스를 만들지 않고 변경할 수 있기 때문에 변한 객체입니다.

  1. 대규모 애플리케이션에서는 문자열 리터럴이 많은 메모리를 차지하는 것이 일반적입니다.따라서 메모리를 효율적으로 처리하기 위해 JVM은 "문자열 상수 풀"이라는 영역을 할당합니다.(메모리에서는 참조되지 않은 문자열이라도 char[], 길이에 대한 int, hashCode에 대한 또 다른 값을 전달합니다.이와 대조적으로 숫자의 경우 최대 8개의 즉시 바이트가 필요합니다.)
  2. 컴파일러가 문자열 리터럴을 발견하면 풀에서 동일한 리터럴이 이미 존재하는지 확인합니다.그리고 하나가 발견되면 새 리터럴에 대한 참조는 기존 문자열로 전달되고 새로운 '문자열 리터럴 개체'는 생성되지 않습니다(기존 문자열은 단순히 추가 참조를 얻습니다).
  3. 따라서 : 문자열 가변성은 메모리를 절약합니다...
  4. 그러나 변수 중 하나라도 값이 변경되면 실제로 변경되는 것은 참조일 뿐이며 메모리의 값은 변경되지 않습니다(따라서 이를 참조하는 다른 변수에는 영향을 미치지 않습니다)....

String s1 = "이전 문자열";

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

           [s1]   --------------->|   "Old 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가 없다면, "오래된 문자열"은 여전히 ​​메모리에있을 것이지만 우리는 그것을 액세스 할 수 없을 것입니다 ...

"불변"은 가치를 바꿀 수 없다는 것을 의미합니다. 문자열 클래스 인스턴스가있는 경우 값을 수정하는 것처럼 보이는 모든 메소드가 실제로 다른 문자열을 만듭니다.

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

변경 사항을 보존하려면이 foo = foo.sustring (3)과 같은 일을해야합니다.

컬렉션으로 작업 할 때는 불변성 대 Mutable이 재미있을 수 있습니다. 맵의 키로 Mutable Object를 사용하면 값을 변경하면 어떤 일이 일어날 지 생각해보십시오 (팁 : 생각해보십시오. equals 그리고 hashCode).

Java.Time

조금 늦었지만 불변의 대상이 무엇인지 이해하기 위해서는 새로운 Java 8 날짜 및 시간 API의 다음 예를 고려하십시오 (Java.Time). 아마도 Java 8의 모든 날짜 개체는 불변 다음 예에서

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

산출:

2014-03-18

이것은 초기 날짜와 같은 해에 plusYears(2) 새 객체를 반환하여 이전 날짜가 여전히 변하지 않기 때문에 여전히 변하지 않습니다. 일단 생성되면 더 이상 수정할 수 없으며 날짜 변수가 여전히이를 가리 킵니다.

따라서 해당 코드 예제는 해당 호출에 의해 인스턴스화되고 반환 된 새 개체를 캡처하고 사용해야합니다. plusYears.

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

date.tostring ()… 2014-03-18

Datejertwoyears.tostring ()… 2016-03-18

나는 설명을 정말 좋아합니다 Java 5 학습 가이드를위한 SCJP Sun Certified 프로그래머.

Java가 메모리를보다 효율적으로 효율적으로 만들기 위해 JVM은 "String Constant Pool"이라는 특별한 메모리 영역을 따로 설정합니다. 컴파일러가 문자열 리터럴을 만나면 수영장에서 동일한 문자열이 이미 존재하는지 확인합니다. 성냥이 발견되면 새 문자에 대한 참조는 기존 문자열로 향하고 새 문자열 문자 그럴 객체가 생성되지 않습니다.

불변의 물체는 생성 된 후 상태를 변경할 수 없습니다.

할 수있을 때마다 불변의 객체를 사용해야하는 세 가지 주요 이유가 있으며, 이들은 코드에서 소개하는 버그 수를 줄이는 데 도움이됩니다.

  • 다른 방법으로 객체의 상태를 변경할 수 없다는 것을 알면 프로그램의 작동 방식에 대해 추론하는 것이 훨씬 쉽습니다.
  • 불변의 객체는 자동으로 스레드 안전합니다 (안전하게 게시되었다고 가정).
  • 불변의 객체는 항상 동일한 해시 코드를 가지므로 해시 맵 (또는 이와 유사한)의 키로 사용할 수 있습니다. 해시 테이블에서 요소의 해시 코드가 변경되면 테이블에서 찾을 수있는 시도가 잘못된 위치를 찾아 내기 때문에 테이블 항목이 효과적으로 손실됩니다. 이것이 문자열 객체가 불변이라는 주된 이유입니다. 그들은 자주 해시 맵 키로 사용됩니다.

예를 들어 객체의 상태가 불변이라는 것을 알면 코드로 만들 수있는 다른 최적화도 있습니다. 예를 들어 계산 된 해시를 캐싱합니다. 그러나 이들은 최적화이므로 거의 흥미롭지 않습니다.

한 가지 의미는 컴퓨터에 값이 저장되는 방식과 관련이 있습니다. 메모리에 문자열과 기존 변수 (다른 곳의 실제 문자 모음에 대한 포인터)를 새 문자열로 향하게합니다.

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 "HI"값으로 만들어졌습니다.

s2=s1 : 객체 s2 S1 객체를 참조하여 생성됩니다.

s1="Bye" : 이전 s1 객체의 값은 변하지 않기 때문입니다 s1 문자열 유형과 문자열 유형은 불변 유형이며 컴파일러는 "bye"값을 가진 새 문자열 객체를 만듭니다. s1 그것을 참조했습니다. 인쇄 할 때 s2 가치, 결과는 "안녕"이 아니라 "bye"가 될 것입니다. s2 이전에 참조 s1 "HI"값을 가진 객체.

불변성은 일단 객체가 생성되면 회원 중 비 변경이 변할 것임을 의미합니다. String 콘텐츠를 변경할 수 없으므로 불변입니다. 예를 들어:

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

위의 코드에서 문자열 S1이 변경되지 않았으며 다른 객체 (s2)를 사용하여 만들어졌습니다 s1.

불변은 단순히 변할 수 없거나 수정할 수없는 것을 의미합니다. 문자열 객체가 생성되면 데이터 또는 상태를 변경할 수 없습니다.

Bellow 예를 고려하십시오.

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  
  }  
 }  

벨로우 다이어그램을 고려한 아이디어를 받자.

enter image description here

이 다이어그램에서는 새로운 객체를 "미래 세계"로 볼 수 있습니다. 그러나 "미래"를 바꾸지 않습니다.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에서는 문자열 객체가 불변입니다.

일단 주입되면 변경 될 수 없습니다. 인스턴스가 해시 가능 또는 이와 유사한 키로 사용될 수있는 클래스를 고려하십시오. Java 모범 사례를 확인하십시오.

불변의 대상

대상은 상태가 건설 된 후에 변경 될 수없는 경우 불변으로 간주됩니다. 불변의 물체에 대한 최대 의존은 간단하고 신뢰할 수있는 코드를 만드는 건전한 전략으로 널리 받아 들여집니다.

불변의 물체는 특히 동시 응용 분야에서 유용합니다. 상태를 바꿀 수 없기 때문에 실 간섭에 의해 손상되거나 일관되지 않은 상태에서 관찰 될 수 없습니다.

프로그래머는 종종 불변의 물체를 사용하는 것을 꺼려합니다. 왜냐하면 그들은 객체를 업데이트하는 것과 반대로 새로운 객체를 만드는 데 드는 비용에 대해 걱정하기 때문입니다. 객체 생성의 영향은 종종 과대 평가되며 불변의 물체와 관련된 일부 효율성에 의해 상쇄 될 수 있습니다. 여기에는 쓰레기 수집으로 인한 오버 헤드 감소 및 부패로부터 변이 가능한 물체를 보호하는 데 필요한 코드 제거가 포함됩니다.

다음 하위 섹션에서는 인스턴스가 변이 가능하고 불변의 사례가있는 클래스를 도출하는 클래스를 사용합니다. 그렇게함으로써, 그들은 이런 종류의 전환에 대한 일반적인 규칙을 제시하고 불변의 대상의 장점 중 일부를 보여줍니다.

원천

Oracle Docs는 말합니다

객체가 고려됩니다 불변 상태가 건설 된 후에 상태가 변경 될 수없는 경우. 불변의 물체에 대한 최대 의존은 간단하고 신뢰할 수있는 코드를 만드는 건전한 전략으로 널리 받아 들여집니다.

불변의 물체는 특히 동시 응용 분야에서 유용합니다. 상태를 바꿀 수 없기 때문에 실 간섭에 의해 손상되거나 일관되지 않은 상태에서 관찰 될 수 없습니다.

나는이 문구를 좋아한다 게시하다

불변의 물체는 동시성 프로그래밍을 용이하게합니다

불변의 물체는 당신이 그것을 만든 후에 수정할 수없는 것입니다. 일반적인 예는 문자열 리터럴입니다.

점점 인기를 얻는 광고 프로그래밍 언어는 "불변성"키워드를 통해 "불변성"이라는 개념을 가지고 있습니다. 이 Dr.Dobb의 기사를 확인하십시오. http://dobbscodetalk.com/index.php?option=com_myblog&show=invariant-strings.html&itemid=29 . 문제를 완벽하게 설명합니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top