3. 클래스와 객체


javalogo

클래스와 객체

객체 지향 프로그래밍에서 실제 세상의 물체들을 객체로 모델링을 한다.

실제 세상에서는 많은 사람들이 있지만 사람을 사람으로 구별지을 수 있는 공통된 특징을 가지고 있다.

이 공통된 특징, 속성을 기술한 것이 클래스이다.

그리고 사람은 그 속성에 따라 만들어진 인스턴스(Instance), 객체(Object)라고 할 수 있다.

즉, 클래스는 객체를 생성하기 위한 설계도, 틀이라고 할 수 있고 객체는 클래스로 만들어낸 실체라고 볼 수 있다.

Class 구성

자바에서 클래스를 선언할 때 class 키워드를 사용한다.

다음은 자바에서 클래스를 선언하는 예이다.

/**
* code_01
*/
public class Person {
    public String name;         // 필드(field)
    public int age;

    public Person() {           // 생성자(Constructor)
    }
    
    public Person(String s) {   // 생성자(Constructor)
        name = s;       
    }

    public String getName() {   // 메소드(method)
        return name;    
    }
}
  • 클래스 접근 권한, public
    • 접근 권한을 표시하는것으로, public 접근 권한은 다른 모든 클래스들이 이 클래스에 대해 사용, 접근이 가능함을 뜻한다.
  • class Person
    • Person이라는 이름의 클래스를 정의한다. class 다음에 클래스의 이름을 정의한다.
    • 클래스는 ‘{‘으로 시작하고 ‘}’으로 닫으며 이곳에 모든 멤버 필드와 메소드를 구현한다.
  • 필드(field)
    • 값을 저장할 멤버 변수를 선언한다. 멤버 변수 혹은 필드라고 부른다.
    • 필드 앞에 붙은 접근 지정자 public은 이 필드가 다른 클래스에서 접근될 수 있도록 공개한다는 뜻이다.
  • 생성자(Constructor)
    • 클래스의 이름과 동일한 메소드를 생성자(constructor)라고 한다.
    • 생성자는 이 클래스의 객체가 생성될 때만 호출되는 메소드이다.
  • 메소드(method)
    • 메소드는 실행 가능한 함수이며 객체의 행위를 구현한다.
    • 메소드 앞에 붙은 접근 지정자 public은 이 메소드가 다른 클래스에서 접근될 수 있도록 공개한다는 뜻이다.

객체 생성과 레퍼런스 변수

code_01에서 정의한 클래스의 객체 혹은 인스턴스를 생성해보자.

Person 클래스의 객체를 생성하고 활용하는 예는 다음과 같다.

/**
* code_02
*/
public static void main(String args[]) {
    Person aPerson;                 // 객체에 대한 레퍼런스 변수 aPerson 선언
    aPerson = new Person("김길동"); // Person 객체 생성

    aPerson.age = 30;
    String s = aPerson.getName();
}
  • Peson 클래스의 레퍼런스 변수 aPerson 선언
    • 객체를 생성하기 전 객체를 가리킬 레퍼런스 변수의 선언이 필요하다.
        Person aPerson;  // 레퍼런스 변수 aPerson 선언
      
    • 이 선언문으로는 Person 객체가 생성되지 않는다. 변수 aPerson은 Person 타입의 객체에 대한 레퍼런스를 가지는 변수의 선언일 뿐 객체 자체는 아니다.
    • 따라서 아직 객체가 생성된 것이 아니며 어떤 객체도 가리키고 있지 않은 상태이다.
  • 객체 생성, new 연산자 이용
    • 자바에서는 반드시 new 연산자를 사용하여 다음과 같이 객체를 생성해야 한다.
        aPerson = new Person("김길동");
      
    • 이 문장은 생성된 Person 객체에 대한 레퍼런스 값을 aPerson 변수에 대입한다.
    • new 연산자에 의해 객체가 생성되는 과정은 다음과 같다.
      1. Person 타입의 객체 메모리 공간확보
      2. Person(String s) {...} 생성자가 생성되어 name 필드 값을 "김길동"으로 설정

객체 멤버 접근

객체의 멤버에 접근할 때는 다음과 같이 점(.) 연산자를 붙인다.

객체 레퍼런스.멤버

예를 들어, 다음 코드는 aPerson 객체의 age 필드에 30을 대입한다.

aPerson.age = 30;

반대로 aPerson 객체의 age 필드의 값을 읽어내고자 하면 다음과 같이 할 수 있다.

int i = aPerson.age;

다음 코드는 aPerson 객체의 getName() 메소드를 호출한다.

String s = aPerson.getName();

Ex. 지수 클래스 MyExp

/**
* code_03
*/
public class MyExp {
    int base;
    int exp;
    int getValue() {
        int res=1;
        for (int i = 0; i < exp; i++) {
            res = res * base;
        }
        return res;
    }

    public static void main(String[] args) {
        MyExp num1 = new MyExp();
        num1.base = 2;
        num1.exp = 3;
        MyExp num2 = new MyExp();
        num2.base = 3;
        num2.exp = 4;

        System.out.println("2의 3승 =" + num1.getValue());
        System.out.println("3의 4승 =" + num2.getValue());
    }
}

MyExp는 지수 값을 표현하는 클래스로서 두 개의 정수형 멤버 필드 baseexp를 가진다.
\(2^3\)의 경우, base는 2이며, exp는 3이다. 또한 MyExp는 정수 값을 리턴하는 getValue()라는 메소드를 제공한다.
getValue()baseexp값으로부터 지수를 계산하여 정수 값으로 리턴한다.
예를 들어, base가 2, exp가 3이라면 getValue()는 8을 리턴한다.

객체 배열

자바 기본타입의 배열과 같이 객체가 원소인 객체 배열도 만들 수 있다.

객체 배열이란 객체에 대한 레퍼런스를 원소로 갖는 배열이다.

다음 코드를 이용하여 객체 배열을 설명한다.

/**
* code_04
*/
Person[] pa;                // 배열에 대한 레퍼런스 선언
pa = new Person[10];        // 레퍼런스 배열 생성
for (int i = 0; i < pa.length; i++) {
    pa[i] = new Person();   // 배열의 원소 객체 생성
    pa[i].age = 30 + i;     // 객체 배열 사용
}
for (int i = 0; i < pa.length; i++) {   // 배열 원소 출력
    System.out.println(pa[i].age);
}

배열에 대한 레퍼런스 선언

  • 다음은 Person 타입의 객체 배열 선언문이다. 객체 배열에 대한 레퍼런스 변수 pa를 선언한다.
    Person[] pa;
    
  • 이 선언문에 의해 레퍼런스 변수만 생성될 뿐 배열이 생성되지 않는다. 다음과 같이 원소 개수를 지정해서는 안된다.
    Person[10] pa;  // <- 오류
    

    레퍼런스 배열 생성

  • 두 번째로 다음 소스는 10개의 레퍼런스 배열을 생성한다. 배열의 원소는 객체에 대한 레퍼런스이다.
    pa = new Person[10];
    

    객체 생성

  • 다음 소스를 이용하여 Person 객체를 하나씩 생성하여 레퍼런스 배열의 각 원소에 대입한다.
    for (int i = 0; i < pa.length; i++) {
      pa[i] = new Person();
      pa[i].age = 30 + i;   
    }
    
  • 배열의 크기만큼 Person 객체를 생성하여 배열의 원소인 레퍼런스 변수에 대입을 한다.
  • 이렇게 하여 Person 타입 객체 배열이 생성된다.

    객체 접근

  • pa의 배열의 각 원소는 Person 객체이므로 배열의 i번째 객체를 접근하기 위해서는 pa[i] 레퍼런스를 사용한다.
    pa[i].age = 30 + i;
    
  • 위 소스는 i번째 Person 객체의 필드 age에 30+i 의 값을 대입하는 소스이다.
  • 배열의 각 Person 객체의 age필드 값을 화면에 출력하는 소스는 다음과 같다.
    for (int i = 0; i < pa.length; i++) {   // 배열 원소 출력
      System.out.println(pa[i].age);
    }
    

    이 소스의 실행 결과는 다음과 같다.

30 31 32 33 34 35 36 37 38 39


기본 메소드

메소드에는 모든 클래스에 공통적인 것이 있다. 객체에 대한 생성자(constructor), 수정자(mutator), 접근자(accessor)와 두 개의 특별한 메소드(toString, equals), 그리고 main()과 같은 정적 메소드(static method)가 있다.


생성자

객체의 기본적인 속성은 객체의 생성과 동시에 그 값을 초기화 시킬 수 있는 것이다. Java에서 객체를 생성하고 초기화를 담당하는 특별한 기능을 하는 메소드가 생성자(constructor)이다. 클래스는 경우에 따라 여러 개의 생성자를 가질 수 있다.

생성자가 만약 명시적으로 정의되지 않은 경우 시스템에 의해 기정 생성자(default constructor)가 묵시적으로 자동 제공되어 각 데이터 필드 멤버들을 미리 정해 놓은 값들로 초기화 한다.

즉, 기본 데이터 타입 필드들은 0으로 초기화 되고, 참조 데이터 타입 필드들은 null로 초기화 된다.

이런 기정 생성자는 매개 변수가 없기 때문에 무인자 생성자(no-arg constructor)라고 한다.

public class Date {
    private int month;
    private int day;
    private int year;
        // 무인자 생성자
    public Date() {
        month = 1;
        day = 1;
        year = 2019;
    }
        // 3인자 생성자
    public Date(int theMonth, int theDay, int theYear) {
        month = theMonth;
        day = theDay;
        year = theYear;
    }
        // Date 객체가 같은 값을 가지고 있으면 true를 반환
    public boolean equals(Object a) {
        if (!(a instanceof Date))
            return false;
        Date d = (Date) a;
        return d.month == month && d.day == day && d.year == year;
    }
        // Date 를 스트링으로 변환
    public String toString() {
        return month + "/" + day + "/" + year;
    }
}

생성자는 그 이름이 클래스 이름과 똑같고 반환 값이 없는 메소드 형식으로 정의된다.

일단 생성자가 명시적으로 정의되면 기정 생성자는 시스템에 의해 더 이상 자동적으로 제공되지 않는다.

따라서 기정 생성자와 같은 것이 필요하다면 명시적으로 다시 정의해야한다.


수정자와 접근자

클래스의 데이터 필드는 일반적으로 private로 선언하여 클래스 외부에서는 이 필드들을 직접 접근할 수 없도록 하는 것이 원칙이다.

그러나 때때로 필드의 값을 검색, 변경해야할 경우가 있지만 필드를 public으로 선언하면 정보 은닉 개념을 위배하는 것이기 때문에 문제가 된다.

이에 대한 해결책으로 각 필드의 값을 다루는 메소드를 제공하는 것이다.

이러한 메소드 가운데 객체의 필드 값을 검색만 할 수 있는 메소드를 접근자(accessor)라고 하고 필드값을 변경할 수 있는 메소드를 수정자(mutator)라고 한다.

경우에 따라 접근자와 수정자를 특정 필드만을 검사하거나 변경시키게 할 수 있다.

이 경우 접근자는 getMonth()와 같이 get으로 시작하고, 수정자는 setMonth()와 같이 set으로 시작하는 것이 일반적이다.

수정자를 사용하는 이유는 객체의 필드 값들을 올바르게 변경시킬 수 있게 하기 위함이다.


toString()과 equals()

객체의 데이터 필드 값들은 일반적으로 print() 메소드를 사용하여 출력한다. 각 클래스는 toString() 메소드를 독자적으로 구현함으로써 원하는 형식으로 출력에 적합한 스트링을 반환한다.

equals() 메소드는 두 개의 참조 변수가 참조하는 객체의 내용이 동일한지를 검사하는 메소드로, 매개 변수는 항상 다음과 같이 Object 클래스의 참조 타입이 된다.

public boolean equals(Object a)

각 클래스마다 그 클래스에 적합한 equals() 메소드를 구현하면 독자적인 객체 비교를 할 수 있다.


static 메소드와 필드

Java에서 메소드는 일반적으로 생성된 객체에 의해 호출되는 형식으로 사용된다.

그러나 정적 메소드(static method)는 객체를 생성하지 않고도 사용할 수 있는 메소드 이다.

예를 들어 정적 메소드 println()System.out.println()처럼 객체를 생성하지 않고 그대로 사용한다.

가장 일반적인 정적 메소드의 예가 main() 메소드이다. 다른 정적 메소드는 String, Integer, Math클래스에서 찾아볼 수 있는데, String.valueOf, Integer.parseInt, Math.max 등이 그 예이다.

이런 정적 메소드는 객체 또는 클래스 이름을 통해서 사용할 수 있다.

또한 클래스의 데이터 필드가 static으로 선언되면 이 멤버는 그 클래스에서 오직 하나의 인스턴스만 생성되어 클래스 전체가 공유한다.

이 필드는 그 클래스의 어떤 객체에도 속하지 않으면서 클래스 내에서 공용으로 사용하는 전역 변수처럼 취급된다.

public class Book {
    private int id;
    private static int count;
}

위의 예에서 모든 Book 클래스의 객체들은 각각의 id를 갖고 있지만 count는 Book 클래스의 모든 객체들이 공유한다.

따라서 보통 상수를 정의해서 사용할 때는 다음과 같이 static으로 선언하는 것이 좋다.

public final static int MAX = 2113895;

패키지

패키지(Package)는 관련된 클래스들을 한곳에 수집해 높은 것이다. 같은 패키지 내에 있는 클래스들끼리는 다른 패키지에 있는 클래스들 보다 정보 은닉의 제한을 상대적으로 덜 받게 된다.

Java는 java.applet, java.awt, java.io, java.lang, java.util 등의 여러 가지 패키지를 기본적으로 제공하고 있다.

일반적으로 패키지 P에 포함되어 있는 클래스 C는 P.C로 나타낸다. 예를 들어 현재 시간과 날짜를 초기 상태로 갖는 Date 객체를 생성하기 위해서는 다음과 같이 java.util 패키지에 있는 Date 클래스를 이용하면 된다.

java.util.Date today = new java.util.Date();

클래스 이름에 패키지 이름을 명세하면 Date 클래스와 같이 다른 패키지 내의 클래스 이름이 서로 충돌하는 것을 막을 수 있다.

그러나 사용할 클래스를 기술할때마다 이에 해당하는 모든 패키지와 클래스 이름을 써야 하는 것은 불편하기 때문에 import 명령을 사용하여 패키지 이름을 생략할 수 있다.

import java.util.Date;
import java.io.*;

참고 문서

  • 이석호. (2004). 자료 구조와 JAVA. 정익사.





© 2019. by RaP0d

Powered by aiden