🏃♂️ 들어가기 앞서..
본 게시물은 스터디 활동 중에 작성한 게시물로 자바의 정석-기초편 교재를 학습하여 정리하는 글입니다.
※ 스터디 Page : 〔투 비 마스터 : 자바〕
*해당 교재의 목차 순서와 구성을 참고하여 작성하며
각 내용마다 부족할 수 있는 내용이나 개인적으로 궁금한 점은
추가적인 검색을 통해 채워나갈 예정입니다.
기존 클래스를 재사용하여 새로운 클래스를 작성하는 것
상속 구현 방법은 간단하다.
entends 키워드만 기억하자.
이 extends
를
" 새로 작성하는 클래스의 이름 뒤 "에
상속받고자 하는 클래스의 이름과 함께 써주면 된다.
class Parent { } // 기존에 존재하는 클래스 (Child class가 상속받을 클래스)
class Child extends Partent {
// Parent class 상속받은 상태
// 새호운 Child 클래스에서의 새로운 코드들
}
위 코드에서의
두 클래스 (Parent
& Child
)의 관계를
" 서로 상속 관계에 있다. "라 표현하고
상속해주는 클래스를 < " 상위클래스 " / " 조상클래스 " / " 부모클래스 " >등으로 부르고
상속받는 클래스를 < " 하위클래스 " / " 자손클래스 " / " 자식클래스 " > 등으로 부른다.
( 클래스를 타원으로 표현하고 상속관계를 화살표로 표시하는 상속관계를 그림으로 표현한 것을 "상속계층도(class hierarchy)"라고 한다.
→ 자손 클래스 타원 내부에 조상 클래스가 포함되어 있는 형태이다.)
[ 자손 클래스 ]는
조상클래스의 모든 멤버를 상속받기 때문에
항상 조상클래스보다 같거나 큰 멤버를 가지게 되지만
( 단 ❗,상속자 & 초기화 블럭은 상속되지 않는다. )
[ 조상 클래스 ]는
자손 클래스에게 어떠한 영향도 받지 않기 때문에
자손 클래스가 변하더라도 변화가 없다.
( 그래서 " 상속 받는 것 "을 조상클래스를 " 확장 "한다는 의미로 볼 수도 있기 때문에 extends키워드를 사용하는 것이다. )
inheritance
) "관계 vs " 포함(composite
) "관계앞서 살펴봤던 상속 이외에도 재사용할 수 있는 방법이 더 있는데
그것이 바로
클래스 간 서로 " 포함 관계 "를 맺어주는 것이다.
포함관계를 맺어준다라 함은
한 클래스의 멤버변수로
" 다른 클래스 타입의 참조변수를 선언하는 것 "이다.
즉, 한 클래스를 작성할 때
다른 클래스를 멤버변수로 선언해서 사용하는 것인데
예를 들어보자면
// 원을 만드는 class
class Circle {
int x ; // x좌표
int y ; // y좌표
int r ; // 반지름
}
// 좌표(점) class
class Point {
int x ; // x좌표
int y ; // y좌표
}
Circle클래스와 Point클래스에서 int x
와 int y
를 공통적으로 사용한다.
공통적으로 사용하는 부분만 사용하는 Point클래스를
Circle클래스에 포함시키는 형태라고 생각하면 된다.
class Point {
int x ; // x좌표
int y ; // y좌표
}
class Circle {
Point p = new Point() ; // x좌표 & y좌표
int r ;
}
이렇게 말이다.
이렇게 단위별로 여러 개의 클래스를 작성한 후,
클래스 간 필요에 따라
포함관계로 서로 재사용하면 간결하고 쉽게 클래스를 작성할 수 있다.
💡 클래스 간 관계 결정 꿀Tip 💡
- 범위를 생각해보자
- " ~은 ~이다. " (is) ▶ 상속(inheritance)관계
- " ~은 ~을 가지고 있다. " (has) ▶ 포함(composite)관계
single inheritance
)( ❗ C++이라는 또 다른 객체지향언어의 경우는 여러 조상클래스로부터 상속받는 다중상속(multiple inheritance)가 가능하지만 JAVA는 단일상속만 허용한다. )
단일 상속만 가능하다는 것은
쉽게 말해 extends 클래스a, 클래스b
같은 형태의 상속이 불가능하다는 것이다.
💡 " 다중상속 "의 장단점 💡
◎ 장점
- 복합적인 기능을 가진 클래스 쉽게 작성
◎ 단점
- 클래스 간 관계 너무 복잡
- 서로 다른 클래스에서 상속받은 멤버간의 이름이 같은 경우, 구별할 수 없다.
(static메서드라면 클래스 이름 붙여서 구분할 수 있지만 인스턴스 메서드라면 답 없음.)→ 다중상속에 비해 불편할 수 있지만
클래스 간 관계가 명확해지고
코드의 신뢰성이 높아지기 때문에" Java는 단일상속만을 허용한다. "
Object
클래스 "" 모든 클래스 상속계층도의 최상위 에 있는 조상클래스 "
말 그대로 모든 클래스들 중 가장 최상위에 위치하고 있는 클래스로
< 다른 클래스로부터 상속받지 않는 모든 클래스들 >은
자동적으로 Object클래스로부터 상속받게 한다.
이게 무슨 강압적인 행위이냐 싶을 수 있지만
이름은 낯설겠지만
어디에나 있지만 어디에서도 볼 수 없던 그런 존재이다.
toString()
메서드와 equals(Object o)
메서드등을 많이 봤을 것이다.
자동으로 상속되기 때문에
별 다른 작업을 하지 않아도 위 메서드들을 아무렇지 않게 사용했던 이유가
Object클래스의 정의된 멤버였기 때문이다.
overriding
) ?* 필자 블로그의 『오버로딩 & 오버라이딩 게시글』 참고
super
" 과 "super()
"헤드라인을 보고
어라? 어디서 본 듯한 비교인데 싶을 것이다.
그렇다.
이전 『객체 지향 프로그래밍 Ⅰ 정리 - (3)』에서 봤던
this와 this()의 차이점을 알아봤을 때와 유사하다.
여기서도 그 분류기준이 유사하다.
먼저 super
에 대해 알아보자.
super
super는
자손클래스에서
" 조상 클래스로부터 상속받은 멤버를 참조하는데 사용하는 참조변수 "이다.
멤버변수와 지역변수의 이름이 같을 때,
this을 통해 구별했던 것 처럼
class SuperTest {
public static void main(String args[]) {
Child c = new Child();
c.method();
}
}
/* 출력
x=20
this.x=20
super.x=10
*/
class Parent { int x= 10 ; } // super.x
class Child extends Parent {
int x = 20 ; // this.x
void method() {
System.out.println("x=" + x) ; Child클래스에서 선언한 x : 20
System.out.println("this.x=" + this.x) ; // 자신(Child클래스)의 멤버 x : 20
System.out.println("super.x=" + super.x) ; // Parent 클래스의 x : 10
}
}
상속받은 멤버와 자신의 멤버의 이름이 같을 때,
super를 통해서 구별한다.
모든 인스턴스 메서드에는
지역변수로서 this
와 super
가 존재한다.
class SuperTest2 {
public static void main(String args[]) {
Child c = new Child();
c.method();
}
}
/* 출력
x=10
this.x=10
super.x=10
*/
class Parent { int x= 10 ; } // x를 조상클래스에서만 -> super.x로도, this.x로도 사용됨.
class Child extends Parent {
// 상속받은 x를 건드리지 않고
void method() {
System.out.println("x=" + x) ; // 10
System.out.println("this.x=" + this.x) ; // 10
System.out.println("super.x=" + super.x) ; // 10
// x == this.x == super.x
}
}
사실상 이 둘은
근본적으로 같다고 볼 수 있고
" 조상의 멤버 "와 "자신의 멤버"를 구별하는데 사용된다는 점에서 차이가 있는 것이다.
super()
여기서도 괄호()
의 효과는 대단했다!
this()
처럼 super()
도 생성자의 역할을 한다.
this()
가 같은 클래스의 다른 생성자를 호출한다면
super()
은 조상의 생성자를 호출한다.
( ❗ 생성자는 상속되지 않는다. )
class Point {
int x ; // x좌표
int y ; // y좌표
// 메서드 추가
Point(int x, int y) {
this.x = x ;
this.y = y ;
}
}
class Point3D extends Point {
int z ;
Point3D(int x, int y, int z) {
// this를 통해 새롭게 초기화 하는 것도 틀리진 않지만
// 이 방법으로 초기화하기보단 super()생성자를 통해 초기화하는 것이 바람직하다.
//this.x = x ; // 조상멤버 "초기화"
//this.y = y ; // 조상멤버 "초기화"
super(x, y) ; // Point(int x, int y) 호출
this.z = z ;
}
}
package
)= 클래스 묶음
( 물리적으로 소속 클래스들이 묶여있는 하나의 디렉토리 )
" 같은 이름의 클래스 "이더라도
" 서로 다른 패키지 "에 존재할 수 있기 때문에
각 개발자들이 자신만의 패키지 체계를 유지함으로서
다른 개발자의 클래스 라이브러리의 클래스&이름이 충돌나는 것을 피할 수 있다.
사실 클래스의 진짜 이름(full name)은
단순히 클래스 이름이 아니라
패키지명이 포함된 이름이다.
String 클래스를 한 번 보자.
이 클래스의 실제이름은
" java.lang.String "이다.
" java.lang
패키지에 속한 String 클래스 "라는 의미이다.
( lang 패키지도 java 패키지의 하위 패키지인 것)
그래서 만약 String이라는 같은 이름의 클래스가 있더라도
다른 패키지에 속해있다면 문제 없이 구별 가능한 것이다.
놀라지마라.
package 패키지명
끝이다.
...
..
그냥 "클래스"나 "인터페이스의 소스파일(.java
) 맨 위"에
저 한 줄만 적으면 된다.
( ❗ 반드시 [주석&공백 제외] 첫 번째 문장이어야 하며 소스파일에 단 한번만 선언될 수 있다. )
대소문자 구분은 가능하나
class 명과 구분하기 쉽도록 소문자를 사용하는 것이 원칙이다.
또한,
모든 클래스는
반드시 하나의 패키지에 포함되어야 한다.
?
여기서 잠깐!
""" 여태까지 우리는 패키지 선언을 한 적없는데 어떻게 된거지..? """
라는
의문이 들 수 있다.
우리에게 여태 소스파일을 작성할 때 문제 없었던 이유는
자바에서
기본 제공하는 " 이름없는 패키지 (unnamed package) "에
자동으로 속할 패키지를 지정하지 않은 클래스를 소속시킨다.
( ❗ 간단한 프로그램은 지정하지 않아도 큰 문제가 없지만 "큰 프로젝트"나 Java API 등과 같은"클래스 라이브러리" 를 작성할 땐
미리 패키지를 구성하여 적용해야한다.)
🔊 클래스 패스(
classpath
)JVM이나 Java 컴파일러에
" 사용자정의 클래스 "와 " 패키지의 위치 "를 지정해주는 파라미터이다.쉽게 말해
Java가 클래스를 찾아 사용을 해야하는데
" 클래스들이 어디 있는지 위치를 지정해주는 값 " 이라고 할 수 있다.Java파일을 컴파일하고 class를 실행시킬 때,
[ class가 있는 위치 ]에서
해당 클래스를 실행하는 명령어를 입력하면 클래스패스를 별도로 지정하지 않아도 되지만
[ 다른 경로 ]에서 실행시킨다면
클래스패스를 지정해주어야 해당 위치를 찾아 실행시킬 수 있다그렇기 때문에
패키지의 루트 디렉토리를 클래스패스에 포함시켜야 한다.
( *루트 디렉토리는 클래스가 소속된 패키지의 상위 디렉토리이다. )[Windows]
제어판
-시스템
-고급 시스템 설정
-환경변수
-새로 만들기
▶ 《변수이름》에 'CLASSPATH' 입력 & 《변수 값》에 루트 디렉토리 path 입력여러 개의 경로를 "
;
구분자 "를 통해 클래스패스에 지정할 수 있다.★
.jar
파일 사용 ★
: java 클래스 생성시,
jar 파일에 포함하여 생성할 수도 있는데,SET CLASSPATH=C:\ (루트 디렉토리 Path);(루트 디렉토리 Path)\xxx.jar
위 코드와 같이 jar를 생성하고
해당 jar의 위치를 클래스패스에 지정하면,
홈 위치에서 해당 클래스를 실행해도
" 클래스패스 위치에서 해당 클래스를 찾아 실행을 하게 된다. "
import
문소스 코드를 작성하면서
다른 패키지의 클래스를 사용하려고 할 때,
원래 " 패키지명이 포함된 클래스 이름 "을 사용해야 하는데
매번 붙여서 작성하는 것은 비효율적이다.
그래서
코드를 작성하기 이전에
사용하고자 하는 클래스의 패키지를
import
문으로 미리 명시하여
코드 내에서 사용할 때
패키지명을 생략하고 사용할 수 있다.
/* 실행 시 성능상의 차이는 전혀 없다 */
import 패키지명.클래스명 ; //방법 1
import 패키지명.* ; // 방법 2 _ 전체(*) 클래스 import
《 작성 위치 》
" package문
다음 " & " 클래스 선언문 이전 "
( ❗ package문과 달리 한 소스파일 내에 여러 번 선언될 수 있다. )
✨
static import
문위에서 알아본 import문보다 더 깊은 import라고 생각하면 이해하기 쉽다.
패키지명 생략을 넘어
static import문을 사용하면
static 멤버를 호출할 때
" 클래스 이름 생략 "이 가능해진다.
( 【특정 클래스의 static 멤버를 자주 사용하는 경우】 효과적 )/* 아래와 같이 간략하게 작성 가능 */ import static java.lang.Math.random ; // Math클래스 내 모든 static 메서드 import static java.lang.System.out ; // System.out class Test { public static void main(String args[]) { // System.out.println(Math.random()) ; out.println(random()); // System.out.println("Math.PI : " + Math.PI) ; out.println("Math.PI : " + Math.PI); } }