Search

'java'에 해당되는 글 3건

  1. 2012.11.23 정규 표현식 기본
  2. 2011.08.25 Java 기본 변수
  3. 2010.12.17 Java Thread Pool 좋은 글(Object Pool Control)

정규 표현식 기본

java 2012. 11. 23. 13:02 Posted by jiddong

설명
.

\n(엔터) 문자를 제외한 모든 한 문자 스페이스 포함

\s space 공백 문자
\S space 공백 문자가 아닌 문자
\w 알파벳이나 한글 및 숫자 같은 문자 (한글이 안되는 곳도 있음)
\W 문자가 아닌 것
^ 문자열이나 줄의 시작
$ 문자열이나 줄의 끝.
 \A 일치하는 부분이 문자열의 시작에 있어야 함
\z 일치하는 부분이 문자열의 끝에 있어야 함
| java || or 과 같음
[abc] 대괄호 안의 문자 중 일치하는 하나를 찾음 하나라도 있을 경우 일치하게 됨
[^abc] 대괄호 안 ^뒤에 오는 문자가 아닌 문자를 찾음 [^abc]는 a,b,c 가 아닌 문자를 찾음
[a-z] 지정된 문자 범위 내에 있는 문자를 찾음 [a-zA-z]는 영문 [ㄱ-ㅎ기-힣] 한글
( ) 정규표현식 내부의 또 다른 부분 정규표현식
? ? 앞의 식이 있거나 없을 수 있음
* * 앞의 식이 0번 이상 올 수 있음
+ * 앞의 식이 1번 이상 올 수 있음 1번은 무조건 들어가야함
{n} {n}앞의 식이 n개인 경우를 찾음
{n,} {n}앞의 식이 최소한 n개인 경우를 찾음
{n, m} {n, m}앞의 식이 최소 n개에서 최대 m개인 경우를 찾음


한글 이메일 가능한 정규 표현식(메일 종류에 따라 약간의 수정필요)

Gmail 의 경우 mailId+k@gmail.com 이 가능 함.

String pattern_text = "^([^@\\s])+[@][^@\\s]+[.][^@\\s]+";

 ^([^@\\s])+   = @를 제외한 모든 문자가 한글자 이상 들어가야함

[@]                  = @하나는 무조건 들어가야함

[^@\\s]+        = @를 제외한 모든 문자가 한글자 이상 들어가야함

[.]                    = . 은 무조건 들어가야함

[^@\\s]+";      = @를 제외한 모든 문자가 한글자 이상 들어가야함


'java' 카테고리의 다른 글

Java 기본 변수  (0) 2011.08.25
Java Thread Pool 좋은 글(Object Pool Control)  (0) 2010.12.17

Java 기본 변수

java 2011. 8. 25. 15:05 Posted by jiddong


1 Bit
 - 0 과 1 로된 단위

1Byte
 - 8 bit 로 구성
0000 0000 이것은 16진수로 0X00 으로 표현  

Java 기본형(Primitive type)

우선 Java 는 데이터를 다루는
최소의 단위가 1Byte임을 알자 




  1 Byte (8 Bit)   2 Byte (16 Bit) 4 Byte (32 Bit)  8 Byte (64 Bit) 
 논리형  boolean       
 문자형   char (unsinged)     
 정수형 byte  short  int  long 
 실수형     float  double 


boolean 
 - 저장 가능한 범위  true/ false 
 - Java 가 데이터를 다루는 최소 범위가 1 Byte 이기 때문에 낭비적이긴 하지만 1 Byte 사용

char 
 - Java의경우 Unicode 를 사용 동양의 글자의 경우 2 Byte 필요하기때문에 char 의경우 2 Byte 이용

 - Java 에서 유일하게 제공되는 
unsinged 형태 (음수 가존재 하지 않음 )
   *  2 Byte  =  16진수 0x00 =  
0000 0000  0000 0000
      맨앞 1 Bit를 가지고 음수나 양수 를 나타냄 하지만 char 형은 
unsinged 형식이기 때문에
      맨 앞 비트를 음수 양수 형식 으로 사용하지 않음
   

      char 형이   1111 1111   1111 1111 의 Bit를 가지고 있을때 10진수의 값 65535
      short 형이  
1111 1111  1111 1111 의 Bit를 가지고 있을때  10진수의 값 -1 

 - char a = 'A'; b = 'B'; 일때 if (a > b) 가 가능한이유 
   *  char 형은 유니코드 정수형태로 저장 char a = 'A' 일시 a변수에는 'A' 의 정수 값인 65가 들어감

byte , short , int , long

- 각형의 표현범위 
  
 byte  -128 ~ 127
(-2의 7승 ~ 2의 7승 -1 )
 shot  -32,768 ~ 32,767
(-2의 15승 ~ -2의 15승 -1)
 int  -2147483648 ~ 2147483647
(-2의 31승 ~ 2의 31승 -1)
 long  무지큼;;;
(-2의 63승 ~ 2의 63승 -1)


 - byte, short 가 더적은  Bit를 가지고 있어 메모리를 조금더 절약할수 있지만  int를 사용하자
   *  JVM 의 피연산자 스택이 피연산자를 4 Byte 단위로 저장 하기 때문에
       int 보다 작은 자료형의 값을 계산시 int 형으로 형변환 되서 연산이 수행됨

 - 요기서 질문 int 1 과 -1을 bit 형태로 표현하면?
   * int 1 = 4 byte = 32 bit 
             
0000 0000, 0000 0000, 0000 0000, 0000 0001
     int -1 = 4 byte = 32 bit 
             
1111 1111, 1111 1111, 1111 1111, 1111 1111
     int -2 = 4 byte = 32 bit 
             
1111 1111, 1111 1111, 1111 1111, 1111 1110
     int -3 = 4 byte = 32 bit 
              
1111 1111, 1111 1111, 1111 1111, 1111 1101 
    맨처음 singed Bit 를 보고 1일경우 음수이기 때문에
    앞의 첫번째 Bit 부터 0이 나올때까지 모든 Bit 를 1로 변경해주면 음수가 됨    

Test Code    
 
    
int shTemp = 0;
   
    shTemp =  ~shTemp; // 1의 보수 표현(not)  각 비트를 반전시킨 값을 반환한다.

    System.out.println("short ::"+shTemp);


  출력 값  short ::-1

float, double
   
 - float 보다 double 이 약 두배의 정밀도를 가지고 있음
 
- 정수형에서 기본 자료형이 int 형이듯 실수형에서는 double 형이 기본자료형이기 때문에
    float pi = 3.14F  뒤에 붙은 리터럴형을 사용하지 않으면 double 형으로 사용된다
   * float pi = 3.14; 이면 오류 발생 float 형 변수에 double 형을 저장할수 없음. 

- double Temp = 1e-3; 이란 ?
   * e 뒤에 붙은 숫자가 10의 몇승을 나타냄
     지금 1e-3의 경우 10의 -3승 으로서 0.001 과 같음 
     1e3 == 1000 (10의 3승 )과 같음 
   



















'java' 카테고리의 다른 글

정규 표현식 기본  (0) 2012.11.23
Java Thread Pool 좋은 글(Object Pool Control)  (0) 2010.12.17

Java Thread Pool 좋은 글(Object Pool Control)

java 2010. 12. 17. 18:24 Posted by jiddong

내용 참좋다 
멀티 Thread 시 참고 하기 좋음~

제목 : Object Pool Control 기법
글쓴이: 이원영(javaservice) 2000/11/21 04:59:10 조회수:25611 줄수:483
Object Pool Control 기법은 JDBC Connection Pool 을 만들거나, TCP/IP Socket Pool을 
만드는 등, 최근엔 많은 자바 개발자들이 즐겨 사용하는 기법 중의 하나 입니다.
특히 JDBC Connection Pool 이 소개 되면서, 너도 나도 직접 Connection Pool을
만드는 시도를 자연스럽게 하게 됩니다.

저 역시 그 중의 하나였고, Object Pooling 기법에 대해 너무 단순하고 쉽게 생각
했었습니다. 최근에야 저의 프로그래밍 기법에 엄청난 잘못이 있었다는 것을
하나은행과 한빛은행 인터넷뱅킹 프로젝트 튜닝작업을 하면서 깨달았습니다. 
저와 같은 잘못을, 행여나 다른 분들이 똑같이 범하지 않았으면 하는 바램에서 저의
잘못을 아래 처럼 기술합니다.


아래의 3 개의 클래스는 Object Pool 을 테스트 하는 프로그램입니다. 뭐가 잘못
되었을까요? 퀴즈입니다.


----8><--------------------------------------------------------------------
package badpool;
/*
 * Lee WonYoung
 * javaservice@hanmail.net, lwy@kr.ibm.com
 * 2000.11.21
 */
public class BadPoolTest extends Thread{
    private int client = 0;
    public BadPoolTest(int n){
        this.client = n;
    }
    public static void main(java.lang.String[] args) {
        // PoolObject 의 최대값인 20 보다 많은 요청을 보내어야 테스트가 됨.
        int max = 40;
        System.out.println("Calling " + max + " threads");

        Thread[] threads = new BadPoolTest[max];
        for(int i=0;i<max;i++){
            threads[i] = new BadPoolTest(i);
            threads[i].start();
        }
    }
    public void run(){
        PoolManager manager = null;
        PoolObject obj = null;
        try {
            manager = PoolManager.getInstance();
            obj = manager.getPoolObject();
            obj.execute();
        }
        finally{
            if ( obj != null ) manager.release(obj);
        }
    }
}
----8><--------------------------------------------------------------------
package badpool;
/*
 * Lee WonYoung
 * javaservice@hanmail.net, lwy@kr.ibm.com
 * 2000.11.21
 */
public class PoolManager {
    private static PoolManager manager = null;

    private int MAX = 20;
    private int count = 0;
    private java.util.Vector pool = new java.util.Vector();

    private PoolManager() {
        super();
        for(int i=0;i<MAX;i++){
            pool.addElement(new PoolObject(new Integer(i)));
        }
    }
    public static synchronized PoolManager getInstance() {
        if ( manager == null ) {
            manager = new PoolManager();
        }
        return manager;    
    }
    public synchronized PoolObject getPoolObject() {
        PoolObject obj = null;
        while( count >= MAX ) {
            try{
                Thread.sleep(1000);
            }catch(Exception e){}
            System.out.println("PoolManager : sleep... current count=" + count);
        }
        obj = (PoolObject)pool.elementAt(0);
        pool.removeElementAt(0);
        count++;
        return obj;
    }
    public synchronized void release(PoolObject obj) {
        count--;
        pool.addElement(obj);
        System.out.println("PoolManager: released ... current count=" + count);
    }
}
----8><--------------------------------------------------------------------
package badpool;

/*
 * Lee WonYoung
 * javaservice@hanmail.net, lwy@kr.ibm.com
 * 2000.11.21
 */
public class PoolObject {
        private static java.util.Random random = new java.util.Random();
        private Integer index = null;
    
    public PoolObject(Integer i) {
        this.index = i;
    }
    public void execute() {
        // 어떤 비즈니스 로직....
        System.out.println("Thread-" + index + ": started.");
        int second = 0;
        try{
            // 5 - 15 seconds random sleep.
            second = 5 + (int)(random.nextDouble()*10);
            Thread.sleep(second*1000);
        }catch(Exception e){}
        System.out.println("Thread-" + index + ": elapsed=" + second);
    }
}
----8><--------------------------------------------------------------------

위 클래스들을 컴파일 하여 돌리면, 40개의 Thread 가 모두 동작하는 것이 아니라
20개만 수행하고 dead-lock 에 빠집니다. 왜냐면, 한정된 20 개의 PoolObject 을
동시에 40개의 Client Thread 가 요청하게 되면, 그 중 20개는 getPoolObject()을
성공하게 되어 정상적인 수행을 진행하게 되고, 나머지 20개는 synchronized lock
으로 인해 대기하게 됩니다. 한참이 지난 후 수행중이었던 20개의 Thread 가
사용하고 난 PoolObject 을 PoolManager 에게 반환(release)하려 합니다.
문제는 여기서 발생하죠. 같은 lock 객체에 의해 synchronized 가 걸려 있으니,
앞에서 getPoolObject() 을 미처 수행하지 못하고 대기하던 20개의 Thread 가
해당 lock 을 미리 점유하고 있어서, release() 메소드 내의 synchronized(lock)
블록으로 들어가지를 못하게 되는 겁니다. 
"다 사용했으나 반환을 못하고, 받지를 못했으니 나머지 20개의 Thread 는 마냥 
 기다리고만 있습니다."

물론 getPoolObject() 내에서 대부분 2-3 회 반복적으로 시도하다가 가용한 자원이
끝까지 생기지 않으면 튕켜 나가게 로직을 짜곤 합니다. 이 상황이라면 dead-lock 까지는
가지 않게 되겠지만, "끼적끼적" 겨우 하나씩 동작하게 되지요.

외견상으로는 "왜 이렇게 시스템이 갑자기 느려졌지?" 라는 형태로 나타나게 됩니다.
혹시 자신이 만든 Connection Pool 에서 Pool 최대값을 넘지 않을 때는 
잘 동작하는데, 동시에 최대값 이상의 요청이 급작스럽게 들어오고 난 이후부터
시스템이 급격하게 느려지거나 hang 이 발생한 적이 없었던가요?



아래는 고민하고 고민하여 나름대로 최적화된 모습이라 생각되는 방식을 찾아
보았습니다. 보다 더 나은 알고리즘이 있으면 조언 부탁합니다.

프로그램 설명은 각자 개인 스스로의 몫으로 돌렸으면 합니다.
주석을 이래저래 풀어 가며 테스트를 해 보시면 어떤 서로다른 결과가 나타나는지
직접 목격할 수 있을 겁니다.


----8><--------------------------------------------------------------------

package pool;

/*
 * Lee WonYoung
 * javaservice@hanmail.net, lwy@kr.ibm.com
 * 2000.11.21
 */
public class PoolTest extends Thread{
    private int client = 0;
    public PoolTest(int n){
        this.client = n;
    }
    public static void main(java.lang.String[] args) {
        // PoolObject 의 최대값인 20 보다 많은 요청을 보내어야 테스트가 됨.
        int max = 40;
        System.out.println("Calling " + max + " threads");

        Thread[] threads = new PoolTest[max];
        for(int i=0;i<max;i++){
            threads[i] = new PoolTest(i);
            threads[i].start();
        }
    }
    public void run(){
        PoolManager manager = null;
        PoolObject obj = null;
        try {
            manager = PoolManager.getInstance();
            obj = manager.getPoolObject();
            obj.execute();
        }
        finally{
            if ( obj != null ) manager.release(obj);
        }
    }
}

----8><--------------------------------------------------------------------

package pool;

/*
 * Lee WonYoung
 * javaservice@hanmail.net, lwy@kr.ibm.com
 * 2000.11.21
 */
public class PoolManager {
    private static PoolManager manager = null;

    private int MAX = 20;
    private int count = 0;
    private java.util.Vector pool = new java.util.Vector();

    private PoolManager() {
        for(int i=0;i<MAX;i++){
            pool.addElement( new PoolObject(new Integer(i)) );
        }
    }
    public static synchronized PoolManager getInstance() {
        if ( manager == null ) {
            manager = new PoolManager();
        }
        return manager;    
    }
    /**
     * PS: getPoolObject() 에서 파라메터로 "int from" 을 실제로는 받을 필요가
     *     없으나 어떤 Client Thread 에서 요청이 온지를 디버깅 하기 위해 넣어둔 것
     *     뿐임.
     */
     
    public synchronized PoolObject getPoolObject(int from) {
        while( count >= MAX ) {
            try{
                System.out.println("getPoolObject(from:" + from + 
                                ") : sleep... active count=" + count);
                
                // Max값을 초과한 나머지는 모두 아래 wait(timeout) 에서 대기함.
                // 아래에서 1초를 wait 하게 하지만, 실제로는 그 이전에 
                // notify() 에 의해 깨어나게 됨.
                wait(1*1000); 
                //System.out.println("awaiked. ...");

            }catch(Exception e){
                System.out.println("getPoolObject: awaiked " + e.toString());
            }
        }
        PoolObject obj = (PoolObject)pool.elementAt(0);
        pool.removeElementAt(0);
        count++;
        System.out.println("getPoolObject(from:" + from +"): return : active count=" + count);
        return obj;
    }
    public synchronized void release(PoolObject obj) {
        count--;
        pool.addElement(obj);

        //notify();
        notifyAll(); 

        // 만약 notifyAll() 을 하면 getPoolObject()에서 wait()에서 걸린
        // 모든 Thread 가 일시에 일어나게 되어 쓸데없이 전부 while 문을 동시에
        // 확인하게되고, 이는 CPU 부하를 야기할 것으로 생각되나,
        // 일부 책에서는 notify() 대신 notifyAll() 을 권장하기도 함.
        // 두가지를 모두 테스트해 보면 아주 재밌는 현상을 만날 수 있을 것임.

        System.out.println("Thread-" + obj.getIndex() + ": released :active count=" + count);
    }
}

----8><--------------------------------------------------------------------

package pool;

/*
 * Lee WonYoung
 * javaservice@hanmail.net, lwy@kr.ibm.com
 * 2000.11.21
 */
public class PoolObject {
        private static java.util.Random random = new java.util.Random();
        private Integer index = null;
    
    public PoolObject(Integer i) {
        this.index = i;
    }
    public void execute() {
        System.out.println("Thread-" + index + ": started.");
        int second = 0;
        try{
            // 5 - 15 seconds random sleep.
            second = 5 + (int)(random.nextDouble()*10);
            Thread.sleep(second*1000);
        }catch(Exception e){}
        System.out.println("Thread-" + index + ": elapsed=" + second);
    }
    public Integer getIndex() {
        return index;
    }
}

----8><--------------------------------------------------------------------


핵심은 어떤 경우든, Thread.sleep() 을 이용하여 가용한 자원이 생길때까지 기다리게
해서는 안된다는 사실입니다.
wait() 와 notify(),notifyAll() 을 이용하여만 합니다.

NOTE: wait() 은 synchronized block 의 lock 을 해제합니다. 따라서 연이어 들오는
      쓰레드들이 sync block 안으로 들어와 wait() 부분에서 함께 waiting 하게 됩니다.

그러나, 위의 샘플은 실 프로젝트 시스템에서는 사용할 법한 소스가 아닙니다. 중요한
한가지 요소가 빠져 있습니다. 갯수에 제한이 있는 자원에 대한 요청이 엄청나게 폭주할
경우, 가용한 자원을 할당 받지 못한 요청들은 위 로직대로라면 상당히 오랫동안 
기다리게 됩니다. 이는 시스템의 hang 현상을 유발시킬 수 있는 절대적인 잠재력을
안고 있습니다. 일정한 TIME_WAIT 을 두어 일정시간을 기대려도 가용한 자원이 
여전히 생기지 않을 경우 Fail 이 일어나게 하여 튕켜 나가게 하여야만 hang 으로 
이어지지 않습니다. 

아래는 실프로젝트에서 사용할 법한 로직입니다.


package org.jsn.pool;

/*
 * Lee WonYoung
 * javaservice@hanmail.net, lwy@kr.ibm.com
 * 2000.11.21
 */
public class PoolManager {
    private static PoolManager manager = null;

    private int MAX_RESOURCE = 20;
    private long TIME_WAIT = 2 * 1000;
    private java.util.Hashtable pool = new java.util.Hashtable();
    private java.util.Stack freeStack = new java.util.Stack();

    private PoolManager() {
        for(int i=0;i<MAX_RESOURCE;i++){
            Integer index = new Integer(i);
            pool.put( index,  new PoolObject(index) );
            freeStack.push(index);
        }
    }
    public static synchronized PoolManager getInstance() {
        if ( manager == null ) {
            manager = new PoolManager();
        }
        return manager;    
    }
    public  PoolObject getPoolObject() throws Exception {
        Integer index = null;
        long start = System.currentTimeMillis();
        synchronized(this){
            while( freeStack.empty()  ) {
                try{
                    // 나머지는 모두 아래 wait(timeout) 에서 대기함.
                    // 아래에서 1 초를 wait 하게 하지만, 실제로는 그 이전에
                    // notify() 에 의해 깨어나게 됨.
                    wait(1 * 1000);
                    
                }catch(Exception e){
                    System.err.println("getPoolObject: awaiked " + e.toString());
                }
                long end = System.currentTimeMillis();
                if ( freeStack.empty() && (end - start) >= TIME_WAIT ) {
                    throw new Exception("getPoolObject : timeout(" + TIME_WAIT + ") exceed");
                }
            }
            index = (Integer)freeStack.pop();
        }
        PoolObject obj = (PoolObject)pool.get(index);
        return obj;
    }
    public void release(PoolObject obj) {
        synchronized ( this ){
            freeStack.push(obj.getIndex());
            
            //notify();
            notifyAll(); 
            // 만약 notifyAll() 을 하면 getPoolObject()에서 wait()에서 걸린
            // 모든 Thread 가 일시에 일어나게 되어 쓸데없이 전부 while 문을 동시에
            // 확인하게되고, 이는 CPU 부하를 야기할 것으로 생각되나,
            // 일부 책에서는 notify() 대신 notifyAll() 을 권장하기도 함.
            // 두가지를 모두 테스트해 보면 아주 재밌는 현상을 만날 수 있을 것임.
        }
        //System.out.println("Object-" + obj.getIndex() + ": released.");
    }
}
----8><--------------------------------------------------------------------

필요할 경우, 위의 java.util.Stack 대신 Queue 를 사용하실 수 있습니다. 
사용하는 자원만 지속적으로 사용하고, Stack 의 저 아래에 있는 특정 자원은 한번도
사용되지 않을 것 같은 불안감(?)이 드신다면 Stack 대신 Queue 를 이용하여 다음처럼
골고루(?) 사용토록 고쳐셔도 됩니다.

package org.jsn.pool;

/*
 * Lee WonYoung
 * javaservice@hanmail.net, lwy@kr.ibm.com
 * 2000.11.21
 */
public class PoolManager {
    private static PoolManager manager = null;

    private int MAX_RESOURCE = 20;
    private long TIME_WAIT = 2 * 1000;
    private java.util.Hashtable pool = new java.util.Hashtable();
    private org.jdf.util.Queue freeQueue = new org.jdf.util.Queue();

    private PoolManager() {
        for(int i=0;i<MAX_RESOURCE;i++){
            Integer index = new Integer(i);
            pool.put( index,  new PoolObject(index) );
            freeQueue.enqueue(index);
        }
    }
    public static synchronized PoolManager getInstance() {
        if ( manager == null ) {
            manager = new PoolManager();
        }
        return manager;    
    }
    public  PoolObject getPoolObject() throws Exception {
        Integer index = null;
        long start = System.currentTimeMillis();
        synchronized(this){
            while( freeQueue.empty() ) {
                try{
                    // 나머지는 모두 아래 wait(timeout) 에서 대기함.
                    // 아래에서 1 초를 wait 하게 하지만, 실제로는 그 이전에
                    // notify() 에 의해 깨어나게 됨.
                    wait(1 * 1000);

                }catch(Exception e){
                    System.err.println("getPoolObject: awaiked " + e.toString());
                }
                long end = System.currentTimeMillis();
                if ( freeQueue.empty() && (end - start) >= TIME_WAIT ) {
                    throw new Exception("getPoolObject : timeout(" + TIME_WAIT + ") exceed");
                }
            }
            index = (Integer)freeQueue.dequeue();
        }
        PoolObject obj = (PoolObject)pool.get(index);
        return obj;
    }
    public void release(PoolObject obj) {
        synchronized ( this ){
            freeQueue.enqueue(obj.getIndex());
            
            //notify();
            notifyAll(); 
            // 만약 notifyAll() 을 하면 getPoolObject()에서 wait()에서 걸린
            // 모든 Thread 가 일시에 일어나게 되어 쓸데없이 전부 while 문을 동시에
            // 확인하게되고, 이는 CPU 부하를 야기할 것으로 생각되나,
            // 일부 책에서는 notify() 대신 notifyAll() 을 권장하기도 함.
            // 두가지를 모두 테스트해 보면 아주 재밌는 현상을 만날 수 있을 것임.
        }
       //System.out.println("Object-" + obj.getIndex() + ": released.");
    }
}
----8><--------------------------------------------------------------------


Queue.java
http://www.javaservice.net/~java/bbs/read.cgi?m=devtip&b=javatip&c=r_p&n=981829799

Re: LinkedList 를 이용한 Queue.java 
http://www.javaservice.net/~java/bbs/read.cgi?m=devtip&b=javatip&c=r_p&n=987580631


-------------------------------------------------------  
  본 문서는 자유롭게 배포/복사 할 수 있으나 반드시
  이 문서의 저자에 대한 언급을 삭제하시면 안됩니다
================================================
  자바서비스넷 이원영
  E-mail: javaservice@hanmail.net
  PCS:019-310-7324
================================================

'java' 카테고리의 다른 글

정규 표현식 기본  (0) 2012.11.23
Java 기본 변수  (0) 2011.08.25