답변:
우선, 나는이 기사를 추천합니다 : Java : 최종 클래스를 만들 때
그들이 사용하는 경우, 언제 사용하면 더 잘 이해하고 사용시기를 알 수 있습니다.
final
클래스는 단순히 클래스 확장 할 수 없습니다 .
(이것은 클래스의 객체에 대한 모든 참조가 마치로 선언 된 것처럼 행동한다는 것을 의미하지는 않습니다 final
.)
클래스를 final로 선언하는 것이 유용한 경우이 질문의 답변에서 다룹니다.
Java가 객체 지향적이고 클래스를 선언하면
final
객체의 특성을 가진 클래스의 아이디어를 멈추지 않습니까?
어떤 의미에서는 그렇습니다.
클래스를 최종으로 표시하면 해당 코드 부분에 대해 강력하고 유연한 언어 기능이 비활성화됩니다. 일부 클래스는하지만, (그리고 어떤 경우에는 안 할 수 있는 좋은 방법에서 고려의 서브 클래스하도록 설계 할 수 없습니다)가. 이러한 경우 OOP를 제한하더라도 클래스를 최종 클래스로 표시하는 것이 좋습니다. 그러나 최종 클래스는 여전히 최종 클래스가 아닌 다른 클래스를 확장 할 수 있습니다.
Java에서 final
수정자가있는 변경할 수 없습니다!
여기에는 최종 클래스, 최종 변수 및 최종 메소드가 포함됩니다.
final
수정자가있는 항목을 변경할 수 없습니다!" 라는 문구 는 너무 범주적이고 사실 완전히 정확하지 않습니다. Grady Booch가 말한 것처럼 "객체는 상태, 행동 및 정체성을 가지고 있습니다". 참조가 최종으로 표시된 후에는 개체의 ID를 변경할 수 없지만 필드 가 아닌 필드 (물론 제공되는 경우)에 새 값을 할당 하여 상태 를 변경할 final
수 있습니다. 시험에서이 부분에 대한 질문이있을 수 있으므로 Oracle Java 인증 (1Z0-808 등)을 얻으려면이 점을 염두에 두어야합니다.
보안상의 이유로 클래스 상속 을 방지 하려는 경우 final이 중요한 시나리오 . 이를 통해 실행중인 코드를 다른 사람 이 재정의 할 수 없도록 할 수 있습니다 .
또 다른 시나리오는 최적화입니다 .Java 컴파일러는 최종 클래스에서 일부 함수 호출을 인라인한다는 것을 기억합니다. 따라서 호출 a.x()
하고 a가 선언 final
되면 컴파일 타임에 코드가 무엇인지 알고 호출 함수에 인라인 할 수 있습니다. 이것이 실제로 수행되는지 여부는 알 수 없지만 최종적으로는 가능합니다.
관련 자료 : Bob Martin 의 공개 원칙 .
주요 인용문 :
확장을 위해 소프트웨어 엔터티 (클래스, 모듈, 기능 등)가 열려 있어야하지만 수정을 위해 닫혀 있어야합니다.
final
키워드는 방법이나 클래스에 사용되는 여부, 자바에서이를 시행 할 수있는 수단입니다.
final
클래스가 개방형이 아닌 확장을 위해 폐쇄되도록 선언하지 않습니까? 아니면 문자 그대로 복용합니까?
final
구현 코드를 수정을 위해 닫히고 상속을 통해 확장을 위해 열려는 경우 클래스 / 메서드 선언 에서 사용하는 것은 의미가 없습니다.
키워드 final
자체는 최종적인 것을 의미하며 어떤 식 으로든 수정해서는 안됩니다. 클래스가 표시되어 있으면 final
확장하거나 하위 클래스 로 만들 수 없습니다. 그러나 문제는 왜 우리가 수업을 표시 final
합니까? IMO에는 여러 가지 이유가 있습니다.
나는 마킹 클래스가 final
효율성을 향상 시킨다고 들었지만 솔직히 말해서 많은 무게를 지니고있는이 주장을 찾을 수 없었습니다.
Java가 객체 지향적이며 클래스를 final로 선언하면 객체의 특성을 가진 클래스의 아이디어를 멈추지 않습니까?
아마도 그렇습니다. 그러나 때때로 그것은 의도 된 목적입니다. 때때로 우리는이 클래스의 능력을 확장하여 보안 등의 더 큰 이점을 얻기 위해 그렇게합니다. 그러나 필요한 경우 최종 클래스는 여전히 하나의 클래스를 확장 할 수 있습니다.
참고로 상속보다 구성을 선호 해야 하며 final
키워드는 실제로이 원칙을 시행하는 데 도움이됩니다.
final class
새로운 메소드를 추가 할 때 퍼블릭 API가 깨지는 것을 피할 수 있습니다
Base
클래스 의 버전 1에서 다음 을 수행 한다고 가정하십시오 .
public class Base {}
그리고 클라이언트는 :
class Derived extends Base {
public int method() { return 1; }
}
그런 다음 버전 2에서 method
메소드를 추가 하려면 Base
다음 을 수행하십시오 .
class Base {
public String method() { return null; }
}
클라이언트 코드가 손상 될 수 있습니다.
우리가 final class Base
대신 사용했다면 , 클라이언트는 상속 할 수 없었고 메소드 추가는 API를 손상시키지 않을 것입니다.
클래스가로 표시 final
되면 클래스 구조를 외부에서 수정할 수 없음을 의미합니다. 이것이 가장 눈에 띄는 곳은 전통적인 다형성 상속을 할 때 기본적으로 class B extends A
작동하지 않습니다. 그것은 기본적으로 코드의 일부를 보호하는 방법 (범위) .
명확히하기 위해 클래스를 final
표시하면 필드가 표시 final
되지 않으므로 객체 속성이 아니라 실제 클래스 구조가 대신 보호됩니다.
최종 문제를 해결하려면 :
수업을 마무리하는 두 가지 방법이 있습니다. 첫 번째는 클래스 선언에서 final 키워드를 사용하는 것입니다.
public final class SomeClass {
// . . . Class contents
}
클래스를 final로 만드는 두 번째 방법은 모든 생성자를 private으로 선언하는 것입니다.
public class SomeClass {
public final static SOME_INSTANCE = new SomeClass(5);
private SomeClass(final int value) {
}
마지막으로 표시하면이 테스트 클래스를 살펴 보는 것이 실제로 최종적인 것으로 밝혀지면 문제가 해결됩니다. 언뜻보기에 공개적으로 보입니다.
public class Test{
private Test(Class beanClass, Class stopClass, int flags)
throws Exception{
// . . . snip . . .
}
}
불행히도 클래스의 유일한 생성자는 private이므로이 클래스를 확장 할 수 없습니다. Test 클래스의 경우 클래스가 최종적인 이유는 없습니다. Test 클래스는 암시 적 최종 클래스가 어떻게 문제를 일으킬 수 있는지에 대한 좋은 예입니다.
따라서 암시 적으로 클래스를 생성자 전용으로 만들어서 클래스를 최종적으로 만들 때는 최종으로 표시해야합니다.
예, 때로는 보안 또는 속도상의 이유로이를 원할 수도 있습니다. C ++에서도 수행됩니다. 그것은하지 않을 수 가 있지만있는 moreso 프레임 워크를위한 프로그램에 적용. http://www.glenmccl.com/perfj_025.htm
자바에서 최종 키워드는 아래 경우에 사용됩니다.
자바에서 최종 변수는 재 할당 할 수없고, 최종 클래스 는 확장 될 수없고 최종 메소드는 재정의 할 수 없습니다.
FINAL을 "줄의 끝"이라고 생각하십시오. 그 남자는 더 이상 자손을 낳을 수 없습니다. 따라서 이런 식으로 볼 때, '줄 끝'마커를 클래스에 표시해야하는 수많은 실제 시나리오가 있습니다. 도메인 기반 디자인입니다. 도메인에서 지정된 ENTITY (클래스)가 서브 클래스를 작성할 수 없도록 요구하는 경우이를 FINAL로 표시하십시오.
"최종 태그"클래스를 상속받지 못하게하는 것은 없습니다. 그러나 그것은 일반적으로 "상속의 남용"으로 분류되며, 대부분의 경우 클래스의 기본 클래스에서 일부 기능을 상속하려고하기 때문에 수행됩니다.
가장 좋은 방법은 도메인을보고 디자인 결정을 지시하는 것입니다.
Android Looper 클래스가 이에 대한 좋은 예입니다. http://developer.android.com/reference/android/os/Looper.html
Looper 클래스는 다른 클래스에서 재정의하지 않는 특정 기능을 제공합니다. 따라서 여기에는 하위 클래스가 없습니다.
Employee
메소드 가있는 클래스가 있다고 가정 해 봅시다 greet
. greet
메소드가 호출 되면 단순히 인쇄합니다 Hello everyone!
. 이것이 방법 의 예상되는 동작 입니다greet
public class Employee {
void greet() {
System.out.println("Hello everyone!");
}
}
이제 아래와 같이 GrumpyEmployee
서브 클래스 Employee
와 greet
메소드를 오버라이드 하자 .
public class GrumpyEmployee extends Employee {
@Override
void greet() {
System.out.println("Get lost!");
}
}
이제 아래 코드에서 sayHello
메소드를 살펴보십시오 . 그것은 소요 Employee
매개 변수로 인스턴스를하고 말 것이라고 기대하는 인사 메소드를 호출 Hello everyone!
하지만 우리가 얻을 것은 Get lost!
. 이 행동의 변화는Employee grumpyEmployee = new GrumpyEmployee();
public class TestFinal {
static Employee grumpyEmployee = new GrumpyEmployee();
public static void main(String[] args) {
TestFinal testFinal = new TestFinal();
testFinal.sayHello(grumpyEmployee);
}
private void sayHello(Employee employee) {
employee.greet(); //Here you would expect a warm greeting, but what you get is "Get lost!"
}
}
이 상황은 수업이 이루어진 경우 피할 수 있습니다 . 클래스가로 선언되지 않은 경우 건방진 프로그래머가 발생할 수있는 혼란의 정도를 상상해보십시오 .Employee
final
String
final
객체 지향은 상속에 관한 것이 아니라 캡슐화에 관한 것입니다. 그리고 상속은 캡슐화를 깨뜨립니다.
클래스 파이널 선언은 많은 경우에 완벽합니다. 색상이나 금액과 같은“가치”를 나타내는 모든 물체는 최종적 일 수 있습니다. 그들은 스스로 서 있습니다.
라이브러리를 작성하는 경우 클래스를 명시 적으로 들여 쓰지 않으면 클래스를 최종 클래스로 만듭니다. 그렇지 않으면 사람들이 클래스를 파생시키고 메소드를 재정 의하여 가정 / 불변을 깨뜨릴 수 있습니다. 보안에 영향을 줄 수도 있습니다.
"Effective Java"의 Joshua Bloch는 상속을 위해 명시 적으로 디자인하거나이를 금지 할 것을 권장하며 상속을 위해 디자인하는 것은 쉽지 않다고 지적합니다.