본문 바로가기
JAVA

[JAVA] 객체지향 복습

by hotdog7778 2023. 6. 20.

점프투자바 객체지향 복습

객체지향

뼈대를 만들어놓고 일부만 다른 완제품을 만들어서 사용한다.

일부만 다르다는건 완제품1과 완제품2는 서로 영향을 주지 않는 독립된 무언가를 사용 할 수 있다는 뜻.

긴줄의 코드를 짧게 만들어서 사용 할 수 있는 장점도 있다.

장점은 이것보다 많지만 이것도 객체지향의 필요 이유중 하나이다.

 

객체와 인스턴스

껍데기 뿐인 클래스(Class)도 객체(object)를 만드는 기능을 가지고 있다.

class Animal {
}

public class Sample {
    public static void main(String[] args) {
        Animal cat = new Animal();
    }
}

Animal 클래스의 인스턴스인 cat

즉, Animal의 객체가 만들어 진 것이다.

 

객체와 인스턴스의 차이

인스턴스 : 객체가 어떤 클래스의 객체인지 관계를 위주로 설명할 때 사용한다.

  • cat 은 Animal의 인스턴스 입니다. (관계를 명시하는 설명)
  • cat 은 객체 입니다.

 

객체 변수 (Instance variable)

클래스에 선언된 변수인 객체 변수(인스턴스 변수, 멤버 변수, 속성).

 

메서드를 사용해서 객체 변수에 값을 대입

이렇듯이 객체 변수의 값이 독립적으로 유지되기 때문에 객체 지향적 일수 있다.

** static을 이용하면 객체 변수를 공유할 수 있다.

class Animal {
    String name; // 클래스에 선언된 변수인 객체 변수(인스턴스 변수, 멤버 변수, 속성)

    // 메서드를 사용해서 객체 변수에 값을 대입
    public void setName(String name){
        this.name = name; // 자신의 name 변수는! 입력받은 name 이다!
    }

}
public class Sample2 {
    public static void main(String[] args) {
        Animal cat = new Animal();

        // 객체 변수에 접근하는 방법과 호출
        System.out.println(cat.name); // null
        cat.setName("doong"); // 메서드 호출
        System.out.println(cat.name); // doong

        // 객체 변수는 공유되는 변수가 아니다.
        Animal dog = new Animal();
        dog.setName("happy");
        System.out.println(dog.name); // happy
        System.out.println(cat.name); // doong
        
    }
}

 

메소드(Mathod) 의 매개변수와 인수

메소드의 구조

리턴자료형 메서드명(입력자료형1 매개변수1, 입력자료형2 매개변수2, ...) {
    ...    
    return 리턴값;  // 리턴자료형이 void 인 경우에는 return 문이 필요없다.
}
  • 매개변수 - 메서드에 전달된 값을 저장하는 변수
  • 인수 - 메서드에 전달하는 값
public class Sample {
    int sum(int a, int b) {  // a, b 는 매개변수
        return a+b;
    }

    public static void main(String[] args) {
        Sample sample = new Sample();
        int c = sample.sum(3, 4);  // 3, 4는 인수

        System.out.println(c);
    }
}

메서드에 객체를 넘길 때

public class Sample {

    int a;  // 객체변수 a

    void varTest(Sample sample) {
        sample.a++;
    }

    public static void main(String[] args) {
        Sample sample = new Sample();
        sample.a = 1;
        sample.varTest(sample);
        System.out.println(sample.a);
    }
}

this 활용 할때

public class Sample {

    int a;  // 객체변수 a

    void varTest() {
        this.a++;
    }

    public static void main(String[] args) {
        Sample sample = new Sample();
        sample.a = 1;
        sample.varTest();
        System.out.println(sample.a);
    }
}

 

Call by reference와 Call by Value

메서드에 객체를 전달할 경우 메서드에서 객체의 객체변수(속성) 값을 변경할 수 있다.

해당 객체의 주소값을 직접 넘기는 게 아닌 객체를 보는 또 다른 주소값을 만들어서 넘긴다.

메서드의 입력으로 객체를 전달받는 경우 >

메서드는 입력받은 객체를 사용 >

메서드가 객체의 속성값을 변경 >

메서드 수행 후에도 객체의 변경된 속성값이 유지

// 값을 전달 받은 Case

class Updater {
    void update(int count) {
        count++;
    }
}

class Counter {
    int count = 0;  // 객체변수
}

public class Sample {
    public static void main(String[] args) {
        Counter myCounter = new Counter();
        System.out.println("before update:"+myCounter.count); // before update:0
		
        Updater myUpdater = new Updater();
        myUpdater.update(myCounter.count);
        System.out.println("after update:"+myCounter.count); // after update:0
    }
}
// 객체를 전달 받은 Case

class Updater {
    void update(Counter counter) {
        counter.count++;
    }
}

class Counter {
    int count = 0;  // 객체변수
}

public class Sample {
    public static void main(String[] args) {
        Counter myCounter = new Counter();
        System.out.println("before update:"+myCounter.count);
        Updater myUpdater = new Updater();
        myUpdater.update(myCounter);
        System.out.println("after update:"+myCounter.count);
    }

 

상속(Extends)

  • 부모클래스의 변수와 메소드를 사용
  • 부모클래스의 기능을 확장해서 사용
  • IS-A / 자식 IS 부모

메소드 오버라이딩

자식클래스 에서 부모클래스의 메소드를 오버라이딩(덮어쓰기) 해서 기능을 변경할 수 있음.

 

메소드 오버로딩

오버 라이딩이랑 비슷한데, 자식 클래스에서 메서드를 생성할때 같은 이름의 메서드가 있어도 입력항목이 다르다면 가능하다.

 

다중상속

클래스가 동시에 하나 이상의 클래스를 상속받는 것을 뜻한다. C++, 파이썬 등 많은 언어들이 다중 상속을 지원하지만,

자바는 다중 상속을 지원하지 않는다.

class C extends A, B {  // 자바는 허용하지 않는다.
		....       // 다중상속을 지원하는 다른 언어들은 이렇게 동일한 메서드를 상속받는 경우 우선순위등을 적용하여 해결한다.
}

 

생성자(Constructor)

객체변수에 값을 무조건 설정해야만 객체가 생성될 수 있도록 강제할 수 있는 방법은 없을까?

생성자(Constructor)를 이용하면 된다.

 

생성자를 사용하면 필수적인 행동을 객체 생성시에 제어할 수 있다.

메서드명이 클래스명과 동일하고 리턴 자료형을 정의하지 않는 메서드를 생성자(Constructor)라고 한다.

  1. 클래스명과 메서드명이 동일하다.
  2. 리턴타입을 정의하지 않는다. (void도 사용하지 않는다.)

생성자는 객체가 생성될 때 호출된다. 즉, 생성자는 다음과 같이 new 키워드가 사용될 때 호출된다.

new 클래스명(입력인수, ...)

생성자는 메서드와 마찬가지로 다양한 입력을 받을 수 있다.

우리가 HouseDog 클래스에 만든 생성자는 다음과 같이 입력값으로 문자열을 필요로 하는 생성자이다.

HouseDog(String name) {
    this.setName(name);
}

따라서 다음과 같이 new 키워드로 객체를 만들때 문자열을 전달해야만 한다.

HouseDog dog = new HouseDog("happy");   // 생성자 호출 시 문자열을 전달해야 한다.

만약 다음처럼 코딩하면 컴파일 오류가 발생할 것이다.

HouseDog dog = new HouseDog();

오류가 발생하는 이유는 객체 생성 방법이 생성자의 규칙과 맞지 않기 때문이다. 생성자가 선언된 경우 생성자의 규칙대로만 객체를 생성할 수 있다.

 

생성자 오버로딩

생성자에도 오버로딩이 있다. 하나의 클래스에 여러개의 입력항목이 다른 생성자를 만들 수 있다.

 

인터페이스

살짝 클래스를 묶어쓰는 느낌

  • 인터페이스는 클래스와 마찬가지로 Example.java와 같은 단독 파일로 저장하는 것이 일반적인 방법이다.
  • 인터페이스의 메서드는 항상 public으로 구현해야 한다.
  • 콤마(,)를 이용하여 여러개를 implements 할 수 있다.
  • 상속이 가능하다. 다중 상속도 가능하다.
interface Predator{
    String getFood(); // 인터페이스에 메소드를 추가
    // 인터페이스의 메서드는 메서드의 이름과 입출력에 대한 정의만 있고 그 내용은 없다.
    // 그 이유는 인터페이스는 규칙이기 때문이다.
    // 위에서 설정한 getFood 라는 메서드는 인터페이스를 implements한 클래스들이 구현해야만 하는 것이다.
}
class Animal {
    String name;

    void setName(String name) {
        this.name = name;
    }
}

class Tiger extends Animal implements Predator {
    // 인터페이스에 있는 메소드 규칙을 따라야 한다. 따라서 getFood 메소드를 작성
    // 인터페이스의 메서드는 항상 public 으로 구현해야 한다.
    public String getFood(){
        return "tiger_food";
    }
}

class Lion extends Animal implements Predator{
    public String getFood(){
        return "lion_food";
    }
}

class ZooKeeper {
    void feed(Predator predator) {  // 동물별로 먹이를 던져 준다.
        System.out.println(predator.getFood());
    }
}

public class Sample {
    public static void main(String[] args) {
        ZooKeeper zooKeeper = new ZooKeeper();
        Tiger tiger = new Tiger();
        Lion lion = new Lion();
        zooKeeper.feed(tiger);  // tiger_food 출력
        zooKeeper.feed(lion);  // lion_food 출력
    }
}

디폴트 메소드

인터페이스의 메서드는 몸통(구현체)을 가질 수 없지만 디폴트 메서드를 사용하면 실제 구현된 형태의 메서드를 가질 수 있다.

그리고 디폴트 메서드는 오버라이딩이 가능하다. 즉, 메서드를 실제 클래스에서 다르게 구현하여 사용할수 있다.

interface Predator {
    String getFood();

    default void printFood() {
        System.out.printf("my food is %s\\n", getFood());
    }
}

스태틱 메소드

인터페이스에 스태틱 메서드를 구현하면 인터페이스명.스태틱메서드명 과 같이 사용하여 일반 클래스의 스태틱 메서드를 사용하는 것과 동일하게 사용할 수 있다.

interface Predator {
    String getFood();

    default void printFood() {
        System.out.printf("my food is %s\\n", getFood());
    }

    int LEG_COUNT = 4;  // 인터페이스 상수
		// 인터페이스에 정의한 상수는 
		// public static final을 생략해도 자동으로 public static final이 적용 (변경불가)

    static int speed() {
        return LEG_COUNT * 30;
    }
}

// 다음과 같이 사용 할 수 있다.
// Predator.speed();

 

다형성(Polymorphism)

예제에서 사용한 tiger, lion 객체는 각각 Tiger, Lion 클래스의 객체이면서 Animal 클래스의 객체이기도 하고 Barkable, Predator 인터페이스의 객체이기도 하다. 이러한 이유로 barkAnimal 메서드의 입력 자료형을 Animal에서 Barkable 로 바꾸어 사용할 수 있는 것이다.

이렇게 하나의 객체가 여러개의 자료형 타입을 가질 수 있는 것을 객체지향 세계에서는 **다형성(Polymorphism)**이라고 한다.

메소드에 객체를 전달할때 다양 하게 전달할 수 있다~~. 이유는, 인터페이스나 상속으로 인해 IS-A 가 성립하기 때문이다.

대략.

자식 클래스의 객체는

부모 클래스의 자료형 타입을 가질 수 있다. // Animal animal = new Tiger();

인터페이스의 자료형 타입을 가질 수 있다. // Predator predator = new Tiger();

 

추상클래스(Abstract Class)

인터페이스의 역할도 하면서 클래스의 기능도 가지고 있는 클래스

'JAVA' 카테고리의 다른 글

[JVM] JVM 메모리구조와 동작방식  (0) 2023.06.29
[JAVA] 객체지향 복습 Quiz  (0) 2023.06.20
[Java] 자료형 / 형변환 복습  (2) 2023.06.18
[Java] 소스코드 형태 복습  (0) 2023.06.18