배경
Java에서 final이 나타날 수있는 위치는 세 곳입니다.
클래스를 마지막으로 만들면 클래스의 모든 서브 클래스가 방지됩니다. 메소드를 final로 설정하면 메소드의 서브 클래스가 메소드를 대체하지 못합니다. 필드를 최종적으로 만들면 나중에 변경되지 않습니다.
오해
최종 방법 및 필드와 관련하여 최적화가 있습니다.
최종 방법을 사용하면 인라인을 통해 HotSpot을보다 쉽게 최적화 할 수 있습니다. 그러나 HotSpot은 메서드가 다른 방법으로 입증 될 때까지 재정의되지 않았다는 가정에 따라 최종 방법이 아닌 경우에도이를 수행합니다. SO에 대한 자세한 내용
최종 변수는 적극적으로 최적화 될 수 있으며 이에 대한 자세한 내용은 JLS 섹션 17.5.3 에서 읽을 수 있습니다 .
그러나 이러한 이해 를 통해 이러한 최적화 중 어느 것도 수업을 마무리 하는 것에 관한 것이 아님 을 알아야합니다 . 수업을 최종적으로 완료하면 성능이 향상되지 않습니다.
클래스의 마지막 측면은 불변성과 관련이 없습니다. final이 아닌 변경 불가능한 클래스 (예 : BigInteger ) 또는 변경 가능 하고 최종적인 클래스 (예 : StringBuilder )를 가질 수 있습니다 . 수업 이 마지막 이어야 하는지에 대한 결정은 디자인의 문제입니다.
최종 디자인
문자열은 가장 많이 사용되는 데이터 형식 중 하나입니다. 그것들은 맵의 키로 발견되며, 사용자 이름과 암호를 저장하며, 키보드 나 웹 페이지의 필드에서 읽은 것입니다. 문자열은 어디에나 있습니다 .
지도
String을 서브 클래 싱 할 수있을 때 어떤 일이 발생하는지 고려해야 할 첫 번째 사항은 누군가가 String 인 것처럼 보이는 가변 가능한 String 클래스를 구성 할 수 있다는 것을 인식하는 것입니다. 이것은 모든 곳의지도를 망칠 것입니다.
이 가상 코드를 고려하십시오.
Map t = new TreeMap<String, Integer>();
Map h = new HashMap<String, Integer>();
MyString one = new MyString("one");
MyString two = new MyString("two");
t.put(one, 1); h.put(one, 1);
t.put(two, 2); h.put(two, 2);
one.prepend("z");
이것은 일반적으로 맵에서 변경 가능한 키를 사용하는 데 문제가 있지만 내가 거기에서 얻으려고하는 것은 갑자기 맵 나누기에 관한 많은 것들입니다. 엔트리가 더 이상 맵의 올바른 지점에 없습니다. HashMap에서 해시 값이 변경되었으므로 더 이상 올바른 항목이 아닙니다. TreeMap에서 노드 중 하나가 잘못된쪽에 있기 때문에 트리가 손상되었습니다.
이러한 키에 문자열을 사용하는 것이 일반적이므로이 동작은 문자열을 최종적으로 만들어 방지해야합니다.
당신은 독서에 관심이있을 수 있습니다 왜 문자열 불변의 자바에? String의 불변 속성에 대한 자세한 내용은.
사악한 줄
문자열에 대한 여러 가지 사악한 옵션이 있습니다. equal이 호출 될 때 항상 true를 반환하는 String을 만들고 암호 확인에 전달했는지 고려하십시오. 또는 MyString에 할당하면 문자열 복사본을 일부 전자 메일 주소로 보내도록 만들었습니까?
이것들은 String을 서브 클래스 화 할 수있는 능력이있을 때 실제로 가능한 가능성입니다.
Java.lang 문자열 최적화
앞서 언급했지만 final은 String을 더 빨리 만들지 않습니다. 그러나 String 클래스 (및의 다른 클래스 java.lang
)는 필드 및 메소드의 패키지 레벨 보호를 자주 사용하여 다른 java.lang
클래스가 String에 대한 공용 API를 항상 거치지 않고 내부와 땜질 할 수있게합니다. 범위 검사가없는 getChars 또는 StringBuffer에서 사용하는 lastIndexOf 또는 기본 배열을 공유하는 생성자 (메모리 문제로 인해 변경된 Java 6 항목) 가 없는 함수 입니다.
누군가가 String의 하위 클래스를 만든 경우 해당 최적화를 공유 할 수 없습니다 (일부 패키지java.lang
가 아닌 한 봉인 된 패키지입니다 ).
확장을 위해 무언가를 설계하기가 더 어렵습니다.
확장 가능한 것을 설계하는 것은 어렵다 . 그것은 당신이 다른 것을 수정할 수 있도록 내부의 일부를 노출시켜야 함을 의미합니다.
확장 가능한 문자열은 메모리 누수를 수정 하지 못했습니다 . 그 부분은 서브 클래스에 노출되어 있어야하며 해당 코드를 변경하면 서브 클래스가 중단됩니다.
Java는 이전 버전과의 호환성에 자부심을 갖고 핵심 클래스를 확장하여 개방함으로써 타사 서브 클래스와의 계산 성을 유지하면서 문제를 해결할 수있는 능력을 잃습니다.
Checkstyle 에는 "DesignForExtension"이라는 규칙이 적용되어 (내부 코드를 작성할 때 실제로 실망합니다) 모든 클래스가 다음 중 하나가되도록 강제합니다.
합리적인 이유는 다음과 같습니다.
이 API 디자인 스타일은 서브 클래스에 의해 수퍼 클래스가 손상되지 않도록 보호합니다. 단점은 서브 클래스의 유연성이 제한적이며, 특히 수퍼 클래스에서 코드 실행을 막을 수는 없지만 서브 클래스가 수퍼 메소드 호출을 잊어서 수퍼 클래스의 상태를 손상시킬 수 없다는 것입니다.
구현 클래스를 확장 할 수 있다는 것은 서브 클래스가 기반이되는 클래스의 상태를 손상시키고 수퍼 클래스가 제공하는 다양한 보장이 유효하지 않게 만들 수 있음을 의미합니다. String과 같이 복잡한 무언가의 경우, 그것의 일부를 바꾸면 무언가 가 깨질 것이라는 것은 거의 확실합니다 .
개발자 hurbis
개발자의 일부입니다. 각 개발자가 자체 유틸리티 모음을 사용하여 자체 String 서브 클래스를 작성할 가능성을 고려하십시오 . 그러나 이제이 서브 클래스는 서로 자유롭게 할당 될 수 없습니다.
WleaoString foo = new WleaoString("foo");
MichaelTString bar = foo; // This doesn't work.
이 방법은 광기로 이어집니다. 사방에 String으로 캐스팅하고 문자열의 인스턴스 있는지 확인 하여 아니, 그냥 ... 그 하나를 기반으로 새로운 문자열을 만들고, String 클래스 아닌지. 하지마
나는 확실히 당신이 좋은 문자열 클래스를 쓸 수 있어요 ...하지만 C 쓰기 ++ 및 처리해야하는 미친 사람들에 여러 문자열 구현을 작성 남겨 std::string
과 char*
및 부스트에서 뭔가 SString 하고, 나머지는 모두 .
자바 문자열 마법
Java가 Strings와 함께하는 몇 가지 마술이 있습니다. 이를 통해 프로그래머는 언어를 다루기 쉽지만 언어의 일부 불일치를 소개합니다. String에서 서브 클래스를 허용하면 다음과 같은 마법적인 일을 처리하는 방법에 대해 매우 중요한 생각이 필요합니다.
문자열 리터럴 (JLS 3.10.5 )
할 수있는 코드가 있습니다.
String foo = "foo";
이것은 정수와 같은 숫자 유형의 권투와 혼동되어서는 안됩니다. 당신은 할 수 없어 1.toString()
,하지만 당신은 할 수 있습니다 "foo".concat(bar)
.
+
연산자 (JLS 15.18.1 )
Java의 다른 참조 유형은 연산자를 사용할 수 없습니다. 끈은 특별하다. 그 때문에 문자열 연결 연산자는 컴파일러 수준에서 작동 "foo" + "bar"
되고 "foobar"
이 실행시보다는 컴파일 할 때.
문자열 변환 (JLS 5.1.11 )
문자열 컨텍스트에서 모든 개체를 사용하여 문자열로 변환 할 수 있습니다.
문자열 인터 닝 ( JavaDoc )
String 클래스는 String 풀에 액세스하여 컴파일 유형으로 채워진 객체를 표준 리터럴로 표시 할 수 있도록합니다.
String의 서브 클래스를 허용한다는 것은 다른 String 유형이 가능할 때 프로그래밍하기 쉬운 String을 가진 비트가 매우 어려워 지거나 불가능 해짐을 의미합니다.