자바

[자바] 객체지향(2) - 1 ( 패키지 )

j9972 2022. 7. 9. 11:54
728x90

1. 상속

- 기존의 클래스를 재사용하여 새로운 클래스를 작성하는 것이다.
- 자바는 단일 상속만 허용

- 모든 클래스들에는 Object클래스가 최상위에 위치한다.

 

클래스간의 관계 - 상속 구현

class Parent { }
class Child extends Parent { }
class Child2 extends Parent { }
// extends 라는 키워드를 써서 상속을 할 수 있다.

Child와 Child2간에는 서로 아무런 관계도 성립되지 않는다.

 

 

멤버 변수, 메소드 상속 코드 예시

class Parent {
    int age;
}

class Child extends Parent {
    void play() {
        System.out.println("놀자");
    }
}

// child class는 age라는 변수도 받는다

 

상속 계층도

 

- 생성자와 초기화 블럭은 상속되지 않는다. 멤버만 상속된다.
- 자손 클래스의 멤버 개수는 조상 클래스보다 항상 같거나 많다.

- 자손 클래스의 인스턴스를 생성하면 조상 클래스의 멤버와 자손 클래스의 멤버가 합쳐진 하나의 인스턴스로 생성된다.

 

클래스간의 관계 - 포함 관계

포함관계는 상속과 마찬가지로 재사용하는 방법 중 하나 이다.

포함 - 한 클래스의 멤버변수로 다른 클래스 타입의 참조변수를 선언하는 것을 뜻한다.

 

재사용 하는 방법

// 재사용 안 할때의 코드 변화
class Circle {
    int x;
    int y;
    int z;
}

class Point {
    int x;
    int y;
}

// 재사용 할때의 코드 변화
class Circle {
    Point c = new Point(); // 원점
    int z;
}

 

상속 : is-a ( ~은 ~이다. )
포함 : has - a ( ~은 ~을 가지고 있다. )

 

TIP, 인스턴스 생성하는 코드인데, 복잡해 보이는것 이해하기 위한 코드

 

Circle c = new Circle(new Point(150,150), 50);

--same--

Point p = new Point(150,150);
Circle c = new Circle(50);

 

 

2. 오버라이딩

- 조상 클래스로부터 상속받은 메서드의 내용을 변경하는 것 ex) 2차원 -> 3차원

class Point {
    int x;
    int y;
    
    String getLocation() {
        return "x" + x + "y" + y
    }
}

class Point3D extends Point {
    int z;
    
    String getLocation() { // 오버라이딩
        return "x" + x + "y" + y + "z" + z
    }
}

 

오버라이딩 조건

- 이름이 같아야 한다
- 매개변수가 같아야 한다
- 반환타입이 같아야 한다.

=> 선언부가 서로 일치해야 한다.

 

조상 클래스의 메서드를 자손 클래스에서 오버라이딩할때
1. 접근 제어자를 조상 클래스의 메서드보다 좁은 범위로 변경할 수 없다.
2. 예외는 조상 클래스의 메서드보다 많이 선언할 수 없다.
3. 인스턴스 메서드를 static 메서드로 또는 그 반대로 변경할 수 없다.

 

2번에서 주의 할점

class Parent {
    void parentMethod() throws IOException, SQLException { 
        ....
    }
}

// 조상 클래스 보다 작은 수의 예외
class Child extends Parent {
    void parentMethod() throws IOException { 
        ....
    }
}

// 예외 수에 대한 조건이 지켜지지 않는 경우
class Parent {
    void parentMethod() throws Exception { 
        ....
    }
}

- 예외 수에 대한 조건이 지켜지지 않은 이유는 Exception는 모든 예외의 최고 조상이므로 가장 많은 개수의 예외를 던질 수 있도록 선언됨

 

Q. 조상 클래스에 정의된 static 메서드를 자손 클래스에서 똑같은 이름의 static메서드로 정의할 수 있나?

있다. 하지만, 호출시엔 ' 클래스이름.메서드이름() '으로 해야하고, static 멤버들은 자신들이 정의된 클래스에 묶여있다 생각하기

 

오버로딩 - 기존에 없는 새로운 메소드를 정의하는 것 ( new )
오버라이딩 - 상속받은 메소드의 내용을 변경하는 것 ( change, modify )

 

super

- super()은 this()와 마찬가지로 생성자다.

   -> 자주 나오는 에러 파트에 나올 수 있는 에러 파트 적었으니 확인! ( 12번 )

- 조상 클래스로부터 상속받은 멤버를 참조하는데, 멤버변수와 지역변수의 이름이 같을 때 this를 써 구별하듯, 상속받은 멤버와 자신의 멤버와 이름이 같을때, super로 구분 

조상 클래스의 멤버와 자손클래스의 멤버가 중복 정의되어 서로 구별할때는 super 사용하기

- super는 static메서드에서는 사용할 수 없고 인스턴스메서드에서만 사용할 수 있다.

 

예시 - 조상의 메서드 호출

class Point {
    int x;
    int y;
    
    String getLocation() {
        return "x :" + x + "y : " + y;
    }
}

class Point3D extends Point {
    int z;
    String getLocation() {
        // 오버라이딩
        // return "x :" + x + "y : " + y + "z : " + z;
        return super.getLocation() + "z : " + z;
    }
}

 

첫 줄에 반드시 자신의 다른 생성자 또는 조상의 생성자를 호출하는 예시

class PointTest {
    public static void main(String[] args) {
        Point3D p3 = new Point3D(1,2,3);
    }
}

class Point {
    int x,y;
    
    Point(int x, int y) {
        this.x = x;
        this.y = y;
    }
    Stirng getLocation() {
        return "x :" + x + "y :" + y;
    }
}

class Point3D extends Point {
    int z;
    
    /*
    Point3D(int x, int y, int z) {
    	super();
        this.x = x;
        this.y = y;
        this.z = z;
    }
    */
    // or
    Point3D(int x, int y, int z) {
    	super(x,y); // 조상 클래스의 생성자를 호출한다
        this.z = z;
    }
    
    Stirng getLocation() {
        return "x :" + x + "y :" + y;
    }
}

 

 

3. 패키지( package )

패키지란, 클래스의 묶음 ( 클래스 or 인터페이스 포함 )
예시, java.lang 패키지  -> java.lang.String은  java.lang에 속한 String클래스 라는 의미

 

- 클래스가 물리적으로 하나의 클래스파일( . class )인 것과 같이 패키즈는 물리적으로 하나의 디렉토리다.

 

- 하나의 소스파일에는 첫 번째 문장으로 단 한번의 패키지 선언만을 허용한다
- 모든 클래스는 반드시 하나의 패키지에 속해야 한다
- 패키지는 점(.)을 구분자로 하여 계층구조 구성할 수 있다.
- 패키지는 물리적으로 클래스 파일을 포함하는 하나의 디렉토리이다.

 

패키지의 선언

package 패키지명;

패키지는 소문자로 하며, 첫번째 문장에 하나의 소스파일에는 단 한번만 선언할 수 있다.

 

import문

import문은 클래스의 패키지를 미리 명시해주면서 클래스이름에서 패키지 명을 생략할 수 있다.

 

import 선언

일반적인 소스파일( *.java )의 구성의 순서

1. package문
2. import 문
3. 클래스 선언

 

선언하는 방법

import 패키지명.클래스명;
or
import 패키지.*;

 

import 패키지.* 이런식으로 호출하는것이 일일이 지정하는것보다 편리하다.

 

전후 비교 코드 예시

import java.text.SimpleDateFormat;
import java.util.Date;

class ImportTest {
    public static void main(String[] args) {
        Date today = new Date();
        
        SimpleDateFormat data = new SimpleDateFormat("yyyy/MM/dd");
        // java.text.SimpleDateFormat data = new java.text.SimpleDateFormat("yyyy/MM/dd");
        SimpleDateFormat data = new SimpleDateFormat("hh:mm:ss a");
        
        System.out.println(date.format(today));
        System.out.println(time.format(today));
    }
}

- java.lang 패키지는 빈번히 사용되서 import문으로 지정하지 않아도 된다.

 

static import 선언

import static java.lang.Integer.* // Integer클래스의 모든 static메서드
import static java.lang.Math.random;   // Math.random()만. 괄호 안붙임
import static java.lang.System.out;  // out만으로 참조가능

-> System.out.println(Math.random()) 
-> out.println(random());

 

 

4. 제어자

제어자는 클래스, 변수 또는 메서드의 선언부에 사용되어 부가적인 의미를 더한다

 

static

static은 클래스의 또는 공통적인 의미를 갖는다. ( 멤버변수, 메서드, 초기화 블록에 사용가능 )

하나의 변수를 모든 인스턴스가 공유하기에 클래스변수(static 멤버변수)는 인스턴스에 관계없이 같은 값을 갖는다.

class Static {
    static int w = 200; // 클래스 변수
    
    static { } // 클래스 초기화 블럭
    
    static int max(int a, intb) { } // 클래스 메서드
}

 

final

final 마지막의 또는 변경될 수 없는 을 의미한다. ( 클래스, 메서드, 멤버변수, 지역변수 )
변수에 사용하면, 변경할 수 없는 상수가 되고,
메서드에 사용되면 오버라이딩을 할 수 없게 되고,
클래스에 사용되면 자신을 확장하는 자손클래스를 정의하지 못하게 된다
final class Final { // 조상이 될 수 없는 클래스 
    final int MAX_SIZE = 200; // 상수 ( 멤버변수 )
    
    final void getMaxSize() { // 오버라이딩을 할 수없는 메서드
        final int LV = MAX_SIZE; // 상수( 지역변수 )
        return MAX_SIZE;
    }
}

 

final이 붙은 벼수는 일반적으로 선언과 동시에 초기화 하지만, 인스턴스벼수는 생성자에게 초기화 되도록 할 수 있다.

class Card {
    final int NUMBRE;
    final String KIND;
    static int w = 100;
    static int h = 100;
    
    Card(String kind, int num) {
    // 이 부분이 생성자에 의해서 final이 붙은 변수를 초기화 하는 것이다.
        KIND = kind;
        NUMBER = num;
    }
    
    Card() {
        this KIND + " " + NUMBER;
    }
    
    public String toString() {
    	return KIND + " " + NUMBER;
    }
}

class FinalCard {
    public static void main(String[] args) {
        Card c = new Card("HEART", 10);
        c.NUMBER = 5; // 에러, cannot assign a value to final variable NUMBER
        System.out.println(c.KIND); // 이렇게 해야함
    }
}

 

abstract

abstract은 미완성의 의미를 갖는다 . 메서드의 선언부만 작성하고 수행내용은 구현하지 않은 추상 메서드를 선언하는데 사용 ( 클래스, 메서드 )
abstract class AbstractTest {
    abstract void move();
}

 

접근 제어자

접근제어자를 사용하는 이유

- 외부로부터 데이터 보호
- 외부에는 불필요한, 내부적으로만 사용되는 부분을 감추기 위해

만일 메서드 하나를 변경해야 할때, 접근제어자를 적절히 선택해서 접근 범위를 최소화하도록 노력하자 ( public이면 전부를 봐야함 )

제어자 같은 클래스 같은 패키지 자손클래스 전체
public  O O O O
protected O O O X
default O O X X
private O X X X

 

생성자의 접근 제어자

보통 생정자의 접근 제어자는 클래스의 접근제어자와 같지만, 다르게 지정할 수 있다.

 

private이니 외부에서 접근할 수 없어서, 인스턴스를 만들 수 없다.

하지만, 클래스 내부에서는 인스턴스 생성 가능

-> 클래스 내부 메서드가 public 이면서 static 이여야 외부에서 이 클래스 인스턴스를 사용할 수 있다.

 

private인 클래스는 다른 클래스의 조상이 될 수 없다.

이유는, 생성자의 접근 클래스가 private이므로 자손클래스에서 호출하는 것이 불가능하기 때문이다

따라서 private 클래스 앞에 final을 더 추가하여 상속할 수 없는 클래스라는것을 알리기

 

제어자의 조합

1. 메서드에 static과 abstract를 함께 사용할 수 없다.
2. 클래스에 abstract와 final을 동시에 사용할 수 없다.
3. abstract메서드의 접근 제어자가 priate일 수 없다.
4. 매서드에 private과 final을 같이 사용할 필요가 없다.