Java 객체지향 프로그래밍 심화
앞서 기초 파트에서 객체지향 프로그래밍의 근간이 되는 클래스와 객체를 배웠고, 이를 바르게 정의하기 위해 필요한 변수와 메서드, 그리고 변수 초기화를 위한 생성자에 대한 내용을 학습했다. 이번 심화 파트에서는 객체지향 프로그래밍 설계를 지탱하는 핵심적인 4가지 기둥인 상속성, 캡슐화, 다형성, 추상화를 정리해보자.
1.상속(Inheritance) = 기존의 클래스를 재활용하여 새로운 클래스를 작성하는 자바의 문법 요소
가장 단순한 형태를 생각해보면, 두 클래스를 상위 클래스와 하위 클래스로 나누어 상위 클래스의 멤버(필드, 메서드, 이너 클래스)를 하위 클래스와 공유하는 것을 의미함.
이 때, 두 클래스를 서로 상속 관계 있다고 하며, 하위 클래스는 상위 클래스가 가진 모든 멤버를 상속받게 됨.
위의 그림을 보면,
Person 클래스가 상위 클래스, 그리고 Programmer, Dancer, Singer 클래스가 상위 클래스로부터 특정한 속성과 기능을 내려받는(또는 확장된) 하위 클래스가 됨.
-상속의 장점
(1)코드를 재사용하여 보다 적은 양의 코드로 새로운 클래스를 작성할 수 있어 코드의 중복을 제거
(2)다형적 표현이 가능하다
*다형성 : 하나의 객체가 여러 모양으로 표현될 수 있다는 것
예)프로그래머는 프로그래머다. 프로그래머는 사람이다.
+자바의 객체지향 프로그래밍에서는 단일 상속(single inheritance)만을 허용하며, 다중 상속은 허용되지 않음.
1-1.포함(composite)은 상속처럼 클래스를 재사용할 수 있는 방법으로, 클래스의 멤버로 다른 클래스 타입의 참조변수를 선언하는 것
*상속과 포함은 저장공간 상의 차이는 없지만 구조상의 차이가 있음.
*클래스 간의 관계가 '~은 ~이다(is-a)'라면 상속관계
*클래스 간의 관계가 '~은 ~을 가지고 있다(has-a)라면 포함관계
1-2.메서드 오버라이딩(Method Overriding) : 상위 클래스로부터 상속받은 메서드와 동일한 이름의 메서드를 재정의함.
*Override = '~위에 덮어쓰다'
-오버라이딩에 필요한 세 가지 조건
(1)메서드의 선언부(메서드 이름, 매개변수, 반환타입)이 상위클래스의 그것과 완전히 일치해야함
(2)접근 제어자의 범위가 상위 클래스의 메서드보다 같거나 넓어야 함.
(3)예외는 상위 클래스의 메서드보다 많이 선언할 수 없음.
*현재 2번의 접근제어자와 3번의 예외는 미학습상태임. 추후 관련내용이 나올때 추가로 언급할 예정.
+메서드 오버로딩과 오버라이딩의 차이
오버로딩(overloading)은 한 클래스 내에, 여러 개의 같은 이름의 메소드를 정의하는 것
오버라이딩(overriding)은 부모로부터 받은 메소드의 내부를 변경하여 우선적으로 사용하는 것
1-3.super vs super()
-super : 상위 클래스의 객체를 의미함
*super 키워드를 사용하면 부모의 객체의 멤버 값을 참고할 수 있음.
-super() : 상위 클래스의 생성자를 호출하는 것을 의미함
*super() 메서드를 사용하면 상위 클래스의 생성자를 호출할 수 있음.
*this()때와 마찬가지로 생성자 안에서만 사용가능하고, 반드시 첫 줄에 와야 함.
+super와 super() 둘 다 공통적으로 상위 클래스의 존재를 상정하며 상속 관계를 전제로 함.
+기초에서 배웠던 this는 자신의 객체, this() 메서드는 자신의 생성자 호출을 의미하는것과 유사함.
+super()가 있는데 상위 클래스에 기본생성자가 없으면 에러가 발생함. 클래스를 만들 때는 자동으로 기본 생성자를 생성하는 것을 습관화하는 것이 좋음.
1-4.Object 클래스 : 자바의 클래스 상속계층도에서 최상위에 위치한 상위클래스
-자바의 모든 클래스는 Object 클래스로부터 확장됨.
-자바 컴파일러는 컴파일 과정에서 다른 클래스로부터 아무런 상속을 받지 않는 클래스에 자동적으로 extends Object를 추가하여 Object 클래스를 상속받도록 함.
-Object 클래스에서 확장되어 사용 메서드들은 매우 많지만, 대표적인 메서드 몇 가지만 소개
*현재 당장은 클래스 계층도의 최상위에 Object 클래스가 있다는 것, 그리고 이에 따라 아래와 같은 메서드들을 따로 정의 하지 않고도 사용가능하다는 점만 기억
2.캡슐화(Encapsulation) : 특정 객체 안에 관련된 속성과 기능을 하나의 캡슐로 만들어 데이터를 외부로부터 보호하는 것
-데이터 보호 및 내부적으로만 사용되는 데이터에 대한 불필요한 외부 노출 방지 목적으로 사용됨.
-장점
(1)정보 은닉(data hiding) : 외부로부터 객체의 속성과 기능이 함부로 변경되지 못하게 막고, 변경되더라도 다른 객체에 영향을 주지 않기에 독립성 확보 가능
(2)유지보수와 코드 확장 시에도 오류의 범위를 최소화할 수 있어 효과적으로 코드를 유지보수하기에 용이
-캡슐화를 수행하기 위한 핵심적인 수단으로 접근제어자(Access Modifier)와 getter와 setter 메서드가 있음.
*접근제어자를 이해하기위해 선행되는 개념인 자바 패키지(package)를 먼저 다룸.
2-1.패키지(package)란 특정한 목적을 공유하는 클래스와 인터페이스의 묶음을 의미
-클래스들을 그룹 단위로 묶어 효과적으로 관리하기 위한 목적(클래스의 충돌을 방지)
-자바에서 패키지는 물리적인 하나의 디렉토리(directory)이고, 하나의 패키지에 속한 클래스나 인터페이스 파일은 모두 해당 패키지에 속해있음. 더 나아가서 이 디렉토리는 하나의 계층구조를 가지고 있는데, 계층 구조 간 구분은 점(.)으로 표현됨.
-패키지가 있는 경우 소스 코드의 첫 번째 줄에 반드시 package 패키지명이 표시되어야 하고, 만약 패키지 선언이 없으면 이름없는 패키지에 속함.
package practicepack.test; // 패키지 구문 포함. 패키지가 없다면 구문 필요없음
public class PackageEx {
}
-예를 들면, 우리가 주로 사용하는 String 클래스의 실제 이름은 java.lang.String인데, 여기서 java.lang은 패키지명을 나타내고 점(.)을 사용하여 디렉터리 계층구조를 나타내고 있음
2-2.Import문 : 다른 패키지 내의 클래스를 사용하기 위해 사용
아래와 같이 작성하여 사용할 수 있음.
import 패키지명.클래스명; 또는 import 패키지명.*;
*일반적으로 패키지 구문과 클래스문 사이에 작성
*만약 같은 패키지에서 여러 클래스가 사용될 때는 import문을 여러번 사용하기보다는 위에 작성된 것처럼 import 패키지명.* 으로 작성하면 해당 패키지의 모든 클래스를 패키지명 없이 사용가능
2-3.제어자(Modifier) : 클래스, 필드, 메서드, 생성자 등에 부가적인 의미를 부여하는 키워드를 의미
-명사를 꾸며주는 형용사의 역할과 같음.
-자바에서 제어자는 크게 접근 제어자와 기타 제어자로 구분함
*하나의 대상에 대해서 여러 제어자를 사용할 수 있지만 접근 제어자는 단 한번만 사용 가능.
(1)접근 제어자(Access Modifier) : 클래스 외부로의 불필요한 데이터 노출을 방지할 수 있고, 외부로부터 데이터가 임의변경되지 않도록 막을 수 있음.
public(접근 제한 없음) > protected(동일 패키지 + 하위클래스) > default(동일 패키지) > private(동일 클래스) 순으로 정리
*변수명 앞에 아무런 접근제어자가 없는 경우에는 자동으로 해당 변수의 접근 제어자는 default가 됨
2-4.getter와 setter 메서드
(1)setter 메서드는 외부에서 메서드에 접근하여 조건에 맞을 경우 데이터 값을 변경 가능하게 해줌
*메서드 명 앞에 set-을 붙여서 사용
(2)getter 메서드는 set- 설정한 변수 값을 읽어오는 데 사용
*메서드 명 앞에 get을 붙여서 사용
아래의 예시를 통해 정확히 알아보자.
1.Worker 클래스를 기반으로 객체 인스턴스를 생성해주고 같은 타입을 가지고 있는 참조변수 w에 담음.
2.w의 setter 메서드를 사용하여 이름, 나이, 아이디에 대한 데이터 값을 저장
3.getter 메서드를 통해 해당 데이터 값을 불러와 변수에 담아 출력
이미지 및 내용 출처 - code states