728x90

 

 

이전에 왜 제네릭이 나왔는지 알았다면,

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

이번에는 기본적인 제네릭 사용 방법에 대해 정리를 해보려 한다.

 

우선 <> 이 다이아몬드 괄호라고 불리는걸 보면 일단 '제네릭이다' 라고 생각하면 된다.

 

 

제네릭은 크게 ​클래스​에 적용할 수 있고 또 ​메서드​에도 적용해서 사용할 수 있다.

 

1. 클래스에 사용할 때는 아래와 같은 형태를 갖는다.

 

package com.xxxelppa.generic;

public class GenericTest {
    public static void main(String[] ar) {
        Generic_sub<Integer> gs = new Generic_sub<Integer>();
    }
}
class Generic_sub<T> {
    private T num1;
    private T num2;
}

 

 

8라인에서 T라고 작성한 부분은 5라인에서 <>안에 작성한 Reference Type의 클래스가 전달되는 곳이다.

그래서 지금의 경우 5라인에서 Integer Wrapper Class를 사용한다고 했기 때문에

지금의 경우 gs객체는 Generic_sub 클래스의 두 멤버 필드 param1과 param2의 자료형이 Integer Type으로 생성했음을 뜻한다.

 

그리고 8라인의 <> 안에 작성한 문자의 형태로 클래스 내부에서 사용할 수 있다.

9, 10 라인에서가 그 예를 보여주는 경우이다.

 

 

2. 메서드에서 사용할 때는 아래와 같은 형태를 갖는다.

 

package com.xxxelppa.generic;

public class GenericTest {
    public static void main(String[] ar) {
        Generic_sub<Integer> gs = new Generic_sub<Integer>();
        gs.setParam1(10);
        gs.setParam2(20);
        System.out.println(gs.printParamNum(gs.getParam1(), gs.getParam2()));
    }
}
class Generic_sub<T> {
    private T param1;
    private T param2;
    
    public T getParam1() { return param1; }
    public T getParam2() { return param2; }
    
    public void setParam1(T param1) { this.param1 = param1; }
    public void setParam2(T param2) { this.param2 = param2; }

    public <T> StringBuffer printParamNum(T param1, T param2) {
        StringBuffer str = new StringBuffer();
        str.append("첫 번째 매개변수 : ");
        str.append(param1);
        str.append(", 두 번째 매개변수 : ");
        str.append(param2);
        return str;
    }
}

 

 

위의 소스를 보면, 지금까지 제네릭을 사용하지 않고

/. 정수 두 개를 setter를 통해 객체의 멤버필드 값에 할당하고

/. getter를 통해 해당 객체의 메서드에 매개변수 두 개를 넘겨주어 출력하는 것

의 흐름과 똑같다.

그저 조금 다른 점이 있다면, 클래스명 뒤에 <T>로 타입을 외부에서 받아온다는 것을 명시해준 부분과, 21라인에 return type 앞에 내가 메서드 안에서 사용할 매개변수 타입을 <>안에 추가작성했다는 것뿐이다.

 

그렇게 복잡하고 어려운 코드가 아니라서 쉽게 이해가 될 것 같다.

 

아래는 실행 결과이다.

 

 

 

 

 

그럼 다음으로, 제네릭의 장점이 무었이었을까?

내가 사용할 데이터 타입을 외부에서 정할 수 있다는 것이다.

그런 의미에서 아래 소스를 보자.

위의 소스에 객체만 하나 더 생성했을 뿐이다.

 

package com.xxxelppa.generic;

public class GenericTest {
    public static void main(String[] ar) {
        Generic_sub<Integer> gs = new Generic_sub<Integer>();
        // 이번에 새로 추가한 부분 1
        Generic_sub<String> gs2 = new Generic_sub<String>();
        
        gs.setParam1(10);
        gs.setParam2(20);
        // 이번에 새로 추가한 부분 2
        gs2.setParam1("고구마");
        gs2.setParam2("감자");
        
        System.out.println(gs.printParamNum(gs.getParam1(), gs.getParam2()));
        // 이번에 새로 추가한 부분 3
        System.out.println(gs.printParamNum(gs2.getParam1(), gs2.getParam2()));
    }
}
class Generic_sub<T> {
    private T param1;
    private T param2;
    
    public T getParam1() { return param1; }
    public T getParam2() { return param2; }
    
    public void setParam1(T param1) { this.param1 = param1; }
    public void setParam2(T param2) { this.param2 = param2; }

    public <T> StringBuffer printParamNum(T param1, T param2) {
        StringBuffer str = new StringBuffer();
        str.append("첫 번째 매개변수 : ");
        str.append(param1);
        str.append(", 두 번째 매개변수 : ");
        str.append(param2);
        return str;
    }
}

 

 

새로 추가된 7라인을 보면

바로 위에 기존에 작성했던 라인과의 차이는 <Integer>부분이 <String>으로 변경된 것 뿐이다.

이렇게 해당 클래스에서 사용할 데이터 타입을 외부에서 정해줄 수 있어서

데이터 타입을 고려하지 않은 상태에서 동일한 작업을 하는 클래스에 대해

데이터 타입별로 클래스를 생성할 필요 없이 하나의 클래스에서 데이터 타입만 바꿔 조립하듯 코드 재활용이 된다는 것이다.

 

아래는 실행 결과이다.

 

 

 

그러면 왜 제네릭을 사용하는지 이전 1편에서보다 더 정확히 느껴질거라고 생각된다.

 

728x90

 

 

마지막으로 다룰 내용은, 이 제네릭에서 내가 객체 생성시 사용하려고 하는 데이터타입에 대한 '제한'을 줄 수 있는데

그 부분에 대해서 알아보자.

우선 이 제한을 두는 배경에 대해 생각해보자.

만약 위와같이 소스를 작성해두면 어떤점이 불편(?)할까?

 

일상생활에서 흔히 일어날 수 있는 일을 가지고 예를 들어보자.

내일 손님이 집에 오기로 약속해서, 내일 손님에게 과일을 대접하기 위해 과일을 담을 바구니를 하나 샀다고 하자.

신선한 과일을 대접하기 위해 과일은 내일 아침에 살 생각으로 바구니를 집에 그냥 두었다.

그리고 다음날이 되었는데 이게 왠걸, 빨랫감이 한가득 들어가 있는게 아닌가?

이럴줄 알았으면 '이 바구니는 과일을 담을 바구니'라고 써서 붙여둘걸 그랬다.

 

여기서 '이 바구니는 과일을 담을 바구니'라고 하는것이 '여기에는 과일만 담으세요'라는 제약을 건것과 마찬가지이다.

이렇게 제약을 명시해주지 않으면 그곳에 빨래가 들어갈지 아니면 누군가 코 푼 휴지를 담아버릴지 모르기 때문이다.

 

예시가 적절했는지는 모르겠지만, 아래 소스를 한 번 보자.

 

package com.xxxelppa.generic;

public class GenericTest {
    public static void main(String[] ar) {
        Generic_sub<Integer> gs = new Generic_sub<Integer>();
        gs.setParam1(10);
        gs.setParam2(20);
        
        System.out.println(gs.addParam(gs.getParam1(), gs.getParam2()));
        
    }
}
class Generic_sub<T extends Number> {
    private T param1;
    private T param2;
    
    public T getParam1() { return param1; }
    public T getParam2() { return param2; }
    
    public void setParam1(T param1) { this.param1 = param1; }
    public void setParam2(T param2) { this.param2 = param2; }
    
    public Double addParam(T param1, T param2) {
        Double d1 = param1.doubleValue();
        Double d2 = param2.doubleValue();
        
        return d1 + d2;
    }
}

 

 

** Integer 클래스는 Number 클래스의 자식임을 알고 있어야 한다.

눈에 확 들어오는 달라진 부분은 바로 13라인의 <T extends Number> 이 부분이다.

여기서 extends는 일반적으로 클래스간 상속시 사용되는 예약어와는 조금 다른 뜻을 가진다.

T로 전달받은 데이터 타입은 Number 클래스의 자식 클래스이다. 라는 뜻을 가진다.

 

이렇게 타입을 제한하였기 때문에 행여나 5라인의 소스를

 

Generic_sub<String> gs = new Generic_sub<String>();

 

이렇게 고치기라도 한다면

 

Multiple markers at this line
    - Bound mismatch: The type String is not a valid substitute for the bounded parameter <T extends 
     Number> of the type Generic_sub<T>

 

 

이렇게 Parameter가 잘못 되었다고, Number클래스의 자식이 아니라고 에러를 보여준다.

 

 

 

데이터 타입을 제한하게 되면 얻을 수 있는 이점이 있다.

그것은 상속 관계에서 그 의미를 알 수 있는데,

24, 25라인에서 사용한 .doubleValue() 가 바로 그 해답이다.

이게 왜? 라고 생각한다면 조금만 더 스스로 생각해보길 권한다.

 

 

상속관계에서 자식은 부모의 모든 것을 가져다 사용할 수 있다.

그렇기 때문에 '부모가 가지고 있는 메서드의 기능을 모든 자식들이 동일하게 가지고 있다라는게 확실하기 때문에'

위의 예시처럼 Number 클래스가 가지고 있는 .doubleValue()에 대해서 안전하게 가져다 사용할 수 있는 것이다.

 

즉, 그냥 무턱대고 <T> 라고 작성하여 아무 데이터타입이나 들어올 수 있게 해주었다면 아래와 같은 상황이 벌어지게 되는 것이다.

 

package com.xxxelppa.generic;

public class GenericTest {
    public static void main(String[] ar) {
        Generic_sub<String> gs = new Generic_sub<String>();
        gs.setParam1("string");
        gs.setParam2("not number");
        
        System.out.println(gs.addParam(gs.getParam1(), gs.getParam2()));
        
    }
}
class Generic_sub<T> {
    private T param1;
    private T param2;
    
    public T getParam1() { return param1; }
    public T getParam2() { return param2; }
    
    public void setParam1(T param1) { this.param1 = param1; }
    public void setParam2(T param2) { this.param2 = param2; }
    
    public Double addParam(T param1, T param2) {
        Double d1 = Double.parseDouble((String)param1);
        Double d2 = Double.parseDouble((String)param2);
        
        return d1 + d2;
    }
}

 

 

컴파일시 아무런 오류가 검출되지 않기 때문에 사용자는 문제가 없다라고 판단할 수 있다.

(지금은 짧은 코드지만 나중에 복잡해지면 실수하기 마련이고 예상하지 못한 입력이 생길 수 있기 때문이다.)

아래는 위 소스의 실행 결과이다.

 

 

 

이러한 경우를 방지하기 위해서 데이터 타입을 제한하는 것이 필요하다.

 

 

이번 포스팅으로 마무리가 될 줄 알았는데 생각보다 길어진것 같아서 정리해야할 것 같다.

다음번에 데이터 타입을 제한하는 super에 대해서 알아보고

와일드 카드에 대해 알아보면서 제네릭의 기본적인 사용 방법에 대한 정리를 마무리 해야겠다.

 

 

 

 

728x90
728x90

 

 

오늘의 주인공 java의 Generic

 

우선 가볍게? 정의 부터 찾아보자. 뭐든지 그 정의가 가장 중요하다.

 

출처] 네이버 어학사전

 

그렇다면 프로그래밍에서의 Generic은 어떻게 정의되어 있는지 찾아보았는데 '제네릭은 뭐다." 이렇게 깔끔하게 딱 떨어지는 정의는 마땅히 찾지 못하고, 가장 잘 설명한 문장을 찾았다.

 

제네릭은 '클래스 내부에서 사용할 데이터 타입을 외부에서 지정하는 기법'이다.

 

그러면 왜 클래스 내부에서 사용할 데이터 타입을 밖에서 지정해주자는 것일까?

그래서 준비했다.

아래 상황을 살펴보자.

 

자바를 너무 좋아하는 촉이 좋은 '감자바'씨는 친구 '좀해조'씨로부터 두개의 숫자를 출력 하는 프로그램을 만들어 달라는 요청을 받았다.

그래서 우리 감자바씨는 흔쾌히 만들어주겠다고 하며 두팔 걷어부치고 코딩을 했다.

 

 

public class GenericExample {
    public static void main(String[] ar) {
        calc cp = new calc(13, 17);
        cp.print();
    }
}
 
class calc {
    private int num1;
    private int num2;
    calc() { }
    calc(int num1, int num2) {
        this.num1 = num1;
        this.num2 = num2;
    }
    public void print() {
        System.out.println("num1 : " + num1);
        System.out.println("num2 : " + num2);
    }
}

 

 

이클립스 켜는 시간 포함 3분만에 뚝딱 만들어낸 감자바씨는 친구에게 소스를 보내줬다.

몇분이나 지났을까 친구 좀해조씨가 에러가 난다고 연락이 왔다.

그럴리 없다고 난색을 표하는 감자바씨

알고보니 이친구가 소스를 아래와 같이 고쳐서 돌리고 있었다.

 

public class GenericExample {
    public static void main(String[] ar) {
        calc cp = new calc(13.2, 17.3);
        cp.print();
    }
}
 
class calc {
    private int num1;
    private int num2;
    calc() { }
    calc(int num1, int num2) {
        this.num1 = num1;
        this.num2 = num2;
    }
    public void print() {
        System.out.println("num1 : " + num1);
        System.out.println("num2 : " + num2);
    }
}

 

 

 

728x90

 

좀해조씨 얘기를 들어보니

자기는 소수점도 출력해야 하는데 감자바 네녀석이 정수만 출력할 수 있게 해놨다 이거다.

 

부탁하는 입장에 짜증을 내자 화가난 감자바씨

다시 마음을 추스리고 어떻게 해야할지 고민에 빠진다.

 

그러다 수업시간에 제네릭에 대해 얼핏 들은걸 기억해낸 감자바씨는 급히 소스 수정에 들어간다.

 

최종 수정본은 아래와 같다.

 

public class GenericExample {
    public static void main(String[] ar) {
        calc<Integer> cp1 = new calc<Integer>(13, 17);
        calc<Double> cp2 = new calc<Double>(13.2, 17.3);
        cp1.print();
        cp2.print();
    }
}
 
class calc<T> {
    private T num1;
    private T num2;
    calc() { }
    calc(T num1, T num2) {
        this.num1 = num1;
        this.num2 = num2;
    }
    public void print() {
        System.out.println("num1 : " + num1);
        System.out.println("num2 : " + num2);
    }
}

 

 

이제 출력하고 싶은게 있으면 3, 4번 라인을 수정해서 원하는 타입을 넣으면 뭐든 출력할 수 있을거라고 전해주었다.

 

이게 바로 제네릭이다.

(너무 뜬금없이 결론이 나왔나..?)

 

 

우선 그 얘기부터 시작해보자.

변수라는 개념이 생겨난 배경에 대해 혹시 알고 있을까?

상수를 표현하기 위해서는 상수가 달라질 때마다 계속 상수를 추가해서 써야만 했을 것이다.

그래서 나온 것이 변수이다.

변수, 수가 변한다는 것이다. 공간은 하나인데 그 공간을 다양한 수가 공유를 하며 이값도 넣고 저값도 넣고

그렇다보니 너무 중구난방이라 일정한 타입의 개념으로 묶어 변수를 만들었다.

그게 정수형은 int, 실수형은 float, 문자열은 String ... 등의 타입으로 구분을 한 것이다.

 

그런데 이렇게 만들어놓고 보니, 간단한 예시로, 성적처리를 할 때

과목수가 많아지면 int kor, eng, math, history, ... 너무 많아지더라는 것이다.

행여나 나중에 과목이라도 하나 추가가 된다면

총점을 구하고 평균을 구하는 곳에 생각할 부분이 너무 많아지고 관리가 힘들고 소스는 길어지는 문제가 있는 것이다.

그래서 나온 개념이 바로 배열이다.

 

동일한 자료형에 대해서 하나의 변수로 묶어 관리하는 것.

그것이 바로 배열이다.

배열이 나온 이후로 이제 과목점수를

int[] subjectScore = new int[10];

이렇게 10과목을 선언하고, 총점을 구할때도

kor + eng + math ... 

이런게 아니고

for(int i = 0; i < subjectScore.length; ++i) { sum += subjectScore[i]; }

이런 식으로 과목이 몇개가 추가되어도 간단하게 수정할 수 있게 된 것이다.

 

하지만 또 고민이 생겼다.

이렇게 과목별 점수를 관리하는건 배열이 생겨서 쉬워졌는데..

특정 학생 단위로 묶어서 과목별 점수와 총점 평균을 관리하려다보니

평균에서 실수형 자료형이 나오면서 하나의 배열로 묶을 수 없게 된 것이다.

심지어는 학생 단위로 묶으면 그 학생에 대한 기본 정보도 있어야 누구의 점수인지 구별을 한텐데..

그래서 나온 개념이 자바의 클래스 이다.

 

클래스는 서로 다른 자료형에 대해서 (+ 그 자료를 조작하는 메서드도) 하나의 묶음으로 관리할 수 있는 개념인 것이다.

그래서 이제 클래스라는 강력한 개념을 가지고 일상생활에서의 문제를 해결할 수 있게 되었는데,

자꾸 쓰다보니 또 뭔가 불편해진 것이다.

뭐가 불편한가.. 고민을 해봤더니 이게 웬걸

소스가 계속 중복되고 있던 것이다.

그것도 자료형이 다르다는 이유 하나만으로.

 

다시 위에 작성한 감자바씨의 최초 소스를 보자.

int라는 자료형에서 double이라는 자료형으로 다루는 데이터 타입이 바뀌면

만약 제네릭이라는 개념이 없었다면 실수형에 대한 처리를 하는 부분을 어떻게든 따로 하나 더 만들었어야 했을 것이다.

캐스팅을 한다면? 이라고 생각할지 모르겠지만, 그건 데이터 손실을 가져오기 때문에 추천할만한 방법은 아니다.

 

 

즉, 제네릭도 "​중복되는 소스를 하나로 묶어 소스코드의 재사용성을 더욱 극대화 하기 위해​" 생겨난 것이다.

 

 

휴,

배보다 배꼽이 더 커진 느낌이지만

제네릭이란것이 왜 생겨났는지 그 이유를 알아야 '어디에 써먹을지' 감이 올것이기 때문에

주저리주저리 설명이 길어졌다.

 

 

제네릭의 사용 문법에 대해서는 2편을 따로 작성해야겠다.

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

 

 

 

 

728x90
728x90

 

 

자바 가상 머신 (Java Virtual Machine, JVM)

 

자바 가상 머신은 물리적인 실제의 기계 장치가 아니라 추상적인 장치이며, 클래스 파일을 실행할 수 있는 기계어 파일로 번역해준다.

 

 

 

자바 가상 머신은 클래스 영역, 자바 스택 영역, 힙 영역, 네이티브 메소드 스택 영역으로 구성되어 있다.

 

1. 클래스 영역

: 실행에 필요한 클래스들을 로드하여 저장하는 공간이다. 로드된 클래스의 멤버 메소드들은 메소드 영역에 저장되고, 상수들은 상수 영역에 저장된다. 또한 사용자가 작성한 클래스 코드를 저장하는 영역이다.

 

2. 자바 스택 영역

: 자바 프로그램을 수행하면서 발생하는 메소드 호출과 복귀에 대한 정보를 생성하여 저장하고 관리하는 공간이다. 그렇기 때문에 인터럽트가 발생하는 등의 요청이 중간에 발생했을 경우에 복귀정보를 담는 곳이기도 하다.

 

3. 힙 영역

: 객체를 생성할 때 동적으로 공간을 할당하여 객체를 저장하는 공간이다. 자바의 가비지컬렉션 (Garbage Collection, GC) 기능으로 사용이 끝난 객체의 공간이 관리되는 공간이다. 가비지 컬렉션 힙 영역으로도 불리며, GC같은 경우에 사용자가 호출하여 사용하지 않는 메모리 공간에 대해 관리 요청을 할 수는 있지만, 실제적으로 작업이 일어나는지는 알 수 없으며 GC의 판단에 의해 스스로 작업을 수행한다.

 

4. 네이티브 메소드 스택 영역

: 자바에는 하드웨어를 직접 제어하는 기능이 없다. 그래서 하드웨어를 제저해야 할 경우에는 C언어와 같은 다른 언어의 기능을 잠시 빌려서 사용하는데, 이때 사용하는 것이 JNI(Java Native Interface) 기술이다. 네이티브 메소드들이 바이트 코드로 변환되면서 사용되고 기록하는 영역이 네이티브 메소드 스택 영역이다.

 

 

 

출처 : 자바로 배우는 쉬운 자료구조, 한빛미디어, 이지영 저

 

 


 

 

 

아무래도 마음에 걸려서 추가 수정을 하게 되었다.

자바에 유명한 말이 있다.

 

Write Once Run Anywhere

 

한번 쓰면 어디서든 실행할 수 있다라는 말이다.

이게 무슨 말일까?

 

728x90

 

 

이렇게 생기진 않았겠지만, 굳이 설명을 위해 다음과 같은 이미지를 그림판으로 준비했다.

 

 

 

세상엔 다양한 종류의 플랫폼이 있다.

그렇기 때문에 해당 플랫폼에서 작동하도록 프로그램을 만들었다면

그 플랫폼에서만 작동을 하고 플랫폼이 바뀌면 그 플랫폼에 맞게 수정하는 과정이 필요하다.

꽤나 번거로운 일이 아닐 수 없다.

 

그래서 자바에는 JVM(Java Virtual Machime)이라는 가상 머신이 존재한다.

이 가상머신은 자바로 작성된 프로그램이 컴파일된 결과 class파일을 한줄씩 읽으면서(인터프리터 방식) 실행을 하는 역할을 한다.

그래서 오해하면 안되는 사실중 하나가 ​class 파일은 실행 파일이 아니라는 것​이다.

클래스 파일을 열심히 더블클릭 해봐라, 얘가 거들떠나 봐줄까보다

 

그렇다면 이 JVM은 어떻게 Write Once Run Anywhere가 가능하게 해주는 것일까?

다음 그림판으로 작성한 그림을 보면 이해가 될 것이다.

 

 

 

바로 이런식이다.

각 플랫폼에 맞는 JVM을 배포하는 것이다.

그러면 자바가 설치된 어느곳에서든 컴파일된 class파일만 있다면 어디서든 플랫폼의 제약을 받지 않고 수십번 수백번 수천 수만번 수정없이 실행할 수 있는 것이다.

 

자바를 처음 접한다면 플랫폼의 제약이라는 말이 다소 낯설게 느껴지겠지만

사실 알고보면 별거 아닌 내용이다.

 

 

 

 

728x90
728x90

 

 

java 개발을 하려면 환경변수를 설정해야한다.

아니 더 정확히는 명령 프롬프트에서 컴파일하고 실행하려면 환경변수를 설정해야 한다.

명령 프롬프트는 cmd창으로 알고 있는 보통 검정색 있어보이는 화면을 말한다.

 

우선 환경변수라는것이 무엇인지부터 알아보자.

 

IT(정보기술) 용어로, OS의 셸(shell) 등에 설정되어 있다. 
변수의 이름과 의미는 미리 정해져 있기 때문에 환경변수를 읽으면 시스템의 설정을 어느 정도 알 수 있다.

OS의 환경변수는 시스템의 실행파일이 놓여 있는 디렉토리의 지정 등 OS 상에서 동작하는 응용소프트웨어가 참조하기 위한 설정이 기록된다. 응용소프트웨어로부터는 시스템 콜(system call:프로그래밍 언어에서 지원하지 않는 기능에 대하여 운영체계의 루틴을 호출하여 이용하는 것)이나 OS의 표준 API 등을 통하여 간단히 값을 얻을 수 있도록 되어 있다.

또한 웹 브라우저의 내부 데이터의 일부를 환경변수라고 하는 경우도 있는데, 이것은 HTTP를 요청할 때 송신되는 것으로, 브라우저의 종류나 링크되어 있는 웹 페이지 등 웹 서버가 웹 브라우저에 대하여 최적의 처리를 하기 위해 송신되는 것이다. HTTP를 요청하는 응용소프트웨어는 모두 환경변수를 송신하고 있다고 할 수 있다.

사용자가 의도적으로 변환할 수 있는 환경변수도 많으며, 특히 웹 브라우저에서는 자신의 정체를 숨길 목적으로 브라우저 등의 변수를 변환하는 경우도 있다.

그러나 사실과 다른 값을 환경변수에 설정하면 그 환경변수를 사용하고 있는 서버나 응용소프트웨어가 올바르게 작동하지 않을 수 있다. 특히, 셸의 환경변수를 변환했을 경우에는 심각한 오작동을 일으킬 수 있기 때문에 이를 취급하는 경우에는 주의가 필요하다. 

 

 

위 본문은 네이버 검색을 통해 가져온 내용이다.

 

 

위 본문을 읽어도 잘 모르겠다면,

"여기여기에(사용할 것들이있는 위치) 있는 것들을 내가 사용할건데, 미리 쓰기 편하게 등록해 놓는거야" 

정도로 생각해도 될 것 같다.

 

환경변수를 설정하기 위해서는 설정하는 곳으로 가야한다.

 

 

 

[내 컴퓨터]를 마우스 우클릭을 해서 속성을 클릭한다.

혹은 키보드의 윈도우키와 오른쪽 위에 pause/break 키를 동시에 눌러주면 바로 아래 이미지와 같은 화면이 뜬다.

 

 

새로 뜬 화면의 좌측에 있는 '고금 시스템 설정' 을 클릭한다.

 

 

 

 

그리고 뜬 화면의 아래쪽에 '환경 변수' 를 클릭하면 바로 위 이미지의 앞쪽에 뜬 팝업이 하나 더 뜬다.

바로 이곳에서 환경변수를 설정할 수 있다.

 

728x90

 

바로 위의 그림에 보이는 것처럼 등록할 수 있는 영역이 두군데가 있다.

1. 특정 사용자에 대한 사용자 변수

2. 시스템 변수

 

사실 말이 특정 사용자이지, 로그온 되어있는 현재 사용자 계정에서만 사용하려면 1번의 위치에 등록을 시키고

이 컴퓨터를 사용하는 모든 사용자 계정에 대해 동일한 환경변수를 설정하려면 2번의 위치에 등록을 하면 된다.

 

그리고 java에서 설정하는 환경변수는 두가지 종류가 있다.

PATH와 CLASSPATH이다.

PATH는 실행 프로그램의 위치만을 나타내며

CLASSPATH는 실행 프로그램에서 사용하는 라이브러리의 위치를 나타낸다.

 

그래서 PATH는 java에서 사용할 실행 프로그램들이 위치하고있는

[자바 설치 경로] 하위의 bin 이라는 디렉토리로 설정한다.

그리고 CLASSPATH는 [자바 JRE 설치 경로]하위의 lib 디렉토리의 위치로 설정한다.

 

/**

 * 책을 보던 중 새로 추가할 만한 내용이 있어서 수정하게 되었다.

 * 이 부분이 java version이 달라지면서 바뀌었을 수도 있지만,

 * 5.0 버전때를 기준으로 CLASSPATH에 대한 설명을 더하자면

 * 

 * 자바 라이브러리 클래스들이 저장된 디렉토리를 설정하는 것으로

 * J2DJK(Java 2 Development Kit)는 필요한 클래스 라이브러리를  ..\jre\lib\ext 디렉토리에 복사하면 사용 가능하므로

 * ​특별한 경우가 아니면 CLASSPATH는 설정하지 않는 것이 좋다.

 */

 

 

환경변수를 등록하거나 수정하는 작업은 간단하다.

[새로 만들기] 버튼을 눌러 이름과 값을 정해주면 된다.

이미 존재한다면 (아마 path는 존재하고 있을 것이다.) 해당 값을 클릭해둔 상태로 [편집] 버튼을 누르면 된다.

 

 

보통 java에 대한 환경변수를 설정할때는

1. JAVA_HOME 이라는 변수를 생성하고, [java 설치 경로]의 jdk 버전이 명시된 디렉토리까지 설정하고

2, PATH에서 JAVA_HOME이라는 1에서 만든 변수를 가져다 사용하여 등록하는데

   PATH에는 이렇게 작성하면 된다.

   %JAVA_HOME%\bin;

 

환경변수의 경로는 항상 세미콜론(;)으로 구분되어야 하며

​환경변수 설정이 바뀌었을 경운에는 명령 프롬프트를 반드시 재시작 해야 수정한 내용이 반영된다.

 

 

만약 환경변수가 제대로 설정되었는지 확인해보고 싶다면

(java의 경우) 명령프롬프트를 띄우고 "javac" 라고 입력했을 때

해당 명령어 사용법이 뜨면 제대로 설치 및 설정이 완료된 것이고

해당 명령어를 찾을 수 없다라는 문구가 나오면

어딘가가 잘못된 것아다.

 

(제대로 설정되지 않은 예)

 

 

그리고 또 하나, Java는 설치할 때 환경변수 세팅이 자동으로 등록되지 않는다.

그렇기 때문에 Hello World를 하기 전에 까먹지 말고 세팅해 주어야 한다.

일반적으로 jdk를 설치하면 jre가 자동으로 설치 되지만

jre를 설치하면 jdk는 자동으로 설치되지 않는다.

 

:jre -> Java Runtime Environment 로 자바 실행 환경이다.

:jdk -> Java Development Kit 으로 개발을 하기 위한 도구이다.

 

 

 

이렇게 환경변수 세팅을 마쳤다면 드디어 Hello World 를 출력할 준비가 ? 되었다.

이왕 세팅한거 티키신이 분노하지 않게 '안녕 세상아' 한 번 찍어주자.

import java.lang.*;

public class Example {
    public static void main(String[] ar) {
        System.out.println("Hello, World!");
    }
}
​

 

메모장에서 작성해도 좋다.

대신, ​파일이름과 public class 뒤의 이름이 대소문자까지 완벽하게 동일해야 한다.​

위의 경우 파일을 Example.java 로 저장해야만 한다.

 

그리고 해당 파일이 있는 위치로 이동한후

javac Example.java

라고 컴파일 명령을 내린다.

정상적으로 컴파일이 되었다면, .java파일이 있던 위치에 동일한 이름의 .class 파일이 생겼을 것이고

명령프롬프트에는 아무런 문구 없이 사용자의 입력을 기다리며 커서가 껌뻑이고 있을 것이다.

 

컴파일이 완료되었으면 다음 명령어로 실행시켜보자.

java Example

만약 위의 과정을 순서대로 잘 수행했다면 "Hello, World!" 라는 문구가 떡하니 출력되어 있을 것이다.

 

 

 

환경변수에 대해 알아보다가 Hello, World! 까지 출력해 보았다.

이제 환경변수가 무엇이며, 그렇기 때문에 왜 설정해줘야 하는지

그리고 어떻게 확인할 수 있는지 까지 알아보았다.

 

다음엔 무엇을 정리해두지

 

** 현재 상황이 직접 java를 설치할 수 있는 환경이 아니라 이미지 자료가 부족하다.

 

 

 

 

728x90
728x90

 

 

static... 스태틱..

자바를 맨 처음 시작하면 보게되는 단어 중 하나인 static이라고 쓰고 스태틱 이라고 읽는 바로 저 스태틱

 

기계처럼 외워서 쓰게되는 그 문장

public static void main(String[] ar) { ... }

바로 저 스태틱

 

dynamic의 반대되는 개념이라고도 하는 저 동적이지 못한 static이란 무엇일까

 

 

나에게 처음 Hello World 출력의 기쁨(?)을 (사실 그렇게 기쁘진 않았고, 처음 보는건데 당연하다 생각했다..) 선물해준

김승현씨 (열혈강의 Java 프로그래밍의 저자)의 가르침을 더듬어 보았다.

 

 

(사실 static도 누군가의 질문이었다)

 

 

 

'그래, static이 정적인건 알겠어, 그래서 뭐. 어떡하라고. 그게 왜. 뭐가 다른데. 이건 어따쓰는데?'

 

충분히 이해한다 저 마음

 

개인적으로 느끼기에, 처음 언어를 접하는 대부분의 사람들은

1단계, 굉장한 마음가짐으로 책을 편다.

2단계, 자바란 무엇인가? 에 대해 읽어보며 일단 '객체지향 만세!' 를 외친다.

3단계, 제임스 고스..ㄹ....리...ㅇ.. Zzz... 역사 부분에서 졸기 시작한다.

4단계, 자바를 설치가 실습이므로 여기서 잠이 깨며 열심히 따라한다.

5단계, 이리오너라 이놈의 세상아! Hello World!! 대박 신기하다.

6단계, 몇몇 분들은 5단계에서 그날의 학습을 멈춘다.

7단계, 변수 음.. 그래 그런게 있겠지

8단계, 롸? 배열? 같은 자료형을 묶는다라... 신기하군

9단계, 분기문(if, switch) 반복분(for, while)을 보며 열심히 *을 찍기 시작하며 고통스러워 한다.

10단계, 클래스.. 아 모르겠다 !!

 

즉, static에 대해 생각할 겨를도 없이 (물론 끝까지 잘 학습하시는 분들도 많이 계시지만)

과제에 허덕이며 열심히 분기와 반복을 하다가 졸업을 한다.

 

그냥 이클립스가 static이 아닌 것을 static에서 사용할 수 없습니다 !! 라고 하면

아 구래? 그럼 static이라고 쓰지모 ㅎㅎ

어? 쓰니까 에러 안나네? 아싸

 

이렇게 넘긴 사람이 꽤 있을것이라 생각된다.

 

 

서론이 길다..

내가 아는 static에 대해 예시를 들어 설명을 해보려 한다.

사실 바쁜 분들은 여기부터 봐도 된다 ..

 

 

 

내가 학습할 당시의 예시가 너무나 좋다고 생각하기 때문에

그 예시를 들어 보겠다.

 

이런 경우를 생각해보자,

A라는 사람이 은행에 계좌를 신설했다.

이 때 이율을 0.05% 였다고 하자.

5년이 지나 0.07%로 오르고, 오르자마자 B라는 사람이 계좌를 신설했다.

그리고 3년이 지나 다시 0.03%로 내려갔다라고 하는 상황이라 하자.

 

이 부분에 있어서 아래와 같은 소스로 관리한다라고 가정을 해보자

 

class Account {
    private String name;
    private int amount;
    private float iyul;

    public Account(String name, int amount, float iyul) {
        this.name = name;
        this.amount = amount;
        this.iyul = iyul;
    }

    public void disp() {
        System.out.println("이름 : " + this.name + " , 돈 : " + this.amount + ", 이율 : " + this.iyul);
    }
}
public class Exam_001 {
    public static void main(String[] ar) {
        //AAA 신설
       Account p1 = new Account("AAA", 10000, 0.07f);
       p1.disp();
       System.out.println();
  
        // 5년 뒤 BBB신설
        Account p2 = new Account("BBB", 20000, 0.05f);
        p1 = new Account("AAA", 10000, 0.05f);
        p1.disp();
        p2.disp();
  
        System.out.println();
        //3년 뒤 이율 업데이트
        p1 = new Account("AAA", 10000, 0.03f);
        p2 = new Account("BBB", 10000, 0.03f);
        p1.disp();
        p2.disp();
    }
}

 

아 물론 뭐라하실 것 같다.
급해서 저렇게 만들었다.
그래도 static에 대해 전달하는데 지장이 없을거라 생각해서 급한김에 작성한 소스라 부끄럽지만 아무튼 그렇다
(이율 업데이트를 getter, setter를 작성하면 너무 길어질 것 같아서, 새로 만들어 덮어써서 할당하는 것으로 만들었으니 양해를..)

아무튼, 위와같은 방식으로 이율을 업데이트 한다면 어떨까?
지금은 두명이기 때문에 그렇지 은행을 이용하는 고객이 두명이라면 그게 은행일지 생각해보길 바란다.

수천 수만명이 이용하는 은행인데, 이율이 바뀔 때마다 저렇게 업데이트를 사람마다 해줘야 한다면
나라면 살고싶지 않을 것 같다.

728x90




그래서 스태틱이 필요하다.
static으로 선언한 변수는 동일 클래스로 만들어진 객체라면 동일한 곳을 바라본다라고 생각하면 될 것이다.
사실 굵게 쓴 저 한줄이 static의 핵심이다.

조금 더 자세한 설명을 더한다면,
자바 메모리는 크게 네 부분으로 구분해 볼 수 있다.
1. constant 영역
2. GC (Garbage Collection)에 의해 관리되는 Heap 영역
3. Runtime Stact 영역
4. 레지스트리 영역
여기서 static으로 선언한 변수는 "​프로그램 실행시에 constant영역에 할당되고 프로그램 종료시에 해제된다.​"
라고 알고 있다.
눈치가 빠른 분들은 눈치 채셨겠지만, 이러한 특성 덕분에 static으로 선언하면
"​해당 클래스의 객체가 생성되지 않아도 클래스 이름으로 static자원에 접근이 가능하다.​" 라는
어마무시한 얘기까지 포함된다.

그래서 static은 자기 자신만의 고유한 초기화 영역을 가질 수 있다.
멤버필드 영역에 static { }
이렇게 작성하여 따로 초기화 시켜주기도 하는데, 이 부분은 별게(?) 아니기 때문에 금방 찾아볼 수 있을 것이다.
** 프로그램 시작시 static { } 선언부를 먼저 실행한다.


그래서 위의 소스 코드를 아래와 같이 static을 써서 고쳐볼 수 있다.

 

class Account {
    private String name;
    private int amount;
    static float iyul;

    public Account(String name, int amount) {
        this.name = name;
        this.amount = amount;
    }

    public void disp() {
        System.out.println("이름 : " + this.name + " , 돈 : " + this.amount + ", 이율 : " + iyul);
        //  System.out.println("이름 : " + this.name + " , 돈 : " + this.amount + ", 이율 : " + Account.iyul);
    }
}
public class Exam_001 {
    public static void main(String[] ar) {
        //Account 블래스의 iyul값 설정
        Account.iyul = 0.07f;
   
        //AAA 신설
        Account p1 = new Account("AAA", 10000);
        p1.disp();
        System.out.println();
  
        // 5년 뒤 BBB신설
        Account.iyul = 0.05f;
        Account p2 = new Account("BBB", 20000);

        p1.disp();
        p2.disp();
  
        System.out.println();
        //3년 뒤 이율 업데이트
        Account.iyul = 0.03f;
        p1.disp();
        p2.disp();
    }
}

 

 


얼마나 간단하고 편해보이는가,

실제로 소스를 실행해보면 알겠지만 클래스의 이름으로 접근하여 수정한 static변수인 iyul은
p1, p2객체로 접근하여 실행시키는 disp() 결과를 확인해보면
동일하게 수정되는것을 확인해볼 수 있다.

스태택이란 이런 것이라는것에 대해 감이 잡혔을 것이다.
(여기까지 참고 실습해가며 읽었다면)


몇가지 사실에 대해 더 얘기하려한다.
그 시절의 기억을 더듬어보면 static이란 동일 클래스로 만들어진 객체라면 네 것 내 것이 없는 녀석이다.
동일 클래스로 만들어진 객체라면 모두가 ​공유해서 사용​한다는 것이다.

역시 눈치 빠른 사람들은 저 말에서 중요한 사실 하나를 캐치했을 것이다.
그렇다, static은 this가 없다.
this는 자기 자신을 가리키는 것인데, static은 공유해서 사용하기 위한 것이기 때문에 this개념이 없다.


그렇기 때문에, 이 글의 서두에서 언급한
그냥 이클립스가 static이 아닌 것을 static에서 사용할 수 없습니다 !! 라고 하면
아 구래? 그럼 static이라고 쓰지모 ㅎㅎ
어? 쓰니까 에러 안나네? 아싸
​이게 이해가 되어야 한다.

물론 static이 아닌 곳에서 static을 쓰는것은 당연히 되고
static에서 non static을 사용하지 못한다고 했는데, 사용할 방법이 있다.
그것은 바로 객체를 생성하여 ​객체를 통해 사용​하는 것,

아무튼 말이 너무 길어졌는데, static에 대해서는 이제 감이 잡혔을 것이라 생각한다.(제발)
this와 static에 대해서도 좀 더 언급 하고 싶지만,
너무 길어지기 때문에 이쯤에서 마무리 하고 다음에 생각날 때 다루려고 한다.

(사실 그렇게 대단한건 없다.. 어쩌면 내가 아직 몰라서 대단해 보이지 않는 것일수도 있다)


다음번엔 장황하지 않게 잘 풀어나가봐야겠다.

 

 

 

 

728x90
728x90

 

 

객체지향 개념

개념

설명

기능적 분해

구조적 언어를 사용하는 프로그래머들은 보통 기능적 분해 관점으로 프로그램 설계에 접근한다.

기능적 분해라고 하는 것은 문제들을 좀 더 작은 단위의 기능들로 나누는 방법을 의미한다.

각각의 함수는 제어하기 쉬워질 때까지 분해된다.

변경되는 요구사항들

변경되는 요구사항은 개발 프로세스의 관점에서 본다면 필수적인 것이다.

훌륭하고 완벽한 요구사항의 정의를 만들어내지 못한 것에 대해 우리 자신이나 사용자들에 대해 비난하기보다,

변경되는 요구사항을 더욱 효과적으로 대응할 수 있는 개발 방법을 채택해야 한다.

객체

객체는 자기 자신의 책임에 의해서 정의된다.

객체는 자기 자신에 대해 책임짐으로써 자신을 이용하는 프로그램의 작업을 단순화한다.

생성자와 소멸자

객체는 생성되고 소멸될 때 호출되는 특별한 메소드를 가지고 있다.

이 특별한 메소드들은 다음과 같다.

- 생성자 : 객체를 초기화하거나 세팅한다.

- 소멸자 : 객체가 삭제될 때 객체를 정리한다.

모든 객체지향 언어들은 객체를 효과적으로 다루기 위해 생성자와 소멸자를 사용한다.

 

 

객체지향 용어 

 

용어

정의

추상 클래스

개념적으로 유사한 클래스들의 집합이 갖는 공통적인 속성과 메소드를 정의한다.

추상 클래스는 절대 인스턴스화되지 않는다.

속성

객체와 관련된 데이터(또한 데이터 멤버, 멤버 필드 라고 부른다)이다.

클래스

객체의 청사진(blueprint)으로서, 자신의 타입인 객체의 메소드와 데이터를 정의한다.

생성자

객체가 생성될 때 호출되는 특별한 메소드 이다.

파생 클래스

상위 클래스로부터 상세화된 클래스이다.

상위 클래스의 모든 속성과 메소드를 가지지만 또한 다른 속성이나 다른 메소드 구현을 가질 수도 있다.

소멸자

객체가 삭제될 때 호출되는 특별한 메소드 이다.

캡슐화

'온갖 방법의 숨기기'이다.

객체는 자신의 데이터를 캡슐과한다.

추상 클래스는 자신으로부터 파생된 구체적인 클래스들을 캡슐화한다.

기능적 분해

제기된 문제점을 좀더 세분화한 기능들로 작게 나누는 분석 방법이다.

상속

하나의 클래스를 상세화하는 방법으로, 파생 클래스와 그들의 추상 클래스를 연결시킬 때 사용한다.

인스턴스

한 클래스의 특정한 객체이다.

인스턴스화

클래스의 인스턴스를 생성시키는 프로세스이다.

멤버

클래스의 데이터나 메소드이다. 

메소드

객체와 관련된 기능이다. 

객체

책임을 가지고 있는 하나의 개체이다.

데이터와 이 데이터를 운영하는 메소드를 스스로 포함하고 있는 특별한 보유자이다.

객체의 데이터는 외부 객체로부터 보호된다.

다형성

자신의 타입에 상세화되어 있는 메소드를 구현하는 것과 관련된 객체의 능력이다.

상위클래스

이 클래스로부터 다른 클래스들이 파생된다.

모든 파생 클래스들이 이용할 속성과 메소드의 주요 정의(그리고 재정의(override)가 가능하다)를 포함하고 있다.

출처 ] 알기 쉬운 디자인 패턴 _ 알란 섈로웨이, 제임스 트로트 공저 _ 김유지, 정지훈, 이형로 공역

 

 

728x90

 

 

책의 내용에서 공감되고 이해되며 시야가 조금 더 넓어진 기분이 들었던 것은 사실이다.

그래서 좋은 기분이 들었지만,

사실 개인적으로 이해할 수 없는 내용들이 있었다.

번역의 한계였을까? 아니면 일부러 어려워 보이게 쓰고 싶은 이유에서 였을까?

개인적으로 이해하기 어렵게 설명되어 있는 것 같다는 느낌에 개운하지 못한 부분이 있었다.

 

 

그 중에서 가장 이해할 수 없는 부분이 캡슐화에 대해 설명한 부분,

내가 배운 내용에 따르면 캡슐화는 위에 명시되어 있는 '온갖 방법의 숨기기'가 아니라는 것이다.

 

 

 

 

객체지향이 가지는 다섯가지 특징이 있다.

1. 추상화 _ Abstraction

2. 캡슐화 _ Encapsulation

3. 상속성 _ Inheritance

4. 다형성 _ Polymorphism

5. 정보 은닉 _ Information Hiding

 

 

본 책의 저자는 캡슐화와 정보 은닉을 구분하지 않고 사용한 것 같다.

캡슐화를 해도 정보 은닉을 하지 않을 수 있으며

제대로 캡슐화를 하지 않더라도 정보 은닉은 할 수 있다고 생각한다.

 

 

이 두 가지는 명확하게 구분 되어야 한다.

 

 

 

소프트웨어 공학에서 객체지향 설계를 할 때

loose coupling high cohesion 이라는 말이 있다.

바로 이 내용이 캡슐화에 해당하는 말이고,

하나의 클래스에는 가능하면 메서드를 통해서만 데이터에 접근해야 한다는게 정보 은닉이다.

 

 

물론 경우에 따라서는 클래스의 멤버 필드에 직접 접근해야 할 경우가 있겠지만

(정말 특이한 케이스가 될거라고 생각한다.)

이것은 클래스를 설계한 사람의 의도와 달리 데이터가 변경될 수 있는 위험을 안고 가는 것이기 때문이다.

그렇기 때문에 한 클래스의 멤버 필드는 가능하면 반드시 멤버 메소드에 의해서 변경되어야 한다.

 

 

 

 

728x90
728x90

 

많은 사람들을 만나본게 아니라 확실히는 모르겠지만,

처음 언어를 시작하는 사람들의 대부분이 API에 대해 생각하지 않는 것 같다.

 

이번엔 JAVA API에 대해 알아보려 한다.

 

개인적으로 무언가 새로운 것을 알아갈 때 그 사전적 정의부터 찾아본다.

 

API : Application Programming Interface
응용 프로그램 프로그래밍 인터페이스

응용 프로그램에서 사용할 수 있도록, 운영체제나 프로그래밍 언어가 제공하는 기능을 제어할 수 있게 만든 인터페이스를 뜻한다.
주로 파일 제어, 창 제어, 화상 처리, 문자 제어 등을 위한 인터페이스를 제공한다.

- 우리 모두의 백과사전 위키백과

 

첫 번째 줄에 주목해 보자

"응용 프로그램에서 사용할 수 있도록, 운영체제나 프로그래밍 언어가 제공하는 기능"

즉, 기본적으로 제공해주고 있다라는 말이다.

 

그런 의미에서 JAVA API문서를 열어보자

JAVA API라고 검색하면 아주 쉽게 접근할 수 있다.

 

 

 

들어가보면 대략 이런 구성이다.

우측 영역의 상단을 보면 이렇게 써있다.

Java Platform, Standard Edition 7 API Specification

 

 

 

첫 번째, Standard Edition이란 우리가 오라클 자바 홈페이지에서 jdk나 jre를 다운받을 때 SE라는 것을 본 적이 있을 것이다.

 

 

자바는 그 사용 용도에 따라서 크게 세 가지로 분류할 수 있다.

 - SE : Standard Edition
 - EE : Enterprise Edition
 - ME : Micro Edition

지금은 api에 대해 알아보기도 했으므로 이것들에 대한 조금 더 자세한 설명은 다음에 따로 작성해야겠다.

 

 

2015년 10월을 기준으로 java는 8버전 까지 나와있는 상태이지만

지금 보고 있는 것은 7버전의 api 문서이다

그리고 API Specification 이라고 써있는 것을 보아하니 API의 자세한 스펙에 관한 문서임을 짐작할 수 있다.

 

 

 

 

두 번째, 좌측 상단 영역은 package 영역이다.

이것 저것 눌러보면 알겠지만 내가 선택한 package에 따라 하단의 Classes 리스트가 변하는 것을 볼 수 있다.

이렇게 좌측 상단 package영역에서 내가 찾고있는 package를 선택하면

해당 package 하위에 포함되어있는 class들의 목록을 확인할 수 있다.

(Packages 문구 위의All Classes를 누르고 ctrl + f 로 찾고있는 class이름을 검색하면 보다 빠르게? 원하는 Class에 대한 명세를 찾아볼 수 있다.)

 

 

 

세 번째, java 코드 작성시 내가 import하지 않아도 default 값으로 import 되어 있는 java.lang 패키지를 열어보자.

 

 

 

packages에서 java.lang을 찾아 누르면 좌측 하단에 해당 package가 명시되고

그 아래 java.lang package 안에 있는 Interface, Classes, Enums, Exceptions, Errors, Annotation Types 등이

abc순서로 리스트업 되어 있는 것을 볼 수 있다.

 

참고로 우리가 무의식적으로 import 구문을 사용하여 특정 package를 삽입한다는 것은

그 package안에 있는 Class들을 가져다 사용 하겠다는 의미이다.

그리고 앞서 api의 정의에서 살펴 봤지만, 프로그래밍 언어에서 제공하는 기능 이라는 것이다

기본적으로 제공 되는 것이기 때문에 우리는 힘들게 구현하지 않고서 그저 가져다 사용하면 된다는 뜻이다.

 

이 내용에 대해서는 바로 아래 조금 더 자세히 설명하겠다.

 

 

728x90

 

 

네 번째, 우리가 처음 언어를 배우던 때가 기억이 날지 모르겠다.

 

나같은 경우에는 '백문이 불여일타', '선 코딩 후 이해' 스타일로 언어를 배웠었다

그때 우리 모두가 생각없이(?) 받아적은 그 문구

System.out.println("Hello, World!");

여기서 도대체 System은 무엇이며 out은 뭐 내보내는 건가?

println? print? 아는 단어인데 뭐 출력하려면 이렇게 써야 하는거구나

라고 통째로 받아들인 이 문장에 대해 이제는 어느정도 알때도 되었다고 생각한다.

 

 

그러면 Classes에서 System을 찾아서 눌러보자.

 

java에 약속이 있는 것은 알고 있을 것이다.

그래도 혹시나 아직 모를 분들을 위해 사족을 달자면

Class 이름은 영문 대문자로 시작하고, method 이름은 소문자로 시작하는 경우가 대부분이다.

그렇기 때문에 이제는 System을 보는 우리는 'System 클래스구나'라고 알아야 한다.

 

 

 

 

 

 

좀 크고 길지만, 그래도 일부를 가져와 보았다

 

여기서 한가지 더 생각하고 넘어가자, 지금 우리가 보고 있는 것은 java.lang 패키지의 "일부"이다.

앞서 얘기했듯 lang 패키지는 개발하는 식에 작성하지 않아도 기본적으로 import 되어 있는 패키지라고 말했다.

즉 우리는 이렇게 많은 클래스와 메서드를 따로 구현하거나 선언하지 않아도 아무 제약 없이 가져다 사용할 수 있다.!!

 

 

첫번째 네모박스는 상속 관계를 나타내 준다.

이 정보로 System Class는 Object Class를 상속받았음을 알 수 있다.

그리고 바로 아래 그 Class에 대한 설명이 달려 있다.

System Class는 몇가지 유용한 필드와 메서드들을 가지고 있다는 그런 내용인것 같다.

 

두번째 세번째 네모박스에는 필드와 메서드의 요약 정보를 리스트업 해서 보여준다.

자 여기 익숙한게 보인다. 바로 out

static으로 선언된 이 out은 표준 출력 스트림 이라고 나와있다. (static과 스트림에 대해서는 다음에 다루겠다.)

 

조금 더 내려와 네번째 다섯번째 네모박스에는 필드와 메서드에 대한 자세한 내용이 적혀있다.

(사실 이것을 일일이 찾을 필요 없이 Summary영역에서 링크를 클릭하면 자세한 내용이 적힌 부분으로 스크롤이 옮겨간다.)

 

Field Detail의 out만 봐도 이것이 어떤 것인지 자세한 설명과 함께

어떻게 사용하는지에 대해 알아야 할 것들에 대해 자세히 기입되어 있다.

 

 

 

마지막으로 이제 api 문서를 보고 내가 필요한 Class를 찾아 가져다 사용하면 된다.

이제 앞에서 주저리 주저리 두서없이 써내려간 것을 정리해보자

 

1. API는 백과사전과 같다고 생각하면 좋다

   - 우리가 사전에서 말을 찾아서 그 의미를 알고 어떻게 사용하는지 보고 문장에 적용하여 말을 한다.

     이것과 마찬가지로 내가 필요로 하는 기능을 api문서에서 찾고 어떻게 사용하는지 설명을 보고

     내가 필요한 부분에 적절하게 적용하여 사용하면 된다.

   - 특히 이 api는 "기본적으로"제공하기 때문에 특별한 제약 없이 사용할 수 있는 장점이 있다.

 

2. 클래스 이름은 대문자로 시작, 메서드 이름은 소문자로 시작

   - 만일 이름이 두 단어 이상이라면 단어의 첫 문자만 대문자로 작성한다.

 

3. 자주 사용하는 api들은 기억해두면 고생하지 않고 쉽고 간결한 코드를 작성할 수 있다.

   - 실제로 학생시절 교수님이 숫자를 입력 받아서 제곱근을 계산해 출력하는 간단한 프로그램 작성을

     지시한 적이 있었다. (물론 api를 참고하지 않는다는 조건이 있었지만) 머리를 굴리고 굴려서

     제곱근을 구했던 기억이 있다.

     하지만 허무하게도 (그때 당시에는 반복문, 조건문에 대해서만 알고 있었기 때문에 다른 클래스를 만든다거나

                           메서드를 따로 작성해서 호출해서 사용할 생각은 하지 못하던 시절이다.)

     sqrt함수를 사용하면, 그것도 미리 정의되어 있어서 호출하기만 하면 되는 것을 그렇게 고생했다는게 억울한 기억이 있다.

     (아마도 api의 중요함을 겸사겸사 어필하고 싶으셨던것 같다.

 

 

 

아무리 백문이 불여일타라지만

어느정도 걷는데 익숙해 졌다면, 고개 들고 앞을 보며 어디로 갈건데 어떻게 가는게 좋을지

주변 살펴 가는게 좋지 않을까 생각한다.

 

 

 

 

728x90

+ Recent posts