📝 이것이 자바다

Chapter 06. 객체 지향 프로그래밍 - 1

j_estory 2022. 11. 14. 15:42

소프트웨어를 개발할 때에도 부품에 해당하는 객체들을 먼저 만들고, 이 객체들을 하나씩 조합하여 완성된 프로그램을 만드는 기법을 말한다. 

 

🏷 객체란?

  • 객체는 필드와 메소드로 구성되어 있다. 
  • 객체 모델링
    • 현실 세계의 객체를 소프트웨어 객체로 설계하는 것을 의미한다. 
    • 현실 세계 객체의 대표 속성과 동작을 추려내어 소프트웨어 객체의 필드와 메소드로 정의하는 과정이라고 볼 수 있다. 

🏷 객체의 상호작용

  • 객체 지향 프로그램에서도 객체들은 서로 상호작용으로 동작한다.
  • 객체들 사이의 상호작용 수단은 메소드이다. 
    • 객체가 다른 객체의 기능을 이용할 때 이 메소드를 호출한다. 
    • 메소드 호출을 통해 객체들은 데이터를 서로 주고받는다. 

 

🏷 객체 간의 관계

  • 객체는 단독으로 존재할 수 있지만, 대부분 다른 객체와 관계를 맺고 있다. 
  • 관계의 종류
    • 집합 관계
      • 완성품과 부품의 관계를 말한다. 
      • 예를들어, 자동차는 엔진, 타이어, 핸들 등으로 구성되므로 자동차와 부품들은 집합 관계라고 볼 수 있다. 
    • 사용 관계
      • 다른 객체의 필드를 읽고 변경하거나 메소드를 호출하는 관계를 말한다. 
      • 예를들어 사람이 자동차에게 달린다. 멈춘다. 등의 메소드를 호출하면 사람과 자동차는 사용 관계라고 볼 수 있다.
    • 상속 관계
      • 부모와 자식 관계를 말한다. 자동차가 기계의 특징을 물려받는다면 기계와 자동차는 상속 관계라고 볼 수 있다. 

 

🏷 객체 지향 프로그래밍의 특징

 

  • 캡슐화
    • 객체의 데이터(필드), 동작(메소드)을 하나로 묶고 실제 구현 내용을 외부에 감추는 것을 말한다. 
    • 외부 객체는 객체 내부의 구조를 알지 못하며 객체가 노출해서 제공하는 필드와 메소드만 이용할 수 있다.
    • 필드와 메소드를 캡슐화하여 보호하는 이유는 외부의 잘못된 사용으로 인해 객체가 손상도지 않도록 하는데 있다. 
    • 자바 언어는 캡슐화된 멤버를 노출시킬 것인지 숨길 것인지를 결정하기 위해 접근 제한자를 사용한다. 
  • 상속
    • 객체지향 프로그래밍에서는 부모 역활의 상위 객체와 자식 역활의 하위 객체가 있다.
    • 부모 객체는 자기가 가지고 있는 필드와 메소드를 자식 객체에게 물려주어 자식 객체가 사용할 수 있도록 한다. 이것이 상속이다.
    • 상속을 하는 이유
      • 코드의 재사용성을 높여준다. 
      • 유지 보수 시간을 최소화 시켜준다. 
        • 부모 객체의 필드와 메소드를 수정하면 모든 자식 객체들은 수정된 필드와 메소드를 사용할 수 있다.
  • 다형성
    • 사용 방법은 동일하지만 실행 결과가 다양하게 나오는 성질을 말한다.
    • 다형성을 구현하기 위해서는 자동 타입 변환과 재정의 기술이 필요하다.

🏷 객체와 클래스

 

객체를 생성할 때에는 설계도가 필요하다. 그리고 이 설계도에 해당하는 것을 클래스 라고 한다. 

클래스로부터 생성된 객체를 해당 클래스의 인스턴스라고 부른다. 

 

그리고 그 클래스로부터 객체를 만드는 과정을 인스턴스화라고 한다. 

동일한 클래스로부터 여러 갱의 인스턴스를 만들 수 있는데, 이것은 동일한 설계도로 여러 대의 자동차를 만드는 것과 동일하다.

 

🏷 객체 생성과 클래스 변수

 

클래스로부터 객체를 생성하려면 객체 생성 연산자인 new 가 필요하다. 

new 클래스();

new 연산자는 객체를 생성시킨 후 객체의 주소를 리턴하기 때문에 클래스 변수에 다음과 같이 대입할 수 있다. 

클래스 변수 = new 클래스();

 

🏷 클래스의 구성 멤버

 

클래스 선언에는 객체 초기화 역할을 담당하는 생성자와 

객체에 포함될 필드와 메소드를 선언하는 코드가 포함된다. 

그래서, 생성자 / 필드 / 메소드 를 클래스 구성 멤버라고 한다. 

public class ClassName {
    // 필드 선언
    int fieldName;
    
    // 생성자 선언
    ClassName() {...}
    
    // 메소드 선언
    int methodName() {...}
}

필드 : 객체의 데이터를 저장하는 역할, 선언 형태는 변수 선언과 비슷하지만 쓰임새는 다르다.

 

생성자 : new 연산자로 객체 생성시 객체의 초기화 역활을 담당, 선언 형태는 메소드와 비슷하지만 리턴 타입이 없고 클래스 이름과 동일

 

메소드 : 객체가 수행할 동작, 메소드는 객체와 객체간의 상호 작용을 위해 호출

 

🏷 필드 선언과 사용

 

필드는 객체의 데이터를 저장하는 역할

객체의 데이터에는 고유 데이터, 현재 상태 데이터, 부품 데이터

고유 데이터 

- 제작 회사, 모델, 색깔, 최고 속도 ...

상태 

- 현재 속도, 엔진회전 수 ...

부품

- 차체, 엔진, 타이어 ...

 

초기값을 제공하지 않을 경우 필드는 객체 생성 시 자동으로 기본 값으로 초기화 된다. 

  • 정수 및 실수 : 0 
  • 참조 : null
  • boolean : false

🏷 필드 사용

 

필드를 사용한다는 것은 필드 값을 읽고 변경하는 것을 말한다.

클래스에서 필드를 선언했다고 해서 바로 사용할 수 있는 것은 아니다 !! 

필드는 객체의 데이터 이므로, 객체가 존재하지 않으면 필드도 존재하지 않는다.

 

객체 생성 후, 객체 내부의 생성자와 메소드, 객체 외부에서도 접근해서 사용할 수 있다. 

 

객체 내부에서는 단순히 필드명으로 읽고 변경할 수 있지만, 

객체 외부에서는 도트(.) 연산자를 이용하여 필드를 읽고 변경해야 한다. 

 

✅ . (도트 연산자)

객체 접근 연산자, 객체가 가지고 있는 필드나 메소드에 접근하고자 할 때 참조 변수 뒤에 붙인다.

 

🏷 생성자 선언과 호출

 

new 연산자는 객체를 생성한 후 연이어 생성자를 호출해서 객체를 초기화 하는 역활을 한다. 

객체 초기화란 필드 초기화를 하거나 메소드를 호출해서 객체를 사용할 준비를 하는 것을 말한다.

 

✔️ 기본 생성자

 

모든 클래스는 생성자가 존재하며, 생성자는 하나 이상을 가질 수 있다.

클래스에 생성자 선언이 없으면 컴파일러는 다음과 같은 기본 생성자를 바이트코드 파일에 자동으로 추가시킨다.

 

class가 public class로 선언되면 기본 생성자도 public이 붙지만, public이 없다면 기본 생성자에도 붙지 않는다.

 

그러나 !! 개발자가 명시적으로 선언한 생성자가 있다면, 기본 생성자를 자동으로 추가하지 않는다.

개발자가 생성자를 선언하는 이유는 객체를 다양하게 초기화하기 위해서이다.

 

✔️ 필드 초기화

 

객체마다 동일한 값을 갖고 있다면 필드 선언 시 초기값을 대입하는 것이 좋고,

객체마다 다른 값을 가져야 한다면 생성자에서 필드를 초기화하는 것이 좋다.

 

보통, 매개변수 명을 필드명과 동일하게 하는게 가독성이 좋아 많이 사용하고 있다. 

하지만 이렇게 되면 어떤 것이 필드이고 어떤것이 매개변수인지 구분하기 힘들기 때문에, this 라는 키워드를 필드명 앞에 붙여준다. 

this 는 현재 객체를 말하며, this.name 은 현재 객체의 필드로서의 name을 뜻한다. 

 

✔️ 생성자 오버로딩

 

매개 값으로 객체의 필드를 다양하게 초기화하려면 생성자 오버로딩이 필요하다.

 

생성자 오버로딩이란 ..?

매개변수를 달리하는 생성자를 여러개 선언하는 것을 말한다. 

public class Car {
    Car() {}
    Car(String model() {...}
    Car(String model, String color) {...}
    Car(String model, String color, int maxSpeed) {...}
}

생성자가 오버로딩 되었을 경우, 생성자 호출 시, 매개값에 따라 실행될 생성자가 결정된다. 

 

✔️ 다른 생성자 호출

 

생성자 오버로딩이 많아질 경우, 생성자 간의 중복된 코드가 발생할 수 있다. 

이럴경우, 공통 코드를 한 생성자에만 집중적으로 작성하고,
나머지 생성자는 this(...) 를 사용하여 공통 코드를 가지고 있는 생성자를 호출하는 방법으로 개선할 수 있다.

Car(String model) {
	this(model, "금색", 250)
}

Car(String model, String color) {
	this(model, color, 170)
}

Car(String model, String color, String speed) {
	this.model = model;
    this.color = color;
    this.speed = speed;
}

 

🏷 메소드 선언과 호출

 

메소드 선언

- 객체의 동작을 실행 블록으로 정의하는 것

메소드 호출

- 실행 블록을 실제로 호출 하는 것

 

객체의 메소드는 객체 내부에서도 호출되지만 다른 객체에서도 호출될 수 있기 때문에,
객체간의 상호작용하는 방법을 정의하는 것이라고 볼 수 있다 .

 

메소드 호출은 클래스에서 메소드를 선언했다고 해서 바로 호출될 수 있는 것은 아니다. 

메소드는 객체의 동작이므로, 객체가 존재하지 않으면 메소드는 호출 될 수 없다. 

클래스로 부터 객체가 생성된 후, 메소드는 생성자와 다른 메소드 내부에서 호출될 수 있고, 객체 외부에서도 호출 될 수 있다. 

 

✔️ 가변길이 매개변수

 

메소드를 호출할 때에는 매개변수의 개수에 맞게 매개값을 제공해야 한다.

하지만 가변길이 매개변수를 사용하면 매개변수의 개수와 상관없이 매개값을 줄 수 있다. 

// 가변길이 매개변수
int sum(int ... values) {
	...
}

가변길이 매개변수 호출 시, 매개값을 쉼표로 구분해서 개수와 상관없이 제공할 수 있다.

 

매개변수로 받은 값들은 자동으로 배열 항목으로 변환되어 메소드에서 사용된다. 

그렇기 때문에 메소드 호출 시, 직접 배열을 통해 매개값으로 값을 전달해도 된다.

 

✔️ 메소드 오버로딩

 

메소드 이름은 같되 매개변수의 타입, 개수, 순서가 다른 메소드를 여러개 선언하는 것을 말한다. 

메소드 오버로딩의 목적은 다양한 매개값을 처리하기 위해서이다. - 생성자 오버로딩과 비슷하다. 

 

대표적인 예

- System.out.println() 메소드로, 호출할때 주어진 매개값의 타입에 따라서 오버로딩된 println() 메소드 중 하나가 실행된다. 

 

 

'📝 이것이 자바다' 카테고리의 다른 글

Chapter 07. 상속  (0) 2022.12.15
Chapter 06. 객체 지향 프로그래밍 - 2  (0) 2022.11.22
Chapter 05. 참조 타입  (0) 2022.11.07
Chapter 04. 조건문과 반복문  (0) 2022.11.02
Chapter 03. 연산자  (0) 2022.10.30