java.net 패키지 


Provides the classes for implementing networking applications.

네트워킹 응용 프로그램을 구현하기위한 클래스를 제공합니다.


java api 설명에 있는 문장을 가져왔다.

이 패키지는 위 설명과 같이 네트워크 환경을 구성하고 소켓 통신 프로그래밍을 만들 수 있게 도와준다.


대표적인 클래스에 대해 알아보고 간단한 TCP소켓 프로그래밍을 해보자!



INnetAddress


자바에서는 IP주소를 다루기 위해 위 클래스를 제공하며 다음과 같은 메서드가 정의되어 있다.


 메서드

설명 

 byte[] getAddress()

 IP주소를 byte배열로 반환한다.

 static InetAddress getByAddress(byte[] addr)

 byte배열을 통해 IP주소를 얻는다.

 static InetAddress getByName(String host)

 도메인명(host)을 통해 IP주소를 얻는다.

 static InetAddress[] getAllByName(String host)

 도메인명(host)에 지정된 모든 호스트의 IP주소를 배열에 담아 반환한다.

 static InetAddress getLocalHost()

 지역호스트의 IP주소를 반환한다. 

 String getCanonicalHostName()

 Fully Qualified Domain Name을 반환한다.

 String getHostAddress()

 호스트의 IP주소를 반환한다. 

 String getHostName()

 호스트의 이름을 반환한다. 

 boolean isMulticastAddress()

 IP주소가 멀티캐스트 주소인지 알려준다. 

 boolean isLoopbackAddress()

 IP주소가 loopback 주소(127.0.0.1)인지 알려준다. 


예제를 보자


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
package source.ch16;
import java.net.*;
import java.util.*;
 
class NetworkEx1 {
    public static void main(String[] args) 
    {
        InetAddress ip = null;
        InetAddress[] ipArr = null;
 
        try {
            ip = InetAddress.getByName("www.naver.com");
            System.out.println("getHostName() :"   +ip.getHostName());
            System.out.println("getHostAddress() :"+ip.getHostAddress());
            System.out.println("toString() :"      +ip.toString());
 
            byte[] ipAddr = ip.getAddress();
            System.out.println("getAddress() :"+Arrays.toString(ipAddr));
 
            String result = "";
            for(int i=0; i < ipAddr.length;i++) {
                result += (ipAddr[i] < 0) ? ipAddr[i] + 256 : ipAddr[i];
                result += ".";
            }
            System.out.println("getAddress()+256 :"+result);
            System.out.println();
        } catch (UnknownHostException e) {
            e.printStackTrace();
        }
 
        try {
            ip = InetAddress.getLocalHost();
            System.out.println("getHostName() :"   +ip.getHostName());
            System.out.println("getHostAddress() :"+ip.getHostAddress());
            System.out.println();
        } catch (UnknownHostException e) {
            e.printStackTrace();
        }
        
 
        try {
            ipArr = InetAddress.getAllByName("www.naver.com");
 
            for(int i=0; i < ipArr.length; i++) {
                System.out.println("ipArr["+i+"] :" + ipArr[i]);
            }            
        } catch (UnknownHostException e) {
            e.printStackTrace();
        }
 
    } // main
}
cs


getHostName() :www.naver.com

getHostAddress() :210.89.164.90

toString() :www.naver.com/210.89.164.90

getAddress() :[-46, 89, -92, 90]

getAddress()+256 :210.89.164.90.


getHostName() :MSDN-SPECIAL

getHostAddress() :192.168.17.1


ipArr[0] :www.naver.com/210.89.164.90

ipArr[1] :www.naver.com/125.209.222.141


InetAddress 타입 변수로 직접 출력을 찍으면 마지막 2행처럼 도메인명/ip주소 형식으로 나온다.



URL 


URL은 Uniform Resource Location 의 줄임말로 흔히 우리가 알고있는 URL주소이다.

URL은 프로토콜://호스트명:포트번호/경로명/파일명?쿼리스트링#참조의 형태로 이루어져 있다.

이 URL을 다루기 위한 클래스로 주로 URL 구성요소를 구하는 메서드로 구성 되어있다.

모든 메서드를 나열할 순 없으니 예제를 통해 간단히 알아보자.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package source.ch16;
import java.net.*;
 
class NetworkEx2 {
    public static void main(String args[]) throws Exception {
        URL url = new URL("http://sleepyeyes.tistory.com/admin/entry/post/?type=post&returnURL=/manage/posts/");
 
        System.out.println("url.getAuthority() : "+ url.getAuthority());
        System.out.println("url.getDefaultPort() : "+ url.getDefaultPort());
        System.out.println("url.getPort() : "+ url.getPort());
        System.out.println("url.getFile() : "+ url.getFile());
        System.out.println("url.getHost() : "+ url.getHost());
        System.out.println("url.getPath() : "+ url.getPath());
        System.out.println("url.getProtocol() : "+ url.getProtocol());
        System.out.println("url.getQuery() : "+ url.getQuery());
        System.out.println("url.getRef() : "+ url.getRef());
        System.out.println("url.getUserInfo() : "+ url.getUserInfo());
        System.out.println("url.toURI() : "+ url.toURI());
    }
}
cs


url.getAuthority() : sleepyeyes.tistory.com

url.getDefaultPort() : 80

url.getPort() : -1

url.getFile() : /admin/entry/post/?type=post&returnURL=/manage/posts/

url.getHost() : sleepyeyes.tistory.com

url.getPath() : /admin/entry/post/

url.getProtocol() : http

url.getQuery() : type=post&returnURL=/manage/posts/

url.getRef() : null

url.getUserInfo() : null

url.toURI() : http://sleepyeyes.tistory.com/admin/entry/post/?type=post&returnURL=/manage/posts/



URLConnection


이 클래스는 어플리케이션과 URL간의 통신연결을 나타내는 클래스의 최상위 클래스로 추상클래스이다.

HttpURLConnection 과 JarURLConnection 이 상속받아 구현한 클래스이다.


이 클래스의 메서드를 이용하면 URL의 관련된 정보를 읽고 쓸 수 있다.



URLConnection의  대표 메소드


 메소드명

설명 

String getContentType()

mime타입 리턴 

getContentEncoding

인코딩 리턴

getHeaderFields()

 헤더 정보 리턴

InputStream getInputStream()

 문서를 읽기 위한 InputStream 객체 리턴








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
package source.ch16;
import java.net.*;
import java.io.*;
 
public class NetworkEx3 {
    public static void  main(String args[]) {
        URL url = null;
        String address = "http://www.kpu.ac.kr/index.do?sso=ok";
        String line = "";
 
        try {
            url = new URL(address);
            URLConnection conn = url.openConnection();
            
            System.out.println("getContentEncoding() : "+conn.getContentEncoding());
            System.out.println("getContentType() : "+conn.getContentType());
            System.out.println("getHeaderFields() : "+conn.getHeaderFields());
            
            try(InputStream raw = conn.getInputStream()){
                InputStream buffer = new BufferedInputStream(raw);
 
int c;
                while((c = buffer.read())!=-1) {
                    System.out.print((char)c);
                }
            }
        } catch(Exception e) {
            e.printStackTrace();
        }
    } 
}
     cs


getContentEncoding() : null

getContentType() : text/html;charset=euc-kr

getHeaderFields() : {Transfer-Encoding=[chunked], null=[HTTP/1.1 200 OK], Server=[Jeus WebContainer/JEUS 5.0 (fix #27)], Connection=[Keep-Alive], Set-Cookie=[JSESSIONID=aFgKdVgDi3OMhdwQ4h20zQHP567USB0MsENPEUcpCVAH0Sa30XUgQhKCrP6aaa4v;Path=/, visitUrlGubun=kpu_1|;Path=/, logNonM=yes;Path=/], Date=[Wed, 24 Oct 2018 12:00:49 GMT], Content-Type=[text/html;charset=euc-kr]}

<!DOCTYPE HTML>

<html lang="ko">

<head>

<meta charset="euc-kr">

<meta http-equiv="X-UA-Compatible" content="IE=edge, chrome=1">

<meta name="format-detection" content="telephone=no">

<meta name="viewport" content="width=1000">

<link id="favicon" href="/images/favicon-32.ico" rel="shortcut icon">

....




소켓 프로그래밍


소켓이란 프로세스간의 통신에 사용되는 양쪽 끝단을 의미한다.

자바에서는 java.net패키지를 통해 소켓 프로그래밍을 지원하는데, 소켓통신에 사용되는 프로토콜에 따라 다른 종류의 소켓을 구현하여 제공한다.


크게 TCP와 UDP로 나뉘는데 이 둘의 차이는 간단하게 알아보면


-TCP 연결지향적이며, 오류제어, 흐름제어, 혼잡제어, 타이머재전송 등의 기능을 하며 연결지향이란말은 데이타를 전송하는 측과 데이타를 전송받는 측에서 전용의 데이타 전송 선로(Session)을 만든다는 의미이다. 데이타의 신뢰도가 중요하다고 판단될때 주로 사용된다.


-UDP 비연결지향이며, 최소한의 오류제어 기능만 수행한다. 단순히 데이타를 받거나, 던져주기만 하는 프로토콜이다. UDP는 특히 실시간 멀티미디어 정보를 처리하기 위해서 주로 사용한다.


http://sleepyeyes.tistory.com/4?category=747108




TCP소켓 프로그래밍

 

Socket 클래스와 ServerSocket 클래스를 제공한다.


Socket : 프로세스간의 통신을 담당하며, InputStream과 OutputStream을 가지고 있다. 이 스트림들을 이용해 프로세스간의 통신이 이루어진다.


ServerSocket : 포트와 연결(bind)되어 외부의 연결요청을 기다리다 연결요청이 오면 Socket을 생성해서 소켓과 소켓간의 통신이 이루어지도록 한다. 


실제 통신은 소켓끼리 하지만 그 소켓끼리를 연결해주는 역할을 서버소켓이 하는 것이다.

한 포트에는 하나의 서버소켓만 연결할 수 있는데 

한 포트에 둘 이상의 서버소켓이 연결된다면 클라이언트 입장에서 그 둘을 구분할 수 없기 때문이다. 


과정


간단한 예제를 보면서 이해해보자


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
package source.ch16;
 
import java.net.*;
import java.io.*;
import java.util.Date;
import java.text.SimpleDateFormat;
 
public class TcpIpServer {
    public static void main(String args[]) {
        ServerSocket serverSocket = null;
        
        try {
            //서버소켓을 생성하여 7777번 포트와 결합(bind)시킨다.
            serverSocket = new ServerSocket(7777);
            System.out.println(getTime()+"서버가 준비되었습니다.");
 
        } catch(IOException e) {
            e.printStackTrace();
        }
        
        while(true) {
            try {
                System.out.println(getTime()+"연결요청을 기다립니다.");
                // 서버소켓은 클라이언트의 연결요청이 올 때까지 실행을 멈추고 계속 기다린다.
                // 클라이언트의 연결요청이 오면 클라이언트 소켓과 통신할 새로운 소켓을 생성한다.
                Socket socket = serverSocket.accept();
                System.out.println(getTime()+ socket.getInetAddress() + "로부터 연결요청이 들어왔습니다.");
                
                // 소켓의 출력스트림을 얻는다.
                OutputStream out = socket.getOutputStream();
                DataOutputStream dos = new DataOutputStream(out);
 
                // 원격소켓(remote socket)에 데이터를 보낸다.
                dos.writeUTF("[Notice] Test Message1 from Server.");
                System.out.println(getTime()+"데이터를 전송했습니다.");
 
                // 스트림과 소켓을 닫아준다.
                dos.close();
                socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        } // while
    } // main
 
    // 현재시간을 문자열로 받환하는 함수
    static String getTime() {
        SimpleDateFormat f = new SimpleDateFormat("[hh:mm:ss]");
        return f.format(new Date());
    }
// class
 
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
package source.ch16;
import java.net.*;
import java.io.*;
 
public class TcpIpClient { 
    public static void main(String args[]) {
        try {
            String serverIp = "127.0.0.1";
 
            System.out.println("서버에 연결중입니다. 서버IP :" + serverIp); 
            // 소켓을 생성하여 연결을 요청한다.
// 연결하고자 하는 서버의 ip와 포트번호를 알아야한다.
            Socket socket = new Socket(serverIp, 7777); 
 
            // 소켓의 입력스트림을 얻는다.
            InputStream in = socket.getInputStream();
            DataInputStream dis = new DataInputStream(in);
 
            // 소켓으로 부터 받은 데이터를 출력한다.
            System.out.println("서버로부터 받은 메시지:"+dis.readUTF());      
            System.out.println("연결을 종료합니다.");
 
            // 스트림과 소켓을 닫는다.
            dis.close();
            socket.close();
            System.out.println("연결이 종료되었습니다.");
        } catch(ConnectException ce) {
            ce.printStackTrace();
        } catch(IOException ie) {
            ie.printStackTrace();
        } catch(Exception e) {
            e.printStackTrace();  
        }  
    } // main
// class
 
cs


위에 그림으로 본 과정과 똑같아 이해하기가 수월하다.


여기에 쓰레드를 이용하면 동시의 여러 클라이언트의 접속, 요청도 동시에 처리할 수 있고

데이터 송신과 수신도 동시에 할 수 있다.


위 두개의 기능을 다 포함시킨 예제를 마지막으로 보자


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
package source.ch16;
import java.net.*;
import java.io.*;
import java.util.*;
 
public class TcpIpMultichatServer {
    HashMap clients;
    
    
    TcpIpMultichatServer() {
        clients = new HashMap();
        Collections.synchronizedMap(clients); //동기화 처리
    }
 
    public void start() {
        ServerSocket serverSocket = null;
        Socket socket = null;
 
        try {
            serverSocket = new ServerSocket(7777);
            System.out.println("서버가 시작되었습니다.");
 
            while(true) {
                socket = serverSocket.accept();
                System.out.println("["+socket.getInetAddress()+":"+socket.getPort()+"]"+"에서 접속하였습니다.");
                ServerReceiver thread = new ServerReceiver(socket);
                thread.start();
            }
        } catch(Exception e) {
            e.printStackTrace();
        }
    } // start()
 
    //모든 클라이언트에게 채팅을 전송
    void sendToAll(String msg) {
        Iterator it = clients.keySet().iterator();
        
        while(it.hasNext()) {
            try {
                DataOutputStream out = (DataOutputStream)clients.get(it.next());
                out.writeUTF(msg);
            } catch(IOException e){}
        } // while
    } // sendToAll
 
    public static void main(String args[]) {
        new TcpIpMultichatServer().start();
    } 
    class ServerReceiver extends Thread {
        Socket socket;
        DataInputStream in;
        DataOutputStream out;
 
        ServerReceiver(Socket socket) {
            this.socket = socket;
            try {
                in  = new DataInputStream(socket.getInputStream());
                out = new DataOutputStream(socket.getOutputStream());
            } catch(IOException e) {}
        }
 
        public void run() {
            String name = "";
            try {
                name = in.readUTF();
                sendToAll("#"+name+"님이 들어오셨습니다.");
                //클라이언트 추가
                clients.put(name, out);
                System.out.println("현재 서버접속자 수는 "+ clients.size()+"입니다.");
 
                while(in!=null) {
                    sendToAll(in.readUTF());
                }
            } catch(IOException e) {
                // ignore
            } finally {
                sendToAll("#"+name+"님이 나가셨습니다.");
                //나가면 HashMap에서 제거
                clients.remove(name);
                System.out.println("["+socket.getInetAddress() +":"+socket.getPort()+"]"+"에서 접속을 종료하였습니다.");
                System.out.println("현재 서버접속자 수는 "+ clients.size()+"입니다.");
            } // try
        } // run
    } // ReceiverThread
// class
 
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
69
70
71
72
73
74
package source.ch16;
import java.net.*;
import java.io.*;
import java.util.Scanner;
 
public class TcpIpMultichatClient {
    public static void main(String args[]) {
        if(args.length!=1) {
            System.out.println("USAGE: java TcpIpMultichatClient 대화명");
            System.exit(0);
        }
 
        try {
            String serverIp = "127.0.0.1";
            // 소켓을 생성하여 연결을 요청한다.
            Socket socket = new Socket(serverIp, 7777); 
            System.out.println("서버에 연결되었습니다.");
            Thread sender   = new Thread(new ClientSender(socket, args[0]));
            Thread receiver = new Thread(new ClientReceiver(socket));
 
            sender.start();
            receiver.start();
        } catch(ConnectException ce) {
            ce.printStackTrace();
        } catch(Exception e) {}
    } // main
 
    static class ClientSender extends Thread {
        Socket socket;
        DataOutputStream out;
        String name;
 
        ClientSender(Socket socket, String name) {
            this.socket = socket;
            try {
                out = new DataOutputStream(socket.getOutputStream());
                this.name = name;
            } catch(Exception e) {}
        }
 
        public void run() {
            Scanner scanner = new Scanner(System.in);
            try {
                if(out!=null) {
                    out.writeUTF(name);
                }    
 
                while(out!=null) {
                    out.writeUTF("["+name+"]"+scanner.nextLine());                    }
            } catch(IOException e) {}
        } // run()
    } // ClientSender
 
    static class ClientReceiver extends Thread {
        Socket socket;
        DataInputStream in;
 
        ClientReceiver(Socket socket) {
            this.socket = socket;
            try {
                in = new DataInputStream(socket.getInputStream());
            } catch(IOException e) {}
        }
 
        public void run() {
            while(in!=null) {
                try {
                    System.out.println(in.readUTF());
                } catch(IOException e) {}
            }
        } // run
    } // ClientReceiver
// class
 
cs


다소 복잡하지만 처음 과정도를 염두해두고 주석과 함께 천천히 보면 이해가 간다.



참고

http://diaryofgreen.tistory.com/98

http://codedragon.tistory.com/6417

https://m.blog.naver.com/PostView.nhn?blogId=tndus0450&logNo=220153206530&proxyReferer=https%3A%2F%2Fwww.google.co.kr%2F

자바의 정석 3판

  1. dasomcap 2020.11.26 02:49

    유용한 글 정말 잘 배우고 가요

Calendar 


Calendar 클래스는 추상클래스이기 때문에 직접 객체를 생성할 수 없고, 메서드를 통해서 완전히 구현된 클래스의 인스턴스를 얻어야 한다.


Calendar today = Calendar.getInstance();


System.out.println(today.get(Calendar.YEAR); 

System.out.println(today.get(Calendar.MONTH);    // ( 0~11 0:1월)

System.out.println(today.get(Calendar.DATE);

System.out.println(today.get(Calendar.DAY_OF_WEEK);    // ( 1~7 1:일요일)


처음 보고 드는 생각. 뭔가 되게 복잡해보이고 왜 월은 0부터 했는데 일은 또 1부터 시작하며 요일에 시작이 일요일이라 헷갈리네..

또 메소드만으로 불러지지 않고 클래스 변수를 매개변수로 사용하여 복잡해 보인다는 느낌을 받았다.



이번에 공부하면서 알아보니 


Date는 jdk 1.0 Calendar는 1.1 부터 써왔던 클래스이고 사용되어지면서 단점이 극명하게 드러나 

다들 기본 API를 외면한체 Joda-Time이라는 오픈소스 라이브러리를 사용한다고 한다. 


기존 Calendar, Date 클래스의 문제점


1. 불변객체가 아니다

- set으로 변경이 가능하다. 여러 객체에서 Calendar이나 Date 객체가 공유되면 한 곳에서 바꾼 값이 다른 곳 에서도 바뀌는 부작용이 생길 수있다.

- 멀티 쓰레드 환경에서 안전하지 못하다.


2. 윤초와 같은 특별한 상황을 고려하지 않았다.


3. 상수 필드 남용

calendar.add( Calendar.SECOND , 2 ); Calendar.JUNE 과같이 의미없는 상수가 들어가도 컴파일 시점에서 걸러낼 수가 없다.


4. 헷갈리는 월 지정

- 위에서 본 것 처럼 월의 시작이 0이다. 그래서 6월을 쓰려면 calendar.set(1582, 5 , 4); 이렇게 써야하고

calendar.set(1582, Calendar.JUNE , 4); 같은 상수도 만들었다 (Calendar.JUNE는 5가 찍힌다.)

일부러 가독성을 높이기위해 calendar.set(1582, 6-1 , 4); 이렇게 쓰기도 한다고 한다.


5. 일관성 없는 요일 상수

Calendar는 일요일부터 1~7 이고 Date는 일요일이 0으로 시작한다. 두 클래스는 빈번히 서로를 불러내는데 두 클래스 사이의 일관성이 없다.

6. 겹치는 클래스명

-java.util.Date  java.sql.Date클래스는 전자의 상속을 받는 클래스다. 그런데 클래스명이 같다니...

이렇게 단점이 많았다고 한다.

 그 후 jdk1.8 버전에서 드디어 개선된 java.time패키지가 등장했다. Joda-Time 라이브러리의 영향을 많이 받았고 기존 클래스들의 단점을 극복한 새로운 API라고 한다.

 아직 Date와 Calendar가 쓰이긴 하지만 구지 앞으로 안쓰여질 클래스에대해 깊게 알아보는것 보다 앞으로 쓰여질 클래스에 대해 공부하는것이 낫겠다 생각이들어 java.time패키지를 알아보려 한다.



- java.time패키지의 핵심 클래스


LocalDate : 날짜를 나타내는 클래스

LocalTime : 시간을 나타내는 클래스

LocalDateTime : 날짜, 시간을 모두 표현하는 클래스

ZonedDateTime : LocalDateTime + 시간대까지 표현하는 클래스.


Period : 두 날짜간의 차이를 표현하기 위한 클래스

Duration : 두 시간의 차이를 표현하기 위한 클래스


이들 클래스의 특징은 String클래스처럼 불변이라는 것이다. 

그래서 날짜나 시간을 변경하는 메서드들은 기존의 객체를 변경하는 대신 항상 새로운 객체를 반환한다.



- 객체 생성하기 


 now() 현재 날짜와 시간을 저장하는 객체 생성


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import java.time.*;
 
public class DateTime {
    public static void main(String args[]) {
    
        
        LocalDate date = LocalDate.now();
        LocalTime time = LocalTime.now();
        LocalDateTime dateTime = LocalDateTime.now();
        ZonedDateTime zonedDateTime = ZonedDateTime.now();
        
        System.out.println(date); //2018-10-01        
        System.out.println(time); //21:45:54.575
        System.out.println(dateTime); //2018-10-01T21:45:54.575
        System.out.println(zonedDateTime); //2018-10-01T21:45:54.576+09:00[Asia/Seoul]        
    }
}
cs

 of() 설정해주기

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import java.time.*;
 
public class DateTime {
    public static void main(String args[]) {
    
        LocalDate date = LocalDate.of(20150318);
        LocalTime time = LocalTime.of(113030);
        LocalDateTime dateTime = LocalDateTime.of(date, time);
        ZonedDateTime zonedDateTime = ZonedDateTime.of(dateTime, ZoneId.of("Asia/Seoul"));
        
        System.out.println(date); //2015-03-18
        System.out.println(time); //11:30:30
        System.out.println(dateTime); //2015-03-18T11:30:30
        System.out.println(zonedDateTime); //2015-03-18T11:30:30+09:00[Asia/Seoul]
    }
}
 
cs




- LocalDate와 LocalTime


 java.time 패키지의 가장 기본이 되는 클래스

위에서 보았던 now() of()는 static 메서드로 구현

of()는 다음과 같이 여러가지로 오버로딩 되어있음

public static LocalDate of(int year, Month month, int dayOfMonth)


public static LocalDate of(int year,int month, int dayOfMonth)

public static LocalTime of(int hour,int minute)


public static LocalTime of(int hour, int minute, int second)


public static LocalTime of(int hour,int minute, int second, int nanoOfSecond)



-특정 필드의 값 가져오기 getxxx()


간단히 get~() 메서드로 호출이 가능하다. Calendar와 달리 월의 범위는 1~12이고 요일도 월~일 1~7 이다.



                     LocalDate  

 int getYear() 

 년도(2000)

 int getMonthValue()

 월(12) 

 Month getMonth() 

 월(DECEMBER) 

 int getDayOfMonth()

 일(31)

 int getDayOfYear()

 같은 해의 1월 1일부터 몇번째 일(365)

 DayOfWeek getDayOfWeek()

 요일(FRIDAY) .getValue()=5

 int lengthOfMonth()

 같은 달의 총 일수(31)

 int LengthOfYear()

 같은 해의 총 일수(365), 윤년이면 366

 boolean isLeapYear()

 윤년여부 확인 (false) 


LocalTime

 int getHour()

 시(23) 

 int getMinute() 

 분(59) 

 int getSecond() 

 초(59) 

 int getNano() 

 나노초(0) 


등이 있다..


필드의 값 변경하기 widh(), plus(), minus()


LocalDate withYear(), Month(), DayOfMonth()....

LocalTime withHour(), Minute(), Second().....  를 이용해 개별 필드 값을 변경 할 수 있다.


위에서 언급 했다시피 객체의 값을 변경하는 것이 아닌 새로운 객체를 생성해서 반환하므로 대입연산자가 필요하다.


1
2
date = date.withYear(2012); // 년도를 2012년으로 변경
time = time.withHour(12);  // 시간을 12시로 변경
cs


LocalDate plusYears(), Months(), Days(), Weeks() ...
LocalTime plusHours, Minutes(), Seconds(), ... 

마찬가지로 대입연사자 필요

1
2
date = date.plusYears(10); // 10년 후
time = time.plusHours(5); // 5시간 후
cs



-날짜와 시간의 비교 isAfter(), isBefore(), isEqual()


예제를 보면 이해가 간다.


1
2
3
4
5
6
7
8
9
10
LocalDate date = LocalDate.of(20150318);
LocalDate date2 = LocalDate.of(20150319);
 
boolean b = date.isAfter(date2);
boolean b1 = date.isBefore(date2);
boolean b2 = date.isEqual(date2);
 
    System.out.println(b); //false
    System.out.println(b1); //true
    System.out.println(b2); //false
cs




-LocalDateTiem과 ZonedDateTime


LocalDate           +      LocalTime        ->  LocalDateTime

LocalDateTime     +     시간대              -> ZonedDateTime


객체 생성법은 위에서 처럼 now() of()를 사용하면 된다.

역시 다양하게 of()가 오버라이딩 되어있다.



LocalDateTime은 LocalDate와 LocalTime을 합쳐서 만들 수도 있지만

만든걸 각각 변환 할 수도 있다.


1
2
3
4
5
6
7
8
9
LocalDate date = LocalDate.of(20150318);
LocalTime time = LocalTime.of(113030);
 
LocalDateTime lo = LocalDateTime.of(date, time); // 만들기
LocalDateTime lo = date.atTime(time); //atTime()
LocalDateTime lo = time.atDate(date); //atDate()
    
LocalDate date = lo.toLocalDate(); // todate
LocalTime time = lo.toLocalTime(); // totime
cs



ZonedDateTime 역시 합쳐서 또는 분해가 된다..


1
2
3
LocalDateTime lo = LocalDateTime.now(); 
ZoneId zid = ZoneId.of("Asia/Seoul");
ZonedDateTime zdt = lo.atZone(zid); //atZone으로 합치기
cs

1
2
3
LocalDate date = zdt.toLocalDate(); 
LocalTime time = zdt.toLocalTime(); 
LocalDateTime lo = zdt.toLocalDateTime(); // 분해
cs


-OffsetDateTime
ZonedDateTime 과의 차이점은 일광절약시간 (써머타임/하절기에 표준시를 원래 시간보다 한 시간 앞당긴 시간을 쓰는 것을 말한다) 규칙을 
포함 하느냐 안하느냐의 차이다. 컴퓨터에게 계절별로 시간을 더했다 뺐다 하는건 안좋으므로 변화 없이 시간체계를 유지하는 것이 안전하다.

1
OffsetDateTime odt = zdt.toOffsetDateTime();
cs



TemporalAdjusters


이달의 4번째 금요일은 며칠인지, 지난 주 토요일이 며칠인지와 같은 날짜계산을 하기위한 클래스이다.


firstDayOfNextYear()다음 해의 첫 날
firstDayOfNextMonth()다음 달의 첫 날
firstDayOfYear()올 해의 첫 날
firstDayOfMonth()이번 달의 첫 날
lastDayOfYear()올 해의 마지막 날
lastDayOfMonth()이번 달의 마지막 날
firstInMonth(DayOfWeek dayOfWeek)이번 달의 첫 번째 요일
lastInMonth(DayOfWeek dayOfWeek)이번 달의 마지막 요일
previous(DayOfWeek dayOfWeek)지난 요일(당일 미포함)
previousOrSame(DayOfWeek dayOfWeek)지난 요일(당일 포함)
next(DayOfWeek dayOfWeek)다음 요일(당일 미포함)
nextOrSame(DayOfWeek dayOfWeek)다음 요일(당일 포함)
dayOfWeekInMonth(int ordinal, DayOfWeek dayOfWeek)이번 달의 n번째 요일
LocalDate with(TemporalAdjuster adjuster) 메소드를 사용
1
2
LocalDate today = LocalDate.now();
LocalDate nextMonday = today.with(TemporalAdjusters.next(DayOfWeek.MONDAY));
cs
이런식으로 사용하면 된다.


Period와 Duration 클래스


앞서 잠시 언급한 것과 같이 Period는 날짜의 차이를 Duration은 시간의 차이를 계산하기 위한 클래스이다.


- between() static 메소드이며 다음과 같이 구현한다.


1
2
3
4
5
6
7
8
9
10
11
12
13
        LocalDate date = LocalDate.of(20150318);
        LocalDate date2 = LocalDate.of(20180420);
        LocalTime time = LocalTime.of(113030);
        LocalTime time2 = LocalTime.of(123550);
        
        Period pe = Period.between(date, date2);
        Duration du = Duration.between(time, time2);
        
        System.out.println(pe.getYears()); //3
        System.out.println(pe.getMonths()); //1
        System.out.println(pe.getDays()); //2
        
        System.out.println(du.getSeconds()); // 3920
cs


애석하게도 Duration에는 시, 분 차이를 구하는 메소드가 없다.. getSeconds() 와 getNano() 뿐이다.

물론 나누고 계산해서 시 분 을 구할 수 있겠지만 불편하다.


1
2
3
4
5
    LocalTime se = LocalTime.of(00).plusSeconds(du.getSeconds());
        int hour = se.getHour();
        int min = se.getMinute();
        int sec = se.getSecond();
        int nano = se.getNano();
cs

이 방법을 사용하면 된다. (그나저나 왜 안만들었지?;;)



- between()와 비슷한 기능을하는 until()도 있다. 다른점은 이 메서드는 인스턴스 메서드라는 것이다.


1
2
3
4
5
6
Period pe = Period.between(date, date2);
Period pe2 = date.until(date2);
long a = date.until(date2, ChronoUnit.DAYS);
 
Duration du = Duration.between(time, time2);
long b = time.until(time2, ChronoUnit.HOURS); //아까와는 다르게 시간과 분을 바로 구할 수 있다.
Time은 until()을 써보자!!
cs

until()메서드는 Duration 반환 타입이 없다. 매개변수가 2개인 until()은 D-Day를 구할때 좋다. 



- of(), with() / 역시 이 클래스에도 설정, 변경 메서드인 of와 with가 있다!





1
2
Period pe = pe.withYears(2);//, Months(), Days()
Duration du = du.withSeconds(2); //Nanos()
cs



아까 LocalDateTime과 ZonedDateTime처럼 to메서드로 변환이 가능하다.


1
2
3
4
5
6
7
8
9
10
    LocalDate date = LocalDate.of(20150318);
    LocalDate date2 = LocalDate.of(20180420);
    LocalTime time = LocalTime.of(113030);
    LocalTime time2 = LocalTime.of(123550);
    
    Period pe = Period.between(date, date2);
    Duration du = Duration.between(time, time2);
        
    System.out.println(pe.toTotalMonths()); //37
    System.out.println(du.toHours());       //1
cs

 Period

 long toTotalMonths()

 년원일을 월단위로 변환 후 반환(일 단위 무시)

Duration

 long toDays()

 일단위로 변환해서 반환

 long toHours()

 시간단위로 변환해서 반환

 long toMinutes()

 분단위로 변환해서 반환

 long toMillis()

 천분의 일초 단위로 변환해서 반환

 long toNanos()

 나노초 단위로 변환해서 반환




참조


https://d2.naver.com/helloworld/645609


오버로딩의  조건

1. 메소드 이름이 같아야하고

2. 매개변수의 개수 또는 타입이 달라야 한다.


자바는 오버로딩이라는 기능이 구현되어 있어

매개변수의 개수타입이 달라도 같은 이름의 메소드명으로 동일 기능을 구현 할 수 있다.


그래서 매개변수의 타입이 달라도 하나의 메소드명으로 오버로딩 할 수 있고

또 매개변수의 개수가 달라도 역시 오버로딩 가능하다.


여기서 드는 생각이 만약 매개변수의 개수가 사용자의 쓰임에 따라 달라진다면? 또 그 수가 무한이 늘어난다면?? (말이 좀 이상..)

일일이 매개변수 하나하나 오버로딩 해주어야 할 것인가? 라는 의문이 생긴다


1
2
3
4
String divide(String s1, String s2){...}
String divide(String s1, String s2, String s3){...}
String divide(String s1, String s2, String s3, String s4){...}
String divide(String s1, String s2, String s3, String s4, String s5){...}
cs

이렇게...?????



하지만 JDK1.5부터 매개변수의 개수를 동적으로 지정해 줄 수 있게 되었는데 이 기능을 가변인자(variable argument)라고 한다.


가변인자가 없었던 전 버전에서는 매개변수로 컬렉션이나 배열을 사용 했었다.


1
2
3
4
void sum(Vector<String> v) {
        for(Object a: v)
            System.out.println(a);
    }
cs
1
2
3
4
void sum(String [] str) {
        for(String a:str)
            System.out.println(a);
    }
cs
 

사용하려면

1
2
3
4
5
6
7
public static void main(String args[]) {
        Varargs v = new Varargs();
        Vector<String> ve = new Vector<String>();
        ve.add("a");
        ve.add("b");
        v.sum(ve);
}
cs
1
2
3
4
5
public static void main(String args[]) {
        Varargs v = new Varargs();
        String [] s = {"a""b""c"};
        v.sum(s);
}
cs


이 같은 경우 따로 백터 객체를 만드는 등 조금 복잡? 하고

v.sum()과  같이 인자를 생략해 줄 수 없다.




가변인자 사용법


 키워드 ... 을 사용한다


1
2
3
4
void sum(String...str) {
        for(String a:str)
            System.out.println(a);
}
cs

다음과 같이 타입...변수명 으로 사용한다.


1
2
3
4
5
6
public static void main(String args[]) {
    Varargs v = new Varargs();
    v.sum("a","b","c");
    v.sum();
    v.sum(new String [2]);
}
cs

빈 인자값이나 같은 타입에 배열도 인자값으로 줄 수 있다.

위 코드들을 보고 눈치 챌 수도 있는데

가변인자는 내부적으로 배열을 생성해서 사용한다! (향상된 for문 str, 인자값으로 배열 넣어짐)

그래서 가변인자를 마구 난발하지는 말도록 하자.



만약 가변인자 외에도 다른 매개변수가 더 있다면 가변인자는 마지막에 선언해야 한다.


1
2
3
4
void sum(String s, String...str) {
        for(String a:str)
            System.out.print(a+s);
}
cs
1
2
3
4
public static void main(String args[]) {
        Varargs v = new Varargs();
        v.sum("-","a","b","c");
    }
cs

 위와 같은 코드로 여러 문자열을 구분자를주어 나누어 줄 수 있다.

a-b-c-




가변인자 메소드 오버로딩 주의점


만약 오버로딩 된 두 메소드가 있다면


1
2
3
4
5
6
7
8
void sum(String s, String...str) {
        for(String a:str)
            System.out.print(a+s);
    }
    void sum(String...str) {
        for(String a:str)
            System.out.print(a);
    }
cs
1
2
3
4
public static void main(String args[]) {
        Varargs v = new Varargs();
        v.sum("-","a","b","c");
    }
cs

이 메인 메소드를 돌려 본다 생각해보자.

컴파일러는 어떤 메소드를 사용 해야하는지 구분을 못한다. 그러므로 컴파일 에러가 난다.


가능하면 가변인자를 사용한 메소드는 오버로딩을하지 않는 것이 좋다.




가변인자를 사용한 대표적인 예


printf() 메소드가 있다.



앞의 문자열에 지시자 ex} %d, %s 로 뒤의 값을 형식화 해주는 메소드 인데

뒤의 값을 사용자가 마음대로 수를 정할 수있다.


가변인자를 사용한 메소드이기 때문에 가능했던 것이다.




참고

http://gyrfalcon.tistory.com/entry/Java-Varargs



자바를 처음 공부할 때 헷갈렸던 것 중에 하나가 있는데

바로 변수를 지칭하는 용어가 너무 많아 헷갈렸었다.


멤버변수 지역변수 인스턴스변수 클래스변수 참조변수...등등

어떤게 어떤건지 왜이리 지칭하는 수식어가 많은지 생각을 했었는데 이를 한 곳에서 정리해주는 곳을 찾고 싶었으나 결국 찬찬히 공부하면서 배웠다.

하지만 다시 정리도 하고 나같은 성질 급한 사람이 있을 수 도 있으니 정리해본다.


기본형변수

참조변수

멤버변수

클래스변수

인스턴스변수

지역변수

매개변수


내가 아는 변수의 종류들이다. 무려 7가지가 있는데 나누는 기준이 다르다 크게 두가지 기준이 있다.


1. data type 에 따른 분류


기본형변수참조변수로 나뉜다.


기본형변수는 자료의 실제값을 저장한다.


8가지 종류의 타입이 있다.

논리형 : boolean 

문자형 : char                      

정수형 : byte            

 short

 int

 long

실수형 : float

 double


참조변수는 값이 저장되어 있는 주소값을 값으로 갖는다.


8가지 기본형 변수를 제외한 나머지 타입이다.

대표적으로 String이 있고 사용자가 얼마든지 새로운 클래스를 만들어 낼 수 있기때문에 무한하다.



2. 선언 위치에 따른 분류


크게 멤버변수지역변수로 나뉘며


멤버변수클래스변수인스턴스변수를 통틀어서 부르는 용어이다.


클래스영역에 선언된 변수를 멤버변수라 하며 그중 앞에 static 이붙은 변수를 클래스 변수라고 한다.


인스턴스변수

클래스 영역에 선언되고

인스턴스 생성시 만들어진다.

각각의 인스턴스마다 다른 값을 가질 수 있다.

그러므로 인스턴스 생성이 필수이며 각 인스턴스마다 다른 값을 가져야 할 때 사용된다



클래스변수

인스턴스변수 앞에 static 을 붙이기만 하면 된다.

인스턴스변수가 각각의 인스턴스마다 다른값을 가질 수 있는 거와 달리 

값을 공유하는 변수이다.

클래스 로딩시 생성되며 

클래스이름.변수이름 이렇게 인스턴스 생성 필요없이 바로 쓸 수 있다.


지역변수

메소드 내에 선언되며 메소드 호출시 생성되고 메소드가 종료되면 사라진다.


매개변수

흔히 파라미터라고 불린다. 메소드에서 입력값을 받을 때가 있는데 그때 사용되는 변수를 매개변수라고 한다.

매개변수도 매소드 내에 선언된 것으로 간주되므로 지역변수이다.

인자값은 호출시 메소드입력부의 넣는 값이며 이값은 매개변수에 복사되어 대입된다.





예제를 보고 직접 구분해보자!


1
2
3
4
5
6
7
8
9
10
11
12
13
public class Variable {
    int a;
    static String b;
    void m(int c){
        int d=c;
    }
    public static void main(String args[]) {
        int e=0;
        Variable v = new Variable();
        v.m(e);
    }
}
 
cs




2번줄     a 기본형변수이면서 멤버변수인스턴스변수

3번줄     b참조변수이면서 멤버변수클래스변수

4번줄     c기본형변수이면서 매개변수 이면서 지역변수

5번줄     d기본형변수이면서 지역변수

7번줄     args참조변수이면서 매개변수 이면서 지역변수

8번줄     e는 기본형변수이면서 지역변수

9번줄     v는 참조변수이면서 지역변수

10번줄   e는 인자값으로 값0이 매개변수에 복사되어 넣어진다.

  1. ㅇㅇ 2019.10.29 19:18

    3번줄 int형 변수를 사용했기에 참조변수가 아니고 기본형변수아닌가요?

  2. 페페 더 프로그 2019.12.12 01:56

    제가 찾던 자료네요. 감사합니다.

  3. ㄱㅅㄱㅅ 2020.07.27 00:23

    작성년도가 꽤 되긴 했는데 지금 읽다보니
    맨 마지막 문단의 // 10번줄 e는 인자값으로 값0이 매개변수에 복사되어 넣어진다.//
    인자값(= 매개변수/parameter )은 메서드 void m(int c) 의 c가 인자값이고 v.m(e); 의 e는 인자값에 입력 되는 인수( argument) 같습니다~

    좋은글 잘보고 갑니다~


객체지향 언어


 흔히 절차지향언어랑 많이 비교 되지만 객체지향언어가 절차적 즉 순서대로 실행 되어지는 것이 아닌건 아니다.

음 말이 어려운데 말하고 싶은거는 객체지향이 절차지향에 반대말이 아니란 것이다! 결국 실행은 한줄씩 순서대로 진행한다.

그리고 또 어떤 것이 좋고 어떤 것이 나쁜 개념이 아니다 각각의 특징이 있다.


 객체지향 언어는 각각의 기능? 을 클래스로 만들고 (모듈화) 그 클래스의 객체를 생성하여 코딩하는 것인데


-재사용성이 높고

-유지보수가 용이하며

-제어자의 이용으로 신뢰성 높은 프로그래밍이 가능하다(데이터 보호) 


 라는 특징을 가지고있다.




클래스와 객체


클래스 : 객체를 정의해 놓은 것

객체 : 클래스를 바탕으로 만들어진 객체


흔히 붕어빵 기계(틀)와 붕어빵 예시가 나오는데

이 때 붕어빵 기계를 클래스

붕어빵을 객체라고 생각한다.


붕어빵 기계는 붕어빵을 만들기 위해 존재하는 것으로

그 자체 만으로는 쓸모가 없다. 단지 붕어빵(객체, 인스턴스)을 만들기 위해 존재한다.


만약 붕어빵 틀 없이 붕어빵을 만들면 매번 다른 붕어빵이 생기고 모양을 잡는 등 힘들 것이다.

하지만 붕어빵 기계가 있으면 고민없이 그대로 찍어내면 된다.


이를 아까 말한 객체지향의 특징과 같이 생각해보면 좋다.




객체와 인스턴스


클래스로부터 객체를 만드는 과정 : 클래스의 인스턴스화

클래스로부터 만들어진 객체 : 인스턴스


객체와 인스턴스는 같은 의미지만

객체는 모든 인스턴스를 대표하는 포괄적인 의미를 가진다.



객체의 구성요소


속성과 기능, 두종류의 요소로 이루어져 있으며

멤버변수와 메소드 라는 용어로 많이 쓰인다.


자동차로 예를들어보면

전폭, 전고, 전장(크기), 배기량, 연비, 색상, 최고속력 등은 속성이고

시동걸기, 가속하기, 감속하기, 에어컨작동 등은 기능이다.


이를 토대로 자동차 클래스를 만들어보면

다음과 같이 만들어 볼 수 있다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Car{
    int speed=0;
    int maxSpeed=200;
    String color;
    boolean power;
    
    void startEngine() {
        power= !power;
    }
    void accel(){
        if(speed<maxSpeed)
            speed++;
    }
    void breaking(){
        if(speed!=0)
            speed--;
    }
}
cs


인스턴스의 생성과 사용


1
2
3
4
5
6
7
8
9
10
public static void main(String args[]) {
        Car car = new Car();
        car.color="white";
        car.accel();
        //car.breaking();
        System.out.println("현재 속력은"+car.speed+"입니다.");
        
        Car car2=car;
        System.out.println(car2.speed);
    }
cs


현재 속력은1입니다.

1


인스턴스 생성방법은 이렇다.

Car car= new Car();

클래스명 변수명 = new 클래스명();

new 키워드를 사용하며 이때 저장 하는 참조변수의 타입과 인스턴스의 타입은 같아야한다. (다형성 예외)


생성한 인스턴스는 변수명에 .을붙여 접근 가능하다

car.멤버변수or메소드


이때 car라는 변수는 참조변수로 생성된 인스턴스공간의 주소값을 저장한다

하나의 참조변수는 하나의 값만을 저장하며

여러 참조변수가 하나의 인스턴스를 가르킬 수 있다.


car 와 car2는 같은 인스턴스 주소값을 저장하고 있으며 

그래서 car.speed와 car2.speed는 같은 값을 반환한다.



 

 자바의 대표적인 특징으로

 플랫폼의 독립적이다, 객체지향적이다, 자동으로 메모리를 관리해준다 라는 특징이 있다.


 여기서 3번째 언급한 자동 메모리 관리는 흔히 알고있는 가비지 컬렉터가 더 이상 쓸모없어진 객체들을 자동으로 지워주는 기능을 뜻한다.


 처음에 자바 공부를 할 때에 별로 와닿지 않았는데 나는 개발자가 직접 메모리 관리를 해주어야하는 다른 언어를 배운적도 없고 

 자동으로 관리를 해주는데 신경 쓸 필요가 있나? 라는 생각이 들어 그냥 그렇게 넘어 갔었다.


 하지만 JVM에 대해 공부하면서 항상 언급되는 heap영역의 설명 중 'JVM의 성능 등의 이슈에서 가장 많이 언급되는 공간이다' 라는 설명을 항상 보았고 의문이 생겨 알아보게 되었다.


이 글을 보기전에 먼저 JVM 메모리구조에 대해 공부하는 것이 좋다. 검색하면 많이 나온다. (내 블로그에도 있지만.. 미약하다)



본론



 heap 영역은 크게 Young Generation 영역과 Old Generation 영역 으로 구성되며 Young 영역은 Eden과 두개의 SS(survivor space)로 나뉘어진다.


heap에 객체가 적재되는 과정


1. 객체가 새로 생성되면 먼저 Eden에 속하게 된다. 그러다 꽉 차게되면 


2. 필요없어진 객체를 비우고 살아남은 객체들을 SS중 한 곳으로 보낸다


3. 1,2의 과정을 반복하다 SS도 차게되면 역시 필요 없어진 객체를 비우고 살아남은 객체들을 남아있던 SS 로 보낸다. (반드시 한 SS는 비워진 상태여야 한다.)


4. 이 과정을 거치며 더 이상 SS에도 공간이 없어지면 살아남은 객체를 Old영역으로 보낸다.





Generation GC가 탄생한 배경


왜 heap영역을 저렇게 나누고 이리저리 옮겨가며 저장을 하는 것일까???



그 이유를 알기전에 먼저 알아야할 개념이 있다.


Stop the World (STW)


 말 그대로 GC발생시 JVM은 실행중인 어플리케이션을 멈추게 한다. GC를 수행하는 쓰레드를 제외한 나머지에 모든 쓰레드의 작업을 중지시킨다. 

그래서 heap 영역의 성능 튜닝 즉 GC튜닝이란 이 STW의 시간을 최소한으로 줄이는 것이다.



David ungar라는 사람이 weak generational hypothesis 라는 가설을 세운다. 그 내용은


     대부분의 객체는 금방 죽는다.  


무슨 말이냐면 대부분의 생성된 객체는 금방 쓸모가 없어진다. 즉 GC의 대상이 된다는 것이다


그래서 heap 전체를 정리해줄 필요 없이 Young영역만을 주기적으로 관리해주고 가끔씩만 Old영역을 정리해주면 되는 것이다.


여기서 Young영역에서 일어나는 GC를 마이너GC라 부르며 상대적으로 속도가 빠르다. (1초이내)

Old 영역에서 일어나는 GC는 메이저GC라 부르며 속도가 느리다. (STW시간이 길다) 


이 heap영역의 Young영역과 Old영역의 메모리는 설정이 가능한데 최대한 마이너GC만으로 메모리가 유지 될 수 있도록 적절하게 설정해주는 것이 필요하다고 생각한다.



GC의 종류


결국에는 Old 영역도 채워질 것이고 그러면 메이저GC가 발생 할 것이다. 이 Old영역의 GC는 채택하고 있는 방식에 따라 달라지므로 다음 5가지 방식에 대해 알아보자.


1. Serial GC (-XX:+UseSerialGC)


1) old 살아있는 개체 식별(mark)
2) heap 앞부분부터 확인해 살아있는것만 남김(sweep)
3) 각 객체들이 연속되게 쌓이도록 heap 의 가장 앞 부분부터 적재(compact)


이렇게 mark-sweep-compact 알고리즘을 사용하며 단일코어 싱글 쓰레드를 사용하여 부담이 적지만 성능이 안좋다.



2. Parallel GC  (-XX:+UseParallelGC)


Serial GC와 같은 알고리즘을 사용하지만 멀티 쓰레드를 사용하여 속도가 더 빠르다는 특징이 있다. 





3. Parallel Old GC


업뎃 예정...



4. CMS (Concurrent Mark-Sweep) GC  (-XX:+UseConcMarkSweepGC)


내용이 어렵지만 쉽게 말하면 

백그라운드 쓰레드를 작동시켜 Old 영역의 쓰레기들을 지속적으로 없애 줍니다. 

그래서 STW가 거의 없다는 장점이 있습니다.


하지만 2가지 단점이 있는데 

 - 백그라운드에서 항상 쓰레드가 도니 CPU를 많이 잡아먹고

 - 기본적으로 Compact 단계를 제공하지 않아 메모리 파편화가 생깁니다 

그래서 결국 파편화가 심해지면 이를 정리해주는 시간이 다른 방식의 GC를 사용할 때 생기는 STW시간보다 길어 질 수 있습니다.



5. G1 GC (-XX:+UseG1GC)


heap을 Young Old로 구조적으로 나누지 않고 개념적으로만 나눈다. 


이렇게 통으로 바둑판식으로 관리한다. 


CMS GC 방식처럼 백그라운드 쓰레드로 Old영역을 관리하며 필요없어진 부분을 통째로 날리며 살아남은 객체는 다른 부분으로 옮겨주어

CMS 의 단점이던 메모리 파편화를 해결한 방식 입니다.




각 jdk 별로 기본적으로 채택한 GC 방식은 다음과 같습니다.


  • Java 7 - Parallel GC
  • Java 8 - Parallel GC
  • Java 9 - G1 (proposed)




마냥 자바의 메모리 관리는 가비지 컬렉터가 알아서 해준다~ 라고 생각 하고만 있었지만

이제는 적어도 적절한 GC를 선택 하고 튜닝을 해줄 수 있고 필요하다는 걸 알게 되었다.



참고,출처

https://okky.kr/article/379036

https://yckwon2nd.blogspot.com/2014/04/garbage-collection.html

https://www.slipp.net/wiki/pages/viewpage.action?pageId=26641949

http://www.holaxprogramming.com/2013/07/20/java-jvm-gc/

http://12bme.tistory.com/57

https://d2.naver.com/helloworld/1329

http://lyb1495.tistory.com/3



 코딩 과제를 하다가 scan.nextLine()이 분명 있는데 값을 받지 않고 넘어가는 경우가 생겼다.

 알아보니  scan.nextLine() 앞에 있는 nextInt()때문이었다.


 nextInt()는 정수형을 입력 받는 메소드인데 사용자가 입력할 때 ' ex) 10을 치고 엔터를 눌렀다 ' 엔터 앞부분까지만 입력 받는다. ( 이 경우 10까지만)

 즉 개행문자를 제거하지 않아 그 다음 나오는 nextLine()이 개행문자(엔터)를 받게 된다.


간단한 내용이지만 모르면 해맬 수 있어 정리를  해 놓는다.


문제 상황


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import java.util.Scanner;
 
public class Problem {
    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        int num;
        String str;
        System.out.print("num 입력 = "); 
        num = scan.nextInt(); // 10입력
        
        System.out.print("str 입력 = "); 
        str = scan.nextLine();             // 분명 이 줄이 있는데 값을 받지 않고 넘어가 진다.
 
        System.out.println();
        System.out.println("num : " + num);
        System.out.println("str : " + str);
        scan.close();
 
    }
}
 

cs


num 입력 = 10

str 입력 =

 

num : 10

str : 



해결방법



 1. nextInt() 뒤에 nextLine을 추가


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import java.util.Scanner;
 
public class Problem {
    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        int num;
        String str;
        System.out.print("num 입력 = "); // 10 입력
        num = scan.nextInt();
        scan.nextLine();                // 추가된 nextLine() 개행문자를 받는다. (제거한다)
 
        System.out.print("str 입력 = "); 
        str = scan.nextLine();          // 문제없이 입력을 받는다. hello 입력
 
        System.out.println();
        System.out.println("num : " + num);
        System.out.println("str : " + str);
        scan.close();
 
    }
}
 
cs


num 입력 = 10

str 입력 = hello


num : 10

str : hello



 2. nextLine()을 입력 받아 형변환 하기


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
import java.util.Scanner;
 
public class Problem {
    public static void main(String[] args) {
       Scanner scan = new Scanner(System.in);
       int num = 0;                   // 지역변수라 초기화 필요
       String str;
       System.out.print("num 입력 = "); 
      
       try {
           num = Integer.parseInt(scan.nextLine()); // 형변환을 한다 10입력
       }
       catch(NumberFormatException e) {
           System.out.println("정수를 입력해!");
       }
        
 
        System.out.print("str 입력 = "); 
        str = scan.nextLine();             // hello 입력
 
        System.out.println();
        System.out.println("num : " + num);
        System.out.println("str : " + str);
        scan.close();
 
    }
}
 
 
cs


num 입력 = 10

str 입력 = hello


num : 10

str : hello



 3. 새로운 Scanner 객체 생성


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import java.util.Scanner;
 
public class Problem {
    public static void main(String[] args) {
        Scanner scanI = new Scanner(System.in); // int를 받는 스캐너
        Scanner scanS = new Scanner(System.in); // String을 받는 스캐너
 
        int num;
        String str;
        System.out.print("num 입력 = ");
        num = scanI.nextInt(); 
 
        System.out.print("str 입력 = ");
        str = scanS.nextLine();
 
        System.out.println();
        System.out.println("num : " + num);
        System.out.println("str : " + str);
        scanI.close();
        scanS.close();
    }
}
 
cs


num 입력 = 10

str 입력 = hello


num : 10

str : hello




 간단하게 1번이나 아님 정수형 요청시 예외 값을 처리 할 수 있는 2번방법이 좋아보인다.




참고

http://allg.tistory.com/17

다형성이란??? 


여러 가지 형태를 가질 수 있는 능력!

자바에선 한 타입의 참조변수여러 타입의 인스턴스를 참조할 수 있는 것!


보통 A a = new A(); 이렇게 참조변수와 인스턴스의 타입이 같은 경우를 흔히 보았을 것이다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package test;
 
public class Poly {
    public static void main(String args[]) {
        
        Human a = new Human();
        Human b = new Programer();
    }
}
 
class Human {
    String name;
    void eat(){
    }
}
 
class Programer extends Human {
    void coading(){
    }
}
 
cs


 

 하지만 위 예제를 보면 Human 타입의 참조변수가 Human의 인스턴스와 Programer의 인스턴스를 참조한다.

한가지 타입의 참조변수로 여러 타입의 인스턴스를 담았다!


다만 아무 타입이 아무 타입의 인스턴스를 참조할 수 있는 것은 아니다


 

 조건

 

 조상타입의 참조변수로 자손타입의 인스턴스를 참조할 수 있다. 하지만 역은 성립하지 않는다!!

 즉 참조변수가 사용할 수 있는 멤버의 개수는 인스턴스의 멤버 개수보다 같거나 적어야 하는 것이다.

 Human b = new Programer(); 의 경우 Programer의 인스턴스를 참조중이라 해도 Human타입의 참조 변수기 때문에 Human이 가지고있는 name과 eat() 멤버만 사용 가능하다 

 즉 참조 변수의 타입에 따라서 참조하고 있는 인스턴스에서 사용할 수 있는 멤버의 수가 달라지게 된다!


Programer b = new Human(); 만약 이런식으로 한다면 Programer의 참조변수이므로 coading()을 쓸 수 있지만 Human인스턴스를 참조중이고 Human은 coading()을 가지고 있지 않으므로 문제가 발생한다. 그래서 위에 언급한 조건이 있는 것이다.



 참조변수의 형변환


참조변수도 기본형과 같이 형변환이 가능하다. 다만 상속관계에 있는 클래스사이에서만 가능하다. (간접적인 상속관계 즉 조상의 조상 같은경우도 가능)


byte b = 4;           업캐스팅 형변환 생략

int i = b; 


int i = 30;             다운캐스팅 형변환 생략 불가

byte b = (byte)i;  


 기본형 변수의 형변환이다. 보면 더 작은 자료형인 byte에서 더 큰 int로의 형변환은 생략됨을 볼 수 있다.

 마찬가지로? 자손타입에서 조상타입으로의 형변환은 생략이 가능(업캐스팅) 하지만 반대는(다운캐스팅) 생략이 불가능하다.


내가 이해하기엔 자손클래스가 부모클래스보다 큰? (더 많은 멤버를 가지고 있으니 당연한거 아닌가?)개념이라 처음에 무지 헷갈렸다.


 아까보았던 Human c = new Programer(); 이 예제도  Human c = (Human)new Programer(); 사실 저형변환이 생략된 형태이다.(업캐스팅)


다운캐스팅


1. Programer p = (Programer)new Human();    그럼 이렇게 형변환을 명시해주면 되는 것일까??

답은 아니다! 컴파일은 되지만 런타임 에러가 뜨고만다.


2. Human h = new Programer();

   Programer p = (Programer)h;

 

 이렇게 해주어야 한다. 왜 일까?

형변환은 단순히 참조변수의 타입을 변환해주는 것이지 인스턴스에는 영향을 끼지지 않는다. 무슨말이냐면!

Human이 Programer의 타입으로 바뀐다해도 Human은 coading()을 가지는 것은 아니다.!!!

그래서 결국 참조변수 p가 다룰 수 있는 멤버의 개수가 Human인스턴스보다 더 많아 불가능한 것이다 (계속 같은말..) 


 2.의 경우 첫번째 줄을 보면 h는 Programer의 인스턴스를 참조하지만 Programer의 멤버를 Human이 가지고 있는 멤버만큼만 이용이 가능하다

 Programer인스턴스를 참조하는 h의 정보를 형변환으로 타입을 맞추어 p에 제공하면 Programer의 타입으로 Programer를 참조하는 것이다. 당연히 되고 모든 멤버를 이용가능해 진다.


 처음 이 개념을 공부 했을때 왜 구지 이런 짓을 하지? 라는 생각이 딱 들었다. 방법은 알아도 활용도가 와닿지 않았다.

하지만 다운캐스팅을 활용을하는 예제를 밑에 두었으니 차차 다음내용도 보자!




매개변수의 다형성


동호회를 만들어 클래스로 관리하려한다. Club클래스를 만들고 클럽 멤버로 프로그래머가 올수도 학생이 올수도 있다. 그러면


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
class Human {
    String name;
    void eat(){
    }
}
 
class Programer extends Human {
    void coading(){
    }
}
class Student extends Human{
    void study() {
        
    }
}
 
class Club {
    static int memberNumber=-1;
    String clubName= "java";
    String [] members = new String[10];
    
    void member(Programer p) {
        members[++memberNumber] = p.name;
    }
    void member(Student s) {
        members[++memberNumber] = s.name;
    }
 
}
cs


 이렇게 Club이라는 클래스에 member라는 메소드를 여러번 오버로딩 해주어야 할까?? 여기서는 2개의 상황만 나열했지만  실제로 가입하려는 사람들은 더 다양한 직업을 가졌을 것이다.

 하지만 매개변수의 다형성을 적용해보자. Programer와 Student 모두 공통의 조상인 Human을 가지고 있다.


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
class Human {
    String name;
    void eat(){
    }
}
 
class Programer extends Human {
    void coading(){
    }
}
class Student extends Human{
    void study() {
        
    }
}
 
class Club {
    static int memberNumber=-1;
    String [] members = new String[10];
    String ClubName;
    Club(String a) {
        ClubName=a;
    }
    void member(Human h) {
        members[++memberNumber] = h.name;
    }
    
}
cs

 

 이렇게 하나의 메소드로도 표현할 수 있게 된다. (24번줄)


 멤버의 특성이 이름만 있을까? 위 코드에는 그렇지만 실제로는 나이도있고 성별도있고 등등 더 많을 것이다.

그렇다면 members라는 배열을 객체의 배열로 만들어 하나의 배열에 객체정보를 담으면 되지 않을까?



여러 종류의 객체를 하나의 배열로 다루기


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Club {
    static int memberNumber=-1;
    Human [] members = new Human[10];
    String ClubName;
    Club(String a) {
        ClubName=a;
    }
    void member(Human h) {
        members[++memberNumber] = h;
    }
    
    void getMemberInfo(int number) {
        System.out.println("회원이름 :"+ members[number].name);
        System.out.println("회원나이 :"+ members[number].age);
    }
    
}
cs


하나의 배열의 여러 종류의 객체를 담아 더 간단하게 코딩이 가능해진다. (3번줄)


1
2
3
4
5
6
7
8
9
10
11
public class Poly {
    public static void main(String args[]) {
        
        Club A = new Club("java");
        Programer Programer1 = new Programer();
        Student Student1 = new Student();
        A.member(Programer1);
        A.member(Student1);
        
    }
}
cs

이렇게 클럽A의 member배열에 여러종류의 객체가 담긴다. (7,8번줄)



 하지만 위에 코딩에서는 클럽멤버의 수를 정해 10으로 정해놓았다. 물론 동호회 인원을 정해놓고 더 안받는 경우도 있겠지만 그렇지 않는 경우는 어떻게 해야할까?  이 블로그에서 다루진 않았지만 가변배열중에 하나인 Vector 클래스를 사용하면 된다.

Vector클래스는 내부적으로 Object타입의 배열을 가지고 있어서 이배열에 어떤 객체든 추가가 가능하다.


다운캐스팅 예제 


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Club {
 
    Vector members = new Vector();
    String ClubName;
    Club(String a) {
        ClubName=a;
    }
    void member(Human h) {
        members.add(h);
    }
    void getMemberInfo(int number) {
        Human h =(Human)members.get(number);
        
        System.out.println("회원이름 :"+ h.name);
        System.out.println("회원나이 :"+ h.age);
    }
    
}
cs


 자 3번줄을 보면 더이상 멤버의 수가 제한되지 않게 members를 Vector로 만들어 주었고 member의 추가도 9번줄 Vector의 메소드인 .add()로 해주었다.

 여기서 알고가야할 점은 Vector의 add() 메소드는 객체를 Object타입의 변수로 저장한다는 것이다. 물론 오늘 배운 핵심인 조상의 참조변수로 자식의 인스턴스를 담을 수 있다. 라는 점을 어긋나지 않아 가능하다 Object는 모든 클래스의 조상이니.. 


 하지만 Object는 name이나 age같은 변수를 가지고 있지 않아 사용할 수 없다.


그래서!


 12줄을 보면 Vector의 있는 정보를 꺼내 Object의 자식중 하나인 Human으로 다운캐스팅 하는 것을 볼 수 있다!!! 그럼다시 Human은 name,age를 가지고 있기 때문에 사용이 가능해진다. 


 이렇게 예제를 통해 다형성, 참조변수의 업캐스팅,다운캐스팅, 매개변수의 다형성, 여러 종류의 객체를 하나의 배열로 다루는 것을 알아보았다.

 나는 이부분을 공부하면서 처음에 이해가 너무 어려웠고 특히 다운캐스팅 같은경우는 왜 저런짓을 하는지 이해가 안갔다. 하지만 이런 예제를 통해 이해가 되어 무리를 해서라도 만들어 보았다.. 


참고

자바의 정석 남궁 성 지음

  1. heyhyo 2018.07.17 17:00 신고

    정말 도움이 많이 되었습니다~^^

  2. heyhyo 2018.07.24 13:49 신고

    하하 그럴리가요^^

  3. 싸록 2021.07.14 16:23 신고

    다형성에서 형변환 부분이 도무지 이해가 안갔었는데 int byte인 기본형으로 설명해주시니까 딱 알겠네요!! 감사합니당~

에러 관계도



이처럼 프로그램 실행 시 발생할 수 있는 예외(Exception)를 대비해 코드를 작성하는 것을 예외처리(Exception handling) 라고 하며 

프로그램의 비정상 종료를 막고 정상적인 실행상태를 유지하는 것이 목적이다!




○ 예외의 종류


자바에서는 에외와 에러를 클래스로 정의 하였다. 역시 Object가 최상위 클래스 이다.

예외클래스 계층도



예외는 그 특징에 따라 크게 2가지로 다시 나뉘어진다.

예외의 두종류


RuntimeException클래스와 아이들 (위 그림에 아랫부분)  //이하 RuntimeException클래스들


RuntimeException클래스와 아이들 제외한 나머지 Exception클래스와 아이들 (위 그림에 윗부분) //이하 Exception클래스들



 RuntimeException클래스들 


-주로 프로그래머의 실수에 의해 발생되는 예외다. (배열의 범위를 벗어남, 정수를0으로나누려함 등등등)

-so 프로그래머의 주의필요

-이런종류의 예외들은 예외처리 없어도 컴파일이 된다. 

-unchecked예외라고 부른다



 Exception클래스들


-주로 사용자의 실수와 같은 외적인 요인에 의해 발생하는 예외 (입력한 데이터 형식이 잘못됨, 존재하지 않는 파일 이름 입력 등등등)

-이런종류의 예외들은 반드시 예외처리가 필요함 

-checked예외라고 부른다





○ 예외 처리 방법

try catch, method에 선언하기 2가지가있다.


1. try catch 문



-보기와 같이 catch문은 여러번 쓸 수 있다.


try문에서 예외가 발생하면 해당하는 클래스의 인스턴스가 만들어지고 그 인스턴스를 가지고 catch문에 선언된 참조변수의 종류와 instanceof 연산자를 이용해 true인 catch문을 찾는다.


그래서 먼저 나오는 catch문에 예외는 뒤에 나오는 예외보다 자식이어야 한다!

why? 만약 첫 catch문에 가장 부모인 Exception이 있다면 모든 예외가 다잡하기에 밑에 선언된 catch문은 쓸모가 없어지게 된다. 


-catch문 괄호안의 선언된 변수는 그 catch문 안에만 유효하므로 보기의 'e'처럼 반복사용 가능하다.


-try문이나 catch문 안에 또 try catch문 선언이 가능하다.


-if문과 달리,  try와 catch문의 포함된 문장이 하나여도 {}를 생략할 수 없다


-흐름

예외가 있을경우 : try문(예외까지만) -> 해당되는 예외의 catch문 -> finally문

예외가 없을경우 : try문 -> finally문

try문에서 예외가 발생하면 나머지 try문은 실행되지 않으므로 try문을 잘 구성해야 한다.



-printStackTrace() : 예외발생 당시의 스택에 있던 메서드의 정보와 예외 메시지를 출력한다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

class Excep {
    public static void main(String args[]){
        System.out.println(1);
        try{
            System.out.println(2);
            System.out.println(0/0);
            System.out.println(3);  //위에서 예외가 발생해서 실행되지 않는다
        }catch(ArithmeticException e){
            System.out.println(4);
e.printStackTrace(); //
        }finally {
            System.out.println(5);
        }
        System.out.println(6);
    }
}

cs

1

2

4

java.lang.ArithmeticException: / by zero

at blog.Excep.main(Excep.java:8)

5

6


다음과 같이 6번줄에 예외가 발생하여 7번줄이 실행되지 않았고 catch문으로 흐름이 이동해 4와 예외 메시지가 출력되었다.


-멀티 catch 블럭

JDK1.7부터 여러 catch문을 | 을 이용해서 하나로 합칠 수 있게 되었다. 합치는 개 수에는 제한이 없다.


1
2
3
4
5
6
7
8
try{
}catch(ExceptionA e){
 
}catch(ExceptionB e){
 
}catch(Exceptionc e){
 
}
cs

1
2
3
4
try{
}catch(ExceptionA | ExceptionB | ExceptionC e){
 
}
cs

예외 종류는 다르지만 처리방법이 같았던 경우 중복 코드가 생겼었지만 위 방법으로 중복을 피할 수 있다.


한 catch문에 부모 자식 관계에 예외를 사용하지 못한다. 의미가 없다 그냥 조상 클래스의 예외만 써주는 거랑 똑같다.


참조변수 e가 어떤 예외클래스를 참조하는지 알 수 없기 때문에 참조변수 e로는 공통 조상 클래스에 멤버만 쓰거나 instanceof 로 확인 후 써야한다.


1
2
3
4
5
6
7
8
9
try{
}catch(ExceptionA | ExceptionB | ExceptionC e){
    if(e instanceof ExceptionA){
        ExceptionA e1 = (ExceptionA)e;
        e1.methodA();
    }else if(e instanceof ExceptionB){
        ......
    }
cs





* 고의로 예외 발생시키기 (throw)

Exception e = new Exception(); 

throw e;


throw new Exception();  아까 말했듯이 반드시 예외처리 필요

throw new RuntimeException();  예외처리 없어도 됨

이렇게 예외처리가 없어서 에러가 뜬다.


예외처리 해주므로 빨간줄 사라짐~


반면 RuntimeException 에러는 예외처리 안해주어도 컴파일 가능! (하지만 실행시 에러가 발생하면 마찬가지로 프로그램 종료됨)




2. 메소드에 예외 선언하기 (예외 던지기)


method () throws Exception1, Exception2 {


}


throws 키워드를 사용하며 역시 , 로 여러 예외를 선언할 수있다. (throw 랑 헷갈리지 말자)



 사실 메소드에 예외를 선언하는건 예외를 처리하는건 아니다. 

저렇게 선언된 메소드에서 에러가 발생하면 그메소드를 호출한 메소드에게 예외를 전달하여 예외처리를 떠맡기는 것이다.


 그렇게 던지고 던져서 만약 main메소드까지 전달되고 main에서도 예외가 처리되지 않으면 프로그램은 종료된다.

이렇게말이다. 저 에러코드로 알수있는 점이 있는데 예외가 발생한 곳은 method1이고 그메소드를 main이 호출했다는 것을 알 수 있다.

 



결국 어디서든 try catch문으로 예외처리가 필요하다

3가지 처리 방법이 있는데




예외가 발생하는곳에서 처리하는경우

 method1 에서 처리

 

 예외가 발생하는 메소드에서 처리를 해버리는 경우다. 이런 경우에는 main은 예외가 발생하는 지도 모르며 그래서 main에 throws가 필요없다.




예외를 받아 처리하는경우

main에서 처리

 

 예외가 발생하는 method1에서는 예외처리를 하지 않으므로 throws를 해주어야한다. (Exception 예외 이므로)




두곳다 처리하는경우



method1에서 처리

main에서 처리


에러가 발생한 method1에서 처리를 해주고 아까배운 throw로 다시 예외를 만들었다. (예외 되던지기)

이미 처리가 끝난다음에 에러가 발생해 method1에서 다시 throws를 해주어야하고 (역시Exception 에러므로) 

이 에러는 main으로 던져진다 

main에서 역시 try catch로 예외처리 해주어야 된다.




참조

http://sjh836.tistory.com/122

http://history1994.tistory.com/10?category=668240

Java의 정석    남궁 성 지음



  1. 코취리 2018.07.10 23:20 신고

    이것이 예외처리 크으 정리 잘되있네요!

반복문 사용시 break 와 continue는 유용하게 사용된다.

다만 중첩 반복문 사용시 원하는 반복문으로 한방에 탈출 할 수는 없을까?



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class Exit {
    public static void main(String[] args) {
 
        int r1 = 0;
        int r2 = 0;
        
        roop : while(true) {    // roop라고 반복문 이름을 주었다  : 키워드로 붙여준다
            r1++;
            System.out.println("첫번째 반복문 "+r1);
            
            while(true) {
                r2++;
                System.out.println("두번째 반복문 "+r2);
                
                if(r2==3)
                    break roop; // roop라는 반복문을 탈출
            }
            
        }
        
    }
}
 
cs


첫번째 반복문 1

두번째 반복문 1

두번째 반복문 2

두번째 반복문 3


적절한 예제가 떠오르지 않았다..... 하여튼 알아두면 된다.




반복문 앞에   name : while , name : for  과 같이  이름을 붙이면


break name; continue name; 과 같이 지정한 반복문을 빠져나가거나 컨티뉴 할 수 있다.


이는 반복문 안에 반복문을 넣었을 때 한번에 빠져 나온다던가 같이 반복문을 요리조리 이동하고 싶을 때 유용하다.

+ Recent posts