Java

클래스와 인스턴스 그리고 객체

클래스와 인스턴스 이전의 프로그래밍

객체를 만들기 전에 이미 익숙한 코드로 돌아가보자. 아래 예제는 간단한 더하기 프로그램이이다. 그런데 이 예제를 잘 활용하기 위해서는 상상력이 필요하다. 아주 간단한 예제지만 상당히 복잡한 작업을 실행하고 있는 로직이라고 가정하자.(실행)

package org.opentutorials.javatutorials.object;

public class CalculatorDemo {

    public static void main(String[] args) {
		// 아래의 로직이 1000줄 짜리의 복잡한 로직이라고 가정하자.
		System.out.println(10 + 20);
		System.out.println(20 + 40);
	}

}

메소드화

실행 결과는 30과 60이다. 프로그래밍의 기본은 중복을 제거하는 것이다. 위의 로직은 두개의 값을 더한다는 중복이 존재한다. 이 중복을 제거하기 위한 방법으로 고려해볼 수 있는 기능이 메소드다. 로직을 메소드로 만들면 코드의 양을 줄일 수 있고, 문제가 생겼을 때 원인을 더 쉽게 찾을 수 있다. 아래의 코드는 위의 코드를 메소드화시킨 예제다. (실행)

package org.opentutorials.javatutorials.object;

public class CalculatorDemo2 {

    public static void sum(int left, int right) {
		System.out.println(left + right);
	}

	public static void main(String[] args) {
		sum(10, 20);
		sum(20, 40);
	}

}

그런데 똑같은 수를 이용해서 더하기 뿐 아니라 평균도 구해야 한다면 어떻게 해야할까? 아래와 같이 할 수 있을 것이다. 입력값(left, right)을 변수화시키고 메소드들(sum, avg)가 이 값을 사용하면 코드의 양을 줄일 수 있게 된다. (실행)

package org.opentutorials.javatutorials.object;

public class ClaculatorDemo3 {

    public static void avg(int left, int right) {
		System.out.println((left + right) / 2);
	}

	public static void sum(int left, int right) {
		System.out.println(left + right);
	}

	public static void main(String[] args) {
		int left, right;

		left = 10;
		right = 20;

		sum(left, right);
		avg(left, right);

		left = 20;
		right = 40;

		sum(left, right);
		avg(left, right);
	}

}

객체화

평균을 계산하는 메소드 avg를 추가했고, 두개의 메소드(sum, avg)에서 동일한 입력값(10, 20)을 사용하고 있기 때문에 변수를 이용해서 좌항(left)과 우항(right)에 값을 담았다. 코드가 복잡해지기 시작한다.

상상력을 이용해서 위의 코드를 더욱 복잡하게 만들어보자. 실행 메소드인 main 에는 계산과 관련된 로직 밖에 없지만, 만약에 위의 코드에 직원 정보를 데이터베이스에서 가져오는 로직이나, 계산된 결과를 그래프로 표시하는 로직과 같은 것이 함께 위치한다면 코드는 점점 복잡해 질 것이다. 그러다 누군가 변수 left, right의 의미를 계산을 위한 좌항과 우항이 아니라 그래프의 디자인을 변경하는 코드로 사용했거나, 메소드 sum을 더하기가 아니라 요약(summary) 정보를 그래프로 표시하는 의미로 사용하게 되었다면 어떻게 될까? 하나의 프로그램 안에서 메소드나 변수의 의미가 달라지게 되는 것이다.

코드가 복잡해짐에 따라서 버그는 많아지고, 팀웍은 서서히 깨지기 시작할 것이다. 여러분이 언어의 설계자라면 이런 상황을 완화하기 위한 고민을 시작할 것이다. 이런 맥락에서 객체 지향이라는 개념이 등장하기 시작하는 것이다. 

객체 지향은 많은 선배 프로그래머들에 의해서 만들어졌기 때문에 다양한 의도가 반영된 프로그래밍 패러다임이다. 그래서 객체 지향이 만들어진 동기를 하나의 케이스로 설명하는 것은 어려운 일이다.

객체 지향의 핵심은 연관되어 있는 변수와 메소드를 하나의 그룹으로 묶어서 그룹핑하는 것이다. 위의 예제를 분석해보자. 연관되어 있는 부분과 반복적인 부분을 찾아 볼 수 있다.

메소드 sum과 avg는 변수 left와 right와 서로 연관 되어 있다. 또한 합계와 평균을 구하는 작업은 다른 에플리케이션에서도 사용할 수 있는 기능이다. 이것들을 그룹핑해서 하나의 부품으로 만들면 필요할 때마다 반복적으로 사용할 수 있을 것이다. 객체를 만들 때가 된 것이다.

아래의 예제는 의미적으로 연관된 로직들을 물리적으로 응집된 하나의 객체로 만드는 법을 설명하는 예제다. (실행)

본 수업에서는 아래의 코드를 기본 예제로 사용할 것이다. 우리 수업의 목적은 개론서이기 때문에 다양한 코드 경험보다는 개념의 빠른 이해를 목적으로 하고 있다. 처음 시작하는 입장에서는 개념도 쉽지 않는데 새로운 개념이 등장할 때마다 새로운 코드가 등장한다면 부담이 두배가 될 것이다. 필자는 개념의 이해는 쉽게 하는 것이 바람직하다고 생각한다. 그래서 본 수업은 계산기의 예제를 크게 벗어나지 않을 것이다. 빠른 속도로 수업 전체를 완주 한 후에 예제가 풍부한 다른 개론서를 천천히 완주하는 방법을 추천한다. 마음 같아서는 적당한 규모의 실제 프로젝트를 한줄 한줄 해석하면서 따라해보는 수업도 만들어보고 싶다. 할 수 있을까? ^^
package org.opentutorials.javatutorials.object;

class Calculator{
    int left, right;
     
    public void setOprands(int left, int right){
        this.left = left;
        this.right = right;
    }
     
    public void sum(){
        System.out.println(this.left+this.right);
    }
     
    public void avg(){
        System.out.println((this.left+this.right)/2);
    }
}
 
public class CalculatorDemo4 {
     
    public static void main(String[] args) {
         
        Calculator c1 = new Calculator();
        c1.setOprands(10, 20);
        c1.sum();       
        c1.avg();       
         
        Calculator c2 = new Calculator();
        c2.setOprands(20, 40);
        c2.sum();       
        c2.avg();
    }
 
}

클래스

코드가 급격하게 변했지만 차근차근 짚어가면 이해 못 할 것도 없다. 4행을 보자.

class Calculator {

위의 예에서 변수 left와 right, 메소드 sum과 avg는 연관되어 있는 로직이다. 이 로직들의 연관성은 계산을 하기 위한 것이다. 그래서 필자는 이 로직들을 대표하는 이름을 계산기라는 의미의 Calculator라고 정하고 이것들을 Calculator이라는 이름으로 그룹핑하고 싶다. 이럴 때 사용하는 키워드가 class이다. class 키워드 뒤에는 클래스 이름이 오고 그 뒤의 중괄호는 클래스의 시작과 끝의 경계를 의미한다. 이렇게 해서 더하기(sum)와 평균(avg)를 계산 할 수 있는 클래스가 만들어졌다. 클래스는 아래와 같이 정의 할 수 있다.

클래스는 연관되어 있는 변수와 메소드의 집합이다.

인스턴스

이제 클래스를 사용하는 방법을 알아보자. 클래스는 일종의 설계도다. 클래스를 정의하는 것 자체로는 할 수 있는 일이 많지 않다. 설계도를 구체적인 제품으로 만들어야 한다. 그 때 사용하는 키워드가 new이다. 25번 라인을 보자.

Calculator c1 = new Calculator();

new Calculator()은 클래스 Calculator를 구체적인 제품으로 만드는 명령이다. 이렇게 만들어진 구체적인 제품을 인스턴스(instance)라고 부른다. 아래의 관계를 기억하자.

  • 클래스 : 설계도
  • 인스턴스 : 제품

위의 코드는 new를 이용해서 만든 인스턴스를 변수 c1에 담고 있다. 변수 c1에 인스턴스를 담은 이유는 c1을 통해서 인스턴스를 제어해야 하기 때문이다. 그런데 c1 앞에 Caculator가 위치하고 있다. 지금까지 우리는 여러가지 변수를 선언해왔다. 예를들어서 변수 a는 변수가 담을 수 있는 데이터 타입에 따라서 아래와 같이 다양한 형태로 선언된다.

그럼 아래와 같은 구문은 변수명 c1의 데이터 타입이 무엇이라는 의미일까?

Calculator c1

Calculator라는 의미다. 즉 클래스를 만든다는 것은 사용자 정의 데이터 타입을 만드는 것과 같은 의미다. 하지만 지금 단계에서는 사용자 정의 타입이라는 개념이 쉽게 와닿지 않을 것이기 때문에 이에 대한 설명은 차차로 하겠다. 지금 기억할 것은 클래스를 인스턴스화 할 때는 변수에 담아야 한다는 것과 이 때 사용하는 변수의 데이터 타입은 그 클래스가 된다는 점이다.

클래스와 인스턴스는 설계도와 제품이라고 설명했다. 그럼 객체는 클래스일까 인스턴스일까? 다소 논란이 있지만 일반적으로 설계도인 클래스가 구체적인 실체인 인스턴스가 되었을 때 객체라고 부른다. 보통은 구체적인 코드 상에서 나타는 객체를 인스턴스라고 부르고, 로직을 설계 할 때 나타나는 인스턴스를 객체라고 부른다. 또는 클래스, 인스턴스의 구분없이 포괄적으로 객체라는 말을 쓰기도 한다. 이런 혼란을 피하기 위해서 필자는 가급적 클래스와 인스턴스를 사용하도록 하겠다. 이에 대한 논의는 생활코딩 페이스북 그룹을 참고하자.

클래스를 단순히 변수와 메소드의 묶음으로 보면 부족하다. 우리가 객체를 만들어서 사용하는 이유는 재활용성을 높이기 위해서다.

Calculator c1 = new Calculator();
c1.setOprands(10, 20);
c1.sum();       
c1.avg();       

Calculator c2 = new Calculator();
c2.setOprands(20, 40);
c2.sum();       
c2.avg();

위의 코드를 보면 두개의 인스턴스를 각각 c1과 c2에 담았다. 그리고 각각의 인스턴스에 소속된 메소드를 호출하고 있다. 각각의 인스턴스는 메소드 setOprands를 통해서 변수 left, right의 값을 설정하고 있다. 그 결과 인스턴스 C1과 C2는 아래와 같이 서로 다른 변수의 값을 내부적으로 가지고 있게 된다.

public void setOprands(int left, int right){
    this.left = left;
    this.right = right;
}

이 때 메소드 setOprands 내에 this.이라는 것이 있다. this는 클래스를 통해서 만들어진 인스턴스 자신을 가리킨다. 위의 코드에서 left는 매개변수이고 이 변수는 setOprands 밖에서는 접근 할 수 없다. 반면에 this.left는 4행에서 선언한 변수에 값을 설정하는 것이고 메소드 밖에서 선언한 변수는 인스턴스 내의 모든 메소드에서 접근이 가능하다. 이에 대한 이야기는 뒤에서 차차 하겠다. 이해가 안가더라도 걱정하지 말자.

변수를 다른 말로는 상태(state)라고도 표현한다. c1.sum의 결과는 30, c2.sum의 결과는 60인 것을 통해서 알 수 있듯이 상태가 다른 객체를 대상으로 메소스를 실행시키면 다른 결과를 나타내게 된다. 메소드를 다른 말로는 행동(behave)라고도 표현한다.

  • 변수 : 상태
  • 메소드 : 행동

즉 하나의 클래스를 바탕으로 서로 다른 상태를 가진 인스턴스를 만들면 서로 다른 행동을 하게 된다는 것을 알 수 있다. 하나의 클래스가 여러개의 인스턴스가 될 수 있다는 점이 객체 지향이 제공하는 가장 기본적인 재활용성이라고 할 수 있다.

객체 지향의 객체를 하나의 작은 프로그램처럼 느껴보자. 프로그램 안에 객체라는 작은 프로그램을 만드는 것이다. 이것들을 레고 블럭처럼 쌓아서 웅장한 프로그램을 만드는 것이 객체 지향이 추구하는 바라고 할 수 있다.

댓글

댓글 본문
  1. 24.01.15 완료
  2. kr1878
    c언어의 구조체와 함수의 장점을 섞어놓은 느낌이네요. 사용법도 비슷한것 같구요
  3. 서달
    덕분에 이해가 빨리 됐습니다.
    대화보기
    • wwwqiqi
      완료
    • infernist
      2022.10.25 완료
    • 연희동짬뽕
      9.18
    • MelonMusk
      09/04
      화성 갈끄니까
    • 하앙
      완료
    • 람보
      2022.8.29
    • Raye
      미쳤다 진짜... 감사합니다 진심으로 (꾸벅)
    • 김경모
      220621
    • PassionOfStudy
      복습 1일차~
    • 치키티타
      220607
    • PassionOfStudy
      클래스와 인스턴스! ^__^ 차근차근한 설명 감사합니다!
    • dogchang
      감사합니다. 일하느라 매일같이 보지못하다보니까 늦게 확인했어요.. 결국 헷갈리던 이유는 번역에서 오는 괴리감? 같은 것 때문이었다고 볼 수 있겠네요.
      대화보기
      • 20220426
      • Clna
        저도 궁금해서 찾아보니까 상황에 맞게 골라서 쓰는거라고 하더라구여 https://blog.naver.com......488 이 글 참고하시면 좋을거같네요
        대화보기
        • Chang Yeon Lee
          아직 강의를 봐도 이해가 잘 안되는 부분이 있는데..

          class는
          class Calulator() {
          ...
          }
          의 내용을 담은 부분을 의미한다고 이해했습니다.

          근데 인스턴스와 오브젝트에 대한 개념이 구분이 잘 안돼요..

          Calulator c1 = new Calulator();
          에서 c1이 인스턴스라는건지 오브젝트라는건지.. 처음 이해는 new Calulator에서 Calulator가 오브젝트라는건지..
          c1.sum();에서 이 오브젝트의 메소드를 호출하는 행위를 인스턴스라고 하는건지..

          이 부분 개념이해가 잘 안되네요 ㅠㅠ 괜히 다른 페이지 검색했다가 c1부분이 객체(Object)라고 적어둔것도 있고 new Calulator에서 Calulator가 인스턴스라는 글도 있고 뭔가 헷갈립니다.. 영상에서도 보다보면 어느부분을 말하는건지 모르겠어요 ㅠㅠ
          누가 도와주십쇼.. 변비걸린거 같은기분이에요..
        • aesop0207
          220321 Mon.
        • 모찌말랑카우
          22.02.14 완료
        • aesop0207
          220207 Mon.
        • 행달
          22.02.04 완료!
        • 구니
          22.01.30 완료
        • Haakoo
          다른 강의 보다가 이해하기 너무 힘들어서 하루종일 붙잡고있다가
          이 강의 보니 이해가 정말 잘가네요 감사합니다
          마지막 말씀이 정말 힘이되네요
        • 민둥빈둥
          22.01.28
        • 안젤라비
          21-12-22 WED
        • syh712
          2021-12-02
          <클래스와 인스턴스 그리고 객체>

          1. **메소드화 - 메소드로 만들면 코드의 양을 줄일 수 있고, 문제가 생겼을 때 원인을 더 쉽게 찾을 수 있다**

          2. 유지보수를 위해 쓰기 전에 그 코드가 있는지 찾아보기(동일이름)

          *유지보수를 위해 코드 안에 연결된 것은 최대한 가까이. (연관되어 있는 것끼리 그룹핑)⇒객체 지향프로그래밍(연관된 것들 묶기)

          1. 객체화: Calculator c1= new Calculator();

          new Calculator()은 클래스 Calculator를 구체적인 제품으로 만드는 명령이다. 이렇게 만들어진 구체적인 제품을 인스턴스(instance)라고 부른다.

          - 클래스 : 설계도
          - 인스턴스 : 제품
        • 드림보이
          2021.11.30. 클래스와 인스턴스 그리고 객체 파트 수강완료
        • 네제가해냈습니다
          211109
        • IaaS
          package org.opentutorials.javatutorials.object;

          class Calculator{

          int left , right ;

          public void setOprands(int left , int right) {
          this.left = left;
          this.right = right;
          }

          public void sum(){
          System.out.println(this.left + this.right);
          }

          public void avg(){

          System.out.println((this.left + this.right) / 2);
          }
          public void sum2(int left , int right) {
          System.out.println(left + right);
          }


          }




          public class CalculatorDemo{

          public static void main(String[] args) {


          Calculator c1 = new Calculator();
          c1.setOprands(10, 20);
          c1.sum();
          c1.avg();


          Calculator c2 = new Calculator();
          c2.setOprands(30, 40);
          c2.sum();
          c2.avg();

          Calculator c3 = new Calculator();
          c3.sum2(50, 60);
          }
          }

          만약 this left = left를 하지 않았다면 sum2 , c3.sum2처럼 파라미터를 계속 입력해주어야 한다.
        • IaaS
          2021.11.1 수강완료


          Calculator c1 = new Calculator(); 이 부분의 설명이 처음에 난해해서 정리를 해보니 생각보다 간단한 문제였다.

          일상생활로 생각해보자면 이렇다.

          CU라는 편의점이있다. 이 편의점에는 아이스크림,과자,음료수 등등이 있다.
          그러면 이 편의점을 이용하려는 사람이 있을 것이다.
          예를들어 철수 , 영희가 이 편의점을 이용한다. 그러면 철수와 영희는 이 편의점에서 과자(포스틱, 초코칩쿠키), 음료수(이프로, 토레타),아이스크림(메로나 , 스크류바) 골라서 먹을 수 있다.

          즉 위의 말을 코드로 짜면

          CU 철수 = new CU();
          철수.아이스크림 = 메로나
          철수.과자 = 초코칩쿠키

          CU 영희 = new Cu();
          영희.아이스크림 = 스크류바
          영희.음료수 = 토레타

          이렇게 설명이 가능하다.

          즉 인스턴스란 CU에 들어오는 손님이라고 생각하면 된다.
        • 성치
          2021-10-26일 완료
        • H4PPY
          1016
        • 미NI언
          10.10 완료!
        • 하성호
          20210915
        • 멋을아는남자
          잘 보고 갑니다.
        • 베이스박
          210824학습완료. 감사합니다.
        • super1Nova
          210819
        • Eunyoung Eunice Kang
          2021.8.16(월) 완료
        • 개발꾸꾸
          5.13 가즈아 뀨뀨
        • 악어수장
          2021-5-12 2회독 완료
        • 드림보이
          수강완료했습니다...
        • 코린이
          갑자기 어려워진 느낌인데 설명이 굿굿
        • 메소드는 함수로서 중복되는 문장을 간결하기위해 사용하는 도구이다.

          이는다른프로그래밍과 동일한 원리로 판단하면될거같은데 자바가 이런프로그래밍과 다른

          근본적인원인을 이곳에서 설명해주는것같다.

          메소드에 입력할값들이 사람에따라서 다른의미를 가지게된다.

          그입력할 값이 코드적으로 거리가멀게되면 memory leak가 발생하는데

          이를 보완해주는 해결책으로 개발자들은 class와 instance를 만들었다

          instance를 통해 지정한값을 받아서 class를 통해 메소드로 결과값을 도출해주는것을 알수있다.
        • 감자
          설명을 이해하기 쉽게 해주시는 거 같습니다. 감사합니다!
        • 하연주
          210206 완료
        • 기밀사항
          아직 완벽히 이해는 안되지만, 넘어갑니다. 화이팅
        • 준Fe
          20210115
        • 자바남
          자바남 수업완료
        • 김태현
          2회 수업완료


          int left, right;
          this.left = left;
          //this 자기 자신 int left,을 가리킴

          left = this.left;
          //left;를 모두 담는다는 의미

          this가 어디에 있느냐에 따라 다름

          -----------------------------------

          class
          // 인트 선언
          // 인스턴스에서 지정한 값을 받아 들이는 소스
          // 받아 드린 값을 계산할 공식 코드를 만듬


          인스턴스
          new에 class에서 만든 공식을 담는다
          >> class에서 받아 쓸 값을 지정한다
          >> 실행시킨다.

          // 객체지향은 클리스와 인스턴스를 나누어 코드를 만드는 것이다.