인터뷰 질문 시리즈 No. 8 : String, StringBuffer 및 StringBuilder의 차이점에 대해 이야기하십시오.

"Java 인터뷰 질문 시리즈": 오랜 지식과 매우 흥미로운 칼럼. 심층 파기, 소스 코드 분석, 원칙 요약, 그림과 텍스트 결합, 공식 계정에 일련의 기사 작성, 인터뷰 수준 올리기. [프로그램 뉴 비전]에 지속적으로 관심을 가져 주셔서 감사합니다. 8 조.

메모리 배포 및 같음 비교 외에도 문자열에 대한 가장 일반적인 인터뷰 질문은 StringBuffer와 StringBuilder의 차이점입니다.

대답하는 경우 : String 클래스는 변경 불가능하고 StringBuffer 및 StringBuilder는 변경 가능한 클래스이며 StringBuffer는 스레드로부터 안전하며 StringBuilder는 스레드로부터 안전하지 않습니다.

위의 요약을 보면 조금 아는 것 같습니다. 이 기사는 세 가지 차이점과 기본 구현에 대한 포괄적 인 이해를 안내합니다.

문자열 연결

String에 대한 많은 기사는 이전 기사에서 자세히 설명했으며, "+"연산이 전달 될 때마다 메모리에 새 문자열이 생성되기 때문에 불변성이 발생합니다.

String a = "hello ";
String b = "world!";
String ab = a + b;

위 코드의 경우 메모리 분포 다이어그램은 다음과 같습니다.

영상

이 중 a와 b는 초기화 될 때 문자열 상수 풀에 있고 스플 라이스 된 객체 ab는 힙에 있습니다. 스 플라이 싱 후 새롭게 생성 된 String 객체를 직관적으로 알 수 있습니다. 여러 번 연결하면 여러 개의 중간 개체가 생성됩니다.

위의 결론은 Java 8 이전에 확립 된 것입니다. Java 8에서는 JDK가 "+"기호의 스 플라이 싱을 최적화했으며 위에서 작성한 스 플라이 싱 메소드는 StringBuilder의 append 메소드를 기반으로 처리에 최적화됩니다.

stack=2, locals=4, args_size=1
     0: ldc           #2                  // String hello
     2: astore_1
     3: ldc           #3                  // String world!
     5: astore_2
     6: new           #4                  // class java/lang/StringBuilder
     9: dup
    10: invokespecial #5                  // Method java/lang/StringBuilder."<init>":()V
    13: aload_1
    14: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
    17: aload_2
    18: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
    21: invokevirtual #7                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
    24: astore_3
    25: return

위는 javap -verbose 명령어를 통해 바이트 코드를 디 컴파일 한 결과로, StringBuilder 생성 및 append 메소드 호출을 볼 수 있습니다.

이 시점에서 일반적인 방법으로 대답하면 더하기 기호를 통해 문자열을 연결하면 여러 개의 String 개체가 만들어 지므로 성능이 StringBuilder보다 나빠져 잘못된 것입니다. 스 플라이 싱 더하기 기호의 효과는 컴파일러에서 처리 한 후 기본적으로 StringBuilder와 동일하기 때문입니다.

코드에 다음 문구를 사용하는 경우 :

StringBuilder sb = new StringBuilder("hello ");
sb.append("world!");
System.out.println(sb.toString());

컴파일러 플러그인은 대신 String을 사용하도록 제안합니다.

StringBuffer와 StringBuilder의 비교

StringBuffer와 StringBuilder의 핵심 코드는 기본적으로 동일하며 많은 코드가 공통입니다. 이 두 클래스는 추상 클래스 AbstractStringBuilder에서 상속됩니다.

생성 방법과 추가 방법의 차이점을 하나씩 살펴 보겠습니다. 먼저 StringBuilder의 생성 방법을 살펴보십시오.

public StringBuilder(String str) {
    super(str.length() + 16);
    append(str);
}

수퍼 메소드는 호출 된 AbstractStringBuilder의 생성 메소드입니다. 해당 StringBuffer의 생성 방법도 마찬가지입니다.

public StringBuffer(String str) {
    super(str.length() + 16);
    append(str);
}

생성 방법에서 StringBuffer와 StringBuilder는 동일합니다. append 메소드를 살펴보면 StringBuilder는 다음과 같이 구현됩니다.

@Override
public StringBuilder append(String str) {
    super.append(str);
    return this;
}

StringBuffer에 해당하는 메소드는 다음과 같습니다.

@Override
public synchronized StringBuffer append(String str) {
    toStringCache = null;
    super.append(str);
    return this;
}

분명히, StringBuffer의 append 메서드 구현에서 toStringCache 변수를 null로 내부 할당하는 것 외에도 유일한 차이점은 메서드가 동기화 처리를 위해 동기화를 사용한다는 것입니다.

toStringCache는 toString 메소드가 마지막으로 호출 될 때 생성 된 문자열을 캐시하는 데 사용되며, StringBuffer의 내용이 변경되면 변경된 값도 변경됩니다.

위의 append 메소드를 비교해 보면 StringBuffer가 스레드로부터 안전하고 StringBuilder가 스레드로부터 안전하지 않음을 쉽게 알 수 있습니다. 물론 동기화 처리에 동기화를 사용하면 성능이 많이 저하됩니다.

StringBuffer 및 StringBuilder의 저수준 구현

StringBuffer와 StringBuilder는 모두 부모 클래스의 생성 메서드를 호출합니다.

AbstractStringBuilder(int capacity) {
    value = new char[capacity];
}

이 구성 방법을 통해 문자열 정보를 처리하는 데 사용하는 키 속성이 값임을 알 수 있습니다. 초기화하는 동안 실제 문자열을 저장하기 위해 수신 문자열의 길이 +16 (값 값) 인 char [] 배열을 초기화합니다.

부모 클래스의 생성 메서드를 호출 한 후 각 append 메서드가 호출되고 (이전 코드 참조) 그 안의 핵심 처리는 부모 클래스의 append 메서드를 호출합니다.

public AbstractStringBuilder append(String str) {
    if (str == null)
        return appendNull();
    int len = str.length();
    ensureCapacityInternal(count + len);
    str.getChars(0, len, value, count);
    count += len;
    return this;
}

위의 코드에서 str.getChars 메서드는 들어오는 str 문자열을 스플 라이스하고 원래 값 배열 뒤에 채우는 데 사용됩니다. 그리고 count는 현재 값 번호에 사용 된 길이를 기록하는 데 사용됩니다.

영상

그렇다면 동기화 작업이 동기화에 사용되지 않을 때 스레드 불안정은 어디에서 발생합니까? 위 코드에서 count + = len은 원자 연산이 아닙니다. 예를 들어, 현재 카운트가 5이고 두 스레드가 동시에 ++ 연산을 수행하고 얻은 값은 모두 5입니다. 더하기 연산이 수행 된 후 값이 카운트에 할당되고 두 스레드에 7이 아닌 6이 할당됩니다. 이때 쓰레드 불안정 문제가 있습니다.

String이 변경 불가능하도록 설계되어야하는 이유

Java에서 변경할 수없는 문자열의 디자인은 다음과 같은 이유로 다양한 요소를 포괄적으로 고려한 결과입니다.

1. 문자열 상수 풀의 필요성 문자열이 가변적 인 경우 한 개체를 변경하면 다른 독립 개체에 영향을줍니다. 이것은 또한 문자열 상수 풀의 존재를위한 전제 조건입니다.

2. Java에서 String 객체의 해시 코드는 HashMap과 같은 컨테이너에서 자주 사용됩니다. 상수 문자열은 캐시하고 사용할 수있는 해시 코드의 고유성을 보장합니다.

3. 보안, 보안 위험을 피하기 위해 매개 변수로 전달 될 때 문자열이 변경되지 않도록합니다. 예를 들어, 데이터베이스 사용자 이름, 암호, 액세스 경로 등은 변경된 문자열이 가리키는 개체의 값이 변경되는 것을 방지하기 위해 전송 프로세스 중에 변경되지 않은 상태로 유지됩니다.

4. 문자열 변수는 불변이므로 여러 스레드에서 공유하고 사용할 수 있습니다.

요약

우리는 모두 간단한 암기 테스트 문제를 알고 있지만 인터뷰 질문을 암기하는 과정에서 기본 구현 원칙에 대해 더 많이 배워야합니다. 이는 "이유"를 이해하는 데 도움이 될뿐만 아니라 관련 지식과 원칙도 더 많이 배웁니다.

이 기사에서는 StringBuilder와 StringBuffer의 내부 데이터를 복사하고 배열을 확장하는 단계에 대한 설명을 단순화하고 관심이있는 사람들은 소스 코드에 대한 심층 연구를 계속할 수 있습니다.

원본 링크 : " 인터뷰 질문 시리즈 No. 8 : String, StringBuffer, StringBuilder의 차이점에 대해 이야기하십시오.


절차의 새로운 비전

소프트 파워와 하드 기술을 동시에 향상시켜 대량의 데이터를 제공 할 수있는 플랫폼 " New Vision of Program " 공개 계정

WeChat 공식 계정 : 프로그램의 새로운 비전

추천

출처blog.csdn.net/wo541075754/article/details/108604389