객체지향 프로그래밍

2023. 11. 28. 08:40
728x90

1. 객체지향 프로그래밍

1) 캡슐화 (Encapsulation)

: 데이터와 기능을 하나의 단위(클래스)로 묶어서 활용하는 것.

- 외부의 코드가 내부의 작동 방식을 고려하는 상황 방지

- 객체 내부의 데이터를 외부에서 자유롭게 접근하지 못하도록 하는 정보은닉의 개념까지 포함. (접근 제어자 활용)

 

2) 상속 (Inheritance)

: 이미 존재하는 클래스를 재활용하여 상속받은 클래스의 기능을 재활용/ 확장할 수 있게 하는 것.

- 자식 클래스가 부모 클래스의 속성과 기능 공유

- 기능을 덮어써서 독자적인 동작 구현

- 코드 재사용성, 확장성 증진

 

3) 다형성 (Polymorphism)

: 서로 다른 객체가 하나의 공통된 클래스 형태로 취급.

- 메서드 오버로딩 : 같은 이름의 메서드로 여러 기능 활용

- 메서드 오버라이딩 : 같은 메서드가 어떤 객체가 사용하느냐에 따라 기능이 변경

- 인터페이스로 여러 클래스가 같은 인터페이스의 구현체로 인식되게.

- 코드 재사용성, 유연성 증진.

 

4) 추상화 (Abstraction)

: 실제 기능이 만들어지지 않은 추상클래스와 인터페이스를 바탕으로, 실제 기능을 자식 클래스(Concrete class)에 위임.

- 추상 클래스에 공유된 기능, 개별적 기능 분리

- 인터페이스로 클래스가 가져야 하는 기능 정의

- 코드를 사용하는 입장에서 불필요한 세부사항에 대한 관심 줄이기.

- 코드 복잡성 낮추고 코드 재사용성, 유연성 증진.

 

2. Inheritance & Composition

- 클래스 생성 시 클래스의 관계를 정리해본다.

 ex. 자동차는 엔진을 가지고 있다. (has-a관계)

         게시물에는 댓글이 있다.

~~~> 자동차 클래스에 엔진이라는 클래스가 속성으로 가지고 있다. >> 이렇게 작성하는 것을 Composition(구성)이라고 한다.

 

ex2. 사각형과 원은 일종의 도형이다. (is-a 관계)

         고양이와 강아지는 일종의 동물이다. 

~~~> 이때 동물이라는 클래스를 만들고, 고양이과 강아지가 동물 클래스를 상속(Inheritance) 받는다. "extends"

~~> 고양이는 일종의 동물이 된다. (동물의 메서드를 사용 가능)

but 반대로 동물이 고양이의 메서드를 사용할 수는 없다.......(?) > 그럴 때에는 Animal을 Cat으로 바꿔주어야 한다.

--> Downcasting 다운캐스팅 : 업캐스팅 되었던 객체를 다시 하위클래스의 자료형을 바꾸어 되돌려 놓는 것. 

cf) Upcasting 업캐스팅 : 하위클래스의 정보를 담을 수 있는 객체에 상위클래스의 자료형을 부여해서 상위클래스처럼 사용하게 하는 것. (ex. Animal animal = new Cat();  Cat 객체에 Animal 자료형을 부여함)

 

ex.

Animal animal = new Cat(); // upcasting
animal.sound();

animal.grooming(); //오류남. 동물인 cat이 Cat의 메서드를 사용할 수 없다. 그럴 때 downcasting
((Cat)animal).grooming();

 

- 만약 형변환 전에 가능한지 여부 확인하고 싶다면 instanceof 사용 

+ instanceof 체크를 하면 객체가 해당 클래스 인스턴스인지 확인이 된 셈이므로 > 직접 downcasting 하지 않고 instanceof에서 임시 변수를 만들 수 있다. (Java 14)

if (animal instanceof Cat) {
	((Cat)animal).grooming();
} else {
	System.out.println("Can't do that!");
}

혹은
if (animal instanceof Cat cat) {
	cat.grooming();
} else {
	System.out.println("Can't do that!");
}

 

 

* 메서드 오버라이딩

: 상황에 따라 부모 클래스와 자식 클래스의 메서드가 다르게 동작해야 한다면...? 

-> 메서드 오버라이딩 하기

- 자식 클래스에서 변경하고 싶은 부모 클래스의 메서드와 동일한 이름, 매개변수, 반환형의 메서드를 만들면 오버라이딩 가능. 

- super : 부모 클래스를 나타내는 키워드. 

- protected 접근 제어자를 활용해 자식 클래스에서도 부모클래스의 속성/메서드에 접근할 수 있도록 한다. 

public void drive(int kilos) { // 부모 클래스의 drive 메서드
	this.fuel -= kilos/10;
}

@Override
public void drive (int kilos) { // 자식 클래스에서 drive 메서드 오버라이딩
	super.drive(kilos);
    fuel -= load/(maxLoad/10);
}

 

 

** 추상 클래스 abstract class & 인터페이스 interface

- 명백한 구현이 존재하는 Concrete class (구상클래스)를 상속받으면 > 부모와 자식이 강하게 결합된다... > 좀더 높은 단계의 추상화를 위해 추상클래스와 인터페이스 사용.

 

1) 추상 클래스

: 구현되지 않은 메서드를 가질 수 있는 클래스.

- 인스턴스 객체를 만들 수 없음. (실제 기능이 없으니까...)

- 여러 클래스의 기본 클래스를 만들고 싶을 때 (공통 기능과 직접 구현 기능 분리.

- 추상 메서드 : 메서드 바디를 작성하지 않고, 상속 받은 클래스에 구현을 위임하는 메서드. ("내 자식이 되려면 넌 이건 할 줄 알아야해~.") >> 자식 클래스에서 직접 구현.

 

ex. 사람 (이름) : 인사를 할거지만 누가 하는지에 따라 다르게 하고싶다.

       학생 (이름, 전공) (인사)

       강사 (이름, 강의 주제) (인사)

>> 이 때 추상 클래스 사람을 만들고 학생, 강사에 따라 인사를 다르게 하는 추상 메서드를 만든다.

// 추상 클래스 Person
public abstract class Person {
    private String name;
    
    public Person(String name) {
        this.name = name;
    }
    public String getName() {
        return this.name;
    }
    // 추상 메서드
    public abstract void sayHello();
}

// Person을 상속받은 Student
public class Student extends Person {
    private String major;

    public Student(String name, String major) {
        super(name);
        this.major = major;
    }

    @Override
    public void sayHello() {
        System.out.println("안녕하세요. " +
                "저는 "+major+"과 "+getName()+"입니다. ");
    }
}

// Person을 상속받은 Lecturer
public class Lecturer extends Person {
    private String subject;

    public Lecturer(String name, String subject) {
        super(name);
        this.subject = subject;
    }
    
    @Override
    public void sayHello() {
        System.out.println(String.format("Hello, I'm %s, and today we will study %s!",  
        getName(), subject));
    }
}

> 요렇게 하면 학생일 때, 강사일 때 다른 인사방법을 표현할 수 있다~!

 

2) 인터페이스 (implements 구현하다)

: 상위 클래스가 아닌 특정 기능을 추가하고 싶을 때 

- 둘이 큰 관계는 없지만 같은 기능을 하도록 보장하고 싶다면......? 

- 변수 없이 구현해야 하는 기능인 추상 메서드의 모음/ 어떤 클래스가 반드시 만들어야 하는 기능.

 

- Java의 Comparable (인터페이스)을 구현하면 객체간의 비교가 가능.

-> Arrays.sort(배열) > 사용하여 정렬 가능

	Person alex = new Student("Alex", 29,"CSE");
        Person brad = new Student("Brad", 21,"CSE");
        Person joy = new Lecturer("Joy", "computers", 40);
        Person eric = new Student("Eric", 25, "MD");

        Person[] people = new Person[]{alex, brad, joy, eric};
        //배열을 정렬하는 메서드
        Arrays.sort(people);
        for(Person person: people) {
            person.sayHello();
        }
        // 결과 : 나이순 정렬

 

 

3. Object 클래스

: 모든 클래스가 상속받는 클래스

- 객체를 다루기 위해 필요한 메서드가 정의되어 있다. 이 메서드들을 오버라이드해서 의도대로 동작하도록 만들 수 있음.

 

1) toString()

: 객체를 문자열로 표현한 형태를 반환하는 메서드.

ex. println 메서드

public void println(Object x) {
    String s = String.valueOf(x);
    if (getClass() == PrintStream.class) {
        // need to apply String.valueOf again since first invocation
        // might return null
        writeln(String.valueOf(s));
    } else {
        synchronized (this) {
            print(s);
            newLine();
        }
    }
}

 

- toString() 메서드 오버라이딩

public abstract class Person implements Comparable{
    // ...
    @Override
    public String toString() {
        return String.format("%s, age: %s", name, age);
    }
}

 

 

2) equals(Object o)

: 두 객체가 동일한 을 들고 있는지 판단하고 싶을 때 사용.

cf) ==는 데이터의 값을 기준으로 비교하여서 참조변수는 데이터 주소값을 저장하기 때문에 > 같은 객체여야만 동일하게 인식을 한다.

 

- 상속 관계의 클래스를 대상으로 구현할 경우 주의 ! 

// Person.java
@Override
public boolean equals(Object o) {
	if (this == o) return true;
    // 서로 다른 자식클래스가 일치하게 하고 싶다면
    if (!(o instanceof Person person)) return false;
    return age == person.age && Objects.equals(name, person.name);
}

// Student.java
@Override
public boolean equals(Object o) {
	// 1. 실제로 두 변수의 값(할당된 주소)이 동일하냐
	if (this == o) return true;
    // 2. null 이거나 둘이 다른 클래스인가 
    if (o==null || getClass() != o.getClass()) return false;
    // 나머지 속성을 비교하자.
    Student student = (Student) o;
    return Objects.equals(major, student.major);
}

//main
Person alex = new Student("Alex", 20, "CSE");
Person alex2 = new Lecturer("Alex", 20, "OOP");

System.out.println(alex.equals(alex2)); //false
System.out.println(alex2.equals(alex)); // true

 

 

3) hashCode()

: 어떤 입력값에 대해서든 일정한 길이의 출력을 돌려주는 함수로 만드는 코드.

- 어떤 객체가 유일한 객체인지 아닌지를 나타내어 빠르게 객체를 찾을 수 있도록 한다.

 

- equals를 구현했다면 일치관계의 두 객체는 hashCode()의 결과가 같아야 한다. 

- equals를 오버라이드 했다면 hashCode()도 같이 오버라이드.

ex) 다운로드한 파일이 정상적인 파일인지 확인하는 Checksum

//Person.java
@Override
public int hashCode() {
	return Objects.has(name, age);
}

 

4) null

: 값이 없음. 아스키코드 0에 대응되어있는 특수한 제어 문자. 객체가 존재하지 않는다는 의미

- 원시타입은 각 타입에 대응하는 기본값이 존재하지만, 참조타입은 실제로 할당할 데이터가 없으므로 null로 할당된다. 

- null 확인을 꼼꼼히 하고 확실히 null이 아닌 객체를 사용하기 !!!

 

728x90

'Programming > Java' 카테고리의 다른 글

제너릭 (Generics)  (1) 2023.11.29
예외처리 (Exception Handling)  (1) 2023.11.28
클래스 (Class)  (1) 2023.11.28
메서드 (Methods)  (1) 2023.11.23
제어문 (Control Statements)  (1) 2023.11.22

BELATED ARTICLES

more