본문 바로가기

개발일기/Java

[Java] Java의 정석 (5) 객체지향개념 2

 

생성자

 

생성자란?

 

- 인스턴스가 생성될 때마다 호출되는 '인스턴스 초기화 메소드'

- 인스턴스 변수의 초기화 및 인스턴스 생성시 수행할 작업에 사용한다.

- 모든 클래스에는 반드시 하나 이상의 생성자가 있어야 한다.

 

 

생성자의 조건

클래스이름 (타입 변수명, 타입 변수명, ...)
{
	// 인스턴스 생성시 수행될 코드
}

1. 생성자의 이름은 클래스의 이름과 같아야 한다.

 

2. 생성자는 리턴값이 없다.

 

3. 한 클래스 내에 매개변수가 다른 여러 개의 생성자가 존재할 수 있다. (오버로딩)

 

 

기본 생성자

- 매개변수가 없는 생성자로, 프로그래머가 생성자를 정의하지 않으면 컴파일러가 내용이 없는 생성자를 추가해준다.

- 그러나, 생성자가 하나라도 있으면 컴파일러는 기본 생성자를 추가하지 않는다.

 

 

this

Car(String color, String gearType, int door){
    this.color = color;
    this.gearType = gearType;
    this.door = door;
}

this : 인스턴스 자신을 가리키는 참조변수로, 인스턴스의 주소가 저장되어있다.

 

Car(){
	this("white", "auto", 4); // 같은 클래스 내의 다른 생성자 호출
}

Car(String c, string g, int d){
	color = c;
	gearType = g;
	door = d;
}

this() : 현재 클래스의 생성자.

 

* this()를 이용해 다른 생성자를 호출할 때는, 생성자의 첫 문장에서 단 한 번만 실행되어야 한다.

 

 

생성자를 이용한 인스턴스 복사

Car (Car c){
    this(c.color, c.gearType, c.door);
}

 

 

 

 

변수의 초기화

 

 

변수의 초기화란?

- 변수를 선언하고 처음으로 값을 저장하는 것.

- 클래스의 멤버변수와 배열은 각 타입의 기본값으로 자동초기화된다.

- 지역변수는 자동초기화가 되지 않으므로 반드시 초기화해주어야 한다.

 

 

초기화 블럭

- 명시적 초기만으로는 처리할 수 없는 복잡한 초기화에 사용.

class InitBlock {
    static { /* 클래스 초기화 블럭 */ }
    
    { /* 인스턴스 초기화 블럭 */ }
    
    // ...
}

1. 클래스 초기화 블럭

- 클래스 변수의 초기화에 사용되며, 클래스가 로딩될 때 실행된다.

 

2. 인스턴스 초기화 블럭 (거의 사용되지 않음)

- 생성자에서 공통적으로 수행되는 작업에 사용되며, 인스턴스가 생성될 때마다 생성자보다 먼저 실행된다.

 

* 초기화블럭 사용 예)

class StaticInitExample {
    static int[] arr = new int[10]; // 명시적 초기화
    
    static { 
    	for(int i=0; i<arr.length; i++){
            arr[i] = (int)(Math.random()*10) +1;
        }
    } // 클래스초기화 블럭
	
    // ...
}

 

 

 

멤버변수의 초기화 시기 및 순서

 

1. 클래스변수 초기화 시점

- 클래스가 처음 로딩될 때 단 한 번 초기화된다.

 

2. 인스턴스변수 초기화 시점

- 인스턴스가 생성될 때 마다 초기화된다.

 

3. 종합

- 기본적으로는 클래스 초기화 -> 인스턴스 초기화 순으로 진행된다.

 

* 초기화 순서

(클래스 변수) 자동초기화

-> 명시적 초기화

-> 클래스 초기화 블럭

-> (인스턴스 변수) 자동초기화

-> 명시적 초기화

-> 인스턴스 초기화 블럭

-> 생성자

 

 

 

 

상속

 

 

상속이란?

 

class 자손클래스 extends 조상클래스 {
	// ...
}

- 기존의 클래스를 재사용해, 새로운 클래스를 작성하는 것.

- 두 클래스를 조상과 자손으로 관계를 맺어주는 것.

- 자손은 조상의 모든 멤버를 상속받는다. (단, 생성자와 초기화 블럭은 제외한다.)

 

* 상속 예시

class Point{
    int x;
    int y;
}

class Point3D extends Point{ // Point의 멤버인 x와 y를 상속받음
    int z;
}

 

 

클래스 간의 관계

1. 상속관계

- 공통부분은 조상에서 관리하고, 개별부분은 자손에서 관리한다.

- 조상의 변경은 자손에 영향을 미치지만, 자손의 변경은 조상에 영향을 미치지 않는다.

 

2. 포함관계

- 한 클래스의 멤버변수로 다른 클래스를 선언하는 것.

- 작은 단위의 클래스를 먼저 만들고, 이 클래스들을 조합해 하나의 커다란 클래스를 만든다.

 

* 클래스 간의 관계 설정 (상속 vs 포함)

- is-a 로 문장을 만들었을 때 자연스러우면 상속으로,

- has-a로 문장을 만들었을 때 자연스러우면 포함으로 설정해 설계해준다.

class Circle extends Point{
    int r;
}

class Circle {
    Point c = new Point();
    int r;
}

// 원은 점을 '가지고' 있으므로, 후자처럼 포함관계로 설계하는 것이 적절하다.

 

* Java는 단일상속만 허용하므로, 비중이 높은 클래스 하나만 상속관계로 두고, 나머지는 포함관계로 설정해주는 것이 좋다.

 

* Object 클래스는 모든 클래스의 최고 조상으로, 조상이 없는 클래스는 자동적으로 Object 클래스를 상속받게 된다.

 

 

오버라이딩

- 조상클래스로부터 상속받은 메소드의 내용을 상속받는 클래스에 맞게 변경하는 것.

 

* 오버라이딩의 조건

1. 선언부가 같아야 한다. (이름, 매개변수, 리턴타입)

2. 접근제어자를 조상 클래스의 메소드보다 좁은 범위로 변경할 수 없다.

3. 조상 클래스의 메소드보다 많은 수의 예외를 선언할 수 없고, 조상 클래스의 메소드에서 선언된 예외만 선언할 수 있다.

 

 

참조변수 super

- this : 인스턴스 자신을 가리키는 참조변수

- super : 조상 클래스의 멤버를 가리킬 때 사용하는 참조변수.

class Parent {
    int x = 10;
}

class Child extends Parent {
    int x = 20;
    System.out.println(x); // 20
    System.out.println(this.x); // 20
    System.out.println(super.x); // 10 : 조상의 멤버변수
}

 

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

class Point3D extends Point {
    int z;
    
    String getLocation(){
        return super.getLocation() + ", z : " + z; // 조상의 메소드 호출

 

 

 

super() 를 사용한 조상 클래스의 생성자 호출

 

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

- 이 때, 조상의 멤버들도 초기화되어야 하기 때문에 자손의 생성자의 첫 문장에서 조상의 생성자를 호출해야 한다.

- 프로그래머가 조상의 생성자를 선언하지 않으면, 컴파일러가 자동적으로 'super();' 를 생성자 첫 줄에 넣어준다.

 

 

 

패키지

 

패키지란?

 

- 서로 관련된 클래스와 인터페이스의 묶음.

- 클래스는 물리적으로 클래스 파일(*.class)이며, 패키지는 물리적으로 폴더이다.

- 패키지는 서브패키지를 가질 수 있고, '.'로 구분된다.

- 클래스의 풀네임은 패키지명이 포함된 것이다. ex) java.lang.String

- rt.jar는 Java API의 기본 클래스들을 압축한 파일이며, jre\lib에 위치한다.

 

 

패키지의 선언

- 패키지는 소스파일의 첫 번째 문장으로 단 한 번 선언한다.

- 하나의 소스파일에 둘 이상의 클래스가 포함된 경우, 모두 같은 패키지에 속하게 된다.

- 하나의 소스파일에는 단 하나의 public 클래스만 허용한다.

- 소스파일의 이름은 public 클래스의 이름과 일치해야 한다.

- 모든 클래스는 하나의 패키지에 속하며, 패키지가 선언되지 않은 클래스는 자동적으로 이름없는 패키지에 속하게 된다.

 

 

클래스패스 설정

- 클래스패스는 클래스파일(*.class)를 찾는 경로로, 구분자는 ';'

- 클래스패스에는 패키지가 포함된 폴더나 jar파일을 나열한다.

- 클래스패스가 없으면 자동적으로 현재 폴더가 포함되나, 클래스패스를 지저어할 때는 현재 폴더(.)도 함께 추가해주어야 한다.

 

 

import문

- 사용할 클래스가 속한 패키지를 지정하는 데 사용한다.

- import문을 사용하면 클래스를 사용할 때 패키지명을 생략할 수 있다.

- 예외적으로, java.lang 패키지의 클래스는 import하지 않고도 사용할 수 있다.

- import문은 패키지문과 클래스 선언 사이에 선언하며, 여러 번 사용할 수 있다.

- import문은 지정된 패키지의 클래스를 가져오는 것으로, 서브패키지의 클래스는 가져오지 않는다.