[Hadoop]JDK 8 버전에서 Hadoop 2.6.2 컴파일시 에러 해결방법

프로그래밍 팁/Hadoop 2015. 11. 19. 12:01

 버전이 높아질 수록 다양한 기능이 제공되기도 합니다만 때로는 호환성에 문제가 생겨 프로그램이 정상적으로 동작되지 않는 경우가 발생합니다. 아래와 같은 경우 Java Development Kit 8에서 hadoop을 컴파일하게 되었을 때 발생하는 에러입니다.


-개발환경

운영체제    : Ubuntu 12.04(64-bit)

하둡        : Hadoop 2.6.2

ProtoBuffer : Protoc 2.5.0

JAVA       : Java Development Kit 8


[INFO] Apache Hadoop Annotations ......................... FAILURE [4.086s]

---

[ERROR] Failed to execute goal org.apache.maven.plugins:maven-javadoc-plugin:2.8.1:jar (module-javadocs) on project hadoop-annotations: MavenReportException: Error while creating archive:

[ERROR] Exit code: 1 - C:\hadoop-src\hadoop-common-project\hadoop-annotations\sr

c\main\java\org\apache\hadoop\classification\InterfaceStability.java:27: error:

unexpected end tag: </ul>

[ERROR] * </ul>

[ERROR] ^

[ERROR]

[ERROR] Command line was: "C:\Program Files\Java\jdk1.8.0_05\jre\..\bin\javadoc.exe" -J-Dhttp.proxySet=true -J-Dhttp.proxyHost=proxy -J-Dhttp.proxyPort=3128 @op

tions @packages

[ERROR]

[ERROR] Refer to the generated Javadoc files in 'C:\hadoop-src\hadoop-common-project\hadoop-annotations\target' dir.

[ERROR] -> [Help 1]

[ERROR]

[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.

[ERROR] Re-run Maven using the -X switch to enable full debug logging.

[ERROR]

[ERROR] For more information about the errors and possible solutions, please read the following articles:

[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/MojoExecutionException

[ERROR]


 위의 에러가 발생하는 이유는 Java Development Kit 8에서부터는 이전 버전에 비해 좀 더 엄격해져서 위의 태그 </ul>이 JDK8에서는 맞지 않아 에러를 발생시킨 경우 입니다. 이 경우 아래와 같이 명령어를 추가하면 간단하게 해결하실 수 있습니다.


$ mvn clean package -Pdist,native -DskipTests -Dtar -Dmaven.javadoc.skip=true


300x250

OpenCV 최신 버전에서 Python으로 SIFT, SURF 사용하기(Install OpenCV 3.0 in Ubuntu)

프로그래밍 팁 2015. 11. 18. 16:07

 최신 버전의 OpenCV 라이브러리를 사용하여 Python 환경에서 SIFT 알고리즘을 수행하려 해보았더니 아래와 같은 화면이 출력됩니다.




 OpenCV 3.0.0 버전 이후에는 이전 버전에서 사용할 수 있었던 일부 라이브러리의 사용이 제한되어 있습니다. 자주 사용되는 SIFT와 SURF 또한 이에 해당되어 OpenCV 최신 버전을 그대로 설치하면 이를 바로 사용할 수 없습니다. 최신 버전의 OpenCV에서 이를 사용하기 위해서는 opencv_contrib을 추가적으로 설정해 주어야 합니다.


 1. OpenCV 최신 버전을 설치하기 위해 아래의 프로그램들을 설치합니다.


$ sudo apt-get install build-essential cmake git pkg-config libjpeg8-dev libtiff4-dev libjasper-dev libpng12-dev libavcodec-dev libavformat-dev libswscale-dev libv4l-dev libatlas-base-dev gfortran python2.7-dev


 2. Python 2.X 버전을 설치합니다. 3.X 버전의 경우 동작이 되지 않는 경우가 종종 발생한다는 점을 유의하셔야 합니다. apt-get에서는 2.X 버전을 제공합니다.


$sudo apt-get install python


 3. Python 패키지 매니저인 pip를 설치합니다.


$ wget https://bootstrap.pypa.io/get-pip.py

$ sudo python get-pip.py


 4. 기존 Python OpenCV의 라이브러리와는 별도로 동작할 수 있게 하는 virtualenv와 virtualenvwrapper를 설치합니다.


$ sudo pip install virtualenv virtualenvwrapper


 5. 설치한 virtualenv와 virtualenvwrapper가 항상 수행될 수 있도록 bash를 수정합니다.


$ vi ~/.bashrc



 6. 수정한 bashrc를 바로 적용합니다.


$source ~/.bashrc


 7. 독립된 별도의 Python OpenCV 라이브러리 환경을 만들기 위한 가상 환경을 생성합니다.


$mkvirtualenv cv


 8. 최신 버전의 OpenCV 버전을 다운로드 받아 OpenCV 폴더에 압축을 풉니다.


http://opencv.org/downloads.html


 또는 Github를 통해 최신 버전의 OpenCV 소스코드를 다운로드 받습니다.


$ cd ~

$ git clone https://github.com/Itseez/opencv.git

$ cd opencv

$ git checkout 3.0.0


 9. 이전 버전의 OpenCV 라이브러리를 사용할 수 있도록 해주는 opencv_contrib를 다운로드 받습니다.


$ cd ~

$ git clone https://github.com/Itseez/opencv_contrib.git

$ cd opencv_contrib

$ git checkout 3.0.0


 10. 이제 본격적으로 OpenCV 라이브러리를 설치해 보도록 합니다. 아래와 같이 cmake를 통해 Makefile을 구성합니다. 이 과정에서 위에서 받은 opencv_contrib을 설정해줍니다.


$ cd ~/opencv

$ mkdir build

$ cd build

$ cmake -D CMAKE_BUILD_TYPE=RELEASE \

-D CMAKE_INSTALL_PREFIX=/usr/local \

-D INSTALL_C_EXAMPLES=ON \

-D INSTALL_PYTHON_EXAMPLES=ON \

-D OPENCV_EXTRA_MODULES_PATH=~/opencv_contrib/modules \

-D BUILD_EXAMPLES=ON ..


 11. 구성요소를 컴파일한 다음 자신의 환경에서 만든 OpenCV를 설치합니다.


$make -j4

$sudo make install

$sudo make ldconfig


 12. OpenCV 설치가 완료되면 이를 Python에서 사용할 수 있도록 Sym-link를 추가해 줍니다.


$ cd ~/.virtualenvs/cv/lib/python2.7/site-packages/

$ ln -s /usr/local/lib/python2.7/site-packages/cv2.so cv2.so


 13. 이로서 OpenCV의 설치가 완료되었습니다. cv2 라이브러리를 불러오는 것으로 실행을 확인해 봅니다.


>>> import cv2

>>> cv2.__version__



 위와 같이 자신이 설치한 OpenCV 버전이 나오면 설치는 성공한 것입니다! 이제 SIFT가 실행이 잘 되는지 확인해 보도록 합니다!


 아래 이미지에 대한 SIFT 연산을 수행하는 프로그램을 작성해 보겠습니다.



1
2
3
4
5
6
7
8
9
10
11
import cv2
import numpy as np
 
img = cv2.imread('elecs.png')
gray= cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
 
sift = cv2.xfeatures2d.SIFT_create()
kp = sift.detect(gray,None)
 
cv2.drawKeypoints(gray,kp,img,flags=cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)
cv2.imwrite('sift_elecs.jpg',img)
cs


- 결과




 -참고자료

http://www.pyimagesearch.com/2015/06/22/install-opencv-3-0-and-python-2-7-on-ubuntu/

http://www.flowdas.com/blog/%ED%8C%8C%EC%9D%B4%EC%8D%AC-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%8B%9C%EC%9E%91%ED%95%98%EA%B8%B0-virtualenv/


300x250

[JAVA] 같은 공유기에 접속중인 기기의 IP 주소 확인하는방법

프로그래밍 팁 2015. 10. 15. 01:50

 TCP를 통해 Socket 통신을 하기 위해서는 접속하고자 하는 기기에 할당된 IP주소를 알고 있어야 합니다. 다시 말하자면, IP 주소를 알지 못할 경우 TCP를 통해서는 기기간의 통신이 불가능하다는 것입니다.


 그렇다면 상대의 IP 주소를 알아낼 수 있는 방법은 없는 것일까요? IP 프로토콜 규약에 의하면 특수 IP 주소를 사용하면 다른 기기와 통신이 가능하도록 설정되어 있습니다. 예를 들어 '255.255.255.255' 주소로 패킷을 보내면 같은 라우터(공유기) 내에 연결된 모든 기기들이 이를 수신하게 됩니다. 이를 Broadcast라고 칭하며 이를 사용하기 위해서는 TCP가 아닌 UDP 방식을 사용하여야 합니다.


 아래의 소스코드는 UDP 방식을 사용하여 서버가 IP주소 '255.255.255.255'를 통해 같은 라우터(공유기) 내에 접속중인 기기들에게 패킷을 보낸 후 이를 수신한 기기가 자신의 IP 주소를 서버에 알려주는 방식입니다.


 - DatagramPacket가 상대에게 전송될 때 해당 패킷 안에는 패킷을 보낸 측의 IP 주소가 담겨 있습니다. 이를 통해 서버측 IP와 클라이언트측 IP를 서로 알 수 있습니다.


Server측

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.net.UnknownHostException;
 
public class Main {
 
    public static void main(String[] args) {
        // TODO Auto-generated method stub       
        RecvServer rm = new RecvServer();
        rm.start();
 
        for (int i = 0; i < 10; i++) {
            try {
                new SearchDevice("255.255.255.255"8200).start();
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
 
        rm.closeServer();
 
    }
 
    static class SearchDevice extends Thread {
        InetAddress ia;
        int port;
 
        SearchDevice(String IPaddr, int Port) {
            try {
                ia = InetAddress.getByName(IPaddr);
                port = Port;
            } catch (UnknownHostException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
 
        public void run() {
            String msg = "Hello, ELECS!";
            try {
                DatagramSocket ds = new DatagramSocket();
                int length = msg.length();
                byte[] msgbyte = msg.getBytes();
                DatagramPacket dp = new DatagramPacket(msgbyte, length, ia, port);
                ds.send(dp);
                ds.close();
 
            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
 
    static class RecvServer extends Thread {
        String text;
        String clientIp;
        DatagramPacket dp;
        DatagramSocket ds;
        Object lock = new Object();
 
        public void closeServer() {
            synchronized (lock) {
                ds.close();
            }
        }
 
        public void run() {
            int port = 8000;
            byte[] message = new byte[1000];
            dp = new DatagramPacket(message, message.length);
            try {
                ds = new DatagramSocket(port);
                ds.receive(dp);
                synchronized (lock) {
                    text = new String(message, 0, dp.getLength());
                    ds.close();
 
                    clientIp = dp.getAddress().getHostAddress();
 
                    System.out.println("Client IP : " + clientIp);
                }
 
            } catch (Exception e) {
                e.printStackTrace();
            }
 
        }
    }
}
 
 
cs


Client측

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.net.UnknownHostException;
 
public class Main {
 
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        String text;
        String serverIp;
        DatagramPacket dp;
        int port = 8200;
        byte[] message = new byte[1000];
        dp = new DatagramPacket(message, message.length);
        try {
            DatagramSocket ds = new DatagramSocket(port);
            ds.receive(dp);
            text = new String(message, 0, dp.getLength());
            ds.close();
            serverIp = dp.getAddress().getHostAddress();
            new SearchDevice(serverIp, 8000).start();
        } catch (Exception e) {
            e.printStackTrace();
        }
 
    }
 
    static class SearchDevice extends Thread {
        InetAddress ia;
        int port;
 
        SearchDevice(String IPaddr, int Port) {
            try {
                ia = InetAddress.getByName(IPaddr);
                port = Port;
            } catch (UnknownHostException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
 
        public void run() {
            String msg = "Hello, ELECS!";
            try {
                DatagramSocket ds = new DatagramSocket();
                int length = msg.length();
                byte[] msgbyte = msg.getBytes();
                DatagramPacket dp = new DatagramPacket(msgbyte, length, ia, port);
                ds.send(dp);
                ds.close();
 
            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
 
}
 
 
cs


결과


300x250

[JAVA] Socket 서버 구현시 안전하게 SocketServer를 종료하는 방법

프로그래밍 팁 2015. 10. 14. 23:18

 안드로이드를 활용한 다양한 소켓 프로그래밍들의 에제를 둘러보다 보면 Server 측의 Socket을 다루는 데 종종 난해한 경우가 있습니다. 가령 Client측으로부터 Socket 통신이 한창 진행중인 상황에서 서버측 Socket을 닫아버리면 진행중이던 통신 관련 작업이 모두 끝나기도 전에 서버가 종료되어 버리는 심각한 상황이 발생할 수도 있는 것이지요.


 본 포스팅에서는 Java를 활용한 서버측의 Socket을 좀 더 안정적으로 종료시키는 방법에 대해 알아보도록 하겠습니다.


- Client와 Socket 통신이 진행중인 상황에서 ServerSocket이 강제로 종료되지 않도록 하기 위해 synchronized를 사용합니다. 이는 C/C++에서 제공하는 Mutex와 유사한 역할을 합니다. synchronized() 의 인자(Argument)를 가지고 있는 쪽에서 실행을 하다가 종료가 되었을 때 이를 다른 쪽에서 점유한 후 실행이 종료될 때 까지 다른 부분에서는 실행되지 않도록 설정합니다.


- ServerSocket이 accpt()를 실행하던 중에 종료되었을 때 SocketException이 발생합니다. 이 때 try-catch를 통해 해당 Exception을 catch한 후 Server가 안전하게 종료되었음을 확인합니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
 
public class Main {
 
    public static void main(String[] args) {
        // TODO Auto-generated method stub
 
        RecvMessage rm = new RecvMessage();
        //Server Thread를 실행합니다.
        rm.start();
 
        ....
 
        //ServerSocket를 닫음으로서 Server Thread를 종료합니다.
        rm.closeServer();
 
    }
 
    static class RecvMessage extends Thread {
        boolean ready = true;
        Socket socket;
        InputStream is;
        ObjectInputStream ois;
        String clientIp;
        ServerSocket sockserver;
 
        //Mutex로 사용할 Object 변수를 선언합니다.
        Object lock = new Object();
 
        public void closeServer() {
            try {
                //Client와 Socket 통신이 진행중일 경우 종료될 때까지 기다립니다.
                synchronized (lock) {
                    sockserver.close();
                }
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
 
        public void run() {
            try {
                sockserver = new ServerSocket(8200);
                while (ready) {
                    socket = sockserver.accept();
                    //Client와의 통신이 종료될 때 까지 SocketServer의 종료를 보류시킵니다.
                    synchronized (lock) {
                        is = socket.getInputStream();
                        ois = new ObjectInputStream(is);
 
                        clientIp = (String) ois.readObject();
 
                        System.out.println("Client IP : " + clientIp);
                        ois.close();
                        is.close();
                        socket.close();
                    }
                }
 
            } catch (SocketException e) {
                //ServerSocket가 종료되었을 때 실행됩니다.
                System.out.println("ServerSocket is closed!");
            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
 
}
 
 
cs


300x250

[C/C++]thread 조건변수 다루기 - pthread_cond_broadcast()

프로그래밍 팁 2015. 9. 1. 01:34

 멀티코어를 기반으로 한 프로그래밍을 수행할 때 가장 중요한 요소라면 여러 개의 thread를 통제할 수 있는 방법에 대한 정확한 이해라고 생각합니다. 다수의 thread가 하나의 변수를 동시에 접근하려 하는 것을 통제하기 위해 주로 mutex와 semaphore가 사용됩니다.

 이번 포스팅에서는 C/C++ 언어를 기반으로 thread를 좀 더 섬세하게 다룰 수 있는 조건변수(Condition Variable)에 대해 살펴보도록 합니다.


 조건변수를 사용하게 되면 기존의 mutex로 통제하는 방식에서 좀 더 발전한 방식을 사용합니다. 이에 사용되는 thread 함수들을 간단하게 살펴보겠습니다.


int pthread_cond_wait( pthread_cond_t* cond, pthread_mutex_t* mutex );

 동작중인 thread를 잠시 중단시킵니다 condition과 mutex인자를 모두 적용합니다. 다른 thread로부터 signal이나 broadcast를 받았을 경우 해당 함수는 아래의 함수와 같은 동작을 하게 됩니다.

int pthread_mutex_lock(pthread_mutex_t *mutex);


int pthread_cond_signal(pthread_cond_t *cond);

 cond 인자를 가지고 pthread_cond_wait() 함수를 실행중인 하나의 thread를 깨웁니다. 만약 cond 인자를 잡고있는 thread가 다수일 경우 단 하나의 thread만 깨어납니다.


int pthread_cond_broadcast(pthread_cond_t *cond);

 cond 인자를 가지고 pthread_cond_wait() 함수를 실행중인 모든 thread를 깨웁니다. 만약 cond 인자를 잡고 있는 thread가 다수일 경우 mutex를 먼저 잡은 thread가 먼저 동작되며 나머지 thread는 mutex를 받을 때 까지 대기상태를 유지합니다.


int pthread_mutex_unlock(pthread_mutex_t *mutex);

 pthread_cond_signal()함수 혹은 pthread_cond_broadcast() 함수에 의해 깨어난 thread가 실행을 끝내고 종료하기 전에 자신이 가지고 있는 mutex를 풀어줍니다. 만약 해당 thread가 이를 실행하지 않고 넘어가게 되면 다른 thread는 mutex가 풀릴 때까지 대기해야 합니다.


다음으로 간단한 예제를 통해 동작을 확인해 보도록 하겠습니다.


 - pthread_cond_signal()을 사용한 예제

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include <iostream>
#include <pthread.h>
using namespace std;
 
void *thread(void *);
pthread_t    tid[4];
pthread_mutex_t    mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t    cond = PTHREAD_COND_INITIALIZER;
 
int main() {
    int i;
    int id[4];
 
    for(i = 0; i < 4; i++){
        id[i] = i;
        pthread_create(&tid[i],NULL,thread,(void*)&id[i]);
    }
 
    sleep(2);
    pthread_cond_signal(&cond);
 
    for(i = 0; i < 4 ; i++){
        pthread_join(tid[i], NULL);
    }
    pthread_mutex_destroy(&mutex);
    pthread_cond_destroy(&cond);
 
    return 0;
}
 
void *thread(void *arg){
    //pthread_mutex_lock(&mutex);
    pthread_cond_wait(&cond, &mutex);
    cout << "hello, world! from " <<  *((int*)arg) << endl;
    pthread_mutex_unlock(&mutex);
}
cs


 결과 :


 pthread_cond_signal() 함수가 실행되어 하나의 thread만 깨어나 수행을 종료하였습니다. 함수가 끝나기 전 pthread_mutex_unlock() 함수를 실행하였으나 다른 thread들은 깨어나지 못하여 프로그램은 계속 대기상태에 머무르게 됩니다.


 - pthread_cond_broadcast()를 사용한 예제

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include <iostream>
#include <pthread.h>
using namespace std;
 
void *thread(void *);
pthread_t    tid[4];
pthread_mutex_t    mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t    cond = PTHREAD_COND_INITIALIZER;
 
int main() {
    int i;
    int id[4];
 
    for(i = 0; i < 4; i++){
        id[i] = i;
        pthread_create(&tid[i],NULL,thread,(void*)&id[i]);
    }
 
    sleep(2);
    pthread_cond_broadcast(&cond);
 
    for(i = 0; i < 4 ; i++){
        pthread_join(tid[i], NULL);
    }
    pthread_mutex_destroy(&mutex);
    pthread_cond_destroy(&cond);
 
    return 0;
}
 
void *thread(void *arg){
    //pthread_mutex_lock(&mutex);
    pthread_cond_wait(&cond, &mutex);
    cout << "hello, world! from " <<  *((int*)arg) << endl;
    pthread_mutex_unlock(&mutex);
}
cs

결과 :


 pthread_cond_broadcast() 함수가 실행되어 모든 thread가 깨어났음을 확인할 수 있습니다. 깨어난 thread들은 마치 pthread_mutex_lock() 함수가 실행되는 것과 같은 상황이 되어 먼저 mutex를 얻은 thread가 동작을 수행한 후 pthread_mutex_unlock() 함수를 실행하여 깨어난 다른 thread들이 mutex를 잡고 동작을 수행할 수 있도록 해주는 것을 확인하실 수 있습니다.

300x250

[JAVA]JDWP(Java™ Debug Wire Protocol)

프로그래밍 팁 2015. 8. 21. 23:20


 JDWP(Java™ Debug Wire Protocol)이란 디버거와 디버그를 하고자 하는 자바 가상머신(JVM) 사이의 통신을 위해 사용되는 프로토콜 입니다. JDWP는 같은 디버거가 다음과 같은 작업을 하는 것을 할 수 있도록 해줍니다.


 - 같은 컴퓨터 상의 다른 프로세스 내부간의 디버깅

 - 원격 컴퓨터 상에서의 디버깅


 JDWP는 통신 뿐 아니라 형식과 레이아웃을 상세히 다룬다는 점에서  다른 프로토콜들과 차이점을 가지고 있습니다. JDWP는 간단한 하나의 API를 통하여 여러가지의 전송 방식들을 수용할 수 있도록 만들어져 있습니다. 특정한 전송방식은 각 디버거와 타겟 JVM의 조합이 반드시 지원되지는 않습니다.


 아직은 JDWP가 장래엔 더욱 발전할 것으로 보이지만, JDWP는 간단한 실행에 있어서 충분히 실행할 수 있도록 고안되어 있습니다.


 안드로이드의 기반인 Dalvik 가상머신 또한 JDWP를 지원합니다. 안드로이드 기기 내의 애플리케이션은 Dalvik 가상머신을 통해 실행되고 있으며 각 애플리케이션은 또한 DDMS를 사용하여 특정한 포트를 통하여 디버깅을 할 수 있습니다. 만약 여러개의 애플리케이션을 디버깅을 할 때, DDMS는 특정한 가상머신의 디버깅 포트를 통한 포트포워딩 기능을 지원합니다. DDMS의 선택 메뉴를 통해 자신이 디버깅 하고자 하는 애플리케이션을 자유롭게 변경할 수 있는 것이지요. DDMS는 포트 8700번을 통해 포트포워딩을 합니다.

 

 실제 안드로이드 기기를 통해 Traceview를 해보면 JDWP 스레드를 확인하실 수 있습니다 JDWP의 Trace까지 추적되는 것 또한 확인하실 수 있습니다.





출저: http://docs.oracle.com/javase/1.5.0/docs/guide/jpda/jdwp-spec.html


300x250

[Java] Error 혹은 Debug시 등장하는 method인 access$000

프로그래밍 팁 2015. 8. 20. 23:57

 안드로이드 프레임워크를 공부하면서 종종 신기한 경우들을 마주치곤 합니다. 그 중 하나가 바로 실제 코드상에는 존재하지 않던 method가 디버깅을 할 때 예상치 못하게 등장하기 때문입니다.




 분명 소스코드 상에서는 존재 자체가 없었던 method인 'access$000'가 이렇게 디버깅을 하던 도중 발견되는 경우가 종종 있습니다. 과연 이것의 정체는 무엇일까요?


 이 문제의 원인은 바로 Java 언어의 특징 중 하나인 Inner class에서 원인을 찾을 수 있습니다.


 Java를 공부하신 분들이라면 누구나 아시는 듯이 Inner class는 말 그대로 Class 안에 내포된 Class를 의미합니다. C언어에서 마치 Struct 구조체 안에 또다른 Struct를 품은 듯한 형태를 띄고 있는 구조라고 이해하시면 되겠습니다.


아래는 Inner class가 구현되어 있는 예제입니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class Test {
 
  public void func() {
    System.out.println(new Inner().a);
    System.out.println(new Inner().getInt());
    System.out.println(new Inner2().b);
    System.out.println(new Inner2().getInt());
  }
 
  class Inner {
    private int a;
 
    public int getInt() {
      return a;
    }
  }
 
  class Inner2 {
    int b;
 
    public int getInt() {
      return b;
    }
  }
}
cs


 위의 예제는 Test 클래스 안에 두 개의 Inner class로 구성되어 있는 소스코드입니다. Test 클래스의 입장에서 Inner class는 private 인 경우에도 접근이 가능하여 위의 func() method에서 실행되는 모든 함수들이 제 기능을 하는 것을 확인하실 수 있습니다.


 그런데 여기서 문제가 발생하게 됩니다. Java의 소스코드를 컴파일한 결과물인 bytecode는 Inner class를 고려하지 않는다는 점입니다. 이로 인해 bytecode 상에서 func() method는 접근할수 없는 field 값은 Inner 클래스의 a의 값에 접근할 수 없게 됩니다.

 그렇기 때문에 실제 bytecode 상에서는 private로 설정된 내부클래스에 접근할 수 있도록 하기 위해 소스코드를 살짝 바꾸어서 기능을 똑같이 구현되게 하는데 실제 위의 설계된 소스코드를 디컴파일하게 되면 위에서 보았던 메소드인  access$000이 등장하는 것을 알 수 있습니다.


 아래의 예제는 Inner class가 적용되었을 경우를 가장한 예제입니다. 위의 소스코드와 비교하시면 자신이 설계한 소스코드에서 등장하는  access$000이 어느 시점에서 등장하게 되는지 어느 정도 감이 오실 것이라 생각됩니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
public class Test {
  public void func() {
    System.out.println(Test$Inner.access$000(new Test$Inner(this)));
    System.out.println(new Test$Inner(this).getInt());
    System.out.println(new Test$Inner2(this).b);
    System.out.println(new Test$Inner2(this).getInt());
  }
}
 
class Test$Inner {
  final Test this$0;
 
  private int a;
 
  Test$Inner(Test test) {
    this$0 = test;
  }
 
  public int getInt() {
    return a;
  }
 
  static int access$000(Test$Inner inner) {
    return inner.a;
  }
 
}
 
class Test$Inner2 {
  final Test this$0;
 
  int b;
 
  Test$Inner2(Test test) {
    this$0 = test;
  }
 
  public int getInt() {
    return b;
  }
}
cs


 아래는 제가 안드로이드 프레임워크를 분석하던 도중  access$000가 뜨던 부분입니다. 보시는 대로 new Handler() 방식으로 내부클래스를 구성하고 있으며 이를  access$000 함수명을 통하여 updateWindow() 함수에 접근하고 있는 것을 확인할 수 있습니다.


/frameworks/base/core/java/android/view/SurfaceView.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class SurfaceView extends View {
 
....
 
    final Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case KEEP_SCREEN_ON_MSG: {
                    setKeepScreenOn(msg.arg1 != 0);
                } break;
                case GET_NEW_SURFACE_MSG: {
                    handleGetNewSurface();
                } break;
                case UPDATE_WINDOW_MSG: {
                    updateWindow(falsefalse);
                } break;
            }
        }
    };
 
....
 
}
cs



출저 : http://www.javacodegeeks.com/2012/05/java-pitfalls-field-access-in-inner.html

300x250

[C/C++] typedef 함수 포인터 구현원리

프로그래밍 팁 2015. 4. 6. 13:33

 최근 안드로이드 프레임워크를 공부하다보니 JAVA는 물론 JNI를 통해 연결되는 C/C++ 코드들에 대해 빠삭하게 공부를 하고 있습니다. 정말이지 흔히 쓰는 저 언어들에 슬슬 도가 트고 있지 않은가 싶을정도로 자신의 실력에 대해 자만심이 들기도 할 정도입니다.

 소스코드들을 공부하는 과정에서 어려운 부분이 있다면 바로 흔히 사용하지 않는 방식으로 구현된 소스코드를 해석하는 때라고 생각합니다. 특히 수업시간에는 이론만 알고 넘어가는 함수 포인터라는 생소한 개념이 쓰였을 때는 처음엔 이것의 정체 조차 모르는 경우도 허다하지요.


 본론으로 들어가기에 앞서 간단한 소스코드를 통하여 함수 포인터에 대한 기념을 알아보도록 하겠습니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include<stdio.h>
 
void (*ptrfunc)(int);
 
void testprint(int n){
    printf("Number : %d\n", n);
}
 
int main(){
    testprint(100);
    ptrfunc = testprint;
    ptrfunc(77);
 
    return 0;
}
cs



위의 결과 출력을 보시면 대략적인 함수 포인터의 동작 원리를 이해하실 수 있을 것이라 생각합니다.

여기서 잠시 코드를 좀 더 자세히 설명 드리도록 하겠습니다.


void (*ptrfunc)(int);

함수 포인터는 위에서 보시는 바와 같은 구조로 이루어져 있습니다. 각 부분의 기능은 다음과 같습니다.

return값의 자료형 (*포인터 함수의 이름) (인자값)


 함수 포인터를 사용하실 때 주의하실 점은 함수 포인터가 이용하고자 하는 함수의 return값의 자료형과 인자값의 자료형 및 갯수가 일치해야 사용할 수 있다는 점입니다. 다음 코드를 확인해 봅시다.


ptrfunc = testprint;


 함수 포인터에 사용하고자 하는 함수의 이름을 입력합니다. 위 과정을 통해 기존 포인터와 같이 함수의 주소값이 포인터에 저장됨으로서 해당 함수 포인터는 자신이 가지고 있는 주소값의 함수와 같은 기능을 구현하게 됩니다.


 다음으로 typedef가 사용된 함수 포인터에 대해 살펴보도록 합시다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include<stdio.h>
 
typedef void (*ptrfunc)(int);
 
void testprint(int n){
    printf("Number : %d\n", n);
}
 
int main(){
    testprint(100);
    ptrfunc elecs;
    elecs = testprint;
    elecs(77);
 
    return 0;
}
cs


 위에서 설명하였던 소스코드에 typedef를 적용하여 보았습니다. typedef문이 이곳에서는 어떻게 적용되고 있는지 살펴보도록 합시다.


typedef void (*ptrfunc)(int);


 보시는 대로 기존에 있던 함수 포인터가 선언된 부분 앞쪽에 typedef가 선언되어 있는 모습을 보고 계십니다. typedef문은 빈번하게 사용되는 소스코드가 복잡하거나 길 경우 이를 간결하게 사용하기 위한 목적으로 사용되는데요 함수 포인터에서의 typedef문은 지금껏 보았던 typedef문과는 약간 사용되는 방법이 다르지만 결국은 사용되는 목적은 같습니다.


 다음으로 typedef 함수 포인터가 응용되는 부분을 보여드리도록 하겠습니다.


    ptrfunc elecs;
    elecs = testprint;


 응용이라고 말씀드려서 뭔가 거창한 걸 하려나 하겠습니다만 사실 typedef로 선언된 함수 포인터는 위에서 보시는 바와 같이 매우 간결하게 쓰이고 있음을 아실 수 있습니다. ptrfunc로 정의된 typedef문의 함수 포인터를 elecs라는 이름의 함수 포인터 하나를 만들었다고 보시면 됩니다. 쉽게 설명해서 함수포인터인 변수 하나가 생겼다고 생각하시면 됩니다. 아직도 이해가 안되신다면 아래의 간단한 소스코드를 보시면 아하! 하고 이해하실 겁니다.


int elecs;

elecs = 199;


 이제 감이 오시는지요? 그렇습니다! typedef문으로 선언된 함수 포인터는 마치 자료형을 선언하는 것과 같이 간단하게 함수 포인터 변수를 선언한다고 생각하시면 되는 것입니다! 혹시나 해서 아직도 이해하지 못하신 분들을 위해 저 위에 typedef 함수 포인터가 실제로는 어떻게 구현되어 있는지 보여드리겠습니다.


    void (*elecs)(int);
    elecs = testprint;


 위에서 보시는 바와 같이 ptrfunc 부분이 elecs로 치환된 것이라고 생각하시면 제 설명을 정확히 이해하시는 것입니다!


 혹시 typedef 함수 포인터의 원리에 대해 알고자 하셔서 오신 분들이라면 포스팅을 여기까지만 읽어주셔도 자신의 실력으로 함수 포인터를 활용하실 수 있으리라 생각합니다. 아래에서 부터는 다소 어려우니 기죽지 마시고 이렇게 활용되고 있구나 하는 생각으로 읽어주셨으면 합니다.


 그렇다면 이제 실전에서 사용되고 있는 코드를 보도록 하겠습니다. 아래의 소스코드는 안드로이드 내에서 구현된 함수 포인터 입니다. 언어는 C++로 구성되어 있습니다만 함수포인터를 설멍하는데 큰 어려움은 없을 것입니다. 소스는 다음과 같습니다.


/frameworks/av/camera/CameraBase.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
template <typename TCam, typename TCamTraits>
sp<TCam> CameraBase<TCam, TCamTraits>::connect(int cameraId,
                                               const String16& clientPackageName,
                                               int clientUid)
{
    ALOGV("%s: connect", __FUNCTION__);
    sp<TCam> c = new TCam(cameraId);
    sp<TCamCallbacks> cl = c;
    status_t status = NO_ERROR;
    const sp<ICameraService>& cs = getCameraService();
 
    if (cs != 0) {
        TCamConnectService fnConnectService = TCamTraits::fnConnectService;
        status = (cs.get()->*fnConnectService)(cl, cameraId, clientPackageName, clientUid,
                                             /*out*/ c->mCamera);
    }
    if (status == OK && c->mCamera != 0) {
        c->mCamera->asBinder()->linkToDeath(c);
        c->mStatus = NO_ERROR;
    } else {
        ALOGW("An error occurred while connecting to camera: %d", cameraId);
        c.clear();
    }
    return c;
}
cs


 여기서 참으로 특이한 구조의 소스코드를 만나게 되었습니다.

TCamConnectService fnConnectService = TCamTraits::fnConnectService;


이제 이 부분이 어떻게 구현되었는지 자세히 보도록 합시다.

/frameworks/av/include/camera/CameraBase.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
template <typename TCam>
struct CameraTraits {
};
 
template <typename TCam, typename TCamTraits = CameraTraits<TCam> >
class CameraBase : public IBinder::DeathRecipient
{
public:
    typedef typename TCamTraits::TCamListener       TCamListener;
    typedef typename TCamTraits::TCamUser           TCamUser;
    typedef typename TCamTraits::TCamCallbacks      TCamCallbacks;
    typedef typename TCamTraits::TCamConnectService TCamConnectService;
....
}
cs

위 코드를 통하여 다음과 같은 사실을 알아내었습니다.

    typedef typename TCamTraits::TCamConnectService TCamConnectService;


 TCamConnectService로 정의된 부분이 CameraTraits<TCam>::TCamConnectService와 동일하다는 것을 알고 다음으로 CameraTraits에 대해 확인해 보도록 하겠습니다.


/frameworks/av/include/camera/Camera.h

1
2
3
4
5
6
7
8
9
10
11
12
template <>
struct CameraTraits<Camera>
{
    typedef CameraListener        TCamListener;
    typedef ICamera               TCamUser;
    typedef ICameraClient         TCamCallbacks;
    typedef status_t (ICameraService::*TCamConnectService)(const sp<ICameraClient>&,
                                                           intconst String16&, int,
                                                           /*out*/
                                                           sp<ICamera>&);
    static TCamConnectService     fnConnectService;
};
cs


위 코드에서 정말 요상하게 친구가 하나 보이는군요.

typedef status_t (ICameraService::*TCamConnectService)(const sp<ICameraClient>&,

                                                           intconst String16&, int,
                                                           /*out*/
                                                           sp<ICamera>&);


 위에서 배운 바와 같이 해당 코드는 typedef 함수 포인터입니다. 다만 포인터 함수의 이름이 참으로 독특한데 이는 멤버 포인터라는 C++에서 사용되고 있는 기능입니다. 멤버 포인터에 대해 좀 더 자세히 알고 싶으신 분은 아래 포스팅을 참조해 주시기 바랍니다.

http://showmiso.tistory.com/210


바로 그 아래에는 typedef로 선언된 함수 포인터에 대한 변수를 static으로 선언되었음을 확인하실 수 있습니다.


 static TCamConnectService     fnConnectService;


 이제 여기서 다시 앞에서 확인하였던 선언문을 다시 한 번 보도록 합니다.


TCamConnectService fnConnectService = TCamTraits::fnConnectService;


 위 소스코드는 TCamConnectService로 선언된 typedef 함수 포인터를 가진 변수명 fnConnectService 안에 TCamTratis::fnConnectService 함수의 주소값을 넣겠다는 의미로 이해해 주시면 되겠습니다. 그렇다면 여기서 TCamTratis::fnConnectService 함수는 어떻게 구현되었는지 찾아보도록 하겠습니다.


/frameworks/av/camera/Camera.cpp

1
2
CameraTraits<Camera>::TCamConnectService CameraTraits<Camera>::fnConnectService =
        &ICameraService::connect;
cs


 Camera.cpp 소스 코드 내에서 'CameraTraits<Camera>::fnConnectService' 라는 이름의 포인터 함수 변수가 선언되었고 해당 포인터 함수에 ICameraService::connect 함수의 주소를 넣어준다고 이해하시면 되겠습니다.


/frameworks/av/include/camera/ICameraService.h

1
2
3
4
5
6
7
8
9
10
11
12
class ICameraService : public IInterface
{
public:
....
    virtual status_t connect(const sp<ICameraClient>& cameraClient,
            int cameraId,
            const String16& clientPackageName,
            int clientUid,
            /*out*/
            sp<ICamera>& device) = 0;
....
}
cs


/frameworks/av/camera/ICameraService.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
class BpCameraService: public BpInterface<ICameraService>
{
public:
    BpCameraService(const sp<IBinder>& impl)
        : BpInterface<ICameraService>(impl)
    {
    }
....
    // connect to camera service (android.hardware.Camera)
    virtual status_t connect(const sp<ICameraClient>& cameraClient, int cameraId,
                             const String16 &clientPackageName, int clientUid,
                             /*out*/
                             sp<ICamera>& device)
    {
        Parcel data, reply;
        data.writeInterfaceToken(ICameraService::getInterfaceDescriptor());
        data.writeStrongBinder(cameraClient->asBinder());
        data.writeInt32(cameraId);
        data.writeString16(clientPackageName);
        data.writeInt32(clientUid);
        remote()->transact(BnCameraService::CONNECT, data, &reply);
 
        if (readExceptionCode(reply)) return -EPROTO;
        status_t status = reply.readInt32();
        if (reply.readInt32() != 0) {
            device = interface_cast<ICamera>(reply.readStrongBinder());
        }
        return status;
    }
....
}
cs

 드디어 우리는 connect 함수를 찾아내는 데 성공하였습니다. 이로서 함수 포인터가 정의 되는 과정을 모두 살펴보았습니다. 마지막으로 이 기나긴 여정을 코드로 간결하게 요약하자면 다음과 같습니다.


TCamConnectService fnConnectService = TCamTraits::fnConnectService;


위 코드는 아래와 같이 변동이 됨을 확인하실 수 있습니다.


status_t (ICameraService::*TCamConnectService)(const sp<ICameraClient>&,

                                                           intconst String16&, int,
                                                           /*out*/
                                                           sp<ICamera>&);

TCamConnectService =
&ICameraService::connect;



 위의 코드를 처음 보시는 분들은 이 시점에서도 모두 이해가 안 되실 수 있습니다. 하지만 위 코드에서 일정 부분 만이라도 이해하셨다면 여러분들은 성공하신 겁니다!


300x250

[JAVA]윈도 CMD를 통해 자바 Command Line 명령어 활용하기

프로그래밍 팁 2014. 10. 4. 01:54

 자바로 프로그래밍을 하시는 분들이라면 많은 분들께서 eclipse를 통해 프로그램을 컴파일 하고 실행하실 겁니다. 아시는 분들이라면 아시겠지만 사실 eclipse는 자바를 좀 더 편하게 작업할 수 있게 해주는 IDE(통합 개발 환경)입니다. 자바를 처음 접하는 분들에게 마치 eclipse 자체가 자바인 것처럼 이해하셨던 분들도 많으셨으리라 생각합니다.

 그렇다면 아마 어떤 분들은 '그렇다면 eclipse를 사용하지 않고도 자바 프로그래밍이 가능하다는 건가?'라는 생각을 하시는 분도 계실겁니다. 정답은 '그렇다'입니다! 그것도 Windows 의 CMD(명령 프롬프트)만으로도 컴파일 및 실행이 된다는 것이지요!

 이번 포스팅에서는 윈도의 CMD를 활용하여 자바 프로그램을 간단하게 만들어보도록 하겠습니다.


※본 포스팅을 읽기 전에 자신의 컴퓨터에 자바 JDK가 설치되어있는지 확인합니다. 만약 설치가 되어있지 않다면 최신 버전의 JAVA JDK를 설치하도록 합니다.


http://www.oracle.com/technetwork/java/javase/downloads/index.html



 위의 사진과 같이 JAVA SE 다운로드 페이지가 나오면 왼쪽의 Java Platform (JDK) 를 선택 후 자신의 운영체제에 맞는 버전을 설치하면 된다.


1. 자신의 컴퓨터에 JDK가 설치되었다면 환경변수를 변경하에 JAVA 컴파일을 할 수 있도록 설정해줍니다. 먼저 자신의 컴퓨터에 JDK가 설치된 폴더로 이동한 후 bin 폴더 내의 javac가 있는 것을 확인합니다.



 JDK가 정상적으로 설치되었다면 다음과 같이 javac 파일을 확인하실 수 있습니다. JDK에서 기본설정된 폴더대로 설치하셨다면 C:\Program Files\Java\자신의 컴퓨터에 깔린 JDK의 버전(jdkX.X.X_XX)\bin


2.창의 윗부분을 오른쪽 클릭을 한 후 '주소 복사'를 클릭힙니다.



3. 컴퓨터에서 마우스 우측 클릭후 '속성'을 클릭합니다.



4. '고급 시스템 설정'을 클릭합니다.



5. 시스템 속성에서 '고급' 탭을 선택한 후 '환경 변수'를 클릭합니다.



6. 환경변수 창에서 시스템변수(S) 내에 Path를 더블클릭합니다.



7. 변수 값의 뒷부분에 다음 값을 추가합니다.

위의 과정에서 복사한 주소를 Ctrl+v로 붙여넣기 후 '\bin;'를 붙여줍니다.



※주의!!

 절대로 변수 값의 내용을 지우시면 안됩니다! 만약 실수로 변수 값을 덮어쓰셨다면 바로 취소 버튼을 누릅니다. 만약 기존의 환경변수를 덮어쓰기로 모두 삭제할 경우 연결된 프로그램들이 동작을 하지 못하게 되는 참사가 발생합니다!

 만약 실수로 덮어쓰기 후 확인 버튼을 눌러 변수가 지워지신 분들은 절대로 당황하지 마시고 제가 이전에 포스팅한 내용을 따라가며 원래 환경 변수값을 복구하시길 바랍니다.


[윈도7] 환경변수 path 삭제시 복구하는 방법 ← 클릭하시면 새창으로 이동합니다.


========================================================================================================

 여기까지 진행하셨다면 Windows의 CMD를 통한 자바 프로그래밍의 설정을 완료하였습니다. 이제부터 본격적으로 CMD를 통해 자바 프로그래밍을 진행해 보도록 하겠습니다.


8. 메모장을 열고 코드를 간단하게 작성합니다.



9. 작성한 코드를 .java 확장명으로 저장합니다.

여기서 주의할 점은 파일명은 반드시 public로 설정된 class의 명칭으로 하여야 합니다!



10. dir 명령어를 입력하여 java 파일이 생성된 것을 확인합니다.



11. 명령어를 입력하여 java 파일을 컴파일 합니다. 컴파일 명령어는 다음과 같이 입력합니다.


> javac 파일명.java



컴파일이 완료되면 위의 그림에서 보는 바와 같이 class 파일이 생성된 것을 확인할 수 있습니다.


12. 생성된 class 파일을 실행합니다. 실행 명령어는 다음과 같이 입력합니다.


> java 클래스명



class 파일을 실행하면 프로그램일 정상적으로 실행되고 있는 것을 확인할 수 있다.

다음은 커맨드 라인으로 입력된 값을 그대로 출력하는 프로그램을 작성해 보겠습니다.


13. Command Line을 통해 입력된 값들을 처리하는 부분을 추가합니다.

Command Line에 추가로 입력된 값들은 main 함수의 인자인 String 배열 변수인 args를 통해 프로그램에 적용됩니다.


14. 위에서 진행했던 과정대로 컴파일한 후 프로그램을 실행하면 다음과 같은 결과를 얻을 수 있습니다.



위에서 입력된 값은 다음과 같은 구성을 하고 있습니다.


>java 클래스명 args[0] args[1] args[2] .....


이와 같이 입력된 String 값들이 main의 args 인자로 출력할 수 있게 됨을 확인하실 수 있습니다.

300x250

[JAVA] 간단한 파일 입출력(FILE I/O) 구현

프로그래밍 팁 2014. 9. 10. 01:13

 자바로 프로그래밍을 하시는 분들이라면 가끔 파일 입출력(File I/O)를 써 줘야할 때가 간혹 발생할 겁니다.

평소엔 잘 쓰지도 않던 것인데 갑작스럽게 사용하게 되면 사용법이 헷갈려서 헤매는 경우도 많지요.


 아래는 JAVA 환경에서 간단하게 구현해 본 파일 입출력입니다. 프로그램은 먼저 FileOutputStream을 통해 파일을 기록한 후 해당 파일을 다시 FileInputStream을 통해 읽어 들이는 구조로 되어 있습니다.


※ 파일 입출력을 위해 사용된 PrintStream 클래스와 BufferedReader 클래스의 경우 새로 선언되더라도 가비지 컬렉터로 바로 사라지지 않습니다. 해당 클래스를 모두 사용한 후 반드시 close() 함수를 통해 해당 스트림을 종료하셔야 합니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public static void main(String[] args) {
        // TODO Auto-generated method stub
        File file;
        
        try {
            file = new File("file.txt");
            
            PrintStream ps = new PrintStream(new FileOutputStream(file));
            PrintStream stdout = System.out;
            System.setOut(ps);
            System.out.print("Hello, World!\n http://elecs.tistory.com/");
            ps.close();
            
            BufferedReader br = new BufferedReader(new FileReader(file));
            System.setOut(stdout);
            String str;
            while(true){
                str = br.readLine();
                if(str==null)
                    break;
                System.out.println(str);
            }
            br.close();
            
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        
    }




프로그램을 실행하면 다음과 같이 프로젝트 폴더 내에 file.txt 파일이 생성된 것을 확인하실 수 있습니다.



생성된 파일을 다시 읽어 다음과 같이 출력하실 수 있습니다.

300x250