452

나는 자바에 관한 책을 읽고 있는데, 전체 클래스를 다음과 같이 선언 할 수 있다고 말한다.final. 나는 이것을 사용할 곳을 생각할 수 없다.

나는 프로그래밍에 익숙하지 않고 궁금하다.프로그래머가 실제로 이것을 자신의 프로그램에서 사용한다면. 그들이 그렇게한다면, 언제 사용합니까? 나는 그것을 더 잘 이해할 수 있고 그것을 언제 사용할 수 있는지 알 수 있습니다.

자바가 객체 지향이고 클래스를 선언한다면final, 그것은 객체의 특성을 가진 클래스의 아이디어를 멈추지 않는가?

21 답변


437

그들이 그렇게한다면, 언제 사용합니까? 나는 그것을 더 잘 이해할 수 있고 그것을 언제 사용할 수 있는지 알 수 있습니다.

에이final클래스는연장 할 수 없다..

(이것은 클래스의 객체에 대한 모든 참조가 마치 다음과 같이 선언 된 것처럼 동작한다는 것을 의미하지는 않습니다.final.)

클래스를 최종 클래스로 선언하는 것이 유용 할 때이 질문의 답변에서 다룹니다.

자바가 객체 지향이고 클래스를 선언한다면final, 그것은 객체의 특성을 가진 클래스의 아이디어를 멈추지 않는가?

어떤 의미에서는 그렇습니다.

클래스를 final로 표시하면 해당 코드 부분에 대한 강력하고 유연한 언어 기능이 사용 중지됩니다. 그러나 일부 수업은하지 않아야합니다 (그리고 어떤 경우에는양철통아닙니다) 좋은 방법으로 하위 클래스 화를 고려하도록 설계되었습니다. 이 경우 OOP를 제한하더라도 클래스를 final로 표시하는 것이 좋습니다. (그러나 최종 클래스는 여전히 다른 비 최종 클래스를 확장 할 수 있음을 기억하십시오.)


관련 기사 :Java : 최종 클래스 생성시기


  • 답을 추가하기 위해 Effective Java의 원리 중 하나는 상속에 비해 합성을 선호하는 것입니다. 최종 키워드를 사용하면 해당 원칙을 적용하는 데 도움이됩니다. - Riggy
  • " 효율성 및 보안상의 이유로 주로합니다. " 나는이 말을 아주 자주 듣는다. (위키피디아도 이것을 언급한다.) 그러나 나는이 논증 뒤에있는 추론을 여전히 이해하지 못한다. 예를 들어, 비 최종 java.lang.String이 비효율적이거나 안전하지 않은 방식으로 종료되는 방법을 설명하는 사람이 있습니까? - MRA
  • @MRA String을 매개 변수로 받아들이는 메서드를 만들면 String이 있기 때문에 변경할 수 없다고 가정합니다. 이 결과, String 객체의 메서드를 안전하게 호출하고 전달 된 String을 변경할 수 없다는 것을 알았습니다. String을 확장하고 부분 문자열의 구현을 변경하여 실제 String을 변경하려는 경우 변경 불가능한 String 객체는 더 이상 변경할 수 없습니다. - Cruncher
  • @Sortofabeginner 그리고 모든 String 메소드와 필드를 최종적으로 추가하기를 원하면 추가 기능을 사용하여 일부 클래스를 만들 수 있습니다 ... 그 시점에서 문자열과 그 문자열을 조작하는 메소드를 생성한다. - Cruncher
  • @Shay final (다른 것들 사이에서)은 객체를 변경 불가능하게 만드는 데 사용되므로 서로 아무 관계가 없다고 말할 수 없습니다. 여길 봐docs.oracle.com/javase/tutorial/essential/concurrency/… - Celeritas

161

자바에서는final변경자는 변경할 수 없습니다!

여기에는 최종 클래스, 최종 변수 및 최종 메소드가 포함됩니다.

  • 최종 수업은 다른 수업으로 확장 될 수 없습니다.
  • 최종 변수는 다른 값을 재 할당 할 수 없습니다.
  • 최종 방법을 재정의 할 수 없습니다.


  • 실제 질문은, 아니라. - Francesco Menzani
  • " Java에서final수식어는 변경할 수 없습니다! ", 너무 범주적이며, 실제로는 완전히 정확하지 않습니다. Grady Booch가 말했듯이, "물체에는 상태, 행동 및 신원이 있습니다." 참조가 최종이라고 표시되면 개체의 ID를 변경할 수는 없지만 개체의 ID가 변경 될 수 있습니다.상태새로운 값을 비 -finalOracle Java 인증 (예 : 1Z0-808 등)을 취득 할 계획이라면이 점에 유의해야합니다. 시험에 관한이 질문에 대한 질문이있을 수 있습니다. . - Igor Soudakevitch

27

결승전이 중요한 한 시나리오, 원하는 경우상속을 막다.보안상의 이유로 클래스의 이를 통해 실행중인 코드를 확인할 수 있습니다.재정의 할 수 없다.누군가에 의해.

또 다른 시나리오는 최적화입니다. Java 컴파일러가 최종 클래스에서 일부 함수 호출을 인라인하는 것을 기억합니다. 전화하면a.x()그리고 a가 선언되었다.final, 우리는 컴파일 타임에 코드가 무엇인지를 알고 호출 함수에 인라인 할 수 있습니다. 나는 이것이 실제로 행해졌는지 전혀 모르지만, 최종적으로는 가능성이있다.


  • 인라이닝은 일반적으로 런타임에 Just-In-Time 컴파일러에 의해서만 수행됩니다. final 없이도 작동하지만 JIT 컴파일러는 확장 클래스가 없다는 것을 확실히하기 위해해야 할 일이 조금 더 있습니다 (또는이 확장 클래스가이 메서드에 닿지 않는다). - Paŭlo Ebermann
  • 인라인 및 최적화 문제에 대한 좋은 글을 다음에서 찾을 수 있습니다.lemire.me/blog/archives/2014/12/17/… - Josh Hemann

19

가장 좋은 예는

public final class String

이 클래스는 변경 불가능한 클래스이며 확장 할 수 없습니다. 물론 최종 클래스를 변경 불가능하게 만드는 것 이상의 의미가 있습니다.


  • Hehe, 때로는 Rube Goldergian 개발자를 보호합니다. - Zoidberg

15

클래스 계층 구조를 트리 (Java에서와 같이)로 상상하면 추상 클래스는 분기 만 가능하며 최종 클래스는 리프로만 될 수있는 클래스입니다. 이러한 범주에 속하지 않는 클래스는 분기와 리프가 될 수 있습니다.

OO 원칙에 위배되는 것은 없으며, 마지막으로 멋진 대칭을 제공하는 것입니다.

실제적으로 당신은 당신의 객체가 불변이거나 API를 쓰고 있다면 API의 사용자에게 클래스가 단지 확장을 목적으로하지 않는다는 것을 알리기 위해 final을 사용하고 싶을 것이다.


15

관련 독서 과제 :개방 원칙Bob Martin.

핵심 견적 :

소프트웨어 실체 (클래스, 모듈,   기능 등)을 공개해야합니다.   확장 프로그램은 종료되었지만 종료되었습니다.   가감.

그만큼final키워드는 메소드 나 클래스에서 사용 되든 상관없이이를 Java에서 시행하는 수단입니다.


  • @Sean : 신고하지 않았습니다.final수업이 열려있는 것이 아니라 확장을 위해 닫히게합니까? 또는 나는 그것을 문자 그대로 취하고 있는가? - Goran Jovic
  • @Goran은 전 세계적으로 최종 신청서를 제출합니다. 핵심은 수정을 원하지 않는 곳에서 final을 선택적으로 적용하는 것입니다 (물론 확장을 위해 좋은 후크를 제공해야합니다) - Sean Patrick Floyd
  • @ 신 : 오, 확실히. - Goran Jovic
  • OCP에서, "수정" 소스 코드를 수정하는 것을 말하며, "확장자" 구현 상속을 말합니다. 따라서,final구현 코드가 수정을 위해 닫히고 상속에 의해 확장을 원한다면 클래스 / 메소드 선언에서 의미가 없습니다. - Rogério
  • @Rogerio 나는 (그리고 해석) 참조를 빌렸다.스프링 프레임 워크 레퍼런스 (MVC). IMHO 원래 버전보다 훨씬 더 의미가 있습니다. - Sean Patrick Floyd

9

키워드final자체적으로 무언가가 최종적이며 어떠한 방식 으로든 수정되지 않아야한다는 것을 의미합니다. 표시된 경우 클래스가final확장되거나 서브 클래스 화 될 수 없습니다. 그러나 질문은 왜 우리가 수업에 표시합니까?final? IMO에는 여러 가지 이유가 있습니다.

  1. 표준화:일부 클래스는 표준 함수를 수행하며 수정 될 수 없습니다. 문자열 조작이나 수학 함수 등과 관련된 다양한 기능을 수행하는 클래스
  2. 보안상의 이유: 때때로 우리는 다양한 인증과 패스워드 관련 기능을 수행하는 클래스를 작성하고, 다른 사람들에 의해 변경되는 것을 원하지 않는다.

그 마킹 수업 듣고있다.final효율성을 향상 시키지만 솔직히 나는이 주장을 찾아서 많은 무게를 지닐 수 없었다.

Java가 객체 지향적이며 클래스를 final로 선언한다면 그렇지 않습니까?   사물의 특성을 가진 계급의 생각을 멈추는가?

아마도 그렇 겠지만 때로는 그것이 의도 된 목적입니다. 때로는이 클래스의 기능을 희생시켜 보안 등의 더 큰 이점을 얻으려는 경우도 있습니다. 그러나 최종 클래스는 필요하다면 하나의 클래스를 확장 할 수 있습니다.

참고로 우리는상속보다 구성을 선호한다.final키워드는 실제로이 원칙을 시행하는 데 도움이됩니다.


5

클래스가 표시된 경우final, 이것은 클래스의 구조가 외부의 어떤 것으로도 변경 될 수 없다는 것을 의미합니다. 이것이 가장 일반적인 경우는 전통적인 다형성 상속을 할 때입니다.class B extends A그냥 작동하지 않습니다. 기본적으로 코드의 일부를 보호하는 방법입니다.(정도까지).

명확하게하기 위해 클래스를 표시합니다.final필드를으로 표시하지 않습니다.final따라서 객체 속성을 보호하지는 않지만 대신 실제 클래스 구조를 보호합니다.


  • 객체 속성은 무엇을 의미합니까? 클래스가 final로 선언 된 경우 클래스의 멤버 변수를 수정할 수 있습니까? 따라서 최종 클래스의 유일한 목적은 상속을 방지하는 것입니다. - Adam Lyu

5

"최종"수업을 만들 때주의하십시오. 최종 클래스에 대한 단위 테스트를 작성하려는 경우 Michael C. Feathers의 "Working With Effective With Legacy Code"에서 설명한 종속성 분석 기술 "Subclass and Override Method"를 사용하기 위해이 최종 클래스를 서브 클래스화할 수 없기 때문에, . 이 책에서 Feathers는 다음과 같이 말했습니다 : "진실로 봉인 된 것과 최종적인 것이 틀린 실수라고 생각하기 쉽습니다. 프로그래밍 언어에 결코 추가되어서는 안될 것입니다. 그러나 진정한 잘못은 우리에게 있습니다. 우리가 통제 할 수없는 도서관은 문제를 묻는 중이다. "


5

최종 강의 문제를 해결하려면 다음을 수행하십시오.

마지막으로 수업을 진행하는 데는 두 가지 방법이 있습니다. 첫 번째는 클래스 선언에서 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 클래스는 암시 적 최종 클래스가 문제를 일으킬 수있는 좋은 예입니다.

따라서 생성자를 private으로 만들어 클래스를 암시 적으로 만들면 최종으로 표시해야합니다.


4

final class새 메소드를 추가 할 때 공개 API가 손상되지 않도록 할 수 있습니다.

버전 1에서Base당신이하는 수업 :

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가 손상되지 않습니다.


3

예, 때로는 보안 또는 속도상의 이유로이 기능을 원할 수도 있습니다. 그것은 C ++에서도 수행됩니다. 그것은 아닐 수도있다.프로그램에는 적용 가능하지만 프레임 워크에는 적용되지 않습니다.http://www.glenmccl.com/perfj_025.htm


3

최종 클래스는 확장 할 수없는 클래스입니다. 또한 메서드는 final로 선언되어 하위 클래스에서 재정의 할 수 없음을 나타낼 수 있습니다.

API 또는 라이브러리를 작성하고 기본 동작을 변경하지 않으려는 경우 클래스가 서브 클래 싱되지 않도록 방지하는 것이 특히 유용 할 수 있습니다.


3

마지막으로 수업을 지키는 이점 :

String 클래스는 final을 유지하므로 아무도 메서드를 재정 의하여 기능을 변경할 수 없습니다. 예를 들어 아무도 length () 메소드의 기능을 변경할 수 없습니다. 항상 문자열의 길이를 반환합니다.

이 클래스의 개발자는 아무도이 클래스의 기능을 변경하지 않기를 원했기 때문에 최종으로 유지했습니다.


1

최종 수업은 연장 할 수 없습니다. 따라서 클래스가 특정 방식으로 작동하고 다른 사람이 메소드를 오버라이드하지 못하도록하려면 (효율적이지 않고 악의적 인 코드로) 전체 클래스를 최종 또는 특정 메소드로 선언 할 수 있습니다. 변경되었습니다.

클래스를 선언한다고해서 클래스가 인스턴스화되는 것을 막지는 못하기 때문에 클래스가 객체의 특성을 갖지 못하게한다는 의미는 아닙니다. 클래스에서 선언 된 방식대로 메서드에 집중해야합니다.


  • 이것은 이미 말한 것에 어떻게 추가됩니까? - user1440897

1

FINAL을 "줄의 끝"으로 생각하십시오. 그 남자는 더 이상 자식을 낳을 수 없습니다. 그래서 당신이 그것을이 방법으로 볼 때, 여러분이 만날 수있는 현실 세계의 시나리오가 있습니다. 그것은 당신이 클래스의 'end of line'마커에 플래그를 붙이도록 요구합니다. 도메인 기반 디자인입니다. 도메인에서 주어진 ENTITY (클래스)가 하위 클래스를 만들 수 없다고 요구하는 경우 FINAL으로 표시하십시오.

나는 당신이 "최종적으로 태그를 붙여야한다"클래스를 상속받는 것을 막을 수있는 아무것도 없다는 것을 알아야한다. 하지만 일반적으로 클래스의 기본 클래스에서 일부 함수를 상속 받기를 원하기 때문에이 클래스는 일반적으로 "상속의 남용"으로 분류되어 수행됩니다.

가장 좋은 방법은 도메인을보고 디자인 결정을 지시하도록하는 것입니다.


1

위에서 말했듯이 아무도 메소드의 기능을 변경할 수 없으면 final로 선언 할 수 있습니다.

예 : 다운로드 / 업로드 응용 프로그램 서버 파일 경로, 오프셋을 기반으로하는 문자열 분할, 이러한 메서드는 Final을 선언하여 이러한 메서드 함수가 변경되지 않도록 할 수 있습니다. 그리고 별도의 클래스에서 이러한 최종 메소드를 원할 경우 해당 클래스를 Final 클래스로 정의하십시오. 그래서 Final 클래스는 모든 final 메소드를 가질 것이고, final 메소드는 final이 아닌 클래스에서 선언되고 정의 될 수 있습니다.


1

Android Looper 클래스는 이에 대한 좋은 예입니다.http://developer.android.com/reference/android/os/Looper.html

Looper 클래스는 다른 클래스에 의해 무시되지 않는 특정 기능을 제공합니다. 따라서 여기에는 하위 클래스가 없습니다.


0

즉, final로 선언 된 클래스, 변수 또는 메서드는 변경할 수 없습니다.

더 중요한 것은 제 의견입니다.

솔직히 나는 키워드가final그 존재는 사용자가 뭔가를 정의 할 수 있기 때문에 실수입니다.아니 final. Java의 모든 것이 있어야합니다.final기본적으로 인터페이스 메소드를 제외하고 (정의에 따라). 모든 인스턴스 메서드를 다음과 같이 설정하는 것은 불행한 선택이었습니다.virtual기본적으로 (). 핵심은 사용자가 먼저 생각하도록 강요하고 부끄러운 방법을 명시 적으로 정의하는 것입니다.virtual대신이 방법으로 다른 사람이이 메서드를 재정의해야하는지 여부를 스스로에게 물어볼 수 있습니다.really그의 코드를 재 설계 할 필요성 또는 설득력.


0

예를 들어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인스턴스를 매개 변수로 사용하여 greet 메서드를 호출하여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.


-2

객체 지향은 상속에 관한 것이 아니라 캡슐화에 관한 것입니다. 상속으로 캡슐화가 중단됩니다.

최종 수업을 선포하면 많은 경우에 완벽합니다. 색깔이나 금액과 같은 "가치"를 나타내는 모든 객체는 최종 수 있습니다. 그들은 독자적으로 서 있습니다.

라이브러리를 작성하는 경우 명시 적으로 들여 쓰기하지 않으면 클래스를 최종적으로 만듭니다. 그렇지 않으면 사람들은 클래스를 파생시키고 가정 / 불변성을 깨고 메소드를 재정의 할 수 있습니다. 이것은 보안과 관련이있을 수 있습니다.

"Effective Java"의 Joshua Bloch는 상속을 위해 명시 적으로 디자인하거나 금지하고이를 상속을위한 설계가 그렇게 쉬운 것은 아니라고 말합니다.

연결된 질문


관련된 질문

최근 질문