해당 게시물은 자바의 정석을 정리한 내용 입니다.
1.1. 상속(inheritance)의 정의와 장점
상속이란?
- 상속은 기존의 클래스를 재사용해서 새로운 클래스를 작성하는 것이다.
- 두 클래스를 조상과 자손으로 관계를 맺어주는 것.
- 자손은 조상의 모든 멤버를 상속받는다.(생성자, 초기화블럭 제외)
- 자손의 멤버 개수는 조상보다 적을 수 없다.(같거나 많다.)
class 자손클래스 extends 조상클래스 {
}
class Tv {
boolean power; // 전원상태(on/off)
int channel; // 채널
void power() { power = !power; }
void channelUp() { ++channel; }
void channelDown() { --channel; }
}
class CaptionTv extends Tv {
boolean caption; // 캡션상태(on/off)
void displayCaption(String text) {
if (caption) { // 캡션 상태가 on(true)일 때만 text를 보여 준다.
System.out.println(text);
}
}
}
class CaptionTvTest {
public static void main(String args[]) {
CaptionTv ctv = new CaptionTv();
ctv.channel = 10; // 조상 클래스로부터 상속받은 멤버
ctv.channelUp(); // 조상 클래스로부터 상속받은 멤버
System.out.println(ctv.channel);
ctv.displayCaption("Hello, World");
ctv.caption = true; // 캡션기능을 켠다.
ctv.displayCaption("Hello, World"); // 캡션을 화면에 보여 준다.
}
}
1.2 클래스 간의 관계 - 상속 관계(inheritance)
- 상속 관계에서 공통부분은 조상에서 관리하고 개별적인 부분은 자손에서 관리한다.
Parent 클래스에 age라는 정수형 변수를 멤버변수로 추가하면, 자손 클래스는 조상의 멤버를 모두 상속받기 때문에
Child 클래스는 자동적으로 age라는 멤버변수가 추가된 것과 같은 효과를 얻는다.
class Parent{
int age;
}
class Child extends Parent{}
class Child2 extends Parent{}
class GrandChild extends Child{}
- 조상의 변경은 자손에 영향을 미치지만, 자손의 변경은 조상에 아무런 영향을 미치지 않는다.
class Parent{
int age;
}
class Child extends Parent{
void play(){ // 새로운 멤버 추가
System.out.println("놀자~");
}
}
1.2 클래스 간의 관계 - 포함 관계(composite)
- 포함 관계는 한 클래스의 멤버 변수로 다른 클래스 타입의 참조변수를 선언하는 것이다.
- 작은 단위의 클래스를 먼저 만들고, 이들을 조합해서 하나의 커다란 클래스를 만든다.
class Car{
Engine e = new Engine(); // 엔진
Door[] d = new Door[4]; // 문, 문의 개수를 넷으로 가정하고 배열로 처리했다.
}
위와 같은 Car 클래스를 작성할 때, Car 클래스의 단위 구성 요소인 Engine, Door와 같은 클래스를 먼저 작성해 놓고
이 들을 Car 클래스의 멤버 변수로 선언하여 포함관계를 맺어주면, 클래스를 작성하는 것도 쉽고 코드도 간결해서 이해하기 쉽다.
1.3 클래스 간의 관계 결정하기
클래스 간의 관계 결정하기
상속 관계 : '~은 ~이다. (is-a)'
포함 관계 : '~은 ~을 가지고 있다. (has-a)'
원(Circle)은 점(Point)이다. - Circle is a Point. (X)
원(Circle)은 점(Point)를 가지고 있다. - Circle has a Point. (O)
→ 대부분의 경우(90% 이상), 포함 관계라고 보면 됨.
클래스 간의 관계 결정하기 예시
class DrawShape {
public static void main(String[] args) {
Point[] p = { new Point(100, 100),
new Point(140, 50),
new Point(200, 100)
};
Triangle t = new Triangle(p);
Circle c = new Circle(new Point(150, 150), 50);
t.draw(); // 삼각형을 그린다.
c.draw(); // 원을 그린다.
}
}
class Shape {
String color = "black";
void draw() {
System.out.printf("[color=%s]%n", color);
}
}
class Point {
int x;
int y;
Point(int x, int y) {
this.x = x;
this.y = y;
}
Point() {
this(0,0);
}
String getXY() {
return "("+x+","+y+")"; // x와 y의 값을 문자열로 반환
}
}
class Circle extends Shape { // Circle과 Shape는 상속 관계
// Circle과 Point는 포함 관계
Point center; // 원의 원점좌표
int r; // 반지름
Circle() {
this(new Point(0, 0), 100); // Circle(Point center, int r)를 호출
}
Circle(Point center, int r) {
this.center = center;
this.r = r;
}
void draw() { // 원을 그리는 대신에 원의 정보를 출력하도록 했다.
System.out.printf("[center=(%d, %d), r=%d, color=%s]%n",
center.x, center.y, r, color);
}
}
class Triangle extends Shape {
Point[] p = new Point[3];
Triangle(Point[] p) {
this.p = p;
}
void draw() {
System.out.printf("[p1=%s, p2=%s, p3=%s, color=%s]%n",
p[0].getXY(), p[1].getXY(), p[2].getXY(), color);
}
}
1.4 단일 상속(single inheritance)
단일 상속이란?
- Java는 단일 상속만을 허용한다.
(둘 이상의 클래스로 부터 상속을 받을 수 없다.)
class TvDVD extends TV, DVD { // 에러. 조상은 하나만 허용된다.
//...
}
- 비중이 높은 클래스 하나만 상속 관계로 하고 나머지는 포함 관계로 한다.
class Tv {
boolean power; // 전원상태(on/off)
int channel; // 채널
void power() { power = !power; }
void channelUp() { ++channel; }
void channelDown() { --channel; }
}
class VCR {
boolean power; // 전원상태(on/off)
int counter = 0;
void power() { power = !power; }
void play() { /* 내용생략*/ }
void stop() { /* 내용생략*/ }
void rew() { /* 내용생략*/ }
void ff() { /* 내용생략*/ }
}
class TVCR extends Tv {
VCR vcr = new VCR();
int counter = vcr.counter;
void play() {
vcr.play();
}
void stop() {
vcr.stop();
}
void rew() {
vcr.rew();
}
void ff() {
vcr.ff();
}
}
1.5 Object 클래스 - 모든 클래스의 최고 조상
Object 클래스?
- Object 클래스는 모든 클래스의 최고 조상이다.
- 조상이 없는 클래스는 자동적으로 Object클래스를 상속받게 된다.
다른 클래스로 부터 상속을 받지 않는 Tv 클래스를 정의 한 다음, 컴파일 하면 컴파일러는 'extends Object'를 추가하여
Tv 클래스가 Object 클래스로 부터 상속 받도록 한다.
class Tv extends Object{
//...
}
- 상속계층도의 최상위에는 Object클래스가 위치한다.
- 모든 클래스는 Object클래스에 정의된 11개의 메서드를 상속받는다.
toString(), equals(Object obj), hashCode(), ...
2.1 오버라이딩(overriding)이란?
오버라이딩은 조상 클래스로 부터 상속 받은 메서드의 내용을 변경하는 것이다.
class Point {
int x;
int y;
Point(int x, int y) {
this.x = x;
this.y = y;
}
String getLocation() {
return "x :" + x + ", y :"+ y;
}
}
class Point3D extends Point {
int z;
Point3D(int x, int y, int z) {
super(x, y);
this.z = z;
}
String getLocation() { // 오버라이딩
return "x :" + x + ", y :"+ y + ", z :" + z;
}
}
2.2 오버라이딩의 조건
오버라이딩의 조건
- 선언부가 조상 클래스의 메서드와 같아야 한다.(이름, 매개변수, 리턴타입)
- 접근 제어자를 조상 클래스의 메서드 보다 좁은 범위로 변경할 수 없다.
- 예외는 조상 클래스의 메서드 보다 많이 선언 할 수 없다.
단순히 선언된 예외의 개수 문제가 아니라는 점을 주의
class Parent {
void parentMethod() throws IOException, SQLException{
. . .
}
}
class Child extends Parent {
void parentMethod() throws Exception{ // 잘못된 오버라이딩
. . .
}
}
위의 코드에서 Exception은 모든 예외의 최고 조상이므로 가장 많은 개수의 예외를 던질 수 있도록 선언한 것이다.
그래서 예외의 개수는 적거나 같아야 한다는 조건을 만족시키지 못하는 잘못된 오버라이딩인 것이다.
2.3 오버 로딩 VS 오버라이딩
오버로딩 (overloading) : 기존에 없는 새로운 메서드를 정의하는 것 (new)
오버라이딩 (overriding) : 상속 받은 메서드의 내용을 변경하는 것(change, modify)
아래의 코드를 보고 오버로딩과 오버라이딩을 구별 할 수 있어야 한다.
class Parent {
void parentMethod() {}
}
class Child extends Parent {
void parentMethod() { } // 오버라이딩
void parentMethod(int i) { } // 오버로딩
void childMethod() { }
void childMethod(int i) { } // 오버로딩
void childMethod() { } // 에러 발생 (중복 정의)
}
2.4 super - 참조변수
- super는 객체 자신을 가리키는 참조변수이다.
- 조상의 멤버와 자신의 멤버를 구별할 때 사용
- this와 마찬가지로 super는 static 메서드에서 사용할 수 없다. (인스턴스 메서드, 생성자 내에만 존재)
class SuperTest2 {
public static void main(String args[]) {
Child c = new Child();
c.method();
}
}
class Parent {
int x=10;
}
class Child extends Parent {
int x=20;
void method() {
System.out.println("x=" + x);
System.out.println("this.x=" + this.x);
System.out.println("super.x="+ super.x);
}
}
this.x는 자손 클래스에서 선언된 멤버 변수를 뜻하며 super.x는 조상 클래스로부터 상속 받은 멤버 변수 x를 뜻한다.
이처럼 조상 클래스에 선언된 멤버 변수와 같은 이름의 멤버 변수를 자손 클래스에서 중복해서 정의하는 것이 가능하며
참조변수 super를 이용해서 서로 구별 할 수 있다.
2.5 super() - 조상 클래스의 생성자
생성자 super()는 조상 클래스의 생성자를 호출하는데 사용된다.
Object 클래스를 제외한 모든 클래스의 생성자는 반드시 첫 줄에 생성자를 호출해야 한다.
그렇지 않으면 컴파일러가 생성자의 첫 줄에 super();를 추가한다.
class PointTest {
public static void main(String args[]) {
Point3D p3 = new Point3D(1,2,3);
}
}
class Point {
int x;
int y;
Point(int x, int y) {
this.x = x;
this.y = y;
}
String getLocation() {
return "x :" + x + ", y :"+ y;
}
}
class Point3D extends Point {
int z;
Point3D(int x, int y, int z) {
super(x, y); // 조상 클래스의 생성자 Poin(int x, int y)를 호출
// 이 부분이 없었다면 컴파일러가 super();를 추가하여 에러 발생
this.z = z; // 자신의 멤버를 초기화
}
String getLocation() { // 오버라이딩
return "x :" + x + ", y :"+ y + ", z :" + z;
}
}
조상 클래스의 멤버 변수는 조상의 생성자를 호출해서 초기화 해야 한다. super(x, y);
인스턴스 초기화 순서
Point3D() → Point3D(int x, int y, int z) → Point(int x, int y) → Object()
어떤 클래스의 인스턴스를 생성하면, 클래스 상속 관계의 최고 조상인 Object 클래스까지 거슬러 올라가면서
모든 조상 클래스의 생성자가 순서대로 호출된다.
class PointTest2 {
public static void main(String argsp[]) {
Point3D p3 = new Point3D();
System.out.println("p3.x=" + p3.x);
System.out.println("p3.y=" + p3.y);
System.out.println("p3.z=" + p3.z);
}
}
class Point {
int x=10;
int y=20;
Point(int x, int y) {
this.x = x;
this.y = y;
}
}
class Point3D extends Point {
int z=30;
Point3D() {
this(100, 200, 300); // Point3D(int x, int y, int z)를 호출한다.
}
Point3D(int x, int y, int z) {
super(x, y); // Point(int x, int y)를 호출한다.
this.z = z;
}
}
3.1 패키지(package)
- 패키지는 서로 관련된 클래스와 인터페이스의 묶음이다.
- 클래스가 물리적으로 하나의 클래스 파일(.class)인 것과 같이 패키지는 물리적으로 하나의 폴더이다.
* java 소스 파일(.java)을 컴파일 하면 클래스 파일(.class)이 됨
- 패키지는 다른 패키지를 포함 할 수 있으며 점(.)으로 구분한다.
- 클래스의 실제 이름(full name)은 패키지 명을 포함한 것이다. (java.lang.String)
- rt.jar는 자바 프로그램을 실행 하는데 필요한 클래스들을 압축한 파일이다.
(Java 9 버전 부터 rt.jar가 없어지고 모듈로 쪼개짐)
* jar 파일 : 클래스 파일을 압축한 것이다.
3.2 패키지의 선언
- 패키지는 소스 파일에 첫 번째 문장(주석 제외)으로 단 한번 선언한다.
package 패키지명;
- 같은 소스 파일의 클래스들은 모두 같은 패키지에 속하게 된다.
- 패키지 선언이 없으면 이름 없는(unnamed) 패키지에 속하게 된다. (이클립스에서는 default package)
// PackageTest.java
package com.codechobo.book;
public class PackageTest {
public static void main(String[] args){
System.out.println("Hello, world!");
}
}
class PackageTest2 {}
3.3 클래스패스(classpath) 설정
- 클래스 패스는 클래스 파일(*.class)의 위치를 알려주는 경로(path)이다.
- 환경 변수 classpath로 관리하며, 경로 간의 구분자는 ';'를 사용한다.
* 환경 변수 : OS에서 관리하는 변수
- classpath(환경 변수)에 패키지의 루트 디렉토리를 등록 해주어야 한다.
명령창에서 실행할 때, 현재 폴더에서 찾지 못하면 classpath에 등록된 경로에서 찾는다.
- 자세한 내용은 자바의 정석 p337~339를 참고하자.
3.4 import 문
- import문은 사용할 클래스가 속한 패키지를 지정하는데 사용한다.
- import 문을 사용하면 클래스를 사용할 때 패키지명을 생략할 수 있다.
class ImportTest {
java.util.Date today = new java.util.Date();
}
import java.util.Date;
class ImportTest{
Date today = new Date();
}
- java.lang 패키지의 클래스는 import 하지 않고도 사용할 수 있다. ( String, Object, System, Thread ... )
3.5 import 문 선언
- import 문은 package 문과 클래스 선언 사이에 선언한다.
① pacakge 문
② import 문
③ 클래스 선언
- import 문을 선언하는 방법은 다음과 같다.
import 패키지명.클래스명;
또는
import 패키지명.*; // 해당 패키지에 속한 모든 클래스
키워드 import와 패키지명을 생략하고자 하는 클래스의 이름을 패키지명과 함께 써주면 된다.
클래스 이름을 지정 해주는 대신 '*'를 사용하는 것도 가능하다.
import java.text.SimpleDateFormat;
import java.util.Date;
class ImportTest
{
public static void main(String[] args)
{
Date today = new Date();
SimpleDateFormat date = new SimpleDateFormat("yyyy/MM/dd");
SimpleDateFormat time = new SimpleDateFormat("hh:mm:ss a");
System.out.println("오늘 날짜는 " + date.format(today));
System.out.println("현재 시간은 " + time.format(today));
}
}
import 문으로 패키지를 지정하지 않으면 java.util.Date today = new java.util.Date();와 같이 모든 클래스 이름 앞에
패키지명을 반드시 붙여줘야 한다.
- import java.*;
→ "java 패키지의 모든 클래스"를 의미하며 * 가 하위 패키지의 클래스까지 포함하는 것은 아니므로 위와 같이
사용해서는 안된다.
- 이름이 같은 클래스가 속한 두 패키지를 import할 때는 클래스 앞에 패키지명을 붙여줘야 한다.
import java.sql.*; // java.sql.Date
import java.util.*; // java.util.Date
public class ImportTest{
public static void main(String[] args){
java.util.Date today = new java.util.Date();
}
}
3.6 static import 문
static import문은 static 멤버를 사용할 때 클래스 이름을 생략 할 수 있게 해준다.
import static java.lang.System.out; // System.out을 out만으로 참조 가능
import static java.lang.Math.*; // Math 클래스의 모든 static 멤버에 대해 클래스 이름 생략 가능
class StaticImportEx1 {
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 :" + PI); // out은 static 변수이며 PI는 static 상수이다.
}
}
'Java > 객체 지향 개념 ~' 카테고리의 다른 글
자바의 정석 (Chapter 7_3. 객체지향개념 2) (0) | 2020.08.03 |
---|---|
자바의 정석 (Chapter 7_2. 객체지향개념 2) (0) | 2020.08.02 |
자바의 정석 (Chapter 6_3. 객체지향개념 1) (0) | 2020.08.01 |
자바의 정석 (Chapter 6_2. 객체지향개념 1) (0) | 2020.07.31 |
자바의 정석 (Chapter 6_1. 객체지향개념 1) (0) | 2020.01.21 |
댓글