[F-Lab 66해빗 페이백 챌린지 ]

[F-Lab 모각코 페이백 12일차] 다형성 개념 복습, 추상화 개념 복습, String, Static

everydeveloper 2023. 5. 25. 23:13

학습목표

  • 팀코칭 질문 답하기
  • 자바의 신 23 Chapter 읽기

 

TIL

  • 다형성 개념 복습 & 심화
  • 추상화 개념 복습 & 심화
  • String
  • Static

 

다형성

다형성이 상속 관계에 있는 클래스에서
부모클래스의 참조변수로 상속관계의 클래스들을 다양하게 활용하는 것으로 알고 있었는데

먼가 피부와 와 닿지 않아서 멘토님꼐 여쭤보고

예시 코드를 좀 더 찾아보고 읽어보았다.

확실히 예시 코드를 보고 활용한 것을 보니 좀 이해되고 이렇게 쓰는 거구나

라는게 좀 더 명확히 와닿았다.

 

부모클래스라는 틀을 이용하기 때문에 부모클래스를 상속 받은 자식 클래스들은

부모클래스라는 틀 안에서 구현하고 거기서 좀 더 추가할 것이 잇으면 추가하는 방식이기 떄문에

부모클래스를 참조 변수로 선언하고 자식 클래스의 인스턴스를 대입해여 사용하면

오버라이딩은 메서드를 재정의해서 사용하므로 매개변수 타입, 매개변수 갯수가 같으니

딱히 문제 될 것은 없어 보이는데

오버로딩은 가능 할 까 싶었다.

오버로딩은 매개변수의 타입과 갯수가 변 할 수 있는 것 이여서 오버로딩을 하는 것 까지는 자유겠지만

다형성이라는 특징으로 다루는 것은 어려워 보였다.

알아보니 오버로딩은 제 생각/예측대로 상속과 다형성과는 크게는 상관없는 기능이라고 한다.

하지만 객체지향 프로그래밍의 중요한 한 기능 중 하나라고 한다.

 

다형성을 좀 더 깊이, 제대로 공부하니 이제 객체지향을 좀 더 알게 된 것 같다.

완전히 안다고 말하기는 여전히 두렵기도 하지만 먼가 예전보다는 알게되고 말할 수 있게 된 것 같다.

 

이번의 학습으로 내가 다형성을 먼가 잘 못 알고 있엇던 것 같다.

다형성은 상속을 받은 후 다양하게 활용하는 것으로 알고 있었는데

완전 틀린 말은 아니였지만 먼가 이제는 어설프게 알고 있었던 것 같다.

 

지금은 이렇게 알고 있다. 다형성은

상속 받아서 오버라이드 한 메서드를 부모 객체의 참조 변수에 담긴 자식 클래스들의 인스턴스들을 참조하여

한번에 하나의 메서드를 호출 하는 것처럼 쓰는 것이라고 하고 싶다.

사실은 각각의 자식 클래스에서 오버라이드를 하였기 때문에 메서드명, 매개변수 타입, 매개변수 수만 같지

내부 구조와 기능은 다를 것이다.

하지만 다 같은 부모 클래스(인터페이스나 추상클래스)를 상속하였기 때문에

같은 메서드처럼 호출하는 것이 가능한다. 즉 인터페이스와 추상클래스가 하는 주 역활인 메서드 명세 역할을 이용하기 때문이다.

다형성을 이해하기 어려웠던 이유는 다형성만 알아서는 안되고 인터페이스, 추상클래스, 상속을 알아야 하고 오버라이딩, 메서드명, 매개변수 타입 및 아울러 객체지향의 목적과 장점과 특징까지도 포괄적이면서도 각각의 특징을 알고 있어야 어느 정도 이해가 된다고 말 할 수 있을 것 같다. 그리고 글로만 읽으면 와 닿지 않던 것들이 멘토님 말씀처럼 예제코드를 찾아서 보니 훨씬 이해하기 쉽고 먼가 퍼즐이 풀리는 느낌이였다. 이 글을 읽는 분들께서도 혹시 글을 많이 읽고 이해하려고 노력을 많이 해보신 경우에도 잘 명확히 개념이 안 잡히신 경우에는 예제코드를 찾아서 보시면 오히려 직관적으로 이해가 되시는 경우도 있으리라 믿습니다. 추천드립니다.

 

추상화

상속의 장점 중 하나가 추상화가 나왔다. 이 와 관련된 해당 상속의 추상화 설명으로

추상화: 상속을 사용하면 공통의 로직을 부모 클래스에 캡슐화하고, 특정 동작을 자식 클래스에 정의할 수 있습니다. 이는 프로그램의 추상화 수준을 높이는 데 도움이 됩니다.

처음에는 이 말이 잘 이해되지 않았다. 좀 더 알아보니

인터페이스는 메서드 영역만 있고 변수(상태)영역은 없다. 캡슐화는 필요한 변수와 메서드 둘 다 한 클래스로 묶어 하나의 단위로 하는 것으로 알고 있는데 메서드 영역만 있어도 캡슐화라고 한다고 했다.

"특정 동작을 자식 클래스에 정의할 수 있습니다. 이 는 프로그램의 추상화 수준을 높이는 데 도움이 됩니다." 라는 부분이 약간 이해가 안되었다. 구체화 수준을 높이는 데 도움이 된다고 하면 이해가 될 것 같았다.

좀 더 공부해서 정리하니

부모 클래스 쪽으로 가면 갈 수록 추상화가 심화되고 자녀 클래스로 가면 구체화가 심화되는 방식이다.

나는 상속을 거의 1단계만 생각해서 약간 개념이 덜 잡혀 있었던 것 같은데

상속에는 단계 제한이 없으니 여러 단계를 상속한다고 하면 상속 받는 클래스(자식클래스)는 기존 것은 이어(그대로 물려받고) 구체화하거나 더 추가 or 오버라이드하는 형태로만 진행이되니 상속이 진행되면 될 수록 구체화 된다고 말할 수 있는 것 같다.

상속, 구체화, 추상화가 개념이 점점 이해되어 가고 있다.

 

Static

Java에서 static 키워드는 특정 요소가 클래스 수준으로 작동하도록 지정하는 데 사용됩니다. 이는 메소드, 변수, 중첩 클래스, 초기화 블록에서 사용될 수 있습니다.

 

Static으로 선언된 메소드, 변수는 클래스와 같이 메소드 영역에 선언된다.

Static 메소드는 메소드가 선언되어 있는 클래스가 인스턴스화 되지 않더라도 호출 되어 사용 할 수 있다.

다만 Static 메소드가 non-static 멤버에 직접 접근할 수 없으므로, non-static 멤버를 사용해야 하는 경우 해당 클래스의 인스턴스를 생성해야 합니다.

Main 메서드는 static 선언이 되어 있는데 Main 클래스가 인스턴스화가 되지 않더라도 main 메서드는 실행 되어야 하기 때문에

그렇다고 한다.

 

String의 객체 특성, 참조 변수 특성

  • String
    • 객체
    • 타입
    • 클래스
    • 참조 변수
      • new 생성자
      • 리터럴 대입

 

 

 

final 키워드는 참조 타입 변수에 대해 약간 다르게 작동합니다. 참조 타입의 final 변수는 한 번 참조가 할당되면 다른 객체를 참조할 수 없게 됩니다. 그러나 참조하는 객체 자체의 상태는 변경될 수 있습니다.

final List<String> list = new ArrayList<>();
list.add("Hello");  // 이것은 허용됩니다. 리스트 객체의 상태를 변경하는 것이지, list 변수가 다른 객체를 참조하도록 만드는 것이 아닙니다.

이 예에서, list 변수는 final로 선언되었으므로 다른 객체를 참조할 수 없습니다. 그러나 list가 참조하는 ArrayList 객체의 상태는 (즉, 그 안에 어떤 요소가 있는지) 변경할 수 있습니다.

 

Q.이 예에서, list 변수는 final로 선언되었으므로 다른 객체를 참조할 수 없습니다. 여기서 다른 객체는 어떤 것이 있을까??

 

A."다른 객체"라는 말은 list 변수가 처음에 참조하게 된 객체 (ArrayList 인스턴스)와 다른 어떤 객체를 말합니다.

다음과 같이 final로 선언된 list 변수에 새로운 ArrayList 객체를 할당하려고 하면, 컴파일 에러가 발생합니다:

 

final List<String> list = new ArrayList<>();
list = new ArrayList<>();  // 컴파일 에러, list가 참조하는 객체를 바꿀 수 없습니다.

 

A.final로 선언된 변수는 한 번 초기화하면 그 값을 변경할 수 없습니다. 즉, list는 처음에 참조하게 된 ArrayList 객체만 참조할 수 있고, 그 후에는 참조하는 객체를 바꿀 수 없습니다. 이것이 final 키워드의 주요 특성입니다.

그러나 list가 참조하는 ArrayList 객체의 상태는 변경할 수 있습니다. 즉, ArrayList에 항목을 추가하거나 제거하는 것은 가능합니다. 이것은 final 키워드가 참조 자체는 변경할 수 없게 하지만, 참조하는 객체의 상태는 변경할 수 있게 하는 방식으로 작동하기 때문입니다.

 

Q.아 참조하는(가리키는 객체)는 그대로지만 참조 변수의 역활은 가리키는 거니 이것은 고정된 것이 맞고 여전히 가리키고 있는 객체의 값이나 상태는 변경이 가능하구나. 가리키는 것이 변하지는 않았으니 ㅎ

 

A.맞습니다! 정확하게 이해하셨습니다.

final 키워드는 변수가 한 번 할당된 값을 변경하지 못하게 합니다. 이는 기본 데이터 타입에 대해 값을 변경할 수 없게 하고, 참조 타입에 대해서는 참조하는 객체를 변경할 수 없게 합니다.

그러나 참조 타입의 경우, final이 변수가 참조하는 객체의 상태 변경을 막지는 않습니다. 따라서 final로 선언된 참조 변수가 가리키는 객체 내부의 필드 값 변경이나, 리스트에 요소를 추가/삭제하는 등의 연산은 가능합니다.

이 특성은 final 변수를 사용할 때 항상 염두에 두어야 하는 중요한 부분입니다.

 

Q.참조 변수에는 어떤 것이 있었지?? 스트링?

String myString = "Hello, World!";
ArrayList<Integer> myList = new ArrayList<>();
MyClass myObject = new MyClass();

 

A.참고로 String은 특별한 경우로, 문자열 리터럴을 사용하여 직접 값을 할당할 수 있습니다 (예: String myString = "Hello, World!"). 그러나 String은 여전히 객체이며, myStringString 객체를 참조하는 참조 변수입니다.

 

Q.String myString = "Hello, World!" 에서 리터널을 입력하였어도 참조 변수 인건가?? 알긴 했는데 충격적이네

 

A.네, 맞습니다. String은 자바에서 객체입니다.

String의 특성 중 하나는 문자열 리터럴을 직접 할당할 수 있다는 것입니다. 따라서 String 객체를 생성하는 표준 방식인 new 키워드를 사용하지 않고도 String 객체를 생성하고 참조할 수 있습니다.

 

 

String str = "Hello, World!";  // 문자열 리터럴로 String 객체 생성 및 참조

 

A.위의 코드에서 str는 String 객체를 참조하는 참조 변수입니다. "Hello, World!"는 String 객체이며, 이 객체는 자바의 문자열 리터럴 문법에 의해 생성됩니다.

다음과 같은 방식으로도 String 객체를 생성할 수 있습니다:

 

String str = new String("Hello, World!");  // 'new' 키워드를 사용한 String 객체 생성

 

위의 코드에서도 strString 객체를 참조하는 참조 변수입니다. 두 경우 모두 strString 객체를 참조하므로 참조 변수라고 할 수 있습니다.

 

 

Q.방금 두 개의 코드는 생성 방식만 다르고 생성 된 후, 두 개의 str는 완전 같은건인가?

 

A.두 String 객체를 생성하는 방식은 서로 다르지만, 그 결과는 동일한 String 객체를 생성합니다. 즉, 두 String 객체는 같은 문자열 값을 가지게 됩니다.

그러나 String str = "Hello, World!";와 String str = new String("Hello, World!"); 사이에는 중요한 차이점이 있습니다.

String str = "Hello, World!";의 경우, 문자열 "Hello, World!"는 자바의 문자열 리터럴 풀(string literal pool)에 저장됩니다. 문자열 리터럴 풀은 JVM(Java Virtual Machine)이 관리하는 특별한 메모리 영역으로, 중복 문자열을 재사용하게 해서 메모리 효율성을 높입니다. 즉, 같은 문자열 리터럴로 생성된 String 객체는 모두 같은 메모리 위치를 참조하게 됩니다.

 

String str1 = "Hello, World!";
String str2 = "Hello, World!";
System.out.println(str1 == str2); // true, 동일한 객체를 참조

 

반면, String str = new String("Hello, World!");의 경우, new 키워드가 사용되어 문자열 리터럴 풀 외부에 새로운 String 객체가 생성됩니다. 이 경우, 동일한 문자열 값을 가진 String 객체라도 new를 사용하여 생성하면 서로 다른 메모리 위치에 위치하게 됩니다.

 

String str1 = new String("Hello, World!");
String str2 = new String("Hello, World!");
System.out.println(str1 == str2); // false, 서로 다른 객체를 참조

따라서, String 객체를 생성하는 방식에 따라 생성된 String 객체가 참조하는 메모리 위치가 달라질 수 있습니다. 그럼에도 불구하고, 두 String 객체는 동일한 문자열 값을 가지므로, equals 메서드를 사용하면 두 객체의 값이 같은지를 확인할 수 있습니다.

 

String str1 = new String("Hello, World!");
String str2 = new String("Hello, World!");
System.out.println(str1.equals(str2)); // true, 같은 문자열 값을 가짐

Q.equals는 동등성만 확인하기 떄문이지? 그래서 true지?

 

A.equals 메서드는 객체의 "동등성(equality)"를 비교합니다. 이는 대상이 가진 내용이 같은지 확인하는 것을 의미합니다.

자바에서 == 연산자는 "동일성(identity)"를 검사합니다. 즉, 두 참조 변수가 동일한 객체를 가리키는지 확인합니다.

다음은 이를 확인하는 코드 예시입니다

 

String str1 = "Hello, World!";
String str2 = "Hello, World!";
System.out.println(str1 == str2);  // true, 동일한 객체를 가리킴

String str3 = new String("Hello, World!");
String str4 = new String("Hello, World!");
System.out.println(str3 == str4);  // false, 서로 다른 객체를 가리킴

System.out.println(str3.equals(str4));  // true, 내용이 같음

 

위의 예시에서 str1과 str2는 문자열 리터럴 풀에서 동일한 String 객체를 참조하므로 str1 == str2는 true를 반환합니다.

반면, str3와 str4는 new 키워드를 사용하여 각각 다른 String 객체를 생성하므로 str3 == str4는 false를 반환합니다.

그러나 str3와 str4는 동일한 문자열 값을 가지므로 str3.equals(str4)는 true를 반환합니다. 이는 equals 메서드가 객체의 내용을 비교하기 때문입니다.

 

Q.String은 타입이 아니고 객체라는 거야?

 

A,Java에서 String은 타입이기도 하고 객체이기도 합니다.

String은 클래스이기 떄문에 따라서 String 객체는 이 클래스의 인스턴스이다.

Java에서는 String이라는 클래스 타입을 사용하여 String 객체를 생성하고 관리한다.

String myString = "Hello, World!";

 

여기에서 myString은 String 클래스의 객체이며, 그 타입은 String입니다. 이를 통해 String이라는 클래스 타입을 사용하여 객체를 생성하고, 그 객체에 대해 메소드를 호출하거나 속성을 조작할 수 있습니다.

그러나 다른 대부분의 객체와 달리, String 객체는 리터럴 구문("Hello, World!"와 같은)을 사용하여 직접 생성할 수 있습니다. 이는 String이 프로그래밍에 있어서 중요하게 사용되는 타입이기 때문에 Java 언어에서 특별히 지원하는 기능입니다.

 

내가 정리해 보자면

 

  • String: 클래스(명)이자 타입(명) -> 클래스 타입
  • myString: 클래스의 객체(인스턴스) 를 가리키는 참조 변수
  • String 타입은 new로 새로운 객체 생성도 가능하고 리터럴 대입으로 생성도 가능하다.

 

이상 String을 참조 변수 측면에서 학습하였습니다.