최종 수업
클래스가 최종 클래스로 정의되면 클래스가 다른 클래스에 의해 상속 될 수 없음을 의미합니다. 즉, 확장 후에 사용할 수 없습니다. 그렇지 않으면 편집 중에 오류가 발생합니다.
패키지 com.iderzheng.finalkeyword; 공개 최종 클래스 FinalClass {} // Error : FinalClass PackageClass에서 상속받을 수 없음 FinalClass {} Java는 클래스를 최종으로 정의하는 것을 지원하며, 이는 객체 지향 프로그래밍의 기본 원칙을 위반하는 것으로 보입니다. 그러나 반면에 동봉 된 클래스는 클래스의 모든 방법이 고정되어 있고 동적으로로드 할 서브 클래스 재정의가 없도록합니다. 이것은 컴파일러가 최적화 할 수있는 더 많은 가능성을 제공합니다. 가장 좋은 예는 최종 클래스 인 String입니다. Java 컴파일러는 문자열 상수 (이중 따옴표에 포함 된 것)를 String 객체로 직접 전환 할 수 있으며 동시에 최종 수정으로 서브 클래스가 스 플라이 싱 작업에 대해 다른 값을 반환하지 않도록하기 때문에 연산자 + 작동을 새로운 상수로 직접 최적화 할 수 있습니다.
모든 다른 클래스 정의의 경우, 최상위 클래스 (글로벌 또는 패키지 가시적), 중첩 클래스 (내부 또는 정적 중첩 클래스)를 최종으로 수정할 수 있습니다. 그러나 일반적으로 Final은 주로 공개로 정의 된 클래스를 수정하는 데 사용됩니다. 비 글로벌 클래스의 경우 액세스 수정자가 가시성을 제한했으며 이미 이러한 클래스를 상속하기가 어렵 기 때문에 최종 제한 계층을 추가 할 필요가 없습니다.
또한 익명 클래스는 상속 될 수는 없지만 컴파일러에 의한 최종에 국한되지 않는다고 언급되어 있습니다.
import java.lang.reflect.modifier; public class main {public static void main (string [] args) {runnable anonymous = new Runnable () {@override public void Run () {}}; System.out.println (modifier.isfinal (anymous.getClass (). getModifiers ()); }}산출:
거짓
최종 방법
상속의 개념과 밀접한 관련이있는 다형성은 다형성이며, 이는 재정의 및 숨기기 개념의 차이를 포함합니다 (편의를 위해 다음은 집합 적으로 "다시 쓰기"). 그러나 가상 키워드가 서브 클래스에 추가되는지 C ++의 메소드 정의와 달리, 서브 클래스 시그니처 방법이 덮어 쓰거나 숨겨져 있는지, 하위 클래스는 동일한 메소드 서명을 사용하여 상위 클래스 메소드 (정적 메소드)를 형성하는 동일한 메소드 서명을 사용하여 객체 메소드 (비 정적 메소드) 만 작성합니다. Java는 객체를 통해 클래스 메소드에 직접 액세스 할 수 있으므로 Java는 클래스 메소드와 객체 방법이 동일한 클래스에서 동일한 서명을 갖도록 허용하지 않습니다.
최종 클래스는 전체 클래스를 상속받을 수 없다고 정의하며, 이는 클래스의 모든 방법을 서브 클래스로 덮고 숨길 수 없음을 의미합니다. 클래스가 Final에 의해 수정되지 않은 경우, 이러한 방법을 서브 클래스에 의해 다시 작성되는 것을 방지하기 위해 Final을 사용하여 일부 방법을 여전히 수정할 수 있습니다.
마찬가지로, 이러한 설계는 객체 지향 다형성을 파괴하지만 최종 방법은 실행의 결정론을 보장하여 방법 호출의 안정성을 보장 할 수 있습니다. 일부 프레임 워크 설계에서는 프레임 워크의 일부 드라이버 코드가 이러한 방법에 의존하여 확립 된 목표를 달성하기 위해 이러한 방법에 의존하므로이를 다루는 서브 클래스가 없기 때문에 일부 추상 클래스의 일부 구현 방법은 종종 최종으로 제한되는 것으로 보입니다.
다음 예는 다른 유형의 방법에서 최종 수정의 역할을 보여줍니다.
패키지 com.iderzheng.other; 공개 클래스 FinalMethods {public static void publicstaticmethod () {} public final void publicfinalmethod () {} public static final void publicstaticfinalmethod () {} Protected Final void ProtectedFinalMethod () {} protected static final void protectedstaticfinalmemtod () {final volid vinoid (final -void)} void staticfinalmethod () {} 개인 정적 최종 void privatestaticfinalMethod () {} 개인 최종 void privateFinalMethod () {}} package com.iderzheng.finalKeyword; com.iderzheng.other.finalmethods import; Public Class Methods는 FinalMethods를 확장합니다 {public static void publicmethod () {} // 오류 : 공개 최종 void PublicFinalMethod () {} // 오류 : 공개 정적 최종 void PublicStaticFinalMethod () {} // error : eRROR : 우선적 인 정적 최종 void void final () {} // error. void publicstaticfinalmethod () {} // error : 보호 된 최종 void protectedfinalmethod () {} // error : protected static final void protectedStaticfinalMethod () {} 최종 무효 FinalMethod ()} 정적 최종 void staticalmethod () {} 정적 개인 voide void void void voide void void voideTestatic ()를 재정의 할 수 없습니다. {} private final void privateFinalMethod () {}} 우선, 위의 예에서 최종 방법과 메소드는 다른 패키지로 정의됩니다. 첫 번째 publicstaticmethod의 경우, 서브 클래스는 부모 클래스의 정적 메소드를 성공적으로 다시 작성하지만 정적 메소드이기 때문에 실제로 발생하는 일은 실제로 "숨겨진"것입니다. 구체적으로, Calling Methods.publicStaticMethod ()는 메소드 클래스에서 구현을 실행합니다. FinalMethods.publicStaticMethod ()을 호출 할 때, 구현은 서브 클래스의 다형성 하중으로 발생하지 않지만 최종 메드의 구현을 직접 사용합니다. 따라서 서브 클래스를 사용하여 메소드에 액세스 할 때 부모 클래스가 서명 한 메소드의 가시성이 숨겨져 있습니다.
최종 수정 방법에 설명 된 글로벌 방법 PublicFinalMethod의 경우 서브 클래스는이를 덮어 쓰는 것이 금지되며 컴파일 시간에 예외가 발생됩니다. 그러나 메소드 이름은 서브 클래스에서 동일하지만 : PublicFinalMethod (String X)는 정상입니다. 동기 메소드 서명이기 때문에 다음과 같은 매개 변수가 있습니다.
Intellij에서 IDE는 'static'메소드가 'Final'으로 선언 된 PublicstaticfinalMethod에 대한 경고를 보여줍니다. 그것은 그것에 대해 중복되는 것처럼 보이지만, 예에서, 최종은 또한 정적 메소드에서 서브 클래스 정의를 금지하는 것을 볼 수 있습니다. 실제 개발에서, 동일한 정적 서브 클래스 및 부모 클래스를 정의하는 동작은 매우 바람직하다. 또한 클래스 내에서 클래스 이름을 사용하지 않고 직접 정적 메소드를 호출 할 수 있습니다. 개발자가 다시 물려 받으면 숨겨진 존재를 눈치 채지 못할 수도 있습니다. 기본적으로 부모 클래스 메소드를 사용할 때는 예상 결과가 아님을 알게됩니다. 따라서 정적 메소드는 기본적으로 최종적이어야하며 숨겨져서는 안됩니다. 따라서 IDE는 그것이 불필요한 수정이라고 생각합니다.
부모 클래스에서 보호 된 수정 및 공공 수정 방법은 서브 클래스에 보이므로 보호 된 방법의 최종 수정 상황은 공개 방법의 상황과 동일합니다. 실제 개발에서, 보호 된 정적 방법은 일반적으로 그러한 방법이 너무 실용적이기 때문에 거의 정의되지 않는다고 언급되어야한다.
부모 클래스 패키지 방법의 경우, 다른 패키지의 서브 클래스는 보이지 않습니다. 개인 메소드가 사용자 정의되었으며 부모 클래스 만 액세스 할 수 있습니다. 따라서 컴파일러를 사용하면 서브 클래스가 동일한 방법을 정의 할 수 있습니다. 그러나 부모 클래스는 하위 클래스의 재 작성로 인해 수정자를 통해 이러한 방법을 숨기고 있기 때문에 이는 재정의 또는 숨기지 않습니다. 물론, 서브 클래스와 상위 클래스가 동일한 패키지에있는 경우 상황은 이전 대중과 동일하고 보호됩니다.
최종 방법이 효율적인 이유는 무엇입니까?
최종 방법은 내장 메커니즘을 사용하여 컴파일 중에 인라인을 최적화합니다. 인라인 최적화는 다음을 말합니다. 런타임에서 함수를 호출하는 대신 컴파일 중에 직접 기능 코드 교체를 호출합니다. 인라인은 컴파일 할 때 결국 어떤 기능을 사용할 것인지 알아야합니다. 분명히, 최종 없이는 그것을 사용할 수 없습니다. 비 결절 방법은 서브 클래스로 다시 작성 될 수 있습니다. 가능한 다형성으로 인해 컴파일러는 컴파일 단계에서 미래에 메소드를 호출 할 객체의 실제 유형을 결정할 수 없으며 어떤 메소드를 호출할지 결정할 수 없습니다.
최종 변수
간단히 말해, Java의 최종 변수는 한 번만 초기화 될 수 있고 초기화해야하며 변수는 값에 바인딩됩니다. 그러나이 할당이 변수가 정의 될 때 즉시 초기화 될 필요는 없습니다. Java는 조건부 명세서를 통해 최종 변수에 대해 다른 결과를 지원하지만 변수는 어쨌든 한 번만 할당 할 수 있습니다.
그러나 Java의 최종 변수는 절대 상수가 아닙니다. Java의 객체 변수는 참조 값이기 때문에 최종적으로는 참조를 변경할 수없고 개체의 내용을 여전히 수정할 수 있음을 의미합니다. C/C ++ 포인터와 비교하여 유형 * 변수보다 유형 * const 변수와 비슷합니다.
Java 변수는 로컬 변수 (로컬 변수)와 클래스 멤버 변수 (클래스 필드)의 두 가지 범주로 나눌 수 있습니다. 다음은 초기화 상황을 개별적으로 소개하는 코드입니다.
로컬 변수
로컬 변수는 주로 메소드에 정의 된 변수를 나타냅니다. 방법은 사라지고 접근 할 수 없게됩니다. 기능 매개 변수로 나눌 수있는 특별한 경우가 있습니다. 이 경우, 초기화는 함수가 호출 될 때 전달되는 매개 변수에 바인딩됩니다.
다른 로컬 변수의 경우 방법으로 정의되어 있으며 그 값은 조건부 초기화 될 수 있습니다.
공개 문자열 메서드 (Final Boolean FinalParam) {// 오류 : 최종 매개 변수 FinalParam이 할당되지 않을 수 있습니다. // finalParam = true; Final Object Finallocal = FinalParam? 새 개체 () : null; 최종 INT FinalVar; if (finallocal! = null) {FinalVar = 21; } else {finalVar = 7; } // error : variable finalVar가 이미 할당되었을 수 있습니다. // finalVar = 80; 최종 문자열 FinalRET; 스위치 (FinalVar) {Case 21 : FinalRet = "Me"; 부서지다; 사례 7 : finalret = "그녀"; 부서지다; 기본값 : finalret = null; } return finalret;} 위의 예에서, Final에 의해 수정 된 함수 매개 변수에 새 값을 할당 할 수 없지만 다른 최종 로컬 변수에 조건부 명령문에 값을 할당 할 수 있음을 알 수 있습니다. 이것은 또한 최종에 대한 특정 유연성을 제공합니다.
물론 조건부 명세서의 모든 조건에는 최종 로컬 변수에 대한 할당이 포함되어야합니다. 그렇지 않으면 변수가 초기화되지 않을 수 있다는 오류가 발생합니다.
공개 문자열 메소드 (Final Object FinalParam) {Final Int FinalVar; if (finalParam! = null) {FinalVar = 21; } Final String FinalRET; // 오류 : 변수 FinalVar가 초기화되지 않았을 수 있습니다 (FinalVar) {CASE 21 : FinalER = "ME"; 부서지다; 사례 7 : finalret = "그녀"; 부서지다; } // ERROR : Variable FinalER이 초기화되지 않았을 수 있습니다. 반환 FinalRET;} 이론적으로, 로컬 변수는 최종으로 정의 될 필요가 없으며 합리적인 설계 방법은 로컬 변수를 잘 유지할 수 있어야합니다. Java 방법을 폐쇄하기 위해 익명 함수를 사용할 때 Java는 참조 된 로컬 변수를 최종으로 정의해야합니다.
공개 실행 가능한 메소드 (String String) {int integer = 12; 새로운 runnable () {@override public void run () {// error : 최종 System.out.println (String)으로 선언해야합니다. // 오류 : 최종 System.out.println (정수)으로 선언해야합니다. }};}클래스 필드
클래스 멤버 변수는 실제로 정적과 비 정적의 두 가지 유형으로 나눌 수 있습니다. 정적 클래스 멤버 변수의 경우 클래스와 관련이 있기 때문에 정의 시간에 직접 초기화되는 것 외에도 정적 블록에 배치 될 수도 있고 후자를 사용하여보다 복잡한 문장을 실행할 수 있습니다.
패키지 com.iderzheng.finalkeyword; import java.util.hashset; import java.util.linkedhashset; import java.util.set; 공개 클래스 staticfinalfields {static final int static_final_init_inline = 7; 정적 최종 세트 <integer> static_final_init_static_block; / ** 정적 블록 **/ static {if (system.currenttimeMillis () % 2 == 0) {static_final_init_static_block = new Hashset <> (); } else {static_final_init_static_block = new LinkedHashset <> (); } static_final_init_static_block.add (7); static_final_init_static_block.add (21); }}Java에는 비 정적 멤버 변수를 초기화 할 수있는 비 정적 블록도 있지만 이러한 변수의 경우 종종 초기화를 위해 생성자에 배치됩니다. 물론, 각 최종 변수가 생성자에서 한 번 초기화되도록해야합니다. 이 ()을 통해 다른 생성자가 호출되는 경우, 이러한 최종 변수는 더 이상 생성자에 할당 할 수 없습니다.
패키지 com.iderzheng.finalkeyword; 공개 클래스 FinalFields {Final Long Final_init_inline = System.CurrentTimeMillis (); Final Long Final_init_block; Final Long Final_init_constructor; / ** 초기 블록 **/ {final_init_block = system.nanoTime (); } finalFields () {this (217); } FinalFields (부울 부울) {final_init_constructor = 721; } finalFields (Long init) {final_init_constructor = init; }}Final이 클래스 (클래스) 및 메소드 (메소드)를 수정하는 데 사용되면 주로 객체 지향 상속에 영향을 미칩니다. 상속이 없으면 부모 클래스에서 서브 클래스의 코드에 의존하지 않습니다. 따라서 유지 보수 중에 코드를 수정할 때 서브 클래스의 구현이 파괴 될지 여부를 고려할 필요가 없으므로 더 편리합니다. 변수에서 사용되면 Java는 변수 값을 수정하지 않도록합니다. 추가 설계가 클래스 멤버를 수정할 수 없도록하는 경우 전체 변수를 상수로 바꿀 수 있으며, 이는 다중 스레드 프로그래밍에 매우 유리합니다. 따라서 Final은 코드 유지 보수에 매우 적합합니다.