패스트캠퍼스 백엔드 부트캠프 3기/JAVA

[JAVA] 객체지향 프로그래밍의 이해 - 1

hail2y 2024. 12. 30. 13:57

객체지향 언어는 코드의 재사용성을 높이고, 유지보수를 용이하게 하고, 중복 코드를 제거하기 위한 목적으로 사용된다.

객체지향 언어는 다음과 같은 4대 핵심 개념을 가진다.

  • 캡슐화
  • 상속
  • 추상화
  • 다형성

클래스는 객체를 정의해 놓은 것이며, 객체를 생성하기 위해서 만든다.

따라서 클래스를 (제품) 설계도라고 이해한다면 객체는 그 설계도에 따라 만든 제품이 된다. 이때 만든다고 하는 것은 프로그래밍 관점에서 "인스턴스화"한다고 표현한다. 설계도에 따라 개별 객체를 만드는 과정이다. 

클래스  객체
제품 설계도 제품

 

객체는 속성(변수)과 기능(메서드)으로 구성된 것으로 볼 수 있다. 그래서 객체를 사용한다고 하는 것은, 객체가 가진 속성과 기능을 사용하는 것으로 이해할 수 있다. 

 

클래스를 작성할 때 주의할 점은 다음과 같다.

  • 하나의 소스 파일에는 하나의 public class만 작성할 수 있다. (public 접근제어자를 붙이지 않은 class는 가능하다.) 
  •  소스파일 이름은 public class 이름과 일치해야 한다.
  • 자바는 대소문자를 구분하는 언어기 때문에 대소문자를 주의하자.

객체를 생성하고 사용하는 과정은 전체적으로 다음과 같다.

  • 클래스를 작성한다.
  • 객체를 생성한다.
  • 객체를 사용한다.

구체적으로 살펴보자면,

  1. 참조 변수를 선언한다. ex. Parent p;
  2. 인스턴스(객체)를 생성하여 대입한다. ex. p = new Parent();

Parent p = new Parent(); 단계별로 구분하기는 했지만 이렇게 한줄로 쓰는 게 일반적이

Parent p = new Parent();

객체 배열은 참조 변수의 배열과 같다. 객체가 참조 변수를 통해 접근하므로 새로운 말은 아니다. 

클래스는 다음과 같이 정리할 수 있다.

  • 객체 설계도
  • 데이터와 함수의  묶음
class Car {
    String color;
    int weight;
    void go() { ... }
}
  • 사용자 정의 타입

그리고 변수는 크게 3가지 타입으로 나눌 수 있다.

  • iv (instace variable) 인스턴스 변수 - 인스턴스가 생성되었을 때
  • cv (class variable, static variable) 클래스 변수 - 클래스가 메모리에 올라갔을 때 (= 클래스가 최초 로딩될 때)
    - 자동으로 만들어짐, 객체 만들기 전
  • lv (local variable) 지역 변수

클래스가 메모리에 올라간다는 의미

 

영역은 클래스 영역, 메서드 영역 크게 두 가지로 나눌 수 있다. 

  • 클래스 영역 - iv, cv(static + iv)
  • 메서드 영역 - lv
class Variable {
    int iv;  
    static int cv;
    void method() {
        int lv = 0;
        ...
    }
}

 

메서드는 작업 단위별로 문장들을 묶어놓은 것으로 이름을 붙여 사용한다. 메서드를 설계할 때 유의해야 할 점은 하나의 메서드가 하나의 기능만 수행하도록 하는 것이다. 보기엔 되게 쉬운 작업 같아 보이지만 프로그램을 실제로 작성해 보면 그렇게 간단한 작업이 아니라는 것을 깨닫게 된다. 우테코 프리코스 당시 한 줄의 입출력 메시지를 출력하는 것 또한 하나의 메서드로 분리했다. 

  • 메서드의 입력은 0 ~ n개, 출력은 0 ~ 1개 허용한다.

호출 스택은 메서드 수행에 필요한 메모리가 제공되는 공간이다. 아래는 하나의 예시 그림이다.

메인 함수에서 println()을 호출하게 되었을 때의 스택 모습

 

처음에 main()이 실행되면 이것도 함수이다 보니 스택에 먼저 쌓이고, main() 안에서 println()이 호출되면 그 위에 또 하나의 스택 프레임이 쌓인다. 그래서 아래에 위치한 메서드가 위의 메서드를 호출한 구조가 된다. 제일 위에 있는 메서드는 실행 상태를 가지며 아래의 그 함수를 호출한 함수는 대기 상태로 바뀐다. 

cf. println("This message contains " + d.x); // 괄호 안을 처리된 후 println()을 호출한다 

 

매개변수는 기본형 매개변수와 참조형 매개변수 2개가 있다.

  • 기본형 매개변수 - read only, 값을 복사해 사용한 것이기 때문에 원본 값 변경 불가
  • 참조형 매개변수 - read & write, 주소를 전달한 것이기 때문에 원본 값 변경

오버로딩은 한 클래스 안에 같은 이름의 메서드를 여러 개 정의하는 것이다. (메서드 이름(1): 메서드(n))

다음은 오버로딩의 조건이다.

  • 메서드 이름 동일 -- 이름이 같다는 것은 하는 작업이 같다는 말과 같다
  • 매개변수 개수 또는 타입이 달라야 한다
  • 반환 타입은 영향이 없다

매개변수로 int 하나, long 하나를 똑같이 받지만 순서를 달리해도 된다. 따라서 아래는 오버로딩이 인정된다.  

long add(int a, long b) { ... } // 하지만 add(3,3) 호출 시 에러 발생(ambiguous)
long add(long a, int a) { ... } // add(3, 3L) long 명시

 

생성자는 인스턴스가 생성될 때마다 호출되는 인스턴스 초기화 메서드이다. 인스턴스는 객체, 즉 iv 묶음이기 때문에 생성자를 다르게 부르면 iv 초기화를 간단히 하기 위한 메서드이다. 

다음은 생성자의 조건이다.

  • 이름이 클래스 이름과 같아야 한다 (생성자 오버로딩 가능)
  • return 값이 없다 (항상 없기 때문에 void 조차 안 붙임)
  • 모든 클래스는 반드시 생성자를 가져야 한다

this()와 this는 완전히 다른 개념이다. 

  • this() 생성자에서 다른 생성자를 호출
    • 같은 클래스 안의 생성자끼리 호출 시 클래스 이름 대신 this()를 사용한다
    • 첫 줄에서만 가능하다
  • this 참조변수
    • 지역변수와 인스턴스 변수를 구별할 때 사용한다

그리고 초기화하기 전의 지역 변수에는 가비지 값이 들어 있기 때문에 초기화를 하고 사용해야 하는데, 만약 초기화를 하지 않고 사용하게 되면 에러가 난다.

 

클래스 변수와 인스턴스 변수의 초기화 방법과 순서는 다음과 같다.

  • 클래스 변수는 클래스가 메모리에 로딩이 된 최초에 1번 초기화 한다.
  • 인스턴스 변수는 객체(인스턴스) 생성이 될 때마다 이루어진다.
  • 그렇기 때문에 초기화 순서는 논리적으로 클래스 변수(cv) 먼저 이루어진다.
    (cv -> iv)
  1. 자동 초기화 = 0으로 
  2. 간단 초기화 (대입 연산자로 명시적 초기화)
  3. 복잡 초기화  - {}, static {} -- cv, 생성자 -- iv