Java 4일차

Minji Lee·2024년 3월 13일
0

JAVA

목록 보기
4/13
post-thumbnail

OOP 특징

캡슐화(Encapsulation)

객체의 필드, 메소드를 하나로 묶고 실제 구현 내용을 감추는 것

  • 외부 객체는 객체 내부 구조 알지 못한 채, 객체가 제공하는 필드와 메서드만 이용 가능
  • 접근 제한자를 통해 캡슐화된 멤버를 노출시킬건지 숨길건지 결정

상속(Inheritance)

상위(부모)객체의 필드와 메소드를 하위(자식) 객체에게 물려주는 행위

  • 하위 객체는 상위 객체를 확장해서 추가적인 필드와 메소드를 가질 수 있음
  • extends: 자식 클래스가 상속할 부모 클래스 지정하는 키워드
    • 다중 상속 X → 여러개의 부모 클래스에서 상속 받을 수 없음
  • 자식 객체 생성시, 부모 객체부터 생성 후 자식 객체 생성됨

[효과]

  • 중복 코드 줄임

    🖌️ 중복 코드 줄이는 방법
    1. 반복문이나 배열/리스트 사용
    2. 함수로 재사용하기
    3. 클래스(필드+함수)로 재사용하기
    4. 클래스 상속 이용 ⇒ 타입의 유연성을 위해 “다형성” 등장
    5. 추상화 클래스나 인터페이스 사용

  • 유지보수 향상
  • 객체 다형성 구현 가능
  • 계층적인 구조로 코드 설계 가능

[상속 대상 제한]

  • 부모 클래스의 private 접근 필드와 메소드 제외
  • 부모 클래스가 다른 패키지에 있는 경우, default 필드와 메소드 제외
// 클래스의 상속
//      : 부모(상위) 클래스의 자산(필드, 메소드)을 자녀(하위)클래스가 물려받는 것
// 사용 이유
// 1. 코드 중복 피할 수 있음.
// 2. 계층적인 구조로 코드를 설계 가능
// 예) 강아지: 동물 속성/행동(중복) + 강아지고유의 속성/행동(유니크)
//    고양이: 동물 속성/행동(중복) + 고양이고유의 속성/행동(유니크)
class Animal {
    int age = 10;

    void eat() {
        System.out.println("먹는다.");
    }
}

class Dog extends Animal {
    void bark() {
        System.out.println("짖는다.");
    }
}

class Cat extends Animal {
    void grooming() {
        System.out.println("손질한다.");
    }
}

public class ex36 {
    public static void main(String[] args) {
        Dog dog = new Dog();
        System.out.println(dog.age); // 10
        dog.eat(); // 먹는다.
        dog.bark(); // 짖는다.

        Cat cat = new Cat();
        System.out.println(cat.age); // 10
        cat.eat(); // 먹는다.
        cat.grooming(); // 손질한다.
    }
}

상속관계에서 생성자 호출

  • 부모의 생성자가 먼저 호출된 후, 자식 생성자 호출됨 → 이때 부모 생성자는 기본 생성자가 호출됨

    // 상속관계에서 생성자 호출되는 순서(초기화 순서)
    class Energy {
        int price = 1000;
    
        // 생성자 자동생성 -> 우클릭 -> 생성 -> 생성자
        public Energy() { // 기본 생성자
            System.out.println("Energy 기본생성자");
        }
    
        public Energy(int price) {
            this.price = price;
            System.out.println("Energy 필드(매개변수)가 있는 생성자");
        }
    }
    
    class WindEnergy extends Energy {
        int price = 2000;
    
        public WindEnergy() {
            System.out.println("Wind 기본생성자");
        }
    
        public WindEnergy(int price) {
            this.price = price;
            System.out.println("Wind 필드 생성자");
        }
    
    }
    
    public class ex39 {
        public static void main(String[] args) {
            WindEnergy we1 = new WindEnergy();
            WindEnergy we2 = new WindEnergy(3000);
        }
    }
    

  • 부모의 필드 생성자가 호출되게 하고 싶은 경우super사용

    • 자식 생성자의 첫 줄에 위치!
    // 상속관계에서 생성자 호출되는 순서(초기화 순서)
    class Energy {
        int price = 1000;
    
        // 생성자 자동생성 -> 우클릭 -> 생성 -> 생성자
        public Energy() { // 기본 생성자
            System.out.println("Energy 기본생성자");
        }
    
        public Energy(int price) {
            this.price = price;
            System.out.println("Energy 필드(매개변수)가 있는 생성자");
        }
    }
    
    class WindEnergy extends Energy {
        int price = 2000;
    
        public WindEnergy() {
            System.out.println("Wind 기본생성자");
        }
    
        public WindEnergy(int price) {
    			  // super(); // 부모의 기본생성자 호출
            super(price); // 부모의 필드 생성자 호출!
            // super는 반드시 첫줄에 넣어야 함!
            
            this.price = price;
            System.out.println("Wind 필드 생성자");
        }
    
    }
    
    public class ex39 {
        public static void main(String[] args) {
            WindEnergy we1 = new WindEnergy();
            // Energy 기본생성자
            // Wind 기본생성자
            WindEnergy we2 = new WindEnergy(3000);
            // Energy 기본생성자
            // Wind 필드 생성자
    
        }
    }
    

this와 super

this. : 자기클래스필드/메소드 접근

this() : 자기클래스생성자 함수 호출

super. : 부모클래스필드/메소드 접근

super() : 부모클래스생성자 함수 호출

오버라이드(Override)

부모 클래스의 메소드를 자식 클래스에서 재정의한다.

  • 부모 클래스의 메소드는 무시됨!
  • 부모클래스의 메소드를 오버라이드할 때, 반환타입 함수이름 매개변수는 동일해야함!
  • @Override 사용해도되고 안해도됨
    • 어노테이션: @로 시작하는 컴파일 지시어 → 개발자나 컴파일러에게 해당 속성 알려줌
  • 자식 클래스에서 수정되기 전 상태인 부모 메소드 호출하기 위해서는 super 사용!
// 오버라이드(Override)
//      : 부모 클래스의 메소드를 자식 클래스에서 재정의하는 것
class Machine {
    String name = "일반기계";

    void work() {
        System.out.println("일한다.");
    }
}
class CoffeeMachine extends Machine {
    String name = "커피머신";
    // 부모 클래스에도 존재하는 메소드를 재정의 -> 오버라이드
    // 오버라이드 메소드: 반환타입 함수이름 매개변수 동일!!
    // 어노테이션: @로 시작하는 컴파일 지시어
    //  - 역할: 개발자나 컴파일러에게 해당 속성을 알려줌.
    @Override
    // @Override는 생략 가능
    void work() {
        System.out.println("커피머신이 일한다.");
    }
}
public class ex40 {
    public static void main(String[] args) {
        CoffeeMachine cm = new CoffeeMachine();
        cm.work(); // 커피머신이 일한다.
    }
}

final

final 키워드 용도

  • final 필드: 상수처럼 동작, 한번 값이 들어가면 다시 대입 X

    // 상수 선언시
    final double PI = 3.141592;
    PI = 6.13; // 다시 값을 넣으려고 하면 오류
    class FinalClass {
        String name = "파이널 클래스";
        int age; // 0으로 초기화, 힙 영역의 참조변수는 강제 초기화
        final int price = 0; // final 시에는 반드시 초기화
    }
    
    public class ex41 {
        public static void main(String[] args) {
            FinalClass fc = new FinalClass();
            fc.price = 2000; // error -> final 이므로 재할당 불가!
            fc.age = 30; // final 이므로 재할당 불가
        }
    }
  • final 클래스: 부모로 사용 불가한 클래스, 상속 불가

    final class FinalClass {
        String name = "파이널 클래스";
    }
    
    // error -> final 클래스이므로 상속 불가
    class LastClass extends FinalClass {
    
    }
    
    public class ex41 {
        public static void main(String[] args) {
            FinalClass fc = new FinalClass();
        }
    }
  • final 메소드: 자식이 재정의할 수 없는 메소드, 오버라이드 불가

    class FinalClass {
        String name = "파이널 클래스";
        final void disp() {
        }
    }
    
    class LastClass extends FinalClass {
        @Override
        void disp(){} // final 메소드이므로 사용 불가
    }
    
    public class ex41 {
        public static void main(String[] args) {
            FinalClass fc = new FinalClass();
        }
    }
    

⭐️ 다형성(Polymorphism)

하나의 타입에 여러가지 객체를 대입해 다양한 실행 결과 얻는 것 ⇒ 유연성!

  • 자식은 자기자신의 타입 또는 부모 타입을 가질 수 있음
  • 용도: Data의 전달을 쉽게하기 위해서
    • 자동 타입 변환: 자식 클래스타입을 부모 클래스 타입으로 자동타입 변환업 캐스팅
    • 바로 위의 부모가 아니어도 상속 계층의 상위면 자동 타입 변환 가능!
    • 강제 타입 변환: 부모타입을 자식 타입으로 변환다운 캐스팅
      • 자식 객체가 부모클래스 타입으로 변경되었다가, 다시 자식 클래스 타입을 갖는 것
  • 객체 타입 확인: instanceof
// 다형성: Polymorphism
//      : 자식 클래스 객체가 자기 클래스 또는 부모클래스의 타입을 모두 가질 수 있는 것
// 이유: 타입의 유연성을 가지기 위해서
//      : 타입이 정확하지 않더라도 객체(참조변수)를 전달가능
class Parent {
    String name = "Parent";

    void parentMethod() {
        System.out.println("Parent 내 이름은 " + this.name);
    }
}

class Child extends Parent {
    String name = "Child";

    void childMethod() {
        System.out.println("Child 내 이름은 " + this.name);
    }
}

public class ex43 {
    public static void main(String[] args) {
        Parent p1 = new Parent();
        System.out.println(p1.name);

        Child c1 = new Child();
        System.out.println(c1.name);

        // 객체는 Child이지만, 타입은 Parent
        // 1. 업캐스팅: 자식 객체가 부모 클래스 타입을 가지는 것
        // 업캐스팅 할 수 있는 경우
        // 1) 대입연산자를 통해서 자동형변환 될 때 -> 강제형변환
        Parent p2 = new Child();
        System.out.println(p2.name); // Parent

        // 2) 형변환 연산자를 통해서
        Parent p3 = (Parent) c1;
        System.out.println(p3.name); // Parent

        // 2. 다운캐스팅: 자식 객체가 자녀클래스의 타입을 갖는 것
        //      -> 강제형변환 불가
        // 1) 형변환 연산자를 통해서 -> 강제형변환
        Child c2 = (Child) p2;
        System.out.println(c2.name); // Child

        // 4가지 형태
        Parent a = new Parent();
        Child b = new Child();
        Parent c = new Child(); // 업캐스팅
        Child e = (Child) c; // 다운캐스팅
        Child b = new Parent(); // 불가능 - 다형성과 무관

    }
}
다운캐스팅업캐스팅
Child 클래스 객체ChildParent

사용 이유: 다양한 객체를 전달하고 싶을 때, 부모 타입을 이용헤서 전달하면 여러 객체 전달 가능!

class People {
    void think() {
        System.out.println("생각한다.");
    }
}

class Man extends People {
    @Override
    void think() {
        System.out.println("남자가 생각한다.");
    }

    void shave() {
        System.out.println("면도한다.");
    }
}

class Woman extends People {
    @Override
    void think() {
        System.out.println("여자가 생각한다.");
    }

    void makeup() {
        System.out.println("화장한다.");
    }
}

public class ex44 {
    public static void main(String[] args) {
        // 1. 업캐스팅: 자식객체가 부모클래스 타입을 가지는 것
        People people = new Man();
        people.think(); // 남자가 생각한다. // 오버라이드 메소드가 실행됨
        // 2. 다운캐스팅: 자식객체가 부모클래스 타입으로 변경되었다가, 다시 자식 클래스 타입 갖는 것
        ((Man) people).shave();

        // 다형성을 이용해서 Man 객체와 Woman 객체도 전달해보자.
        myFunc(new Man());
        myFunc(new Woman());

    } // main

    static void myFunc(People p) {
        // instanceof 연산자: 객체 타입을 확인하는 연산자
        if (p instanceof Man) {
            ((Man) p).shave();
        }
        if (p instanceof Woman) {
            ((Woman) p).makeup();
        }

    }
} // class

추상클래스

추상(abstract): 실체들 간에 공통되는 특성을 추출한 것 → 유사한 특성 유추

ex) 새, 곤충, 물고기 → 동물(추상)

추상 클래스: 실체 클래스들의 공통되는 필드와 메소드를 정의한 클래스

  • 일반 클래스 + 가상함수(추상화 메소드)

    가상함수: 함수의 선언만 있고, 본체(코드)가 없는 것
    → 본체는 하위 클래스에서 재정의(Override)해서 사용함!

  • 추상 클래스는 실체 클래스의 부모 클래스 역할
  • abstract 키워드 사용!
    • new 연산자로 객체 생성 못하고, 상속 통해 자식 클래스만 생성 가능!
public abstract class 클래스 {
	// 필드
	// 생성자
	// 메소드
}
혹은
abstract public class 클래스 {
	// 필드
	// 생성자
	// 메소드
}

[사용 이유]

  • 기존 코드 건드리지 않고 새로운 기능 확장 가능!
  • 새로운 기능을 가진 구현클래스로 버전 향상시킬 수 있음 → 안정성
  • 실체 클래스의 공통된 필드와 메소드의 이름 통일할 목적
  • 실체 클래스 작성할 때 시간 절약
  • 실체 클래스 설계 규격 만들고자 할 때 → 설계와 구현 관점
    • 실체 클래스는 추상 클래스를 무조건 상속 받아 작성해야함!
      • 설계: 건축에서 설계도 역할, 단시간에 전체구조와 기능 명세 설계 가능
      • 구현: 건축에서 시공의 역할, 실제 기능 구현은 Coder가 구현함
    • 기능을 정의하고 실제 구현은 나중에!
// 추상화 클래스
abstract class Picture {
    // 가상함수(추상 메소드): 선언만 있고, 코드본체가 없다.
    abstract void draw();

    // 일반함수
    void sale() {
        System.out.println("판다.");
    }
}

class Picasso extends Picture {
    // 메소드 재정의(오버라이드)를 통해 실제 코드를 구현함.
    @Override
    void draw() {
        System.out.println("피카소가 그림을 그린다.");
    }
}

// 기존 코드 건드리지 않고 새로운 기능을 확장할 수 있다.
// 새로운 기능을 가진 구현클래스로 버전업 할 수 있음 -> 안정성
class SuperPicasso extends Picture {
    // 메소드 재정의(오버라이드)를 통해 실제 코드를 구현함.
    @Override
    void draw() {
        System.out.println("수퍼 피카소가 그림을 그린다.");
    }
}

public class ex45 {
    public static void main(String[] args) {
        Picasso picasso = new Picasso();
        picasso.draw(); // 피카소가 그림을 그린다.
        SuperPicasso sp = new SuperPicasso();
        sp.draw(); // 수퍼 피카소가 그림을 그린다.
    }
}

인터페이스(Interface)

가상함수(추상메서드)만 있는 코드 뭉치

  • JDK8버전 이후에 default 메소드를 통해 일반 메소드 정의 가능해짐
  • 추상화 메소드의 설계/구현 기능과 유사함
    추상화 클래스인터페이스
    1. 가상함수있음있음
    2. 일반함수있음없음(JDK8버전 이후 default 메서드로 사용 가능)
    3. 예약어abstract class ,abstract 메서드명(abstract 생략 가능), extendsinterface, implements
    4. 다중상속불가능가능
    5. 객체 생성불가능(자식클래스에서 상속해야가능)불가능(구현해야)
    6. 접근제한자public/protected/privatepublic만 가능
    7. 필드선언가능public static만 가능
  • 개발 코드가 객체에 종속되지 않게, 객체 교체할 수 있도록 하는 역할
interface 인터페이스명 {
	// 상수
	타입 상수명 =;
	// 추상 메소드
	타입 메소드명(매개변수, ...);
	// 디폴트 메소드
	default 타입 메소드명(매개변수, ...){...}
	// 정적 메소드
	static 타입 메소드명(매개변수) {...}
}
  • 인터페이스는 상수 필드만 선언 가능!public static final이 컴파일과정에서 붙음
    • 상수명은 대문자로 작성
  • 선언과 동시에 초기값 지정!
  • static 초기화 블록으로 초기화 불가 → static 초기화 블록 작성 X
interface IDrawing {
    // 일반함수 선언안됨
    // void sale() {
    //     System.out.println("판다.");
    // }

    // 가상함수만 선언 가능, abstract는 써도 되고, 안써도됨
    // abstract (접근제어자) 반환타입 추상화메서드명();
    abstract void draw();

    // abstract void public draw(); 오류
    void sketch();
}

class Painter implements IDrawing {
    @Override
    public void draw() {
        System.out.println("드로잉");
    }

    @Override
    public void sketch() {
        System.out.println("스케치");
    }
}

public class ex46 {
    public static void main(String[] args) {
        Painter p = new Painter();
        p.draw();
        p.sketch();

        // 자체적으로 new 사용 못함
        // IDrawing d = new IDrawing(); // 인터페이스 // error
        // Abs a = new Abs(); // 추상클래스 // error
    }
}

abstract class Abs {

}
  • 추상화(일반) 클래스는 다중상속 X! 단, 다단계 상속은 가능
  • 인터페이스는 다중상속 가능
// 추상화클래스와 인터페이스 차이점
// 추상화(일반) 클래스: 다중상속은 안됨, 다단계 상속은 가능

// 다단계 상속 -> 가능!
class A {
}

class B extends A {
}

class C extends B {
}

// class D extends C, B, A { // 다중 상속 -> 불가능!
// }

interface IA {
}

interface IB {

}

class E implements IA, IB { // 다중상속 (한방에 기능 확장!)
}

class SupermanClass extends C implements IA, IB {
}

public class ex47 {
    public static void main(String[] args) {

    }
}

default 메소드

인터페이스에서 일반함수처럼 코드를 가진 메소드 정의

  • JDK8부터 추가된 기능
  • 접근제한자 default랑 다른 개념임!!
class ClassM {
    int price = 10; // 접근제한자가 default -> 앞에 default라고 명시하면 안됨!
}

interface Vehicle {
    public void drive(); // 추상메소드(가상함수)

    public default void run() {
        System.out.println("달린다.");
    }
}

// 인터페이스 상속을 통한 확장
interface Truck extends Vehicle {
    
}

class Tesla implements Vehicle {
    @Override
    public void drive() {
        System.out.println("운전한다.");
    }
}

public class ex48 {
    public static void main(String[] args) {
        Tesla tesla = new Tesla();
        tesla.drive();
        tesla.run();

    }
}

0개의 댓글