상속과 오버라이딩

상속(extends, inheritance)

상속이란 자식 클래스(서브 클래스)에서 부모클래스(슈퍼 클래스)의 필드와 메소드를 참조하여 쓸 수 있게 해준다. 기본적으로 하나의 클래스에 하나의 슈퍼 클래스 밖에 상속 할 수 없다.

Class Vehicle {

    public int speed;
    
    public int getSpeed() {
        return speed;
    }
    public void setSpeed(int speed) {
        this.speed = speed;
    }
    
}

class Car extends Vehicle {

}

public class Test {

    public static void main(String[] args) {

        Car A = new Car();
        A.getSpeed();
    }
}

Car클래스에는 getSpeed메소드가 없지만 상속된 Vehicle에 getspeed메소드가 있기 때문에 사용 가능하다.

상속의 특징은 다음과 같다.
상속(부모->자식)

  1. 부모꺼는 내꺼다.
  2. private 선언한 것은 상속이 안된다.
  3. protected 선언한것은 상속이 된다.
  4. 자식꺼는 자식꺼다.
  5. 부모도가지고있고 나도 같은 객체를 가지고 있으면 내꺼쓴다.

오버 라이딩(Override)

슈퍼 클래스에 존재하는 필드나 메소드를 서브 클래스에서 재정의하여 사용할 수 있다.

오버 라이딩을 통한 슈퍼 클래스를 생성할 때는 super 키워드를 사용한다.

오버라이딩을 위해 필드나 메소드를 정의할 때 바로 위 상단에 @Override 문구를 넣어주면 오타를 방지할 수 있다. (슈퍼 클래스에 존재하지 않은 메소드를 오버라이드 하려고하면 컴파일 과정에서 에러 발생)

Comment and share

Call by value

값에 의한 호출.

Class CallByValue{

public static void swap(int x, int y) {

int temp = x;

x = y;
y = temp;

}

public static void main(String[] args) {

int a = 10;
int b = 20;

System.out.println("swap() 호출 전 : a = " + a + ", b = " + b);

swap(a, b);

System.out.println("swap() 호출 후 : a = " + a + ", b = " + b);
     }
}

결과값

swap() 호출 전 : a = 10, b = 20

swap() 호출 후 : a = 10, b = 20

할당 된 메모리 변수에는 각각 10과 20의 값이 저장된다. 이후,
swap() 메서드 호출 시에 사용한 인자 a와 b는 할당 된 메모리
주소가 아닌 메모리에 담겨져 있던 값만이 복사되어 swap() 메서드
내부의 매개변수 x와 y의 메모리 주소에 담겨지게 된다.
당연하게도 swap() 메서드가 수행하는 동안 사용되는 변수들은 main
()에 존재하는 a와 b가 아닌 swap() 내부에 새로 생성 된 x와
y이기 때문에 메서드 수행 후에도 결과 값에 변화가 없습니다.

Call by value는 메서드 호출 시에 사용되는 인자의 메모리에 저장되어 있는 값(value)을 복사하여 보낸다.

Call by reference

참조에 의한 호출.

Class CallByReference{
int value;

CallByReference(int value) {
this.value = value;
}

public static void swap(CallByReference x, CallByReference y) {

int temp = x.value;

x.value = y.value;
y.value = temp;

}
public static void main(String[] args) {

CallByReference a = new CallByReference(10);
CallByReference b = new CallByReference(20);

System.out.println("swap() 호출 전 : a = " + a.value + ", b = " + b.value);

swap(a, b);

System.out.println("swap() 호출 전 : a = " + a.value + ", b = " + b.value);
    }
}

결과값

swap() 호출 전 : a = 10, b = 20

swap() 호출 후 : a = 20, b = 10

main()에서 선언 된 CallByReference 타입의 변수 a와 b는 각각 객체를 생성하여 10과 20의 주소 값을 저장하게 된다. 이후, swap() 메서드 호출 시에 인자 a와 b는 메모리에 저장 된 주소 값을 복사하여 매개변수 x와 y의 메모리에 저장합니다. 결국 swap() 메서드는 10과 20이 저장 된 주소를 참조하여 연산하기 때문에, 연산 결과에 따라 원본 데이터가 변하게 된다.

Call by reference는 메서드 호출 시 사용되는 인자 값의 메모리에 저장되어있는 주소(Address)를 복사하여 보낸다.

Comment and share

오버로딩과 오버라이딩

오버로딩(Overloading) : 같은 이름의 메소드를 여러 개 가지면서 매개변수의 유형과 개수가 다르도록하는 기술
오버라이딩(Overriding) : 상위 클래스가 가지고 있는 메소드를 하위 클래스가 재정의 해서 강요한다.

오버로딩

오버로딩은 메소드 오버로딩과 생성자 오버로딩이 있다. 하지만 둘다 같은 개념이다. 같은 이름의 함수를 여러 개 정의하고, 매개변수의 유형과 개수를 다르게 하여 다양한 유형의 호출에 응답하게 한다.
오버로딩

오버라이딩

상위 클래스가 가지고 있는 멤버변수가 하위 클래스로 상속되는 것처럼 상위 클래스가 가지고 있는 메소드도 하위 클래스로 상속되어 하위 클래스에서 사용할 수 있다. 하위 클래스에서 메소드를 재정의해서 사용할 수 있다.

상속 관계에 있는 클래스 간에 같은 이름의 메소드를 정의하는 기술을 오버라이딩 이라고 한다.
오버라이딩
@Overriding을 쓰면 Overriding의 특성을 가져오며 컴파일 과정에서 에러를 표시해줄 수 있다.
오버라이딩은 자신에게 메소드가 있으면 자신의 메소드를 사용하고, 자신의 메소드가 없으면 분모의 메소드를 사용한다.
자신의 메소드를 사용하더라도 분모의 메모리에 저장한다.

오버로딩과 오버라이딩 생성조건

오버로딩 오버라이딩
메소드 이름 동일 동일
매개변수, 타입 다름 동일
리턴 타입 상관없음 동일

Comment and share

매개변수

매개 : 둘 사이에서 양편의 관계를 맺어 줌
변수 : 어떤 상황의 가변적 요인, 어떤 관계나 범위 안에서 여러 가지 값으로 변할 수 있는 수
즉, 매개변수란 둘 사이에서 양편의 관계를 맺어주면서, 어떤 관계나 범위 안에서 여러가지 값으로 변할 수 있는 가변적 요소이다.

인자

어떤 사물의 원인이 되는 낱낱의 요소나 물질.
인수는 함수가 호출 될 때 함수에 전달되는 값이다. 프로그램 실행 중에 함수가 호풀 될 때마다 함수와 함께 일부 값이 전달된다. 이러한 값을 인수라고 한다. 함수와 함께 전달 될 때 인수는 함수 정의 중에 사용 된 변수로 대체되고 함수는 이 값으로 실행된다.

매개변수와 인수

매개변수는 메소드, 또는 생성자를 선언할 때, 괄호 안에 적으며, 어떤 입력 값이 들어올지 값의 형태를 정의해준다.
인자는 메소드, 또는 생성자를 호출할 때, 괄호 안에 적으며, 미리 선언해 두었던 매개변수에 값을 대입해 준다.

매개변수는 실제로 값이 존재하지는 않고, 어떤 형태로 입력값이 들어올 것인지를 정의해 준다.
인자는 매개변수에 대입되는 실제로 메모리에 할당되어 있는 변수이다.
즉, 매개변수와 인자의 차이는 실제로 메모리에 할당되어 있느냐, 없느냐의 차이이다.

숫자 두 개를 입력받아, 두 숫자의 합을 리턴하는 메소드를 만든다고 생각해보자.

public int sum(int a, int b) {

return a+b;

}

이렇게 메소드를 선언할때 int형 변수 두 개를 받겠다고 괄호 안에 적은 int a와 int b가 매개변수이다.
그럼, 이 메소드를 호출해서 숫자 3과 숫자 5를 더해보자.
sum(3, 5)
이렇게 메소드를 호출하여, 매개변수에 각각의 값을 대입했다.
이 3과 5가 바로 인자이다.

Comment and share

생성자와 오버로딩 그리고 this

생성자

객체가 생성될 ‘때마다’ 호출되는 것

클래스이름() {}

클래스에 아무런 생성자가 없다면 컴파일러가 기본 생성자는 넣어준다.
기본 생성자는 매개 변수도 없고, 구현부에서 아무런 일도 하지 않는다.

클래스이름(자료형 매개변수) {
    this.멤버변수 = 매개변수
}

명시적 생성자가 하는 일은 Field Initialization(필드 초기화). 명시적 생성자가 있으면 컴파일러에서 디폴트 생성자를 제공하지 않는다. 디폴트 생성자를 사용하려면 개발자가 디폴트 생성자를 추가로 직접 구현해야 한다.

오버로딩

매개변수가 다른 생성자를 두 개 이상 구현하거나, 매개변수의 개수 또는 타입이 다른 같은 이름을 가진 메소드를 정의하는 것.

this

자신의 메모리를 가리킨다.
생성자에서 다른 생성자를 호출할 수 있다.
인스턴스 자신의 주소를 반환한다.
this를 사용하면, 메서드의 인수나 변수에 필드와 같은 이름을 붙여도 그것들을 구분하여 사용할 수 있다.
this는 주로 생성자와 메소드의 매개변수 이름이 필드와 동일한 경우, 인스턴스 멤버인 필드임을 명시하고자 할때 사용된다.

Comment and share

생성자

우리는 프로그램을 만들 때 변수를 선언하고 반드시 초기화를 해주어야 한다고 알고있다. 만약 초기화를 하지 않으면 해당 변수에는 쓰레기값이 들어가게 되고 심한 경우에는 초기화 오류가 발생하기 때문이다.

이와같이 변수의 초기화를 진행하는 프로세스를 클래스에 대입한 개념으로, 객체를 호출할 때 클래스는 생성자를 통해 객체를 초기화 하게 되는데 구현 방법에 따라 기본생성자, 묵시적 생성자, 명시적 생성자로 나뉘게 된다.

여기서 객체의 초기화란 클래스가 객체를 생성/호출하였을 때, 객체의 필드를 초기화 하거나 메소드를 호출해서 객체가 사용할 준비를 하는 것으로 일련의 준비단계라고 생각하면 쉽다.

생성자 : 클래스로부터 객체를 생성할 때 수행되어 객체의 초기화를 담당하는 것.
  1. 생성자는 리턴타입을 갖지 않는다.
  2. 생성자는 클래스 이름과 동일해야 한다.
  3. 모든 클래스는 생성자가 반드시 존재하며, 하나 이상을 가질 수 있다.
  4. 클래스 내부에 생성자를 선언하지 않으면 자동으로 기본 생성자가 선언된다.
  5. 클래스 내부에 파라미터가 없는 생성자를 선언하는 경우 이를 묵시적 생성자라 한다.
  6. 클래스 내부에 파라미터가 있는 생성자를 선언하는 경우 이를 명시적 생성자라 한다.

기본 생성자

public class Sub1 {
// public Sub1() {}
    public int iNum1 = 0; 

// 생성자가 명시되어있지 않으므로 기본 생성자가 선언된다.
}

기본 생성자는 클래스 내부에 어떠한 생성자도 없을 시 객체 생성 과정에서 컴파일러가 자동으로 추가하는 생성자다.

왜 굳이 의미도 없는 기본 생성자를 선언해주느냐 함은 클래스에서 생성자가 없으면 객체에 접근 자체를 하지 못한다.

메모리 구조상 new 연산자가 생성자를 정상적으로 실행시키면 힙(heap)영역에 객체가 생성되고, 생성된 객체의 주소가 클래스 타입 변수에 리턴되어 객체에 접근할 수 있게 되는데 생성자가 실행되지 않으면 힙 영역에 객체가 생성되지 않으니 객체의 주소도 리턴받을 수 없다.

따라서 객체를 사용하기 위해서는 반드시 생성자가 필요하고 생성자를 명시하지 않았을 때는 자동으로 기본생성자가 선언된다.

기본 생성자 + 메소드

public class Sub {
// public Sub(){} 
    public int Sum(int iNum1, int iNum2){
    // 생성자 없이 메소드만 구현되어 있으므로 기본 생성자로 선언된다.
        int sum = iNum1 + iNum2;
        return sum;
}
}

위와같이 생성자 없이 메소드만 구현되어있는 경우에도 컴파일러가 기본 생성자를 추가하므로 정상적인 프로그램 구동이 가능하다.

묵시적 생성자

public class Sub2 {
    public Sub2(){
    // 파라미터가 없는 묵시적 생성자
        System.out.println("묵시적 생성자");
    }
}

묵시적 생성자는 파라미터를 갖지않는 생성자다. 기본 생성자와의 차이점은 보다 원하는 방식으로 초기화를 할 수 있다는 점이다.
하지만 파라미터를 받지 않으므로 초기화의 개념이 명시적 생성자 보다 상대적으로 떨어진다.

명시적 생성자

public class Sub3 {
    public Sub3(String s){
    // 파라미터 값을 받는다.
        System.out.println(s);
    }
}

명시적 생성자는 객체 생성시 초기화를 위한 파라미터를 받으며 객체가 실행될 때 받은 값으로 초기화가 진행된다.

Sub3 sub3 = new Sub3("명시적 생성자"); // 명시적 생성자

//객체를 생성할 때 String 변수에 파라미터를 줌으로써 String s는 "명시적 생성자"로 초기화된다.

명시적 생성자를 만드는 이유

생성자 내부에는 어떠한 작업을 하는 코드는 쓸 수 있다.
즉, 인스턴스가 만들어지는 그 순간에 생성자 내부에 작성한 어떠한 작업을 해줄 수 있는 것이다.
그래서 명시적으로 생성자를 만드는 이유는,
인스턴스 변수를 초기화 할때, 0,false,null이 아닌 값으로 설정하기 위해서 이거나
매개변수로 값을 넘겨받아 객체를 다양하게 초기화 하기 위해서 이거나
인스턴스가 생성될 때 수행되어야하는 작업이 있기 때문이다.
즉, 필드에 초기값을 저장 하거나 메소드를 호출하여 객체 사용 전에 필요한 준비를 한다고 보면 된다.

Comment and share

변수

데이터 타입에 따른 분류

기본형 변수와 참조변수로 나뉜다.
기본형변수는 자료의 실제값을 저장한다.

종류
논리형 boolean
문자형 char
정수형 byte, short, int, long
실수형 float, double
참조변수는 값이 저장되어 있는 주소값을 값으로 갖는다.
기본형 변수를 제외한 나머지 타입이며, 대표적으로 String이 있다.

선언 위치에 따른 분류

크게 멤버변수와 지역변수로 나뉘며
멤버변수는 클래스변수와 인스턴스변수를 통틀어서 부르는 용어이다.
클래스 영역에서 선언된 변수를 멤버변수, 그중 앞에 static이 붙은 변수를 클래스 변수라고 한다.

멤버변수

인스턴스 변수

클래스 영역에 선언되고 인스턴스 생성시 만들어 진다. 각각의 인스턴스마다 다른 값을 가질 수 있다. 그러므로 인스턴스 생성이 필수이며 각 인스턴스마다 다른 값을 가져야 할 때 사용된다.

클래스 변수

인스턴스변수 앞에 static을 붙이기만 하면 된다. 인스턴스변수가 각각의 인스턴스마다 다른값을 가질 수 있는 것과는 달리 값을 공유하는 변수이다.
클래스이름.변수이름 이렇게 인스턴스 생성 필요없이 바로 쓸 수 있다.

지역변수

메소드 내에 선언되며 메소드 호출시 생성되고 메소드가 종료되면 사라진다.

매개변수

흔히 파라미터라고 불린다. 메소드에서 입력값을 받을 때가 있는데, 그때 사용되는 변수를 매개변수라고 한다.
매개변수도 매소드 내에 선언된 것으로 간주되므로 지역변수이다.

public class Variable {
    int a;
    static String b;
    void m(int c){
        int d=c;
    }
    public static void main(String args[]) {
        int e=0;
        Variable v = new Variable();
        v.m(e);
    }
}

2번줄 a는 기본형변수이면서 멤버변수의 인스턴스변수
3번줄 b는 참조변수이면서 멤버변수의 클래스변수
4번줄 c는 기본형변수이면서 매개변수 이면서 지역변수
5번줄 d는 기본형변수이면서 지역변수
7번줄 args는 참조변수이면서 매개변수 이면서 지역변수
8번줄 e는 기본형변수이면서 지역변수
9번줄 v는 참조변수이면서 지역변수
10번줄 e는 인자값으로 값0이 매개변수에 복사되어 넣어진다.

Comment and share

인스턴스와 객체

객체

객체는 사람, 통장계좌, 자동차, 주문, 학생, 버튼 등 개별적으로 구분할 수 있는 실체를 의미한다.
객체는 자신의 고유의 이름과 상태와 행동을 갖는다.
객체의 상태를 표한혈 때 멤버변수라고 표현할 수 있으며, 객체의 행동을 메소드 혹은 멤버함수라고 표현한다.
클래스는 객체의 상태와 행동이 어떻게 만들지를 결정한 청사진이다.
즉, 클래스에서는 버튼이라는 객체가 클릭되어지면 다음화면으로 이동하는 행동을 정의하고 지금 버튼의 이름을 지정하는 상태 등이 동작할 수 있게끔 정의가 되어져 있는 설계도이다.

인스턴스

클래스가 메모리에 생성된 상태를 말한다. 클래스로부터 생성된 객체라고도 한다.
힙 메모리에 멤버 변수의 크기에 따라 메모리가 생성된다.
클래스를 기반으로 New 예약어를 통해 인스턴스를 생성한다.

객체와 인스턴스의 차이

객체는 소프트웨어 세계에 구현할 대상이고, 이를 구현하기 위한 청사진이 클래스 이며, 이청사진에 따라 생성된 실체가 인스턴스이다.

즉 개념적으로 인스턴스는 객체에 포함된다고 할 수 있고 인스턴스라고 표현을 하는 것이 더 정확하지만 둘을 같다고 봐도 틀린말은 아니다.

Comment and share

Static

staitc은 고정된이란 의미를 가지고 있다. static이라는 키워드를 사용하여 static변수와 static메소드를 만들 수 있는데 다른말로 정적필드와 정적 메소드라고도 하며 이 둘을 합쳐 정적 멤버라고 한다.(클래스 멤버라고도 한다.) 정적 필드와 정적 메소드는 객체에 소속된 멤버가 아니라 클래스에 고정된 멈버이다. 그렇기에 클래스 로더가 클래스를 로딩해서 메소드메모리 영역에 적재할 때 클래스 별로 관리된다. 따라서 클래스의 로딩이 끝나는 즉시 바로 사용할 수 있다.

정적 멤버 생성

static키워드를 통해 생성된 정적멤버들은 Heap영역이 아닌 Static영역에 할당된다. static영역에 할당된 메모리는 객체가 공유하여 하나의 멤버를 어디서든지 참조할 수 있는 장점을 가지지만 ㅁ의 관리 영역 밖에 존재하기에 static영역에 있는 멤버들은 프로그램의 종료시까지 메모리가 할당된 채로 존재하게 된다. 그렇기에 static을 너무 남발하게 되면 만들고자 하는 시스템 성능에 악영향을 줄 수 있다.

정적 멤버 선언

필드나 메소드를 생성 시 인스턴스로 생성할 것인지 정적으로 생성할 것인지에 대한 판단 기준은 공용으로 사용하느냐 아니냐로 내리면 된다. 그냥 생성한다면 자동으로 인스턴스로 생성되며 정적으로 생성하려면 메소드 선언시 static이라는 키워드를 추가적으로 붙이면 된다.

Comment and share

Hyeon Soo Ahn

author.bio


author.job