728x90

 

Q > 예를 들어 int[] a = new int[5]가 있으면 이 배열도 객체라는데 무슨말인지 잘 모르겠습니다.

    int라는 이름의 클래스가 있다는 말과 동일하나요? Integer Wrapper 클래스와 연관성은 없는 것 인가요? 알기 쉽게 설명해주세요.

 

 

A >

배열을 객체라고 부른다. 그 이유가 무엇일까? 보통 객체를 생성한다라고하면 클래스의 객체를 많이 떠올린다. 그렇다고 클래스부터 설명을 할 수는 없는 노릇이다. 클래스에 대해서는 안다고 가정하고 클래스와 객체와의 관계에 대해 생각해보자.

 

클래스는 자료형이다. 이 사실을 잊어선 안된다. 왜냐면 자료형은 그 자료가 가진 형태를 나타내는 것이기 때문에 스스로는 사용될 수 없다. 누군가가 그 형태를 가지는 실체를 만들어 내야만 사용할 수 있다. 이런 관점에서 배열과 배열 객체를 바라보자. 배열도 동일한 자료형의 값을 담을 수 있는 공간이다. 그렇기 때문에 int[] a; 라고한다면, a라는 변수는 이제부터 int라는 자료형의 값을 담을 수 있어. 딱 여기까지다. 그 이상도 이하도 아니다. '담을 수 있다'는 것과 '사용할 수 있다'는 다르다. (전달이 잘 되고 있는지 모르겠다.)

 

728x90

 

int[] a; 는 어떤 의미에서 추상적인 표현이다. int라는 자료형의 값을 여러개 담을 수 있는 것. 더 나아가 이것을 실체화 시켜야 사용 가능하다는 것이다. 바로 그 실체화 하는 코드가 new int[5] 인 것이다. (다형성에 대해서 알고 있으면 더 좋을 것 같다.) 이 new int[5]라는 코드 덕분에 a는 배열 객체가 되는 것이다. 그 이전에는 그저 int[] 타입의 변수 a를 정의했는데 아직 객체가 할당되지 않은 상태인 것이다.

 

 

그리고 다른 관점에서 한번 더 생각해보자. 배열은 다른 리터럴 값들과 달리, '배열은 어떠한 값이다.'라고 정의할 수 없다. 즉, new라는 연산자를 통해 heap 메모리 영역에 생성된 공간을 참조하는 주소값을 가지고 있기 때문이다. a는 배열에 할당된 값의 주소를 가리키고 있지 배열의 값을 가리키고 있지 않다는 얘기이다. 그렇기 때문에 이 배열도 객체라고 한다.

 

 

 

int라는 이름의 클래스가 있다는 말과 동일하다는 얘기는 클래스도 결국 자료형임을 알고 있는 것 같다. 자료형의 관점에서 int[] 타입의 클래스 라고도 볼 수 있다고 생각한다. 하지만 그렇게는 잘 얘기하지 않는다. 흔히 primitive 타입이라고 부르는 원시타입에 대해서 자료형이라고 하지 클래스라고는 잘 하지 않기 때문이다.

 

배열 객체의 얘기를 함에 있어서 Integer Wrapper 클래스와는 관련성이 부족한것 같다. Integer는 primitive 타입인 int의 reference타입으로, 흔히 8대 자료형이라 부르는 primitive 타입들의 reference 타입의 클래스를 Wrapper 클래스라고 하기 때문이다. 이런 연관성을 생각한 이유는, 내 생각에, 배열 객체 -> 객체 -> 클래스 -> int -> primitive type -> Wrapper class 이런 흐름이었지 않나 생각한다. Wrapper class는 primitive type을 클래스화 한것이다.

 

 

 

 

728x90
728x90

 

나는 개인적으로 클래스와 객체를 설명할 때 붕어빵 틀을 비유하는걸 싫어한다.

마음에 들지 않는다. 내가 배울 당시 이렇게 배우지 않았을 뿐만 아니라 개인적으로 잘 와닿지도 않았다.

그래서 내가 배운 방법으로 클래스란 무엇이며 이 클래스와 객체와의 관계를 어떻게 이해하면 좋은지 정리해보려 한다.

 

클래스는 우리가 일상 생활속에서 볼 수 있는 모든 것들을 표현할 수 있는 도구라고 생각하는 것도 좋은 발상이다.

클래스를 구성하고 있는 큰 두가지 개념이 있다. 하나는 멤버필드라는 개념이며 다른 하나는 멤버 메서드라는 개념이다.

 

멤버 필드란, 그 클래스가 표현하고자 하는 대상이 가지는 속성들을 의미한다. 높이, 색상, 속도, 속력, 방향, 무게 등 일반적인 사물들이 가질 수 있는 모든 속성들을 말한다.

멤버 메서드란, 그 클래스가 표현하고자 하는 대상이 할 수 있는 어떤 동작, 행위들을 의미한다. 클래스가 사람을 표현하고 싶다면, 앞으로 걷기, 뒤로 걷기, 밥먹기 등이 될 수 있다. 강아지라면 밥먹기, 짖기, 뛰기, 재롱부리기 등이 해당한다.

 

실제로 사람은 훨씬 복잡하지만 클래스로 사람을 표현한 예시이다.

 

class Person {
    private String gender;
    private int tall;
    private double weight;
    private double footSize;

    public void walk() { }
    public void eat() { }
}

 

이 클래스에서 정의하고 있는 사람이라는 것은, 성별, 키, 몸무게, 발 사이즈를 알 수 있으며, 걷거나 먹는 행위를 할 수 있다.

앞서 말했듯 이정도로 사람을 표현하기엔 턱없이 부족하지만 간단한 예시를 들자면 이렇다는 것이다.

하지만 이렇게 정의했다고 해서 바로 사용하고 있는것은 아니다. 이건 마치 설계도면과도 같은 것이다.

스스로는 할 수 있는게 아무것도 없기 때문이다.

김춘수 시인의 꽃이라는 시에서 처럼

내가 클래스의 객체를 만들어 주기 전에는 클래스는 다만 하나의 설계 도면에 지나지 않는다.

 

클래스의 개념에 대해 찾아본다면 기본적인 자바 프로그램을 만들어 보면서 알게 되었을 것이다.

모든 자바 프로그램은 main 메서드에서 시작한다고.

이 main 메서드 안에서 클래스의 객체를 만들어주어야 비로소 나에게 사용할 수 있는 객체로서의 의미를 가지게 된다.

 

 

728x90

 

 

자꾸 객체 객체 하는데, 그래서 객체가 뭔지 답답할지 모르겠다.

다음 예시를 보기 전에 클래스와 객체의 관계에 대해서 알아보자.

 

우리가 많이 사용하는 스마트폰. 이건 클래스다. 하지만 내가 손에 들고 있는 스마트폰은 클래스가 아니다. 객체다.

컴퓨터를 하기 위해서 지금 내가 손으로 쥐고있는 마우스는 객체다. 하지만 마우스는 객체가 아니가. 클래스다.

지금 책상위에 놓여있는 달력은 객체다. 하지만 달력은 객체가 아니다. 클래스다.

'머릿속에 손목 시계를 떠올려보세요' 해서 떠오르는 일반적으로 알고있는 손목시계는 클래스다. 하지만 지금 손목에 차고 있는 이 시계는 객체다.

 

무슨 말장난이냐 싶을지 모르겠지만. 위에서 말한 그대로다.

클래스란 이렇게 우리가 특정 사물에 대해서 일반적으로 떠올릴수 있는 개념적인 것이라면, 객체는 이것이 실제 눈앞에 형태를 갖추고 있는 것 그 자체이다. 즉, 클래스는 '이 사물은 이렇게 생겼고 어떤어떤 기능을 가지고 있어요'라면 객체는 클래스가 설명하고 있는 것을 토대로 만든 것이라고 할 수 있겠다. 그래서 마우스는 클래스지만 지금 내 손 안에 들어와 있는 눈에 보이는 이 마우스는 클래스가 아니고 객체라는 것이다.

 

 

 

위에 작성한 Person 클래스를 사용하는 main 메서드를 작성해 보자.

 

public class ClassTest {
    public static void main(String[] ar) {
        Person p1 = new Person();
        Person p2;
        p2 = new Person();
    }
}

 

ClassTest라는 이름의 클래스는 main 메서드를 담고 있다.

3라인과 4-5라인이 의미하는 바는 똑같다.

하지만 굳이 4라인과 5라인으로 나눈데는 이유가 있다.

3라인의 의미는, p1변수는 Person클래스에서 정의한 형태를 가지는 객체를 담을 변수야. 그리고 난 이 변수에 Person 객체를 담았어. 라는 말이다.

4라인의 의미는, p2변수는 Person클래스에서 정의한 형태를 가지는 객체를 담을 공간이야. 라는 말이다.

5라인의 의미는, Person객체를 하나 만들었고, 나는 이 형태를 가지는 변수 p2에 담았어. 라는 말이다.

 

건축물에 비유하자면 단순히 클래스를 만들기만 해서는, 객체화를 하지 않는다면, 그저 설계도만 만들고 건물은 올리지 않은것이 된다.

하지만 설계도만 가지고 있다면 수십 수백 수천개라도 객체를 만들어낼 수 있다. 너무나 당연하게도. 그렇기 때문에 자바는 재사용성이 뛰어난 언어라는 얘기와도 일맥상통한다.

 

이번에는 클래스와 객체의 관계에 대해서 다루었기 때문에 이정도만 정리해두려 한다.

다음에는 자바의 특징중에 하나인 다형성에 대해 정리하면서 재사용성에 대해서 자세히 정리 해둬야 겠다.

 

 

 

 

728x90
728x90

 

자바에는 문자열을 처리하기 위한 클래스로 String, StringBuilder, StringBuffer 클래스가 있다.

단순히 문자열을 처리하기 위함이라면 (예를 들면 System.out.println("문자열");  이런 경우) 어떤 것을 사용해도 별다른 차이는 없다고 알고 있다. 하지만 문자열을 더하는 (+) 연산을 할 경우에 퍼포먼스상의 차이가 발생한다.

 

백번 듣느니 한 번 보는게 낫다.

실제로 아래와 같은 예제는 많은 곳에서 흔히 볼 수 있는 예제일 것이다.

 

package xxxelppa.tistory.com;

public class StringTest {
    public static void main(String[] ar) {
        long StartTime = 0;
        long EndTime = 0;
        
        String str;
        StringBuilder strBdr;
        StringBuffer strBfr;
        
        for(int k = 0; k < 9; ++k) {
            str = new String("문자열");
            strBdr = new StringBuilder("문자열");
            strBfr = new StringBuffer("문자열");
            
            System.out.println("============== " + (k + 1) + "번째 비교 결과" + " ==============");
            
            StartTime = System.nanoTime();
            for(int i = 0; i < 10000; ++i) {
                str += i;
            }
            EndTime = System.nanoTime();
            System.out.println("String의 실행 시간\t\t: " + (EndTime - StartTime));
            
            StartTime = System.nanoTime();
            for(int i = 0; i < 10000; ++i) {
                strBdr.append(i);
            }
            EndTime = System.nanoTime();
            System.out.println("StringBuilder의 실행 시간\t: " + (EndTime - StartTime));
            
            StartTime = System.nanoTime();
            for(int i = 0; i < 10000; ++i) {
                strBfr.append(i);
            }
            EndTime = System.nanoTime();
            System.out.println("StringBuffer의 실행 시간\t: " + (EndTime - StartTime));
            System.out.println("=============================================");
            System.out.println();
            
        }
        
        
    }
}

 

위 예제는 똑같이 문자열을 더하는 연산을 했을 때 실행 속도를 비교하는 소스이다.

실행 결과는 다음과 같다.

 

============== 1번째 비교 결과 ==============
String의 실행 시간             : 192313252
StringBuilder의 실행 시간    : 417904
StringBuffer의 실행 시간     : 613102
=======================================

============== 2번째 비교 결과 ==============
String의 실행 시간             : 165631465
StringBuilder의 실행 시간    : 427854
StringBuffer의 실행 시간     : 460046
=======================================

============== 3번째 비교 결과 ==============
String의 실행 시간             : 178263707
StringBuilder의 실행 시간    : 276554
StringBuffer의 실행 시간     : 340644
=======================================

============== 4번째 비교 결과 ==============
String의 실행 시간             : 184807363
StringBuilder의 실행 시간    : 371959
StringBuffer의 실행 시간     : 376934
=======================================

============== 5번째 비교 결과 ==============
String의 실행 시간             : 178070851
StringBuilder의 실행 시간    : 436048
StringBuffer의 실행 시간     : 479068
=======================================

============== 6번째 비교 결과 ==============
String의 실행 시간             : 133046927
StringBuilder의 실행 시간    : 268945
StringBuffer의 실행 시간     : 361423
=======================================

============== 7번째 비교 결과 ==============
String의 실행 시간             : 153004492
StringBuilder의 실행 시간    : 210708
StringBuffer의 실행 시간     : 355277
=======================================

============== 8번째 비교 결과 ==============
String의 실행 시간             : 137168318
StringBuilder의 실행 시간    : 289139
StringBuffer의 실행 시간     : 418782
=======================================

============== 9번째 비교 결과 ==============
String의 실행 시간             : 136230374
StringBuilder의 실행 시간    : 309039
StringBuffer의 실행 시간     : 373129
=======================================

 

728x90

 

 

실행 시간 결과는 String > StringBuffer > StringBuilder 순서이다.

그렇다면 왜 이런 결과가 나왔을까?

 

String은 문자열을 더하기 위해 String 공간을 하나 더 만드는 작업을 한다.

이게 무슨 소리일까? String이라는 클래스가 가지는 특성이라고 받아들일 수 밖에 없다.

String 클래스형 자료형으로 선언된 문자열이 메모리상에 할당이 되면, 이 값을 바꿀 수 없는 값이 된다.

만약 이 문자열이 더해지거나 빼는 연산을 하게 될 경우 기존에 할당한 메모리에 저장된 값이 바뀌지 않는다는 소리다.

 

String addr100 = new String("문자열A");
String addr101 = new String("문자열B");

addr100 = addr100 + addr101;

 

이 소스가 실행되면 메모리상에서 어떤 일이 생길까?

 

 

위에 첨부한 이미지를 함께 보자.

만약 메모리상에 위처럼 문자열이 저장 되어있다고 했을 때, 100번지의 "문자열A"와 101번지의 "문자열B"를 더해 새로운 문자열 "문자열A문자열B"를 만든 경우이다.

addr100 변수는 100번지를 바라보고 있다가 새로운 102번지를 바라보게 된다. (바라본다 보다 참조한다는 말이 더 옳다)

그럼 100번지는?

가비지 컬렉터가 나중에 알아서 처리해주니 신경쓰지 말자.

 

String 클래스의 경우 기존 값에 새로운 값을 더하는 것이 아닌, 위와 같이 추가 연산이 필요하기 때문에 StringBuilder나 StringBuffer보다 퍼포먼스가 떨어진다.

그렇기 때문에 문자열 연산에서는 StringBuilder나 StringBuffer 클래스의 사용을 권장한다.

 

(내용이 너무 길어질 것 같아서 추가하지 않으려 했지만, 급하면 이 부분은 넘어가도 좋다.)

지금까지 문자열을 더하는 연산이 빈번하게 발생한 부분은 배치 프로그램에서 쿼리를 작성할 때였다.

다음과 같은 쿼리가 있다고 하자. (다소 과장된 부분이 있다.)

 

SELECT
    AAA
    , BBB
    , CCC
    , DDD
    , EEE
FROM TEST
WHERE
    1=1
    AND AAA > 100
    AND BBB > 200
    AND CCC > 300
    AND DDD > 400

 

이 때 이 쿼리문을 다음과 같이 변수에 담을 수 있다.

 

String query = "";

query += "SELECT           ";
query += "    AAA          ";
query += "    , BBB        ";
query += "    , CCC        ";
query += "    , DDD        ";
query += "    , EEE        ";
query += "FROM TEST        ";
query += "WHERE            ";
query += "    1=1          ";
query += "    AND AAA > 100";
query += "    AND BBB > 200";
query += "    AND CCC > 300";
query += "    AND DDD > 400";

 

단순히 더하는게 아니라고 생각해보면 이게 얼마나 재앙인지 이제는 알게 되었으리라 생각한다.

(위와 같은 경우 반드시 StrinfBuilder 클래스나 StringBuffer 클래스를 사용하기를..)

 

 

 

그럼 마지막으로, String과 StringBuilder, StringBuffer의 차이는 알겠는데 StringBuilder와 StringBuffer의 차이는 무엇일까?

StringBuilder와 StringBuffer 클래스는 사실 하는 일은 똑같다. 다만 하나의 차이가 있는데 동기화 처리의 문제이다.

 

StringBuffer클래스는 동기화 (Synchronized)를 지원하며 멀티 스레드 환경에서도 동기화를 지원한다. 그렇기 때문에 단일 스레드일 경우 StringBuilder 클래스를, 멀티 스레드일 경우 StringBuffer 클래스의 사용이 권장된다. (StringBuffer 클래스의 경우 혼용해도 상관없지만, 동기화 처리 문제로 퍼포먼스상의 이슈가 있다고 한다.)

실제로 맨 위에 예제로 만든 소스의 실행 결과를 보면 단일 스레드 환경에서 StringBuilder 클래스가 StringBuffer클래스보다 퍼포먼스가 좋은 것을 확인할 수 있다.

 

동기화라는 개념은 어려운게 아니다.

마치 한칸짜리 공중 화장실이라고 생각하면 될 것 같다.

누구나 사용할 수 있지만, 동시에 사용할 수 없도록 하는 것이 동기화이다.

 

 

 

흔히 찾아볼 수 있는 예제이지만, 보다 정확하게 확인하고 싶어서 주소값을 직접 출력 해보고 싶었는데 그렇게 하지 못해 아쉬움이 남는다.

 

 

 

 

728x90
728x90

 

자바 애플리케이션이 동작하는 관점에서의 원자단위라고 생각되는 클래스라는 것의 개념에 대해 정리해보려 한다.

지난번 제네릭에 대해 알아볼 때 살짝 언급되었던 적이 있었다.

(java 제네릭 (Generic), 내가 알아보기 쉽게 정리 - 1편, 왜 제네릭)

 

사실 클래스라는 것에 도달하기 위해서 거쳐가야할 개념들이 좀 있다.

쭉 나열해보자면 대강 '상수' -> '변수' -> '배열' -> '구조체' -> '클래스' 이렇다고 할 수 있다.

 

 

 

우선 자바에서 상수는 아래처럼 사용할 수 있다.

public class Example {
    public static final int NUMBER = 10;
    public static void main(String[] ar) {
        System.out.println(NUMBER);
    }
}

 

일반적으로 알고있는 숫자도 하나가 하나의 의미를 갖는 상수로 사용하고 사용할 수 있지만

상수가 아닌 것을 상수로 사용하기 위해 자바에서는 3라인에서 작성한 것과 같이 final 예약어를 사용해서 상수화 시켜준다.

만약 아래처럼 새로운 값을 넣으려고 하면 에러가 발생한다.

 

public class Example {
    public static final int NUMBER = 10;
    public static void main(String[] ar) {
        System.out.println(NUMBER);
        NUMBER = 20;
        System.out.println(NUMBER);
    }
}

 

 

 

final 변수 NUMBER에 값을 assign (할당) 할 수 없다는 에러를 보여준다.

 

 

 

이렇게 고정된 값을 사용하다보니 아래 소스와 같은 문제가 생기게 되었다.

(물론 이렇게 사용하는 사람은 없겠지만)

 

public class Example {
    public static final int NUMBER_1 = 1;
    public static final int NUMBER_2 = 2;
    public static final int NUMBER_3 = 3;
    public static final int NUMBER_4 = 4;
    public static final int NUMBER_5 = 5;
    
    public static void main(String[] ar) {
        System.out.println(NUMBER_1);
        System.out.println(NUMBER_2);
        System.out.println(NUMBER_3);
        System.out.println(NUMBER_4);
        System.out.println(NUMBER_5);
    }
}

 

 

728x90

 

 

다소 극단적일 수 있지만, 나는 그저 1부터 5까지의 숫자를 출력하고 싶었을 뿐인데

각각의 숫자를 표현하기 위해 너무 많은 자원이 소모되기도 하고

만약 나중에 100개의 수를 출력하고 싶다면? 더 나아가 1,000개 10,000개의 숫자를 출력한다고 생각해보자.

우리에게 훌륭한 ctrl + c, ctrl + v 가 있다고는 하지만 너무 고통스러운 작업이 될것이다.

 

그래서 어떡하면 보다 편하고 효율적으로 만들수 있을까 생각하다보니 변수의 개념이 도입된 것이다.

 

public class Example {
    public static int number;
    
    public static void main(String[] ar) {
        number = 1;
        System.out.println(number);
        number = 4000;
        System.out.println(number);
    }
}

 

 

위의 소스와 다른점이 있다면 7라인과 9라인에서 number라는 동일한 값을 출력하는데 서로 다른 결과가 나온다는 것이다.

그 이유는 너무나 잘 알겠지만, 6라인과 8라인에서 number라는 변수의 값을 바꿔주었기 때문이다.

상수를 사용했을 경우와 비교해서 ​하나의 변수에 서로 다른 값들을 할당하여 표현​하며 ​재사용​하고 있음을 주목해야 한다.

 

 

이제 더이상 여러 값을 표현하기 위해 그 수만큼의 상수를 만들 핊요가 없어졌다.

그렇게 변수를 사용해서 코딩을 열심히 하던 감자바씨는 친구 좀해조씨가 자신의 시험 성적을 입력하면 총점을 구해주는 프로그램을 하나 만들어 달라고 해서 열심히 코딩을 했다.

 

import java.util.Scanner;

public class Example {
    public static void main(String[] ar) {
        Scanner sc = new Scanner(System.in);
        int sub_1 = 0;
        int sub_2 = 0;
        int sub_3 = 0;
        int tot = 0;
        
        System.out.print("첫 번째 과목 점수 : ");
        sub_1 = sc.nextInt();
        System.out.print("두 번째 과목 점수 : ");
        sub_2 = sc.nextInt();
        System.out.print("세 번째 과목 점수 : ");
        sub_3 = sc.nextInt();
        
        tot = sub_1 + sub_2 + sub_3;
        
        System.out.println("총점 : " + tot);
        
    }
}

 

 

 

자신이 직접 무언가를 만들어냈다는 뿌듯함에 심취해있던 감자바씨의 기분에 갑자기 좀해조씨가 찬물을 끼얹었다.

 

"자바야 나 사실 이번 시험에서 3과목이 아니고 5과목을 봤는데 어떻게 해야해?"

 

그래서 감자바씨는 아래와 같이 수정했다.

 

import java.util.Scanner;

public class Example {
    public static void main(String[] ar) {
        Scanner sc = new Scanner(System.in);
        int sub_1 = 0;
        int sub_2 = 0;
        int sub_3 = 0;
        int sub_4 = 0;
        int sub_5 = 0;
        int tot = 0;
        
        System.out.print("첫 번째 과목 점수 : ");
        sub_1 = sc.nextInt();
        System.out.print("두 번째 과목 점수 : ");
        sub_2 = sc.nextInt();
        System.out.print("세 번째 과목 점수 : ");
        sub_3 = sc.nextInt();
        System.out.print("네 번째 과목 점수 : ");
        sub_4 = sc.nextInt();
        System.out.print("다섯 번째 과목 점수 : ");
        sub_5 = sc.nextInt();
        
        tot = sub_1 + sub_2 + sub_3 + sub_4 + sub_5;
        
        System.out.println("총점 : " + tot);
        
    }
}

 

 

 

감자바씨가 좀해조씨에게 전해준 소스는 완벽(?)하게 좀해조씨의 요구를 만족했다.

그런데 문제는 학기말고사에 터지고야 말았다.

 

"감자바야 이번에 내가 메꿔야할 학점이 많아서 과목을 좀 많이 수강했는데, 놀라지말고 들어

   52과목에 대한 총점을 알아야하는데, 이번에도 부탁해도 될까?"

 

순간 감자바씨의 머릿속에서 지난번 소스코드가 기억나면서 울상이 되어버렸다.

"그까짓꺼 금방하지!" 라고 큰소리는 쳤는데 마땅한 대안이 없는 감자바씨는 고민에 빠진다.

그러던중 기적처럼 며칠전 수업시간에 들은 배열이라는것이 생각났다.

 

import java.util.Scanner;

public class Example {
    public static void main(String[] ar) {
        Scanner sc = new Scanner(System.in);
        int[] sub_score = new int[12];
        int tot = 0;
        
        for(int i = 0; i < sub_score.length; ++i) {
                   System.out.print((i+1) + " 번째 과목 점수 : ");
            sub_score[i] = sc.nextInt();
            tot += sub_score[i];
        }

        System.out.println("총점 : " + tot);
        
    }
}

 

 

 

(52개는 너무 많아서 12개로 줄여서 예제를 작성해보았다.)

 

이제 배열을 사용해서 아주 간결한, 더 많은 과목에 대한 총점을 구하고 싶으면 간단히 배열의 크기를 조절하는 것으로 유연한 코드가 완성 되었다.

감자바씨는 만족만족 하며 소스를 들고 좀해조씨에게 갔다.

그런데 곧 울상인 좀해조씨를 마주쳤다.

그 이유를 들어보니 요노마가 갑자기 여러명의 학생에 대해 총점뿐만 아니라 평균도 알고싶고 등수도 알고 있다라는거다.

 

배열은 동일한 자료형에 대해서만 묶을 수 있다는 사실을 알고있는 감자바씨는 또다시 고민에  빠진다.

안그래도 짱구 굴리느라 머리가 지끈거리는데 좀해조씨가 정적을 깨고 속을 뒤집는다.

옆동네 사는 '씨개발'씨는 구조체인가 뭔가 써가지고 학생별로 묶어서 점수를 관리한다는데 우리도 그렇게 하면 안되냐며 독촉을 한다.

열이받은 감자바씨, '구조체는 자바에 없단말야!' 그렇다 자바에는 구조체가 없다.

 

그래서 자바언어도 보여줄수는 없지만, 그 개념은 이렇다.

동일한 자료형에 대해 묶음으로관리할 수 있도록 해주는게 배열이었다면,

서로 다른 자료형에 대해서도 묶음으로 관리할 수 있도록 해주는게 구조체이다.

 

struct 구조체의 이름 {
  멤버 변수;
};

 

 

참고 용도로만 작성해 본다면 위와 같이 생긴게 구조체이다.

지금 우리가 처한 상황에 대입시킨다면 아래와 같은 구조체를 선언할 수 있다.

 

typedef struct _scoreInfo{
    char sub_score[52];
    double avg;
} scoreInfo;

 

여기서 구조체를 다루기엔 범위가 아니므로 지금은 그저

'구조체라는건 서로 다른 데이터 타입에 대해서도 하나의 묶음으로 관리할 수 있도록 해놓은 것' 정도로 기억하면 될 것 같다.

 

 

좀해조씨의 구조체 발언에 번뜩 자바에서의 클래스가 생각이 난 감자바씨.

좀해조씨의 짜증섞인 말투를 뒤로한 채 집으로 향했다.

 

import java.util.Scanner;

public class Example {
    public static void main(String[] ar) {
        Scanner sc = new Scanner(System.in);
        Student[] stdnt;
        int subCnt = 0;
        
        System.out.print("학생 수를 입력 하세요 : ");
        stdnt = new Student[sc.nextInt()];
        
        System.out.print("과목 수를 입력 하세요 : ");
        subCnt = sc.nextInt();
        
        for(int i = 0; i < stdnt.length; ++i) {
            stdnt[i] = new Student(subCnt);
        }
    }
}
class Student {
    private int[] sub_score;
    private int tot;
    private double avg;
    
    public Student(int subCnt) {
        sub_score = new int[subCnt];
        tot = 0;
        avg = 0.0;
    }
}

 

 

감자바씨는 집에 오자마자 짐도 풀지 않고 컴퓨터에 앉았다.

우선 감자바씨는 위와같이 틀을 잡았다.

 

21라인에서 학생의 과목별 점수와 총점 그리고 평균을 관리하는 Student 클래스를 선언한다.

이 Student 클래스는 매개변수가 1개인 생성자만 가지고 있는데, 이 매개변수만큼 sub_score 배열을 생성한다.

(sub_score 배열은 과목별 점수를 저장할 배열이다.)

 

학생 수와 과목의 수는 5라인의 main 메서드 안에서 사용자로부터 입력을 받고

16라인의 반복문을 통해 Student 타입의 배열 stdnt에 Student 객체를 생성하고 있다.

 

 

 

이번 포스팅에서 전달하고 싶은건 클래스의 개념이기 때문에 바로 소스코드를 작성하지 않고 이어서 설명을 더하자면

클래스는 서로 다른 데이터 타입과 그 데이터를 조작하는 행위들의 묶음,

즉 ​자료와 행위(메서드)의 묶음​이라고 생각할 수 있다.

 

여기서 오해를 하면 안되는 부분이 있다.

흔히 일반 서적에서 말하는 붕어빵과 붕어빵 틀을 빗대어 클래스와 객체를 설명하는데

아주아주 개인적인 입장에서 이 설명을 그리 좋아하지 않는다.

(그렇다고해서 나에게 적절한 설명 방법이 있는건 아니다... 쭈굴)

 

이렇게 생각하면 조금 더 편하게 전달될지 모르겠다.

클래스는 우리가 일상생활에서 볼 수 있는 개념들(티비, 자동차, 동물 등등)추상화시킨 것이다.

그러니까 예를 들어 사람이라는 Person 클래스를 작성한다고 하자.

유명인사를 예로 들어보자.

사람중에는 박지성, 김연아, 박찬호, 손흥민 등등 이 있다.

이들의 공통점이 무엇일까? 너무나 당연하게도 (위에서 언급 했으니까 다른 답은 없길 바라며) 사람이라는 거다.

이것이 바로 추상화다.

공통적인 속성을 이끌어 내는 것.

그렇다면 이 사람이 가지는 속성은 무엇이 있을까?

엄청나게 많겠지만 이름, 나이, 키, 몸무게, 발사이즈 등등이 있을것이다.

그러면 표현하면 된다!

 

class Person {
    private String name;
    private int age;
    private double tall;
    private double weight;
    private double shoeSize;
}

 

 

이렇게 하면 ​내가 사용하고자 하는 속성을 포함시킨 사람이라는 추상화된 개념을 클래스로 표현한 것​이다.

모든 속성을 다 표현하기도 어려울 뿐만 아니라 다 쓰지도 못할거다.

 

여기까지라면 C언어에서의 구조체가 가지는 개념과 동일한 기능을 한다고 생각하면 된다.

하지만 클래스에는 구조체와 달리 ​행위라는 것​을 더 가질 수 있다.

여기서 행위는 메서드를 뜻한다.

 

사람이 할 수 있는 행위에 무엇이 있을까?

걷는것, 뛰는것, 점프하는것, 무언가를 잡는것 등등이 있을것이다.

그러면 뭐다? 메서드를 추가하면 된다.

 

class Person {
    private String name;
    private int age;
    private double tall;
    private double weight;
    private double shoeSize;

    public double walk(double speed, double time) {
        double distance = 0.0;
        distance = speed * time;
        return distance;
    }
}

 

 

너무 길어질 것 같아서 걷는것만 추가해보았다.

클래스라는 것이 참 간단하지 않은가?

 

 

그럼 여기서 하나 더,

이렇게 추상화시킨 개념적 틀을 만들어 놓으면 끝일까?

아니다, 써먹어야 한다.

위에서 언급한 '사람에는 박지성, 김연아, 박찬호, 손흥민 등이 있다.' 라고 한 부분에서

우리는 지금 사람을 만든것이다.

 

그럼 이제 박지성, 김연아, 박찬호, 손흥민을 만들어 보자.

 

public class ClassExample {
    public static void main(String[] ar) {
        Person parkJiSung = new Person();
    }
}

class Person {
    private String name;
    private int age;
    private double tall;
    private double weight;
    private double shoeSize;
    public double walk(double speed, double time) {
        double distance = 0.0;
        distance = speed * time;
        return distance;
    }
}

 

 

역시 너무 길어져서 박지성 객체만 만들어 보았다.

4라인에서 만든 것이 바로 ​사람 이라는 추상적 개념을 표현한 Person 클래스 타입, 형태를 가지는 parkJiSung (박지성) 객체를 생성한 부분​이다.

이렇게 클래스를 객체화(다른 곳에서는 이것을 인스턴스화 라고도 하더라)해야 사용할 수 있게 되는 것이다.

 

좀 더 자세히 설명하면,

4라인을 두 부분으로 나누어 볼 수 있다.

 

Person parkJiSung;
parkJiSung = new Person();

 

 

2라인은 Person 타입의 parkJiSung 이라는 이름의 변수를 선언한 것이고

3라인은 ​Person 객체를 생성해서 2라인에 선언한 parkJiSung 변수에 할당​해주고 있는 부분이다.

 

 

여기까지 길지만 잘 따라왔다면 클래스가 왜 생겼으며 무엇인지, 그리고 객체와 무엇이 다른지 충분히 알것이라고 생각한다.

 

 

그럼 마지막으로 아까 작성하던 학생별 시험 점수를 관리하는 프로그램 소스를 (생각나는대로 작성한..) 첨부하고 마무리 해야겠다.

 

import java.util.Scanner;

public class Example {
    public static void main(String[] ar) {
        Scanner sc = new Scanner(System.in);
        Student[] stdnt;
        int subCnt = 0;
        
        System.out.print("학생 수를 입력 하세요 : ");
        stdnt = new Student[sc.nextInt()];
        
        System.out.print("과목 수를 입력 하세요 : ");
        subCnt = sc.nextInt();
        
        for(int i = 0; i < stdnt.length; ++i) {
            stdnt[i] = new Student(subCnt);
        }
        
        System.out.println();
        for(int i = 0; i < stdnt.length; ++i) {
            System.out.println(">>>>> " + (i+1) + "번째 학생 점수 입력");
            stdnt[i].inputScore();
                System.out.println();
        }
    
    System.out.println(">>>>> 계산중");
    System.out.println();
    for(int i = 0; i < stdnt.length; ++i) {
        stdnt[i].calcTotAvg();
    }
    
    System.out.println(">>>>> 결과 출력");
    for(int i = 0; i < stdnt.length; ++i) {
        System.out.println(">>>>> " + (i+1) + "번째 학생 총점과 평균");
        System.out.println("총점 : " + stdnt[i].getTot());
        System.out.println("평균 : " + stdnt[i].getAvg());
        System.out.println();
    }
    }
}
class Student {
    private Scanner sc;
    private int[] sub_score;
    private int tot;
    private double avg;
    
    public int getTot() { return this.tot; }
    public double getAvg() { return this.avg; }
    
    public Student(int subCnt) {
        sc = new Scanner(System.in);
        sub_score = new int[subCnt];
        tot = 0;
        avg = 0.0;
    }
    
    public void inputScore() {
        for(int i = 0; i < sub_score.length; ++i) {
            System.out.print((i+1) + "번째 과목 점수 : ");
            sub_score[i] = sc.nextInt();
        }
    }
    
    public void calcTotAvg() {
        for(int i = 0; i < sub_score.length; ++i) {
            tot += sub_score[i];
        }
        avg = (double)tot /  (double)sub_score.length;
    }
}

 

 

너무 생각나는대로 쓴것 같기도하고...

format을 적용해서 소숫점 정리라도 할껄 그랬나..

머암튼 아래는 실행 결과다.

 

 

 

만드는 방법은 다양할거니까 .. ?!

 

 

 

 

728x90

+ Recent posts