CS-Java-1. 기초
포스트
취소

CS-Java-1. 기초

1. JDK와 JRE의 차이점

JDK (Java Development Kit)와 JRE (Java Runtime Environment)는 Java 프로그래밍 언어와 관련된 두 가지 주요 구성 요소입니다. 이 둘의 주요 차이점은 다음과 같습니다:

  1. 목적과 사용:

    • JDK: Java Development Kit의 약자로, Java 프로그램을 개발할 때 필요한 도구 모음입니다. JDK에는 JRE가 포함되어 있으며, Java 프로그램을 개발, 컴파일, 디버깅, 실행하기 위한 도구들이 포함되어 있습니다.
    • JRE: Java Runtime Environment의 약자로, Java 프로그램을 실행하는 데 필요한 소프트웨어 환경입니다. JRE에는 Java Virtual Machine (JVM), Java 클래스 라이브러리, 사용자 인터페이스 도구 및 기본 클래스가 포함되어 있습니다.
  2. 구성 요소:

    • JDK: JDK에는 JRE 전체가 포함되어 있으며, 추가로 Java 컴파일러 (javac), Java 문서 생성기, Java 디버거 등 개발에 필요한 도구들이 포함되어 있습니다.
    • JRE: JRE는 Java 애플리케이션을 실행하는 데 필요한 JVM과 라이브러리, 기타 파일을 포함합니다. 그러나 Java 애플리케이션을 개발하거나 컴파일하는 데 필요한 도구는 포함되어 있지 않습니다.
  3. 사용 사례:

    • 개발자는 Java 애플리케이션을 개발하고 테스트하기 위해 JDK를 사용합니다.
    • 사용자는 Java 애플리케이션을 실행하기 위해서만 JRE를 사용합니다.

간단히 말해서, JDK는 Java 개발을 위한 완전한 패키지이며, JRE는 Java 애플리케이션을 실행하는 데 필요한 최소한의 환경을 제공합니다.

2. ==와 equal()의 차이점

== 연산자와 equals() 메소드는 Java에서 객체의 동등성을 비교하는 데 사용되지만, 그들이 비교하는 방식에는 중요한 차이가 있습니다:

  1. == 연산자:

    • 주소 비교: ==는 두 객체의 참조(주소)를 비교합니다. 즉, 두 객체가 메모리 상에서 동일한 위치에 있는지 확인합니다.
    • 기본 타입에 대한 값 비교: 기본 데이터 타입 (int, float, char 등)의 경우, ==는 실제 값을 비교합니다.
    • 예시: if (object1 == object2)
  2. equals() 메소드:

    • 내용 비교: equals() 메소드는 두 객체의 내용이 같은지 비교합니다. 이 메소드는 Object 클래스에 정의되어 있으며, 많은 클래스에서 이 메소드를 오버라이드(재정의)하여 객체의 상태를 비교합니다.
    • 사용자 정의 가능: 개발자는 equals() 메소드를 오버라이드하여 두 객체가 “동등”한지를 정의할 수 있습니다. 예를 들어, 두 String 객체가 같은 문자열을 가지고 있는지 확인할 때 사용됩니다.
    • 예시: if (object1.equals(object2))

주요 차이점:

  • 참조 vs 내용: ==는 객체의 참조를 비교하는 반면, equals()는 객체의 내용을 비교합니다.
  • 기본 데이터 타입 vs 객체: 기본 데이터 타입에 대해서는 ==가 값 비교를 수행하지만, 객체에 대해서는 equals()를 사용해야 의도한 대로 내용 비교가 이루어집니다.
  • 오버라이드: equals() 메소드는 클래스에 따라 다르게 동작하도록 오버라이드할 수 있지만, == 연산자의 동작은 변경할 수 없습니다.

이러한 차이점 때문에, 객체의 동등성을 비교할 때는 equals() 메소드를 사용하는 것이 일반적으로 권장됩니다.

3. 두 객체가 동일한 hashCode를 가졌다면 equals()가 항상 참일까?

두 객체가 동일한 hashCode를 가진다고 해서 반드시 equals() 메서드가 true를 반환하는 것은 아닙니다. 이는 hashCode 메서드와 equals 메서드의 계약 및 목적에 기인합니다.

hashCode와 equals 메서드의 관계:

  1. hashCode 규약:

    • 같은 객체에 대해 여러 번 호출될 경우, 프로그램 실행 동안 일관된 정수 값을 반환해야 합니다.
    • 두 객체가 equals 메서드로 비교했을 때 true를 반환한다면, 두 객체의 hashCode 값도 동일해야 합니다.
    • 두 객체가 equals 메서드로 비교했을 때 false를 반환한다 해도, hashCode 값이 서로 다를 필요는 없습니다. 즉, 서로 다른 두 객체는 동일한 hashCode 값을 가질 수 있습니다.
  2. hashCode 충돌:

    • hashCode 메서드는 제한된 범위의 정수를 반환하므로, 서로 다른 객체가 동일한 해시 코드를 가질 수 있는 상황(해시 충돌)이 발생할 수 있습니다.
    • 이러한 해시 충돌은 equals 메서드로 추가적인 동등성 검사를 수행함으로써 해결할 수 있습니다.

결론:

  • 두 객체가 동일한 hashCode 값을 가진다면, 이것은 그들이 동일할 수도 있음을 나타내지만, 반드시 동일하다는 것을 보장하지는 않습니다.
  • 객체의 동등성을 확실히 판단하기 위해서는 equals 메서드를 사용해야 합니다. equals 메서드가 true를 반환하면, 두 객체는 동등하다고 볼 수 있습니다.

4. final 키워드

Java에서 final 키워드는 여러 가지 용도로 사용됩니다. 각각의 사용 방식에 따라 final은 클래스, 메서드, 변수에 대해 다른 의미와 제약을 부여합니다:

  1. 변수에 사용될 때:

    • 불변성 부여: final이 변수에 사용되면, 그 변수는 한 번 초기화되면 그 값을 변경할 수 없습니다. 이는 기본 타입 변수뿐만 아니라 객체 참조 변수에도 적용됩니다.
    • 상수 선언: final 변수는 종종 상수를 선언하는 데 사용되며, 관례적으로 대문자와 밑줄을 사용하여 이름을 지정합니다 (예: final int MAX_WIDTH = 100;).
    • 참조 불변성: 객체 참조 변수가 final로 선언되면, 참조 자체는 변경할 수 없지만, 참조된 객체의 내부 상태는 변경할 수 있습니다.
  2. 메서드에 사용될 때:

    • 오버라이딩 방지: final 키워드가 메서드에 적용되면, 해당 메서드는 서브클래스에서 오버라이드(재정의)될 수 없습니다. 이는 메서드의 동작을 변경하지 않고 유지하려는 경우에 유용합니다.
  3. 클래스에 사용될 때:

    • 상속 방지: final 클래스는 다른 클래스에서 상속할 수 없습니다. 이는 클래스의 설계와 구현을 변경하지 않고 유지하려는 경우, 예를 들어 보안이나 단순성을 위해 사용됩니다.
    • 불변 클래스: 클래스를 final로 선언하면, 이 클래스의 인스턴스는 변경할 수 없는 불변의 성격을 갖게 됩니다. 대표적인 예로 java.lang.String 클래스가 있습니다.

요약:

  • 변수: 값을 변경할 수 없게 만듭니다 (상수).
  • 메서드: 오버라이딩을 방지합니다.
  • 클래스: 상속을 방지합니다.

final 키워드의 사용은 프로그램의 안정성과 불변성을 증가시킬 수 있지만, 유연성을 제한할 수도 있기 때문에 사용할 때 주의가 필요합니다.

5. String은 기본 데이터 타입인가?

Java에서 String은 기본 데이터 타입(Primitive Type)이 아닙니다. String은 참조 타입(Reference Type)입니다.

기본 데이터 타입 (Primitive Types)과 참조 타입 (Reference Types)의 차이:

  1. 기본 데이터 타입:

    • 이들은 Java 언어에 의해 사전 정의된 가장 기본적인 데이터 타입들입니다.
    • 예를 들어 int, float, double, char, boolean 등이 여기에 속합니다.
    • 기본 타입의 변수들은 실제 값을 직접 저장합니다.
  2. 참조 타입:

    • 참조 타입의 변수들은 객체의 참조(메모리 주소)를 저장합니다.
    • String, 배열, 클래스, 인터페이스 등이 참조 타입에 속합니다.
    • String은 클래스로, 문자열을 나타내는 객체를 참조합니다.

String의 특징:

  • String은 불변 객체(Immutable Object)로, 한 번 생성되면 그 내용을 변경할 수 없습니다.
  • 문자열 리터럴은 String Pool에 저장되어 재사용될 수 있어 메모리 효율성을 높입니다.
  • String은 다양한 메서드와 함께 문자열을 처리하기 위한 풍부한 기능을 제공합니다.

따라서, String은 Java에서 기본 데이터 타입이 아닌 클래스 기반의 참조 타입입니다.

6. 자바에서 문자열을 조작하는 클래스들

자바에서 문자열을 조작하기 위한 주요 클래스로는 String, StringBuilder, 그리고 StringBuffer가 있습니다. 이들 클래스는 유사한 기능을 제공하지만, 내부 구현과 사용 목적에서 차이가 있습니다.

1. String

  • 불변성: String 클래스의 인스턴스는 불변(immutable)입니다. 즉, 한 번 생성된 String 객체의 내용은 변경할 수 없습니다. 문자열을 수정할 때마다 실제로는 새로운 String 객체가 생성됩니다.
  • 용도: 일반적인 문자열 연산에 사용되며, 문자열 변경이 빈번하지 않은 경우에 적합합니다.
  • 성능: 불변성 때문에 문자열 연결이나 수정이 많은 경우에는 성능상 불리할 수 있습니다.

2. StringBuilder

  • 가변성: StringBuilder는 가변(mutable)합니다. 즉, 객체를 한 번 생성한 후에도 내용을 변경할 수 있습니다.
  • 비동기화: StringBuilder는 동기화를 지원하지 않습니다. 따라서 멀티스레드 환경에서는 적합하지 않습니다.
  • 성능: 문자열을 자주 변경해야 하는 상황에서 String보다 성능이 좋습니다.
  • 용도: 단일 스레드 환경에서의 문자열 조작에 적합합니다.

3. StringBuffer

  • 가변성: StringBuffer도 가변성을 가지며, StringBuilder와 유사한 방식으로 작동합니다.
  • 동기화: StringBuffer는 메서드들이 동기화되어 있어 멀티스레드 환경에서 안전합니다 (thread-safe).
  • 성능: 동기화로 인해 StringBuilder보다 성능이 약간 떨어질 수 있습니다.
  • 용도: 멀티스레드 환경에서의 안전한 문자열 조작에 적합합니다.

요약:

  • String: 불변 클래스로, 변경이 빈번하지 않은 간단한 문자열 조작에 적합합니다.
  • StringBuilder: 가변 클래스로, 단일 스레드 환경에서 문자열을 자주 변경해야 할 때 사용합니다.
  • StringBuffer: 가변 클래스이지만 동기화가 되어 있어 멀티스레드 환경에서 안전하게 문자열을 조작할 수 있습니다.

7. String str = “i” 와 String str = new String(“i”)의 차이

String str = "i"String str = new String("i")는 같은 문자열 “i”를 참조하지만, 메모리 상에서는 다르게 취급됩니다. 두 선언 방식의 차이점을 이해하는 것은 자바의 문자열 처리와 메모리 관리에 중요합니다.

String str = “i”;

  • 문자열 리터럴 방식: 이 방식으로 선언된 문자열은 문자열 리터럴 풀(String Pool)에 저장됩니다.
  • 재사용: 같은 문자열 리터럴이 코드의 다른 부분에서 사용될 경우, JVM은 이미 풀에 있는 문자열을 재사용합니다.
  • 효율적인 메모리 사용: 이 방식은 메모리를 효율적으로 사용합니다. 동일한 문자열 리터럴은 풀에서 한 번만 저장됩니다.

String str = new String(“i”);

  • 새로운 String 객체 생성: new 키워드를 사용하면, 매번 새로운 String 객체가 생성됩니다.
  • 힙 메모리: 이 객체는 힙(Heap) 메모리에 저장됩니다.
  • 메모리 사용 증가: 같은 문자열이라도 new 키워드를 사용하면 매번 새로운 객체가 생성되므로, 메모리 사용량이 증가할 수 있습니다.
  • 리터럴 풀 사용 안 함: 문자열 리터럴 풀을 사용하지 않으므로, 동일한 문자열이라도 다른 객체로 취급됩니다.

결론:

  • 내용상으로는 두 선언 방식 모두 동일한 문자열 “i”를 나타냅니다.
  • 하지만 String str = "i";는 문자열 리터럴 풀을 사용하고, String str = new String("i");는 힙에 새로운 객체를 생성합니다.
  • 이로 인해 메모리 사용과 객체의 동일성 측면에서 차이가 발생합니다.

실제로 str1 == str2 같은 비교를 수행하면, 첫 번째 방식으로 선언된 문자열은 동일한 참조를 가지지만, 두 번째 방식으로 선언된 문자열은 항상 다른 참조를 가지게 됩니다. 이는 ==가 객체 참조를 비교하기 때문입니다. 문자열의 내용을 비교하려면 equals() 메서드를 사용해야 합니다.

8. StringBuilder, StringBuffer도 문자열 리터럴 풀 영역에 저장되는가?

StringBuilderStringBuffer 객체는 문자열 리터럴 풀(String Pool)에 저장되지 않습니다. 또한, 문자열 리터럴 풀 자체는 힙(Heap) 영역의 일부입니다.

StringBuilder와 StringBuffer

  • 별도의 객체: StringBuilderStringBuffer는 가변적인 문자열을 처리하는 클래스로, 각각의 인스턴스는 힙 영역에 별도의 객체로 생성됩니다.
  • 문자열 풀 사용 안 함: 이들 클래스는 String 클래스와 달리 문자열 리터럴 풀을 사용하지 않습니다. 따라서, 동일한 문자열 내용을 가진 StringBuilderStringBuffer 객체라도 각각 별도의 메모리 공간을 차지합니다.

문자열 리터럴 풀 (String Pool)

  • 힙 영역의 일부: 문자열 리터럴 풀은 Java 힙 메모리 내에 위치하며, 문자열 리터럴을 저장하고 재사용하기 위한 특별한 영역입니다.
  • 효율적인 메모리 사용: 이 풀은 메모리를 효율적으로 사용하기 위해 동일한 문자열 리터럴이 여러 번 필요할 경우 해당 문자열을 재사용합니다.
  • String 클래스 전용: 문자열 리터럴 풀은 String 객체에 대해서만 적용됩니다. new 키워드를 사용하여 String 객체를 명시적으로 생성하는 경우에도 문자열 풀은 사용되지 않습니다.

결론

  • StringBuilderStringBuffer는 문자열 리터럴 풀을 사용하지 않으며, 각각의 객체는 힙 메모리에 독립적으로 존재합니다.
  • 문자열 리터럴 풀은 힙 메모리의 일부로, String 객체의 효율적인 메모리 사용을 위해 사용됩니다.

9. 문자열을 반전시키는 가장 좋은 방법

문자열을 반전시키는 가장 효과적인 방법은 StringBuilder (또는 StringBuffer)를 사용하는 것입니다. 이 클래스들은 내부적으로 가변적인 문자 배열을 사용하여 문자열을 처리하기 때문에, 문자열을 반전시키는 과정에서 새로운 문자열 객체를 생성하지 않아도 됩니다.

다음은 문자열을 반전시키는 간단한 예시입니다:

1
2
3
4
5
public String reverseString(String str) {
    StringBuilder sb = new StringBuilder(str);
    sb.reverse();
    return sb.toString();
}

이 메서드는 다음과 같이 작동합니다:

  1. 주어진 문자열 str을 사용하여 StringBuilder 객체를 생성합니다.
  2. StringBuilderreverse() 메서드를 호출하여 문자열을 반전시킵니다.
  3. 반전된 문자열을 얻기 위해 StringBuilder 객체를 String으로 변환합니다.

StringBuilder는 단일 스레드 환경에서 사용하기 적합하며, 멀티스레드 환경에서는 StringBuffer를 사용하는 것이 좋습니다. StringBufferStringBuilder와 유사하지만 동기화되어 있어 스레드 안전합니다.

문자열을 수동으로 반전시키는 방법

만약 StringBuilderStringBuffer를 사용하지 않고 직접 문자열을 반전시키고 싶다면, 다음과 같이 할 수 있습니다:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public String reverseStringManually(String str) {
    char[] charArray = str.toCharArray();
    int left = 0;
    int right = str.length() - 1;

    while (left < right) {
        // 왼쪽과 오른쪽의 문자를 교환
        char temp = charArray[left];
        charArray[left] = charArray[right];
        charArray[right] = temp;

        // 인덱스 이동
        left++;
        right--;
    }

    return new String(charArray);
}

이 방법은 문자열의 각 문자를 배열로 변환한 후, 배열의 양 끝에서부터 시작하여 중간으로 이동하면서 문자 위치를 바꾸는 방식으로 작동합니다.

10. String 클래스의 일반적인 메서드 종류

Java의 String 클래스에는 문자열을 처리하기 위한 다양한 메서드가 있습니다. 여기에는 문자열의 길이, 문자 추출, 부분 문자열, 문자열 비교, 검색, 변환, 분할 등과 관련된 일반적인 메서드들이 포함됩니다. 다음은 String 클래스의 몇 가지 주요 메서드입니다:

  1. length(): 문자열의 길이를 반환합니다.

    1
    
    int length = str.length();
    
  2. charAt(int index): 지정된 인덱스에 있는 문자를 반환합니다.

    1
    
    char ch = str.charAt(0);
    
  3. substring(int beginIndex, int endIndex): 문자열의 부분 문자열을 반환합니다.

    1
    
    String substr = str.substring(1, 4);
    
  4. equals(Object anObject): 문자열의 내용이 주어진 객체와 동일한지 비교합니다.

    1
    
    boolean isEqual = str.equals("anotherString");
    
  5. equalsIgnoreCase(String anotherString): 대소문자를 구분하지 않고 문자열을 비교합니다.

    1
    
    boolean isEqualIgnoreCase = str.equalsIgnoreCase("AnotherString");
    
  6. startsWith(String prefix), endsWith(String suffix): 문자열이 특정 접두사나 접미사로 시작하거나 끝나는지 확인합니다.

    1
    2
    
    boolean startsWith = str.startsWith("prefix");
    boolean endsWith = str.endsWith("suffix");
    
  7. contains(CharSequence s): 문자열이 특정 문자열을 포함하는지 확인합니다.

    1
    
    boolean contains = str.contains("someString");
    
  8. indexOf(int ch), lastIndexOf(int ch): 특정 문자나 문자열이 처음으로 등장하는 인덱스를 반환합니다.

    1
    2
    
    int index = str.indexOf('a');
    int lastIndex = str.lastIndexOf('a');
    
  9. toLowerCase(), toUpperCase(): 문자열을 소문자나 대문자로 변환합니다.

    1
    2
    
    String lower = str.toLowerCase();
    String upper = str.toUpperCase();
    
  10. trim(): 문자열 양쪽의 공백을 제거합니다.

    1
    
    String trimmed = str.trim();
    
  11. replace(char oldChar, char newChar), replaceAll(String regex, String replacement): 문자열 내의 문자나 부분 문자열을 다른 문자열로 대체합니다.

    1
    2
    
    String replaced = str.replace('a', 'b');
    String regexReplaced = str.replaceAll("abc", "xyz");
    
  12. split(String regex): 문자열을 정규식 또는 다른 문자열을 기준으로 분할하여 배열로 반환합니다.

    1
    
    String[] parts = str.split(",");
    

String 클래스의 메서드들은 문자열을 처리하는 데 필수적이며, 다양한 문자열 관련 작업을 효율적으로 수행할 수 있도록 도와줍니다.

11. 추상 클래스에서 추상 메서드는 필수적인가?

추상 클래스에서 추상 메서드는 필수적이지 않습니다. 추상 클래스는 추상 메서드를 포함할 수도 있고, 포함하지 않을 수도 있습니다. 추상 클래스와 추상 메서드의 주요 특징을 이해하는 것이 중요합니다:

추상 클래스 (Abstract Class):

  • 정의: 추상 클래스는 다른 클래스가 상속받을 수 있는, 하나 이상의 추상 메서드를 포함할 수 있는 클래스입니다.
  • 객체 생성 제한: 추상 클래스의 인스턴스를 직접 생성할 수 없습니다. 즉, new 키워드를 사용하여 추상 클래스의 객체를 직접 생성하는 것은 불가능합니다.
  • 부분적 구현: 추상 클래스는 일부 메서드를 구현하고, 다른 메서드를 구현하지 않고 하위 클래스에서 구현하도록 남겨둘 수 있습니다.

추상 메서드 (Abstract Method):

  • 정의: 추상 메서드는 선언만 있고 구현이 없는 메서드입니다. 이는 하위 클래스에서 반드시 구현해야 합니다.
  • 선택적: 추상 클래스는 추상 메서드를 포함하지 않아도 됩니다. 즉, 추상 클래스 내에 일반 메서드만 있어도 됩니다.

예시:

1
2
3
4
5
6
7
8
9
abstract class MyAbstractClass {
    // 일반 메서드
    public void regularMethod() {
        // 구현
    }

    // 추상 메서드 (선택적)
    public abstract void abstractMethod();
}

이 예시에서 MyAbstractClass는 추상 클래스이며, regularMethod는 일반 메서드, abstractMethod는 추상 메서드입니다. 하지만 abstractMethod와 같은 추상 메서드는 없어도 MyAbstractClass는 여전히 유효한 추상 클래스입니다.

결론:

  • 추상 클래스는 하나 이상의 추상 메서드를 포함할 수도 있고, 포함하지 않을 수도 있습니다.
  • 추상 클래스의 주된 목적은 부분적인 구현을 제공하고, 특정 메서드의 구현을 하위 클래스에게 강제하는 것입니다.
  • 추상 메서드를 포함하지 않는 추상 클래스는 다형성을 위해 사용될 수 있으며, 이러한 클래스는 다른 클래스와의 계약(인터페이스)을 정의하는 데 사용될 수 있습니다.

12. 보통 클래스와 추상 클래스의 차이점

1. 인스턴스 생성:

  • 일반 클래스: 일반 클래스의 인스턴스는 new 키워드를 사용하여 직접 생성할 수 있습니다.
  • 추상 클래스: 추상 클래스의 인스턴스는 직접 생성할 수 없습니다. 이를 상속받는 서브클래스를 통해 간접적으로 인스턴스를 생성해야 합니다.

2. 추상 메서드의 포함 여부:

  • 일반 클래스: 모든 메서드에는 구현이 있어야 하며, 추상 메서드를 포함할 수 없습니다.
  • 추상 클래스: 하나 이상의 추상 메서드를 포함할 수 있습니다. 추상 메서드는 선언만 있고 구현은 없는 메서드로, 서브클래스에서 구현해야 합니다. 하지만 추상 메서드를 포함하지 않아도 됩니다.

3. 메서드 구현:

  • 일반 클래스: 모든 메서드는 구체적인 구현을 가져야 합니다.
  • 추상 클래스: 일부 메서드는 구현될 수 있고, 일부는 구현되지 않고 서브클래스에 구현을 위임할 수 있습니다.

4. 사용 목적:

  • 일반 클래스: 일반 클래스는 독립적인 기능을 가지며, 이를 사용하여 객체를 생성하고 사용할 수 있습니다.
  • 추상 클래스: 추상 클래스는 주로 상속을 위한 기본 클래스(base class)로 사용됩니다. 이는 일부 공통 기능을 구현하고, 나머지 구현을 서브클래스에 위임하는 데 사용됩니다.

5. 다형성:

  • 일반 클래스: 일반 클래스는 다형성을 위해 인터페이스를 구현할 수 있습니다.
  • 추상 클래스: 추상 클래스는 다형성을 제공하며, 서브클래스는 추상 클래스의 타입으로 참조될 수 있습니다.

예시:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 일반 클래스
class RegularClass {
    public void method() {
        // 메서드 구현
    }
}

// 추상 클래스
abstract class AbstractClass {
    public abstract void abstractMethod(); // 추상 메서드
    public void concreteMethod() {
        // 구체적인 메서드 구현
    }
}

결론:

일반 클래스는 완전히 구현된 클래스이며, 객체 생성과 사용이 가능합니다. 반면, 추상 클래스는 완전히 구현되지 않았거나 일부 메서드가 서브클래스에서 구현되기를 기대하는 클래스로, 주로 상속과 확장을 위해 사용됩니다.

13. final은 추상 클래스를 수정할 때 사용할 수 있는가?

final 키워드와 추상 클래스는 서로 상충되는 개념을 가지고 있어, final 키워드를 추상 클래스에 사용하는 것은 Java에서 허용되지 않습니다. 이 두 개념의 기본적인 특성을 살펴보면 그 이유를 이해할 수 있습니다:

  1. 추상 클래스 (Abstract Class):

    • 추상 클래스는 완전히 구현되지 않은 클래스로, 다른 클래스가 상속받아 완성해야 하는 메서드들을 포함할 수 있습니다.
    • 추상 클래스의 주된 목적은 상속을 통해 확장되고, 구체화되는 것입니다.
  2. final 키워드:

    • final 키워드가 클래스에 사용될 때, 그 클래스는 상속될 수 없습니다.
    • 즉, final 클래스는 확장 또는 수정이 불가능한 클래스로, 다른 클래스에 의해 상속되지 않습니다.

이 두 가지 특성을 결합하면, final 키워드를 추상 클래스에 사용하는 것은 모순이 됩니다. 추상 클래스는 기본적으로 상속을 통해 확장되어야 하는 반면, final은 상속을 방지하기 때문입니다.

예시:

1
2
3
final abstract class MyAbstractClass {
    // 이러한 선언은 Java에서 컴파일 오류를 발생시킵니다.
}

이 코드는 Java에서 컴파일 오류를 발생시키는데, 이는 final이 클래스의 상속을 방지하고, abstract가 클래스를 상속받아 구체화해야 한다는 서로 상반되는 요구사항을 가지고 있기 때문입니다.

따라서, finalabstract 키워드는 서로 배타적이며, 함께 사용할 수 없습니다.

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.