implements와 extends의 차이

  • extends는 일반 클래스와 abstract 클래스 상속에 사용되고, implement는 interface 상속에 사용된다.
  • class가 class를 상속받을 땐 extends를 사용하고, interface가 interface를 상속 받을 땐 extends를 사용한다.
  • class가 interface를 사용할 땐 implements를 써야하고
    interface가 class를 사용할 땐 implements를 쓸수 없다.
  • extends는 클래스 한 개만 상속 받을 수 있다.
  • extends 자신 클래스는 부모 클래스의 기능을 사용한다.
  • implements는 여러개 사용 가능하다.
  • implements는 설계 목적으로 구현 가능하다.
  • implements한 클래스는 implements의 내용을 다 사용해야 한다.

Comment and share

Interface

하나의 시스템을 구성하는 2개의 구성요소(하드웨어, 소프트웨어) 또는 2개의 시스템이 상호작용할 수 있도록 접속되는 경계(boundary), 이 경계에서 상호 접속하기 위한 하드웨어, 소프트웨어, 조건, 규약 등을 포괄적으로 가리키는 말

자바에서 인터페이스는 여러가지 역할로 사용한다.

  • 개발자 사이의 코드 규약을 정한다.
  • 여러 구현체에서 공통적인 부분을 추상화한다.(다형성)

자바 인터페이스는 기본적으로 추상 메소드의 모음이다. 추상메소드는 아래와 같이 구현부가 없는 메소드를 말한다.

public interface Walkable {
void walk();
}

구현부가 없으므로 인터페이스를 만든다면 반드시 구현하는 클래스를 만들어야 하며, 인터페이스를 구현하기로 한 클래스는 반드시 인터페이스에 명시되어 있는 추상메소드들을 모두 구현해야 한다. 만약 이를 구현하지 않으면 컴파일 에러가 발생한다.

public class Dog implements Walkable {
// ...
@Override
public void walk() {
    // ...
    }
}

인터페이스는 구현과 상속을 모두 할 수 있다.

  • 인터페이스를 사용하는 구체 클래스는 해당인터페이스를 구현해야한다.
  • 인터페이스 사이에는 상속을 할 수 있다.

인터페이스를 사용하면 다중 상속이 가능하다. 인터페이스 사이에서도, 구체 클래스에서도 여러 인터페이스를 구현 및 상속할 수 있다.

public interface Walkable {
void walk();
}

public interface Flyable {
void fly();
}

public interface Moveable extends Walkable, Flyable {
}
    public class Bat implements Moveable {
@Override
public void walk() {
    // ...
}

@Override
public void fly() {
    // ...
}
}

인터페이스는 클래스와 달리 기본 접근제어자는 public이다.
인터페이스에 필드 변수를 선언하면 public static final로 선언해야 하며, 이 것도 기본 설정되어 있다.

인터페이스를 사용하는 이유

인터페이스를 사용하는 주된 이유는 다형성을 위해서이다. 다형성은 상속받은 클래스 또는 인터페이스의 메소드를 재정의하여 서로 다른 행동을 만들 수 있다. 상속을 통해 상위 클래스의 타입으로 통일한 후 하위 클래스들을 하나의 타입으로 관리할 수 있다. 이를 사용해서 변경에 유연한 코드를 만들 수 있다.

Comment and share

super

super는 자식 클래스가 부모 클래스로 부터 상속받은 멤버를 참조할 때 사용하는 참조 변수이다. 클래스 내의 멤버변수와 지역변수의 이름이 같은 경우 구분을 위해 this를 사용하듯 부모 클래스와 자식 클래스의 멤버의 이름이 같을 경우 super를 사용한다. this와 super는 인스턴스의 주소값을 저장하는데 static메소드(클래스 메소드)와는 무관하게 사용된다.

class JavaApp {
    public static void main(String[] args) {
        Child child = new Child();
        child.childMethod();
    }
}

class Parent {
    int x = 10;
}

class Child extends Parent {
    int x = 20;

    void childMethod() {
        System.out.println("x=" + x);
        System.out.println("this.x=" + this.x);
        System.out.println("super.x=" + super.x);
    }
}

결과값

x=20
this.x=20
super.x=10

super()

super()는 부모 클래스의 생성자를 호출하는 메서드이다. 상속받은 자식 클래스가 부모 클래스의 멤버를 사용할 경우가 있을 수도 있으므로 부모 클래스를 우선적으로 초기화해줘야 한다. 부모 클래스의 생성자는 자식 클래스의 생성자 첫줄에서 호출해준다. 이러한 부모 클래스에 대한 생성자 호출은 상속관계에 따라 Object 클래스까지 올라가서 마무리된다.

정리하면, Object 클래스를 제외한 모든 클래스의 생성자의 첫줄에는 반드시 자신의 클래스의 또다른 생성자, this() 또는 부모 클래스의 생성자, super()를 호출해줘야 한다. 이렇게 하지 않으면 컴파일러가 자동으로 super()를 생성자의 첫줄에 호출한다.

class JavaApp {
    public static void main(String[] args) {
        Point3D point3d = new Point3D();    // Point3D() 생성자로 초기화 및 인스턴스 생성
        System.out.println("point3d.x=" + point3d.x);
        System.out.println("point3d.y=" + point3d.y);
        System.out.println("point3d.z=" + point3d.z);
    }
}

class Point {
    int x = 10;
    int y = 20;

    Point(int x, int y) {
        // 생성자의 첫줄에 다른 생성자를 호출하지 않았기 때문에,
        // 컴파일러가 이 부분에 super()를 호출한다.
        // 부모 클래스이므로 Object 클래스의 super()가 호출된다.
        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) {
        super(x, y);    // 부모 클래스 생성자 호출
        this.z = z;
    }  
}

결과값

point3d.x=100
point3d.y=200
point3d.z=300

Comment and share

상속과 오버라이딩

상속(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

Hyeon Soo Ahn

author.bio


author.job