728x90

Java Network 프로그래밍 기초 

( 참고 서적 : 자바 네트워크 프로그래밍 [자바 8과 함께하는 네트워크 애플리케이션 개발] )


IP 주소를 나타내는 InetAddress 클래스


InetAddress 클래스는 public 생성자가 없으므로 여러 static type의 get 메소드 중 하나를 사용한다. (.get~~)

다음은 특정 사이트의 ip주소를 얻는 예제이다. 


1
2
3
4
5
6
7
8
9
import java.net.InetAddress;
import java.net.UnknownHostException;
 
public class Address {
    public static void main(String[] args) throws UnknownHostException {
         InetAddress address = InetAddress.getByName("www.packtpub.com");
         System.out.println(address);
    }
}
cs


결과 : 

www.packtpub.com/83.166.169.231


해당 주소의 접속 여부를 결정하기 위해 필요한 대기시간을 다음과 같이 지정할 수 있다.

isReachable 메소드의 인자는 m/s 단위의 대기 시간이다.


1
 address.isReachable(3000);
cs




NIO 지원



selector : 다중 채널을 처리하기 위한 싱글 스레드를 허용하는 기술




자바 서버와 안드로이드 클라이언트 연동


Server.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
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
 
public class Server {
 
    public static void main(String[] args) {
        ServerStart();
    }
 
public static void ServerStart() {
    try {
        int portNum = 11001;
 
        System.out.println("서버 시작이당");
        @SuppressWarnings("resource")
        ServerSocket serverSocket = new ServerSocket(portNum);
        System.out.println(portNum);
 
        while (true) {
            Socket socket = serverSocket.accept();
            InetAddress clientHost = socket.getInetAddress();
            int clientPort = socket.getPort();
            System.out.println("클라이언트 접속, 호스트 : " + clientHost + ", port : " + clientPort);
 
            ObjectInputStream inputStream = new ObjectInputStream(socket.getInputStream());
            Object obj = inputStream.readObject();
            System.out.println("Input : " + obj);
 
            ObjectOutputStream outputStream = new ObjectOutputStream(socket.getOutputStream());
 
            outputStream.writeObject(obj + " 서버로부터 메시지 ");
            outputStream.flush();
            socket.close();
        }
    }
    
    catch(Exception ex)
        {
            ex.printStackTrace();
        }
    }
}
 
cs



안드로이드 클라이언트


인터넷을 사용하기 위해 AndroidManifest.xml에 유저 권한 추가

<uses-permission android:name="android.permission.INTERNET" />


클라이언트 레이아웃 (activity_main.xml 디자인)




MainActivity.java의 전체 소스

package com.example.pknu.hello;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;

import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;

public class MainActivity extends AppCompatActivity {

EditText input01;
Button button01;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

input01 = (EditText) findViewById(R.id.input01);

// 버튼 이벤트 처리
button01 = (Button) findViewById(R.id.button01);
button01.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
String addr = input01.getText().toString().trim();

ConnectThread thread = new ConnectThread(addr);
thread.start();
}
});

}

class ConnectThread extends Thread {
String hostname;

public ConnectThread(String addr) {
hostname = addr;
}

public void run() {

try {
int port = 11001;

Socket sock = new Socket(hostname, port);
ObjectOutputStream outstream = new ObjectOutputStream(sock.getOutputStream());
outstream.writeObject("Hello AndroidTown on Android");
outstream.flush();

ObjectInputStream instream = new ObjectInputStream(sock.getInputStream());
String obj = (String) instream.readObject();

Log.d("MainActivity", "서버에서 받은 메시지 : " + obj);

sock.close();

} catch(Exception ex) {
ex.printStackTrace();
}

}
}
}



'Study > Java' 카테고리의 다른 글

자바 - 식별자  (0) 2021.01.06
자바 NIO 셀렉터  (0) 2017.07.05
NIO 기반 네트워킹  (0) 2017.05.30
JAVA - NIO  (0) 2017.05.29
Java - OOP_인터페이스와 다형성  (0) 2017.03.20
728x90

서버소켓 채널 생성과 다중 클라이언트 연결 수락 (서버)


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
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
 
public class ServerTest {
 
    public static void main(String[] args) {
        ServerSocketChannel serverSocketChannel = null;
        
        try {
            serverSocketChannel = ServerSocketChannel.open();
            serverSocketChannel.configureBlocking(true);
            serverSocketChannel.bind(new InetSocketAddress(5001));
            
            while(true) {
                System.out.println("연결 대기..");
                SocketChannel socketChannel = serverSocketChannel.accept();
                InetSocketAddress inetSockAddr = (InetSocketAddress) socketChannel.getRemoteAddress();
                System.out.println("연결 수락!" + inetSockAddr.getHostName());
                
                ByteBuffer byteBuffer = null;
                Charset charset = Charset.forName("UTF-8");
                
                byteBuffer = ByteBuffer.allocate(100);
                int byteCount = socketChannel.read(byteBuffer);
                byteBuffer.flip();
                String message = charset.decode(byteBuffer).toString();
                System.out.println"[데이터 수신 성공] : " + message);
                
                byteBuffer = charset.encode("Hello Client");
                socketChannel.write(byteBuffer);
                System.out.println"[데이터 전송 성공]");
            }
        }
        catch (Exception e) {
        }
        
        if (serverSocketChannel.isOpen()) {
            try {
                serverSocketChannel.close();
            } catch (Exception e) {
            }
        }
    }
}
cs


소켓 채널 생성과 연결 요청 (클라이언트)


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
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
 
public class ClientTest {
    public static void main(String[] args) {
        SocketChannel socketChannel = null;
        try {
            socketChannel = SocketChannel.open();
            socketChannel.configureBlocking(true);
            System.out.println"[연결 요청]");
            socketChannel.connect(new InetSocketAddress("localhost"5001));
            System.out.println"[연결 성공]");
            
            ByteBuffer byteBuffer = null;
            Charset charset = Charset.forName("UTF-8");
            
            // client가 데이터(헬로서버) 보냄
            byteBuffer = charset.encode("Hello Server");
            socketChannel.write(byteBuffer);
            System.out.println"[데이터 전송 성공]");
            
            // client가 데이터 받음
            byteBuffer = ByteBuffer.allocate(100);
            int byteCount = socketChannel.read(byteBuffer);
            byteBuffer.flip();
            String message = charset.decode(byteBuffer).toString();
            System.out.println"[데이터 수신 성공] : " + message);
            
        } catch(Exception e) {}
        
        if(socketChannel.isOpen()) {
            try {
                socketChannel.close();
                System.out.println"[연결 닫음]");
            } catch (IOException e1) {}
        }    
    }
}
cs



'Study > Java' 카테고리의 다른 글

자바 NIO 셀렉터  (0) 2017.07.05
Java Network 프로그래밍 기초  (0) 2017.06.03
JAVA - NIO  (0) 2017.05.29
Java - OOP_인터페이스와 다형성  (0) 2017.03.20
Java - OOP_클래스와 상속, 생성자, 오버로딩/오버라이딩  (0) 2017.03.17
728x90

NIO (New Input/Output)    (참고 서적 : 이것이 자바다)


java.nio 패키지는 자바4부터 포함된 패키지로, 새로운 입출력이라는 뜻을 가지고 있다.

자바7에서는 네트워크 지원과 IO와 NIO사이의 클래스 일관성을 강화해 NIO.2 API가 추가되었다.


NIO는 IO 와 다르게 스트림이 아닌 채널 방식을 사용하며, 버퍼와 비동기 방식을 기본적으로 제공한다. 

또한, IO가 블로킹 방식만 지원하는 것과 달리 NIO는 블로킹과 넌 블로킹 방식을 모두 지원한다.



스트림 vs 채널

스트림 기반에서 입출력을 위해 입력 스트림과 출력스트림을 모두 생성해야하는 것과 달리, 

채널은 스트림과 달리 양방향으로 입출력이 가능하다.



non 버퍼 vs 버퍼


IO는 입출력을 위한 버퍼를 제공해주는 보조 스트림이 있다. NIO는 기본적으로 버퍼를 사용해 입출력을 하기 때문에 성능이 좋다.


IO가 스트림에 읽은 데이터를 즉시 처리하는 것과 달리 NIO에서는 읽은 데이터를 무조건 버퍼에 저장한다.

이는 별도로 입력된 데이터를 저장하지 않아도, 버퍼내에서 데이터의 위치를 이동해 가며 필요한 부분을 읽고 쓸 수 있는 유연성을 제공한다.



블로킹(blocking : 대기상태) vs non 블로킹

IO에서 입력 스트림의 read() 메소드를 호출하면 데이터가 입력되기 전까지 스레드가 블로킹(대기상태)가 된다. write() 메소드도 마찬가지로 출력되기전까지 블로킹 상태가 된다. 블로킹 상태에서 다른 일을 할 수 없고 이 상태를 빠져나오기 위해 인터럽트 될 수도 없다. 블로킹을 빠져나오기 위해선 스트림을 닫는 수 밖에 없다.


non 블로킹은 스레드를 인터럽트함으로써 빠져나올 수 있으며, 스레드가 블로킹 되지 않는다. 이는 입출력 준비가 완료된 채널만 선택해, 작업 스레드가 처리하기 때문에 블로킹 되지 않는다.



어떤 경우에 IO와 NIO중에서 취사 선택해야 하는가?

IO는 연결 클라이언트 수가 적고, 전송되는 데이터가 대용량으로 순차처리 될 때에 사용하는 것이 적다.


NIO는 연결 클라이언트 수가 많고, 하나의 입출력 처리 작업이 오래 걸리지 않는 경우에 사용하는 것이 좋다. 처리가 오래 걸리면 대기 작업의 수가 늘어나기 때문에 비효율적이다. 또한, 모든 입출력에 버퍼를 사용하기 때문에 대용량 데이터의 처리에는 버퍼 할당의 문제도 있다.



경로 정의 및 탐색


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
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Iterator;
 
public class PathExample {    
    public static void main(String[] args) throws Exception {
        Path path = Paths.get("src/sec02/exam01_path/PathExample.java");
        // or ("src/sec02/", "exam01_path/PathExample.java");
        
        System.out.println("[파일명] " + path.getFileName());
        System.out.println(("부모 디렉토리명]: " + path.getParent().getFileName()));
        
        System.out.println("중첩 경로수: " + path.getNameCount());
        
        System.out.println();
        for(int i=0; i<path.getNameCount(); i++) {
            System.out.println(path.getName(i));
        }
        
        System.out.println();
        Iterator<Path> iterator = path.iterator();
        while(iterator.hasNext()) {    // 이동할 항목이 있다면 true 리턴 
            Path temp = iterator.next();    // 현재 위치를 순차적으로 하나 증가해 이동
            System.out.println(temp.getFileName());
        }
    }
}
cs



WatchService

자바 7에서 도입된 와치 서비스는, 디렉토리 내부에서 파일 생성, 삭제, 수정 등의 변화를 감시하는데 사용된다. 

(에디터에서 파일이 변경되면 다시 불러올 것인지를 묻는 것이 와치 서비스의 예)



와치 서비스 실행 결과 및 예제 (Java FX)

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
import java.nio.file.FileSystems;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardWatchEventKinds;
import java.nio.file.WatchEvent;
import java.nio.file.WatchEvent.Kind;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;
import java.util.List;
 
import javafx.application.Application;
import javafx.application.Platform;
import javafx.scene.Scene;
import javafx.scene.control.TextArea;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
 
public class WatchServiceExample extends Application {
    class WatchServiceThread extends Thread {
        @Override
        public void run() {
            try {
                WatchService watcher = FileSystems.getDefault().newWatchService();
                Path directory = Paths.get("C:/temp");
                directory.register(watcher, StandardWatchEventKinds.ENTRY_CREATE,
                        StandardWatchEventKinds.ENTRY_DELETE,
                        StandardWatchEventKinds.ENTRY_MODIFY);                
                                
                while(true) {
                    WatchKey watchKey = watcher.take(); // 블로킹(Watch키가 큐에 들어올 때까지)
                    List <WatchEvent<?>> list = watchKey.pollEvents(); // WatchEvent목록 얻음
                    
                    for(WatchEvent<?> watchEvent : list) {
                        // 이벤트 종류 얻음
                        Kind<?> kind = watchEvent.kind();
                        // 감지된 path 얻음
                        Path path = (Path)watchEvent.context();
                        
                        if(kind == StandardWatchEventKinds.ENTRY_CREATE) {
                            Platform.runLater(()-> textArea.appendText("파일 생성됨 " + 
                        path.getFileName() + "\n"));
                        } else if (kind == StandardWatchEventKinds.ENTRY_DELETE) {
                            Platform.runLater(()-> textArea.appendText("파일 삭제됨 " +
                        path.getFileName() + "\n"));
                        } else if (kind == StandardWatchEventKinds.ENTRY_MODIFY) {
                            Platform.runLater(()-> textArea.appendText("파일 변경됨 " +
                        path.getFileName() + "\n"));
                        } else if (kind == StandardWatchEventKinds.OVERFLOW) {                            
                        }
                    }
                    boolean valid = watchKey.reset();
                    
                    if(!valid) { break; }
                    }
            } catch (Exception e) { }
        }
    }
    
 
    TextArea textArea = new TextArea();
    
    @Override
    public void start(Stage primaryStage) throws Exception {
        BorderPane root = new BorderPane();
        root.setPrefSize(500300);
        
        textArea.setEditable(false);
        root.setCenter(textArea);
        
        Scene scene = new Scene(root);
        primaryStage.setScene(scene);
        primaryStage.setTitle("워치 서비스 예제");
        primaryStage.show();
        
        WatchServiceThread wst = new WatchServiceThread();
        wst.start();
    }
    
    public static void main(String[] args) throws Exception {
        launch(args);
    }
}
cs



non 다이렉트 버퍼와 다이렉트 버퍼


non 다이렉트 버퍼는 JVM이 관리하는 heap 메모리 공간을 이용하는 버퍼로 버퍼 생성 시간이 빠르다.  반면에 heap 공간은 상당히 제한되어 있으므로 버퍼의 크기를 크게 잡을 수가 없다. 또한 입출력을 위해 non다이렉트 버퍼의 내용을 임시 다이렉트 버퍼를 생성해 복사하고, 그곳에서 OS의 native I/O와 같은 기능을 수행하기 때문에 입출력 속도는 느리다.


반면에 다이렉트 버퍼는 OS의 native C 함수를 호출하고 여러 가지 처리할 것이 있기 때문에 버퍼 생성이 느리다. 하지만 OS가 관리하는 메모리 공간을 이용하므로 버퍼의 크기가 크고, 입출력 성능이 높다는 장점이 있다. 따라서 한 번 생성해놓고, 빈번한 입출력과 큰 데이터 처리에 사용하는 것이 효율이 좋다.



다음 예제에서는 non 다이렉트와 다이렉트 버퍼를 생성하고 그 결과를 보는 예제이다. non 다이렉트 버퍼는 앞서 설명했듯 JVM이 관리하는 제한된 heap메모리 공간을 사용하므로 크기가 작아, OutOfMemoryError가 발생한다.


1
2
3
4
5
6
7
8
9
10
11
12
13
import java.nio.ByteBuffer;
 
public class BufferSizeExample {
 
    public static void main(String[] args) {
        ByteBuffer directBuffer = ByteBuffer.allocateDirect(200 * 1024 * 1024);
        System.out.println("다이렉트 버퍼 생성 " + directBuffer);
        
        ByteBuffer nonDirectBuffer = ByteBuffer.allocateDirect(2000 * 1024 * 1024);
        System.out.println("non 다이렉트 버퍼 생성 " + nonDirectBuffer);
    }
 
}
cs


다이렉트 버퍼 생성 java.nio.DirectByteBuffer[pos=0 lim=209715200 cap=209715200]

Exception in thread "main" java.lang.OutOfMemoryError: Direct buffer memory

at java.nio.Bits.reserveMemory(Bits.java:693)

at java.nio.DirectByteBuffer.<init>(DirectByteBuffer.java:123)

at java.nio.ByteBuffer.allocateDirect(ByteBuffer.java:311)

at BufferSizeExample.main(BufferSizeExample.java:9)





allocate() 메소드

JVM heap 메모리에 non다이렉트 버퍼 생성.

1
2
ByteBuffer byteBuffer = ByteBuffer.allocate(100);    // 최대 100개의 바이트를 저장하는 ByteBuffer 생성
CharBuffer charBuffer = CharBuffer.allocate(100);    // 최대 100개의 문자를 저장하는 CharBuffer 생성
cs


Wrap() 메소드

각 타입별 Buffer클래스는 모두 wrap() 메소드를 가지고 있다. wrap() 메소드는 이미 생성된 자바 배열은 Wrapping해서 Buffer 객체를 생성한다.

자바 배열은 JVM heap 메모리에 생성되므로(배열도 객체이다) wrap()은 non다이렉트 버퍼를 생성한다.


1
2
byte[] byteArray = new byte[100];
ByteBuffer byteBuffer = ByteBuffer.wrap(byteArray);
cs


다음과 같이 배열의 일부 데이터만 Buffer객체를 생성할 수도 있다.

1
2
3
4
char[] charArray = new char[100];
 
// 0 인덱스부터 50개만 버퍼로 생성 
CharBuffer charBuffer = CharBuffer.wrap(charArray, 050);  
cs



CharBuffer는 CharSequence 인터페이스로 구현한 클래스이므로 CharSequence 타입의 매개값을 갖는 wrap() 메소드도 제공한다. 즉, 매개값으로 문자열을 제공해서 다음과 같이 CharBuffer 생성이 가능하다.


1
CharBuffer charBuffer = CharBuffer.wrap("문자열 매개값 넣기");
cs


CharSequence 인터페이스

http://impartially.tistory.com/23



allocateDirect() 메소드

이 메소드는 다이렉트 버퍼(OS가 메모리 관리)를 생성하며, ByteBuffer만 제공한다. 각 타입별 버퍼는 없지만 as-Buffer()를 통해 각 타입별 Buffer를 얻을 수 있다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.IntBuffer;
 
public class DirectBufferCapacityExample {
    public static void main(String[] args) {
        ByteBuffer byteBuffer = ByteBuffer.allocateDirect(100);
        System.out.println("저장용량: " + byteBuffer.capacity() + " 바이트");
        
        
        CharBuffer charBuffer = ByteBuffer.allocateDirect(100).asCharBuffer();
        System.out.println("저장용량: " + charBuffer.capacity() + " 문자");
        
        IntBuffer intBuffer = ByteBuffer.allocateDirect(100).asIntBuffer();
        System.out.println("저장용량: " + intBuffer.capacity() + " 정수");
    }
}
 
cs



byte 해석 순서

1
2
3
4
5
6
7
8
import java.nio.ByteOrder;
 
public class ComputerByteOrderExample {
    public static void main(String[] args) {
        System.out.println("운영체제 종류 : " + System.getProperty("os.name"));
        System.out.println("네이티브 바이트 해석 순서 : " + ByteOrder.nativeOrder());
    }
}
cs

결과 :

운영체제 종류 : Windows 7

네이티브 바이트 해석 순서 : LITTLE_ENDIAN



CPU 등 환경에 따라 바이트의 처리 순서에는 차이가 있다. (Little Endian, Big Endian)

따라서, 네트워크로 데이터를 주고 받을 때는 하나의 Endian에 맞도록 Byte Order해줄 필요가 있다.



JVM은 무조건 Big Endian으로 자동적으로 처리해주지만, 다이렉트 버퍼일 경우 OS의 native I/O를 사용하므로 

다음과 같이 기본 해석순서로 맞춰주면 성능에 도움이 된다.


1
    ByteBuffer byteBuffer = ByteBuffer.allocateDirect(100).order(ByteOrder.nativeOrder());
cs




728x90

참고 강의 : https://wikidocs.net/book/31

 

 

인터페이스

 

인터페이스의 필요성

 

육식 동물에게 먹이를 주는 사육사(ZooKeeper) 클래스가 있다고 하자. 그리고 Animal 클래스를 상속하는 Tiger와 Lion 클래스가 있다.

 

ZooKeeper 클래스의 feed 메소드는 메소드 오버로딩 되어 있다. (입력값이 각각 Tiger, Lion으로 자료형이 다름)

1
2
3
4
5
6
7
8
9
public class ZooKeeper {    
    public void feed(Tiger tiger) {
        System.out.println("feed tiger");
    }
 
    public void feed(Lion lion) {
        System.out.println("feed lion");
    }
}
cs

 

하지만 이 경우, Animal을 상속하는 새로운 동물 클래스가 생길 때 마다, ZooKeeper에도 feed 메소드가 필요하게 된다.

 

이럴때 필요한 것이 바로 "인터페이스"이다.

 

 


 

Predator.java

 

1
2
public interface Predator {
}
cs

육식 동물 인터페이스를 작성했다. class 키워드가 아닌 interface 키워드를 사용해 구현한다.

 

그리고 Tiger와 Lion 클래스는 Animal 클래스를 사용하며, Predator 인터페이스를 구현하도록 한다. 이 때는 implements라는 키워드를 사용한다.

1
2
public class Tiger extends Animal implements Predator {    
}
cs

 

1
2
public class Lion extends Animal implements Predator { 
}
cs

 

이제부터는 어떤 육식 동물이 추가되더라도 다음과 같이 인터페이스를 구현한 클래스를 정의하면 된다.

1
2
public class Crocodile extends Animal implements Predator {
}
cs

 

하지만 아직도 문제는 있다. 

1
2
3
public void feed(Predator predator) {
    System.out.println("feed ???");
}
cs

 

ZooKeeper 클래스가 육식동물(클래스)의 이름(name)에 따라 다른 문자열을 출력해야 할 것이다.

 

이럴 때 인터페이스에 새로운 메소드 추가가 필요하다.

 


 

Predator.java

 

1
2
3
public interface Predator {
    public String getName();
}
cs

위에서 특이한 점은 getName 메소드에 몸통(body)가 없다는 점이다. 보통은 메소드명(입력값) { 몸통 } 으로 정의되어야 할 것인데 말이다.

이것은 인터페이스의 규칙으로 위의 getName이라는 메소드는, 다음과 같이 인터페이스를 implements한 클래스들이 구현해야 한다.

 

1
2
3
4
5
public class Tiger extends Animal implements Predator {
    public String getName() {
        return this.name;
    }
}
cs

 

이제 ZooKeeper 클래스의 feed 메소드를 다음과 같이 정의하면 육식동물의 이름에 따라 다른 문자열을 출력할 것이다.

1
2
3
4
5
public class ZooKeeper {    
    public void feed(Predator predator) {
        System.out.println("feed "+predator.getName());
    }
}
cs

predator.getName()을 호출하면, Predator 인터페이스를 구현한 구현체(Tiger, Lion)의 getName()이 호출된다.

 

 

Tiger와 Lion에 있는 getName 메소드가 중복되어 있으므로, 제거하고 Animal 클래스에 getName 메소드를 구현하도록 한다. 

(육식동물 클래스는 Animal클래스를 상속받으니깐)

이렇게 중복된 메소드를 제거하고, 이동하는 행위를 리팩토링(Refactoring)이라고 한다.

 

메인 메소드도 다음과 같이 정의하고 실행결과를 보자.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class ZooKeeper {    
    public void feed(Predator predator) {
        System.out.println("feed "+predator.getName());
    }
 
    public static void main(String[] args) {
        Tiger tiger = new Tiger();
        tiger.setName("tiger");
 
        Lion lion = new Lion();
        lion.setName("lion");
 
        ZooKeeper zooKeeper = new ZooKeeper();
        zooKeeper.feed(tiger);
        zooKeeper.feed(lion);
    }
}
cs

Output : 

feed tiger

feed lion

 

 

인터페이스의 개념적인 이해

 

컴퓨터의 USB 포트에는 다양한 기기를 연결할 수 있다. 바로 이 USB포트가 물리적 세계의 인터페이스라고 할 수 있다.

USB포트의 규격만 알면 어떤 기기도 만들 수 있고, 컴퓨터는 USB 포트만 제공하고 어떤 기기가 만들어지는 지 신경쓸 필요가 없다. 바로 이 점이 인터페이스의 핵심이다.

 

위에서 만든 사육사(ZooKeeper) 클래스가 호랑이던 사자던 상관하지 않고 먹이를 줄 수 있는것과 같다.

물리적세계 자바세계
컴퓨터 ZooKeeper
USB 포트 Predator
하드디스크, 디지털카메라,... Tiger, Lion, Crocodile,...

※ USB 포트에는 전자기기들이 지켜야만 하는 각종 규칙들이 있다. (인터페이스의 메소드)

'Study > Java' 카테고리의 다른 글

Java Network 프로그래밍 기초  (0) 2017.06.03
NIO 기반 네트워킹  (0) 2017.05.30
JAVA - NIO  (0) 2017.05.29
Java - OOP_클래스와 상속, 생성자, 오버로딩/오버라이딩  (0) 2017.03.17
Java 개발PC 설정 및 기초  (0) 2017.03.16
728x90

참고 강의 : https://wikidocs.net/book/31

 

 

클래스

클래스는 객체를 생성하는 기능이 있다.

1
2
3
public class Animal {
 
}
cs

 

객체의 생성

1
Animal cat = new Animal();
cs

 

객체와 인스턴스의 차이

 

인스턴스 : 클래스에 의해서 만들어진 객체이며, 위에서 cat은 객체. 

cat이라는 객체는 Animal의 인스턴스(instance). 

 

즉, "cat은 인스턴스" 보다는 "cat은 객체" 이며, Animal의 객체가 아닌 "cat은 Animal의 인스턴스" 라고 표현한다.

 

 

붕어빵 틀은 클래스, 붕어빵 틀에 의해 만들어진 붕어빵 들은 객체

또는 Animal은 클래스이고 고양이와 개, 말 등은 객체

 

아래와 같이 Animal 클래스에서 무수히 많은 동물 객체들이 생성될 수 있다.

1
2
3
4
Animal cat = new Animal();
Animal dog = new Animal();
Animal horse = new Animal();
...
cs

 

 

Animal 클래스에 인스턴스 변수(또는 멤버변수나 속성) name을 추가

인스턴스 변수에 접근하려면 인스턴스.변수 와 같이 접근한다. (cat.name)

1
2
3
4
5
6
7
8
9
10
11
package jump2java;
 
public class Animal {
        String name;
    
    public static void main(String [] args){
        Animal cat = new Animal();
        System.out.println(cat.name);
    }
}
 
cs

 

Output :

null

 

 

값이 할당되지 않았기에 null이 출력된다.

 

 

 

특정 클래스가 있는 자바 파일 내에 또 다른 클래스 정의 가능
 * - 단, 원래 클래스가 아닌 다른 클래스들은 public 키워드를 붙일 수 없다!

 

 

Java의 메소드 

Java에서 클래스 내에 구현된 함수를 메소드라고 한다. 

(함수라는 용어를 쓰지 않으며, 클래스 내에서만 사용가능)

 

아래와 같이 return값이 없는 void 메소드를 정의해준다. 

return값이 없다는 거에 당황할 수 있지만 setName의 name은 메소드 내에서 할당되며 돌려주는 값은 없다.

 

 

this 키워드

아래의 코드에서 Animal 클래스의 인스턴스 변수명도 name, SetName 메소드의 인수도 name이다. (String name)

아래 예제에서 this는 Animal 클래스에 의해서 생성된 인스턴스를 가리키며,  cat을 가리킨다.

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package jump2java;
 
public class Animal {
        String name;
        
    public void setName(String name) {
        this.name = name;
    }
    
    public static void main(String [] args){
        Animal cat = new Animal();
        cat.setName("Ludwig");
        System.out.println(cat.name);
    }
}
cs

 

Output :

Ludwig

 

 

 

 

메소드 내에서만 쓰이는 지역 변수 (local variable)

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package jump2java;
 
public class Test {
        String name;
        
        public void vartest(int a) {
            a++;
        }
 
        public static void main(String[] args) {
            int a = 1;
            Test myTest = new Test();
            myTest.vartest(a);
            System.out.println(a);
        }
}
 
cs

 

Output :

1

 

 

vartest 메소드의 인수 a는 메소드 안에서만 쓰이는 변수이며, 메소드 밖(main함수)의 변수 a가 아니다.

메소드에서 쓰이는 변수는 지역변수로 밖의 변수와는 상관없다.

 

위에서 vartest 메소드를 이용해 값을 증가시키려면 다음과 같이 쓴다.

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package jump2java;
 
public class Test {
        String name;
        
        public int vartest(int a) {
            a++;
            return a;
        }
 
        public static void main(String[] args) {
            int a = 1;
            Test myTest = new Test();
            a = myTest.vartest(a);
            System.out.println(a);
        }
}
 
cs

 

입력으로 들어온 값이 1만큼 증가해 return되며 2가 출력된다.

주의할 점은, 이 역시 vartest 메소드 안의 a 변수와 메소드 밖의 a 변수는 다른 것이다!

 

 

Call by value

 

메소드에 을 전달하는 것과 객체를 전달하는 것의 차이를 알 수 있는 예제이다.

다음은 메소드에 값을 전달하는 예제이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package jump2java;
 
class updator {
    void update(int cnt) {
        cnt++;
    }
}
 
public class Counter {
    int count = 0;
 
    public static void main(String[] args) {
        Counter myCounter = new Counter();
        System.out.println(" 초기 값 " + myCounter.count);
        updator myUpdator = new updator();
        myUpdator.update(myCounter.count);
        System.out.println(" update 값 " + myCounter.count);        
    }
}
cs

Output : 

 초기 값 0

 update 값 0

 

위의 예제는 값이 바뀌지 않음을 알 수 있다. update 메소드는 값을 전달받았으며, cnt 변수는 메소드 안에서만 쓰이는 변수이기 때문이다.

 

아래 예제는 객체를 전달해서 객체의 속성 값을 변경하도록 한 예제이다. 

위와 같이 int cnt로 값을 전달받는 것이 아니라 Counter cnt 와 같이 객체를 전달받도록 했다. (Counter 자료형 변수 cnt를 입력받아 증가시킴)

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package jump2java;
 
class updator {
    void update(Counter cnt) {
        cnt.count++;
    }
}
 
public class Counter {
    int count = 0;
 
    public static void main(String[] args) {
        Counter myCounter = new Counter();
        System.out.println(" 초기 값 " + myCounter.count);
        updator myUpdator = new updator();
        myUpdator.update(myCounter);
        System.out.println(" update 값 " + myCounter.count);        
    }
}
cs

Output:

초기 값 0

update 값 1

 

 

자바에서의 상속

 

자바에서는 클래스 상속을 위해서 extends 키워드를 사용한다. Dog 클래스가 Animal 클래스를 상속함 (물려받음)

1
2
3
public class Dog extends Animal {
 
}
cs

 

먼저 프로젝트에서 Animal 클래스와 Dog 클래스를 생성한다.

 

Animal.java

1
2
3
4
5
6
7
8
9
10
11
12
13
package jump2java;
 
public class Animal {
    String name;
    
    public void setName(String name) {
        this.name = name;
    }
 
    public static void main(String[] args) {
        
    }
}
cs

 

Dog.java

1
2
3
4
5
6
7
8
9
10
package jump2java;
 
public class Dog extends Animal {
    public static void main(String[] args) {
        Dog dog = new Dog();
        dog.setName("Ludwig");
        
        System.out.println(dog.name);
    }
}
cs

Output :

Ludwig

 

Dog 클래스에서는 setName 메소드를 정의하지 않았지만, Animal 클래스를 상속했으므로 setName 메소드를 그대로 사용할 수 있다.

 

 

Dog.java에 bark 메소드를 하나 추가해보자.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package jump2java;
 
public class Dog extends Animal {
    public void bark() {
        System.out.println(this.name + "은 멍 멍! 하고 짖었다!");
    }
    
    public static void main(String[] args) {
        Dog dog = new Dog();
        dog.setName("Ludwig");
        
        System.out.println(dog.name);
        dog.bark();
    }
}
cs

 

Output :

Ludwig

Ludwig은 멍 멍! 하고 짖었다!

 

 

메소드 오버라이딩 

 

HouseDog.java

 

아래와 같이 HouseDog 클래스에 Dog 클래스와 동일한 원형의 bark 메소드를 구현하였다.

1
2
3
4
5
6
7
8
9
10
11
12
13
package jump2java;
 
public class HouseDog extends Dog {
    public void bark() {
        System.out.println(this.name + "는 도둑을 보면 짖는다");
    }
    
    public static void main(String[] args) {
        HouseDog houseDog = new HouseDog();
        houseDog.setName("Toni");
        houseDog.bark();
    }
}
cs

Output (HouseDog.java에서 실행결과) : 

Toni는 도둑을 보면 짖는다

 
부모클래스의 메소드를 자식클래스가 동일한 원형으로 정의했지만, HouseDog 클래스에서 bark 메소드는 Dog 클래스의 bark 메소드보다 더 높은 우선순위를 갖게 된다. 그 결과, HouseDog 클래스의 bark 메소드가 호출되고 위와 같은 결과가 나온다.

 

이렇게 부모 클래스의 메소드를 자식 클래스가 동일한 원형을 가지고 재정의 하는 행위를 Method Overriding(메소드 덮어씌우기)이라고 한다. 

 

 

자바는 다중상속을 지원하지 않는다

다중상속의 모호함 때문에 자바는 일반 클래스의 다중상속을 지원하지 않는다. 

 

 

생성자

HouseDog클래스로 만든 인스턴스에 setName 메소드를 생성하지 않으면, name 인스턴스 변수에 값이 할당되지 않았기에 null이 출력된다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package jump2java;
 
public class HouseDog extends Dog {
    public void bark() {
        
        System.out.println(this.name + "은(는) 도둑을 보면 짖는다");
    }    
    
    public static void main(String[] args) {
        HouseDog houseDog = new HouseDog();
        //houseDog.setName("Toni");        
        houseDog.bark();
    }
}
cs

Output :

null은(는) 도둑을 보면 짖는다

 

 

생성자(Constructor)를 이용하면 name 이라는 인스턴스 변수를 무조건 초기화하도록 강제할 수 있다.

아래 소스에서 public HouseDog(String name) { ... 과 같이 메소드명이 클래스명과 동일하고 반환형이 없는 메소드가 생성자이다.

 

1
2
3
4
5
6
7
8
9
10
11
12
package jump2java;
 
public class HouseDog extends Dog {
    public HouseDog(String name) {
        this.setName(name);
    }
    
    public static void main(String[] args) {
        HouseDog houseDog = new HouseDog();
        System.out.println(houseDog.name);
    }
}
cs
Output :
Exception in thread "main" java.lang.Error: 분석되지 않는 컴파일 문제점: 
생성자 HouseDog()이(가) 정의되지 않았습니다.
 
at jump2java.HouseDog.main(HouseDog.java:9)
 

생성자의 규칙대로 객체를 선언하지 않았기 때문에 컴파일 오류가 발생한다. (String변수로 값을 전달해 setName메소드 호출)

위의 소스 9번째 줄을 다음과 같이 변경해주면 정상적으로 컴파일되고 실행결과를 출력한다.

 

9
HouseDog houseDog = new HouseDog("Toni");
cs

Output :

Toni

 

 

생성자의 규칙

  1. 클래스명과 메소드명이 동일하다.
  2. 리턴타입을 정의하지 않는다.

 

default 생성자

1
2
3
4
5
6
7
8
public class Dog extends Animal {
    public Dog() {
    }
 
    public void sleep() {
        System.out.println(this.name + " zzz");
    }
}
cs

만약 생성자를 정의하지 않았다면 public Dog() {    } 와 같은 default 생성자를 컴파일러가 자동 생성한다.

생성자를 정의한다면 컴파일러는 따로 생성자를 만들지 않고, 그 생성자의 정의대로만 객체를 선언할 수 있다.

 

생성자 오버로딩

하나의 클래스에 다음과 같이 입력항목(인수)이 다른 여러개의 생성자를 만들 수 있다. 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package jump2java;
 
public class HouseDog extends Dog {
    public HouseDog(String name) {
        this.setName(name);
    }
 
    public HouseDog(char type) {
        if (type == 'm') {
            this.setName("Steve");
        } else if (type == 'f') {
            this.setName("Stephanie");
        }
    }
 
    public static void main(String[] args) {
        HouseDog houseDog = new HouseDog("Toni");
        System.out.println(houseDog.name);
        HouseDog male = new HouseDog('m');
        HouseDog female = new HouseDog('f');
        System.out.println(male.name);
        System.out.println(female.name);
    }
}
cs

 

Output :

Toni

Steve

Stephanie

 

위의 소스에서 HouseDog 클래스는 두 개의 생성자를 갖는다. 하나는 String type, 하나는 char type을 입력으로 받는 생성자이다.

메소드 오버로딩과도 비슷한 개념이다. 단, 메소드 오버로딩은 반환형은 반드시 일치해야 함에 유의!

 

오버로딩 시 시그니처가 같다면 오버로딩할 수 없다.

시그니처란 매개변수의 리스트를 가리키며 매개변수의 개수와 타입, 그 순서까지 모두 같다면 시그니처가 같다.

리턴값만 다를 경우 오버로딩이 성립되지 않는다.

 

그리고 오버로딩과 오버라이딩은 완전히 다른 개념이다.  

'Study > Java' 카테고리의 다른 글

Java Network 프로그래밍 기초  (0) 2017.06.03
NIO 기반 네트워킹  (0) 2017.05.30
JAVA - NIO  (0) 2017.05.29
Java - OOP_인터페이스와 다형성  (0) 2017.03.20
Java 개발PC 설정 및 기초  (0) 2017.03.16
728x90

참고 강의 : https://wikidocs.net/book/31


JDK 다운로드 : 

http://www.oracle.com/technetwork/java/javase/downloads/index.html?ssSourceSiteId=otnjp/


JDK 다운로드 시 오픈소스인 8 버전을 받는다


자바의 소스코드 파일명은 *.java 

이것을 컴파일하면 *.class 파일이 생성되며 Java VM이 class 파일의 바이너리 코드를 해석해 실행한다.


JVM은 이란 중간 단계를 거치기 때문에 속도면에선 단점이 있지만 모든 OS에서 실행가능한 장점을 가진다.



javac (Compiler) 확인 

: 콘솔에서 javac 실행


참고 : 주석은 프로그램 실행과는 상관없이 코드에 설명을붙인 것을 말한다.컴파일 과정에서 주석은 무시되고 실행문만  바이트 코드로 번역된다. 따라서 주석을 많이 작성한다고 해서 전체 프로그램의 크기가 커지는 것은 아니므로 가급적이면 설명이 필요한 코드에 주석을 달아 두는 것이 좋다.


명령실행이 안되면 환경변수 추가

: 컴퓨터-속성-고급 시스템 설정-고급 탭에서 환경변수 추가

( ...); C:\Program Files\Java\jdk1.8.0_121\bin


jdk tools 환경변수 추가

시스템 변수 - 새로만들기    

변수이름 : CLASSPATH

변수 값 : .;C:\Program Files\Java\jdk1.8.0_181\lib\tools.jar;


이클립스 설치 (Get Eclipse Neon)

http://www.eclipse.org/downloads/


처음 실행할 때 작업공간(work space) 설정



프로젝트 생성

File -> New -> Java Project


신규 클래스 생성

File -> New -> Class



메인 메소드 작성 (HelloWorld)

1
2
3
4
5
6
7
package jump2java;
 
public class HelloWorld {
    public static void main(String args[]) {
        System.out.println("Hello World");
    }
}
cs

public class 


Run -> Run As -> Java Application로 실행하면 아래 창에 실행결과가 뜬다.

(단축기 Ctrl + F11 이며 Window-Preferences-General-Keys에서 단축키 설정 가능)


이클립스 소스 자동 정렬기능

Ctrl + Shift + F


문자열 출력

1
2
3
4
5
6
7
8
9
package jump2java;
 
public class HelloWorld {
    public static void main(String[] args) {
        String name = "홍길동";
        System.out.println("내 이름은  " + name + "입니다.");
    }
}
 
cs

(" " + 변수명 + " ");



사용자 정의 자료형 정의


Animal 클래스를 먼저 정의

class Animal {
}

Animal 자료형 변수 cat

Animal cat;



자바의 부울 자료형 예제


1
2
3
4
5
6
7
8
9
package jump2java;
 
public class HelloWorld {
    public static void main(String args[]){
        int i=3;
        boolean isOdd = i % 2 == 2;
        System.out.println(isOdd);
    }
}
cs

Output : false



자바에서 문자열을 new 키워드로 객체 생성 가능 (하지만 가독성과 컴파일 최적화를 이유로 권장되지는 않음)


String a = new String("Happy Java");
String b = new String("a");
String c = new String("123");

primitive(원시) 자료형

int, long, double, float, boolean, char 등을 자바에서 primitive 자료형이라하며, new 키워드로 생성 불가 (literal 로만 초기화 가능)


문자열 비교 equals
== 은 두개의 자료형이 동일한 객체인지를 판별할 때 사용하는 연산자이기 때문에 문자열을 비교할 때는 equals를 쓴다.

1
2
3
4
5
6
7
8
9
10
package jump2java;
 
public class HelloWorld {
    public static void main(String args[]){
        String a = "hello";
        String b = new String("hello");
        System.out.println(a.equals(b));
        System.out.println(a==b);
    }
}
cs

Output : 

true

false



문자열의 index를 return하는 indexOf


1
2
3
4
5
6
7
8
9
package jump2java;
 
public class HelloWorld {
    public static void main(String args[]){
        String a = "Hello Java";
        System.out.println(a.indexOf("Java"));
    }
}
 
cs

Output

6



substring 메소드

1
2
3
4
5
6
7
8
9
package jump2java;
 
public class HelloWorld {
    public static void main(String[] args) {
        StringBuffer sb = new StringBuffer();
        sb.append("Hello");
        System.out.println(sb.substring(04));
    }
}
cs
Output :
Hell

end index의 문자는 제외되고 출력된다.


배열의 길이를 알 수 있는 length 메소드
1
2
3
4
5
6
7
8
9
10
package jump2java;
 
public class HelloWorld {
    public static void main(String[] args) {
        String[] weeks = {"월""화""수""목""금""토""일"};
        for (int i=0; i<weeks.length; i++) {
            System.out.print(weeks[i]);
        }
    }
}
cs

Output :

월화수목금토일



제네릭스 (Generics)


제네릭스를 사용하지 않은 경우

1
2
3
4
5
6
ArrayList aList = new ArrayList();
aList.add("hello");
aList.add("java");
 
String hello = (String) aList.get(0);
String java = (String) aList.get(1);
cs


다음과 같이 형 변환이 필요

1
2
String hello = (String) aList.get(0); // Object 를 String 으로 캐스팅한다. (String)
 
cs


제네릭스를 사용한 경우

1
2
3
4
5
6
ArrayList<String> aList = new ArrayList<String>();
aList.add("hello");
aList.add("java");
 
String hello = aList.get(0);
String java = aList.get(1);
cs


명시적으로 type을 지정함으로써 잘못된 형 변환을 피할 수 있다.




맵(Map) 자료형

Map은 Key와 Value라는 것을 한 쌍으로 갖는 자료형이다. Associative array, Hash 라고도 불린다.

keyvalue
people사람
baseball야구

리스트나 배열처럼 순차적이 아닌 key를 통해 value를 얻는다.


제네릭스를 이용한 HashMap 생성

1
import java.util.HashMap;
cs
HashMap을 사용하기위한 import


1
2
3
HashMap<StringString> map = new HashMap<StringString>();
map.put("people""사람");
map.put("baseball""야구");
cs

key와 value가 String 형태인 HashMap 생성, 입력은 put메소드를 이용



List 자료형과 contains 메소드 활용한 예제

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package jump2java;
import java.util.ArrayList;
 
public class HelloWorld {
    public static void main(String[] args) {
        ArrayList<String> pocket = new ArrayList<String>();
        pocket.add("paper");
        pocket.add("handphone");
        pocket.add("money");
 
        if (pocket.contains("money")) {
            System.out.println("택시를 타고 가라");
        }else {
            System.out.println("걸어가라");
        }
    }
}
 
cs


boolean 활용

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package jump2java;
import java.util.ArrayList;
 
public class HelloWorld {
    public static void main(String[] args) {
        boolean watch = true;    // false 대입하면 걸어가라 
        ArrayList<String> pocket = new ArrayList<String>();
        pocket.add("paper");
        pocket.add("handphone");
 
        if (pocket.contains("money")) {
            System.out.println("택시를 타고 가라");
        }else if(watch) {
            System.out.println("택시를 타고 가셈");
        }else {         
            System.out.println("걸어가라");
        }
    }
}
 
cs

Output : 

택시를 타고 가셈




for each 문

for문과 동일한 키워드이나 조건식이 다르다.

1
2
3
for (type var: iterate) {
    body-of-loop
}
cs

iterate는 루프를 돌릴 객체이며, 한개씩 순차적으로 var에 대입되어 for문을 수행. 

iterate부분에 들어가는 타입은 배열 및 ArrayList 등이 가능.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package jump2java;
import java.util.ArrayList;
 
public class HelloWorld {
    public static void main(String[] args) {
        ArrayList<String> aList = new ArrayList<String>();
        aList.add("abc");
        aList.add("DEF");
        
        String[] numbers = {"one""two""three"};
        for(String number: numbers) {
            System.out.print(number+" ");
        }
        System.out.print("\n");
        for(String hello: aList) {
            System.out.println(hello);
        }
    }
}
 
cs


Output :

one two three 

abc

DEF



'Study > Java' 카테고리의 다른 글

Java Network 프로그래밍 기초  (0) 2017.06.03
NIO 기반 네트워킹  (0) 2017.05.30
JAVA - NIO  (0) 2017.05.29
Java - OOP_인터페이스와 다형성  (0) 2017.03.20
Java - OOP_클래스와 상속, 생성자, 오버로딩/오버라이딩  (0) 2017.03.17

+ Recent posts