<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>seo-faper.log</title>
        <link>https://velog.io/</link>
        <description>gotta go fast </description>
        <lastBuildDate>Tue, 21 Mar 2023 08:50:46 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>seo-faper.log</title>
            <url>https://velog.velcdn.com/images/seo-faper/profile/061c9b6e-49a2-4156-9c27-0b4b3f592fd3/image.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. seo-faper.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/seo-faper" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[백준 15686 치킨 배달]]></title>
            <link>https://velog.io/@seo-faper/%EB%B0%B1%EC%A4%80-15686-%EC%B9%98%ED%82%A8-%EB%B0%B0%EB%8B%AC</link>
            <guid>https://velog.io/@seo-faper/%EB%B0%B1%EC%A4%80-15686-%EC%B9%98%ED%82%A8-%EB%B0%B0%EB%8B%AC</guid>
            <pubDate>Tue, 21 Mar 2023 08:50:46 GMT</pubDate>
            <description><![CDATA[<h3 id="문제">문제</h3>
<p>크기가 N×N인 도시가 있다. 도시는 1×1크기의 칸으로 나누어져 있다. 도시의 각 칸은 빈 칸, 치킨집, 집 중 하나이다. 도시의 칸은 (r, c)와 같은 형태로 나타내고, r행 c열 또는 위에서부터 r번째 칸, 왼쪽에서부터 c번째 칸을 의미한다. r과 c는 1부터 시작한다.</p>
<p>이 도시에 사는 사람들은 치킨을 매우 좋아한다. 따라서, 사람들은 &quot;치킨 거리&quot;라는 말을 주로 사용한다. 치킨 거리는 집과 가장 가까운 치킨집 사이의 거리이다. 즉, 치킨 거리는 집을 기준으로 정해지며, 각각의 집은 치킨 거리를 가지고 있다. 도시의 치킨 거리는 모든 집의 치킨 거리의 합이다.</p>
<p>임의의 두 칸 (r1, c1)과 (r2, c2) 사이의 거리는 |r1-r2| + |c1-c2|로 구한다.</p>
<p>예를 들어, 아래와 같은 지도를 갖는 도시를 살펴보자.</p>
<pre><code>0 2 0 1 0
1 0 1 0 0
0 0 0 0 0
0 0 0 1 1
0 0 0 1 2</code></pre><p>0은 빈 칸, 1은 집, 2는 치킨집이다.</p>
<p>(2, 1)에 있는 집과 (1, 2)에 있는 치킨집과의 거리는 |2-1| + |1-2| = 2, (5, 5)에 있는 치킨집과의 거리는 |2-5| + |1-5| = 7이다. 따라서, (2, 1)에 있는 집의 치킨 거리는 2이다.</p>
<p>(5, 4)에 있는 집과 (1, 2)에 있는 치킨집과의 거리는 |5-1| + |4-2| = 6, (5, 5)에 있는 치킨집과의 거리는 |5-5| + |4-5| = 1이다. 따라서, (5, 4)에 있는 집의 치킨 거리는 1이다.</p>
<p>이 도시에 있는 치킨집은 모두 같은 프랜차이즈이다. 프렌차이즈 본사에서는 수익을 증가시키기 위해 일부 치킨집을 폐업시키려고 한다. 오랜 연구 끝에 이 도시에서 가장 수익을 많이 낼 수 있는  치킨집의 개수는 최대 M개라는 사실을 알아내었다.</p>
<p>도시에 있는 치킨집 중에서 최대 M개를 고르고, 나머지 치킨집은 모두 폐업시켜야 한다. 어떻게 고르면, 도시의 치킨 거리가 가장 작게 될지 구하는 프로그램을 작성하시오.</p>
<h3 id="입력">입력</h3>
<p>첫째 줄에 N(2 ≤ N ≤ 50)과 M(1 ≤ M ≤ 13)이 주어진다.</p>
<p>둘째 줄부터 N개의 줄에는 도시의 정보가 주어진다.</p>
<p>도시의 정보는 0, 1, 2로 이루어져 있고, 0은 빈 칸, 1은 집, 2는 치킨집을 의미한다. 집의 개수는 2N개를 넘지 않으며, 적어도 1개는 존재한다. 치킨집의 개수는 M보다 크거나 같고, 13보다 작거나 같다.</p>
<h3 id="출력">출력</h3>
<p>첫째 줄에 폐업시키지 않을 치킨집을 최대 M개를 골랐을 때, 도시의 치킨 거리의 최솟값을 출력한다.</p>
<p>입력 : </p>
<pre><code>5 3
0 0 1 0 0
0 0 2 0 1
0 1 2 0 0
0 0 1 0 0
0 0 0 0 2</code></pre><p>출력 : 5</p>
<p>입력 : </p>
<pre><code>5 2
0 2 0 1 0
1 0 1 0 0
0 0 0 0 0
2 0 0 1 1
2 2 0 1 2</code></pre><p>출력 : 10</p>
<h3 id="풀이">풀이</h3>
<p>완전탐색으로 풀었다.</p>
<ol>
<li>임의의 치킨 집 중에서 M 개만 선택했을 때 치킨거리를 모두 구한다. </li>
<li>그리고 모두 더해서 도시의 치킨 거리를 구한다.</li>
<li>구해진 조합의 모든 도시의 치킨 거리 중에서 최소를 출력한다.</li>
</ol>
<p>1을 구현하기 위해서는 입력을 받을 때 2가 들어오면 해당 좌표를 집의 목록에 추가하는 것 이다.
그렇게 생성된 특정 길이의 벡터를 Chiken 이라 하고 이 벡터의 크기를 Chiken_Num 이라 하자.
결국 $_nC_r$ 의 경우의 수에서 n은 Chiken_Num, r 은 입력받은 m이 된다.</p>
<p>그렇게 만들어진 조합의 경우의 수에서 치킨거리를 계산하는 공식을 통해 최소값을 찾아주면 된다.</p>
<pre><code class="language-c">#include &lt;iostream&gt;
#include &lt;vector&gt;
#include &lt;cmath&gt;
#include &lt;algorithm&gt;

#define endl &quot;\n&quot;
#define MAX 50
using namespace std;

/*
1. 전체 치킨 가게 중 M 개를 선택하는 조합을 만든다. M개의 모든 경우의 수가 나온다. 
2. 해당 조합의 치킨거리 값을 계산해 최소를 찾는다.

*/

int N, M, Chicken_Num, Answer;
int CITY[MAX][MAX];
vector&lt;pair&lt;int, int&gt; &gt; Home, Chiken;
int calculate(int r1, int c1, int r2, int c2){
    return abs(r1 - r2) + abs(c1 - c2);
}
int main()
{

    cin &gt;&gt; N &gt;&gt; M;
    Answer = 99999;
    for (int i = 0; i &lt; N; i++)
    {
        for (int j = 0; j &lt; N; j++)
        {
            cin &gt;&gt; CITY[i][j];
            if (CITY[i][j] == 1)
            {
                Home.push_back(make_pair(i, j));
            }
            else if (CITY[i][j] == 2)
            {
                Chiken.push_back(make_pair(i, j));
            }
        }

    }
     Chicken_Num = Chiken.size();
     vector&lt;int&gt; v(Chicken_Num),p(Chicken_Num);
    for(int i = 0; i &lt; Chicken_Num; i++){ v[i] = i;}
    for(int i = 0; i&lt;M; i++){ p[i] = 1;}


    do{
        int tmp = 0;
        for(int k = 0; k &lt;Home.size(); k++){

            int w = 0xffffff;

            int Hx = Home[k].first;
            int Hy = Home[k].second;
            for(int i = 0; i&lt;v.size(); i++){

            if(p[i]==1){

                //Home 에 대해 가장 가까운 치킨 거리는 몇? -&gt; w라 하자
                w = min(w, calculate(Hx,Hy, Chiken[v[i]].first, Chiken[v[i]].second));

                }


             }
             //cout &lt;&lt; w&lt;&lt;endl;
             tmp +=w;
        }

        // M 개의 w가 나오고 그걸 모두 더한 값이 경우1 에 대한 도시의 치킨거리 값 -&gt; tmp 라 하자.
       // cout&lt;&lt;tmp&lt;&lt;endl;
        Answer = min(Answer,tmp);
    }while(prev_permutation(p.begin(),p.end()));



    cout&lt;&lt; Answer &lt;&lt;endl;
    return 0;
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[백준 17413 단어 뒤집기 2]]></title>
            <link>https://velog.io/@seo-faper/%EB%B0%B1%EC%A4%80-17413-%EB%8B%A8%EC%96%B4-%EB%92%A4%EC%A7%91%EA%B8%B0-2</link>
            <guid>https://velog.io/@seo-faper/%EB%B0%B1%EC%A4%80-17413-%EB%8B%A8%EC%96%B4-%EB%92%A4%EC%A7%91%EA%B8%B0-2</guid>
            <pubDate>Mon, 20 Mar 2023 07:15:25 GMT</pubDate>
            <description><![CDATA[<h3 id="문제">문제</h3>
<p>문자열 S가 주어졌을 때, 이 문자열에서 단어만 뒤집으려고 한다.</p>
<p>먼저, 문자열 S는 아래와과 같은 규칙을 지킨다.</p>
<p>알파벳 소문자(&#39;a&#39;-&#39;z&#39;), 숫자(&#39;0&#39;-&#39;9&#39;), 공백(&#39; &#39;), 특수 문자(&#39;&lt;&#39;, &#39;&gt;&#39;)로만 이루어져 있다.
문자열의 시작과 끝은 공백이 아니다.
&#39;&lt;&#39;와 &#39;&gt;&#39;가 문자열에 있는 경우 번갈아가면서 등장하며, &#39;&lt;&#39;이 먼저 등장한다. 또, 두 문자의 개수는 같다.
태그는 &#39;&lt;&#39;로 시작해서 &#39;&gt;&#39;로 끝나는 길이가 3 이상인 부분 문자열이고, &#39;&lt;&#39;와 &#39;&gt;&#39; 사이에는 알파벳 소문자와 공백만 있다. 단어는 알파벳 소문자와 숫자로 이루어진 부분 문자열이고, 연속하는 두 단어는 공백 하나로 구분한다. 태그는 단어가 아니며, 태그와 단어 사이에는 공백이 없다.</p>
<h3 id="입력">입력</h3>
<p>첫째 줄에 문자열 S가 주어진다. S의 길이는 100,000 이하이다.</p>
<h3 id="출력">출력</h3>
<p>첫째 줄에 문자열 S의 단어를 뒤집어서 출력한다.</p>
<h3 id="풀이">풀이</h3>
<ol start="0">
<li>공백, &#39;&lt;&#39;, &#39;&gt;&#39; 를 제외한 문자열을 k라고 하자. k는 출력 후 초기화 된다. </li>
<li>&#39;&lt;&#39; 가 시작되면 그 전에 뒤집어야 하는 문자열이 있을 수도 있기 때문에 k를 뒤집어서 출력, k 초기화</li>
<li>1 스택에 &#39;&lt;&#39;를 push (올바른 태그 확보) </li>
<li>&#39;&gt;&#39; 가 시작되면 그 전에 문자를 그대로 출력, k 초기화
 2.1 스택에서 pop (올바른 태그 확보)</li>
<li>공백이 나오고 스택에 아무것도 없다면 k를 뒤집어서 출력, k 초기화 </li>
<li>마지막 문자열 k를 뒤집어서 출력</li>
</ol>
<pre><code class="language-c">#include &lt;iostream&gt;
#include &lt;stack&gt;
#include &lt;algorithm&gt;
using namespace std;
int main()
{
    string s;
    stack&lt;char&gt; sk;
    string k = &quot;&quot;;
    getline(cin, s);
    for (int i = 0; i &lt; s.length(); i++)
    {

        if (s[i] == &#39;&lt;&#39;)
        {
            reverse(k.begin(), k.end());
            cout &lt;&lt; k;
            k = &quot;&quot;;
            sk.push(s[i]);
        }
        else if (s[i] == &#39;&gt;&#39;)
        {
            cout &lt;&lt; &#39;&lt;&#39; &lt;&lt; k &lt;&lt; &#39;&gt;&#39;; // 뒤집지 말고 출력
            k = &quot;&quot;;
            sk.pop();
        }
        else if (s[i] == &#39; &#39; &amp;&amp; sk.size() == 0) // 공백이면 뒤집어 출력
        {
            reverse(k.begin(), k.end());

            cout &lt;&lt; k &lt;&lt; &#39; &#39;;
            k = &quot;&quot;;
        }
        else // 공백 포함 &lt;, &gt; 제외한 글자를 담은 다음에 뒤집어야 하는지 어쩐지를 정하면 됨.
        {
            k += s[i];
        }
    }
    reverse(k.begin(), k.end());
    cout &lt;&lt; k;
    cout &lt;&lt; &quot;\n&quot;;
    return 0;
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[백준 1138 한 줄로 서기]]></title>
            <link>https://velog.io/@seo-faper/%EB%B0%B1%EC%A4%80-%ED%95%9C-%EC%A4%84%EB%A1%9C-%EC%84%9C%EA%B8%B0</link>
            <guid>https://velog.io/@seo-faper/%EB%B0%B1%EC%A4%80-%ED%95%9C-%EC%A4%84%EB%A1%9C-%EC%84%9C%EA%B8%B0</guid>
            <pubDate>Sun, 19 Mar 2023 13:57:28 GMT</pubDate>
            <description><![CDATA[<p>N명의 사람들은 매일 아침 한 줄로 선다. 이 사람들은 자리를 마음대로 서지 못하고 오민식의 지시대로 선다.</p>
<p>어느 날 사람들은 오민식이 사람들이 줄 서는 위치를 기록해 놓는다는 것을 알았다. 그리고 아침에 자기가 기록해 놓은 것과 사람들이 줄을 선 위치가 맞는지 확인한다.</p>
<p>사람들은 자기보다 큰 사람이 왼쪽에 몇 명 있었는지만을 기억한다. N명의 사람이 있고, 사람들의 키는 1부터 N까지 모두 다르다.</p>
<p>각 사람들이 기억하는 정보가 주어질 때, 줄을 어떻게 서야 하는지 출력하는 프로그램을 작성하시오.입력
첫째 줄에 사람의 수 N이 주어진다. N은 10보다 작거나 같은 자연수이다. 둘째 줄에는 키가 1인 사람부터 차례대로 자기보다 키가 큰 사람이 왼쪽에 몇 명이 있었는지 주어진다. i번째 수는 0보다 크거나 같고, N-i보다 작거나 같다. i는 0부터 시작한다.</p>
<p>출력
첫째 줄에 줄을 선 순서대로 키를 출력한다.</p>
<pre><code>예제 입력 
4
2 1 1 0
예제 출력 
4 2 1 3</code></pre><p>아이디어 : </p>
<p>가장 큰 사람, 즉 N번 째 사람은 무조건 0 이다. 어디에 있든 자기보다 큰 사람이 왼쪽에 있을 리는 없으니까.
그 다음 N-1 사람, 2가지로 나눌 수 있다. 0 또는 1 이다. N 이 왼쪽에 있으면 1, 오른쪽에 있으면 0 이다.
N-2 사람은? 0, 1, 2 중에 하나다. N과 N-1이 모두 자기 오른쪽에 있거나, 하나만 왼쪽에 있거나, 둘 다 왼쪽에 있거나.
이걸 코드로 구현하면 다음과 같다.</p>
<pre><code>    N-1 번 사람이 0 이라면  L = [ N-1 , N ]  1이면 맨 뒤에 삽입한  L = [ N, N-1 ]형태가 된다.

    N-2 가 
    0 이면 인덱스 0에 삽입,
    1 이면 인덱스 1에 삽입, 
    2 라면 인덱스 2에 삽입 해야 하지만 인덱스 2는 존재하지 않으므로 맨 뒤에 append
    -&gt; vector의 사이즈와 i가 같다면 맨 뒤에 push 아니면 인덱스에 insert 
    ..반복

</code></pre><pre><code class="language-cpp">  #include &lt;iostream&gt;
#include &lt;vector&gt;
using namespace std;
int N;

int main()
{

    cin &gt;&gt; N;
    vector&lt;int&gt; v;
    int arr[N + 1];
    for (int i = 1; i &lt;= N; i++)
        cin &gt;&gt; arr[i];

    v.push_back(N);
    for (int i = N - 1; i &gt; 0; i--)
    {
        if (arr[i] == v.size())
        {
            v.push_back(i);
        }
        else
        {
            v.insert(v.begin() + arr[i], i);
        }
    }
    for (int i = 0; i &lt; N; i++)
        cout &lt;&lt; v[i] &lt;&lt; &quot; &quot;;
    cout &lt;&lt; endl;
    return 0;
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[static에 대한 고찰]]></title>
            <link>https://velog.io/@seo-faper/static%EC%97%90-%EB%8C%80%ED%95%9C-%EA%B3%A0%EC%B0%B0</link>
            <guid>https://velog.io/@seo-faper/static%EC%97%90-%EB%8C%80%ED%95%9C-%EA%B3%A0%EC%B0%B0</guid>
            <pubDate>Tue, 21 Feb 2023 20:53:05 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/seo-faper/post/7d828c87-518d-4e52-9359-6af401678ec2/image.png" alt="스태틱의 단검"></p>
<h2 id="static-키워드의-특징">static 키워드의 특징</h2>
<p>static 키워드의 특징은 다음과 같다. static은 변수 앞에 쓸 수도, 메소드 앞에 쓸 수도 있다.
이렇게 생성된 정적멤버들은 다음과 같은 특징을 지닌다.</p>
<ol>
<li><p>메모리에 고정적으로 할당된다.</p>
</li>
<li><p>객체 생성 없이 사용할 수 있다.</p>
</li>
<li><p>프로그램이 시작되면 메모리의 static 영역에 적재되고, 프로그램이 종료될 때 해제된다.</p>
</li>
<li><p>static 메서드 내에서는 인스턴스 변수를 사용할 수 없다.</p>
</li>
</ol>
<p>이러한 특성들을 자세히 알아보자.</p>
<h3 id="1-메모리에-고정적으로-할당된다">1. 메모리에 고정적으로 할당된다.</h3>
<p>static으로 선언된 멤버들은 선언 시 heap 영역에 저장되지 않는다. 
static이 붙지 않는 메소드나 변수의 경우 해당 메소드나 변수가 객체에 의해 생성될 때 마다 호출되어 서로 다른 메모리 주소를 가질 수 있다. 그래서 각 객체들이 공통적으로 하나의 값이 유지되어야 할 경우 static으로 선언해 주는 것이 좋다.</p>
<h3 id="2-객체-생성-없이-사용할-수-있다">2. 객체 생성 없이 사용할 수 있다.</h3>
<p>일반적인 인스턴스 변수나 메소드라면 자신이 소속된 객체를 <code>new</code> 키워드를 통해 인스턴스를 생성할 후 접근한 수 있다.
그러나 static은 별도의 객체 생성 없이 즉시 접근해 사용할 수 있다.</p>
<pre><code class="language-java">public class Main {

    public void print(){
        System.out.println(&quot;Hello!&quot;);
    }

    public static void main(String[] args){
        print();
    }
}
</code></pre>
<p>이런 메소드가 있다면 print() 함수는 실행되지 않는다.
대신 <code>new</code>를 통해 Main 클래스의 객체를 생성한 후 사용할 수 있다.</p>
<pre><code class="language-java">public class Main {

    public void print(){
        System.out.print(&quot;Hello!&quot;);
    }

    public static void main(String[] args){
        Main main = new Main();
        main.print();
    }
}
</code></pre>
<p>이렇게 해야 하는 이유는 바로 print()가 아직 메모리상에 올라가 있지 않은 메소드이기 때문이다.
명시적으로 <code>*.class</code> 파일에 올라가는 있지만 사용하려면 new를 통해 Heap 영역에 객체를 만들어 줘야한다. 그리고 Heap역역에 존재하는 객체의 주소값을 변수에 담아 Stack에서 관리하게 된다. </p>
<pre><code class="language-java">public class Main {

    public static void print(){
        System.out.println(&quot;Hello!&quot;);
    }

    public static void main(String[] args){
        print();
    }
}
</code></pre>
<p>하지만 이렇게 static으로 선언한 메소드라면 Main 클래스가 로드될 때 main함수와 같이 메모리에 적제된다. 그래서 이미 메모리상 존재하는 메소드 이므로 별도의 객체 생성 없이 사용할 수 있다. 이 부분에 대해 더 자세히 알아보자.</p>
<h3 id="3프로그램이-시작되면-메모리의-static-영역에-적재되고-프로그램이-종료될-때-해제된다">3.프로그램이 시작되면 메모리의 static 영역에 적재되고, 프로그램이 종료될 때 해제된다.</h3>
<p>프로그램이 시작되고 JVM이 구동되면 static이 붙은 멤버들은 자동으로 메모리의 static 영역에 생성된다.  자동으로 static 영역에 적재되므로 별도의 선언 없이 사용할 수 있는 것이다.</p>
<p>일반적인 메서드는 객체를 생성할 때 Heap에 올라가는데, 이는 GC(가비지 컬렉터)의 탐색 범위이므로 자동으로 관리되는 반면 static으로 선언된 정적 멤버들은 GC(가비지 컬렉터)의 관리 대상에서 제외되는 영역에 존재하기 때문에 자동으로 관리되지 않는다. 그래서 프로그램이 종료될 때 까지 사용하지 않더라도 일단 선언되어 있으면 해제되지 않는다.</p>
<p>그래서 과도한 static 사용은 메모리 낭비를 초례한다.</p>
<p>즉 정리하자면 정적 필드와 정적 메서드는 객체(인스턴스)에 소속된 멤버가 아니라 클래스에 고정된 멤버이다.</p>
<p>그렇기에 클래스 로더가 클래스를 로딩해서 메서드 메모리 영역(이른바 static 영역) 에 적재할 때 클래스별로 관리된다.</p>
<p>따라서 클래스의 로딩이 끝나는 즉시 바로 사용할 수 있다.</p>
<h3 id="4-static-메서드-내에서는-인스턴스-변수를-사용할-수-없다">4. static 메서드 내에서는 인스턴스 변수를 사용할 수 없다.</h3>
<p>static 메서드는 프로그램이 실행됨과 동시에 메모리에 올라가기 때문에 인스턴스 변수를 안에 쓸 수 없다. 인스턴스 변수는 생성과 호출이 이루어져야 하는데, static 메서드는 객체를 생성하기 전에 먼저 메모리에 올라가기 때문에 사용할 수 없다. </p>
<h2 id="static-퀴즈">static 퀴즈</h2>
<pre><code class="language-java">...
public static void main(String[] args){

    int ans = 0;
    ArrayList&lt;Integer&gt; list = new ArrayList&lt;&gt;();
    for(int i = 1; i&lt;=100; i++){
      list.add(i);
    }
    //1~100 까지 중 짝수인 수의 합을 ans에 구하고 싶어!

    list.stream().filter(e-&gt;e % 2 == 0).forEach(e-&gt;{
        ans += e;
    });

    System.out.println(ans);

}</code></pre>
<p>실행 결과는??
ans가 static으로 선언되어 있지 않아 컴파일 에러가 난다.
어떻게 이런 일이 일어나는 것 일까?
<img src="https://velog.velcdn.com/images/seo-faper/post/1c76819f-a8aa-47eb-89e0-3b750a28da4c/image.png" alt=""></p>
<p>왼쪽은 기존 for문, 오른쪽은 스트림의 forEach이다.
왼쪽은 흔히들 말하는 for-loop 방식, 오른쪽은 sequential stream 방식이다.
스트림은 내부 반복자(iterator)를 이용해 개발자 코드가 스트림에게 처리 방식만을 제공하고 나머지 연산은 스트림이 처리하는 원리이다.</p>
<p>내부 반복자의 경우, 기본적으로 원시 타입(primitive type)이 아닌 ArrayList, 또는 String 같이 참조 타입(wrapped type) 이기 때문에 메모리에 저장되는 영역은 Heap이다. for-loop의 경우 main함수 안에 선언된 모든 원시 타입 변수들이 Stack에서 관리되기 때문에 즉시 사용할 수 있는 것이고 스트림의 내부 반복자의 경우 Heap 영역에서 전달받은 처리 코드에 기반해 반복이 진행되고 결과값만이 Stack영역에 변수로서 호출될 수 있다. 그렇기 때문에 미리 사전에 접근할 수 있는 static으로 선언된 멤버만이 내부 연산자에서 사용될 수 있는 것이다. </p>
<pre><code class="language-java">...
public static void main(String[] args){

    int ans = 0;

    ArrayList&lt;Integer&gt; list = new ArrayList&lt;&gt;();
    for(int i = 1; i&lt;=100; i++){
      list.add(i);
    }
    //1~100 까지 중 짝수인 수의 합을 ans에 구하고 싶어!

    list.stream().filter(e-&gt;e % 2 == 0).forEach(e-&gt;{
        ans += e;
    });

    Main m = new Main();
    m.printNumber();

    System.out.println(ans);

}
public void printNumber(){
    System.out.println(ans);
}</code></pre>
<h2 id="내부-클래스와-static">내부 클래스와 static</h2>
<p>static은 클래스로더가 클래스를 읽을 때 클래스와 함께 올라간다고 했다.
그렇다면 클래스 안에 내부 클래스를 만들었을 때 static으로 선언하게 되면 어떻게 될까?</p>
<pre><code class="language-java">class MyClass {
    class A{}
    static class B{} //내부 클래스에 static이 붙는다면?
}</code></pre>
<p>클래스 B는 정적 멤버 클래스가 된다.</p>
<pre><code class="language-java">MyClass.B test1 = new MyClass.B();
MyClass.B test2 = new MyClass.B();

if(test1==test2) System.out.println(&quot;static으로 선언한 내부 클래스는 같은 참조.&quot;);
else System.out.println(&quot;아무리 그래도 인스턴스를 만들었는데 어떻게 같은 참조냐.&quot;);</code></pre>
<p>정답은 바로 false이다.
클래스는 인스턴스를 만들어 주는 설계도와 같기 때문에 그 자체가 인스턴스로 존재할 수는 없기 때문이다. 그럼 그냥 내부 클래스와 static으로 만든 내부 클래스가 같느냐? 그건 또 아니라는 것이다.</p>
<p>사실 우리가 만드는 모든 클래스들은 원래 static 영역에 올라가는 &#39;static&#39;이다. 내부 클래스에 static 키워드를 붙이면, 외부 인스턴스 없이 내부 클래스의 인스턴스를 바로 생성할 수 있다는 차이점이 존재 할 뿐 기능적 차이는 없다. </p>
<p>내부 클래스 A는 MyClass의 인스턴스가 존재해야지 만들어 질 수 있다. 그렇다면 내부 클래스 A는 자신을 만들어준 인스턴스에 대한 &#39;외부 참조&#39;를 갖게 된다. 그리고 이 참조는 숨겨져 있어서 &#39;숨은 외부 참조&#39; 라고 불린다. </p>
<p>그러나 B 처럼 내부 클래스에 static을 붙이게 되면 숨은 외부 참조를 차단할 수 있다.</p>
<pre><code class="language-java">MyClass mc = new MyClass();
MyClass.A test1 = mc.new A(); //test1은 &quot;mc에 대한 숨은 외부 참조&quot;를 갖는다.

MyClass.B test2 = new MyClass.B(); //test2는 그딴 거 없다.</code></pre>
<p>내부 클래스에서 이런 &#39;외부 참조&#39;가 존재할 시, 내부 클래스에서 외부 클래스의 메소드에 접근 할 수 있게 되는데 이는 다음과 같은 단점을 가진다.</p>
<pre><code class="language-java">class MyClass {
    void myMethod() {
        ...
    }

    class A{
        void Method_A() {
            MyClass.this.myMethod(); //숨은 외부 참조가 있기 때문에 가능
        }
    }

    static class B{
        void Method_B() {
            MyClass.this.myMethod(); //접근 불가, 컴파일 에러
        }
    }
}</code></pre>
<ol>
<li>외부 인스턴스에 대한 참조값을 담아야 하기 때문에 인스턴스 생성시 시간적, 공간적으로 성능이 낮아진다.</li>
<li>외부 인스턴스에 대한 참조가 존재하기 때문에, 외부 클래스와 내부 클래스가 연결된 관계에서 외부 클래스를 사용하지 않더라도 계속 남아있어 가비지 컬렉션이 인스턴스 수거를 하지 못하여 메모리 누수가 생길 수 있다.</li>
</ol>
<p>그러므로 내부 클래스를 만들때에는 static을 쓰는 것이 좋다.</p>
<h2 id="요약">요약</h2>
<ol>
<li>필드나 메소드를 static으로 선언할 때는 공통된 하나의 참조가 필요할 때 쓰자.</li>
<li>내부 클래스를 만들 때는 꼭 static으로 만들어 주자. </li>
</ol>
<h2 id="참고자료">참고자료</h2>
<p><a href="https://siyoon210.tistory.com/141">https://siyoon210.tistory.com/141</a>
<a href="https://sigridjin.medium.com/java-stream-api%EB%8A%94-%EC%99%9C-for-loop%EB%B3%B4%EB%8B%A4-%EB%8A%90%EB%A6%B4%EA%B9%8C-50dec4b9974b">https://sigridjin.medium.com/java-stream-api%EB%8A%94-%EC%99%9C-for-loop%EB%B3%B4%EB%8B%A4-%EB%8A%90%EB%A6%B4%EA%B9%8C-50dec4b9974b</a>
<a href="https://jooona.tistory.com/m/164">https://jooona.tistory.com/m/164</a>
<a href="https://minhamina.tistory.com/211">https://minhamina.tistory.com/211</a>
<a href="https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&amp;blogId=dungga77&amp;logNo=80015030666">https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&amp;blogId=dungga77&amp;logNo=80015030666</a>
<a href="https://sorjfkrh5078.tistory.com/108">https://sorjfkrh5078.tistory.com/108</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[스레드 with 자바 메모리 구조]]></title>
            <link>https://velog.io/@seo-faper/%EC%8A%A4%EB%A0%88%EB%93%9C-with-%EC%9E%90%EB%B0%94</link>
            <guid>https://velog.io/@seo-faper/%EC%8A%A4%EB%A0%88%EB%93%9C-with-%EC%9E%90%EB%B0%94</guid>
            <pubDate>Tue, 14 Feb 2023 14:47:35 GMT</pubDate>
            <description><![CDATA[<h2 id="용어-정리">용어 정리</h2>
<h3 id="processor">Processor</h3>
<p>프로세스를 실행시켜주는 하드웨어 유닛 (레지스터, 산술 논리 장치 등), 큰 의미로는 CPU 그 자체를 의미한다.
하나의 CPU를 지니면 싱글 Core, 여러개가 있으면 멀티 Core가 된다.
CPU가 하나이더라도 Multi-Threading이 가능한데, 이는 하나의 CPU가 Time Slicing 방식으로 여러개의 Thread를 번갈아가며 작업하는 형태로 진행된다. 이는 특별히 무거운 작업이 아닌 이상 눈치채지 못할 정도로 빠른 속도로 처리된다는 특징이 있다. 
<img src="https://velog.velcdn.com/images/seo-faper/post/963f1270-d00c-4681-bb0b-dda82356aca2/image.png" alt="">
여기 보면 CPU가 하나뿐이지만 Time Slice 기술을 통해 여러개의 프로세스를 돌리는 것을 볼 수 있다. </p>
<h3 id="process">Process</h3>
<p>메모리에 적재되어 프로세서에 의해 실행중인 프로그램 </p>
<h3 id="multi-process">Multi-Process</h3>
<p>프로세스가 메모리에 여러개 올라가 있는 상태 </p>
<h3 id="thread">Thread</h3>
<p>어떤 프로세스 내에서 실행되는 흐름의 단위 
Thread는 운영체제의 스케쥴러가 관리하는 최소 단위의 Instructuion이다.</p>
<h3 id="multi-thread">Multi-Thread</h3>
<p>하나의 프로세스에 둘 이상의 스레드가 동시에 진행되는 것 </p>
<p><img src="https://velog.velcdn.com/images/seo-faper/post/463ce229-2818-4b03-b8b2-ed3c8cf5dd41/image.png" alt=""></p>
<h3 id="thread-safety">Thread Safety</h3>
<p>멀티스레드 환경에서 충돌이 없는 구조.
 하나의 함수가 한 스레드로 부터 호출되어 실행 중일 때, 다른 스레드가 그 함수를 호출하여 동시에 함께 실행되더라도 각 스레드에서의 함수의 수행 결과가 올바르게 나오는 것을 말한다. 멀티 스레드 환경에서 하나의 프로세스는 여러 스레드로 구성될 수 있는데, 프로세스 자원은 스레드간에 공유된다. 그 때 발생하는 문제가 여러 스레드 간에서 참조하고 활용하는 데이터 구조나 객체들이 서로 꼬이고 값이 중간중간에 변경되면서 엉킬 위험이 있다. 이러한 위험이 제거된 상태를 Thread Safe라고 한다. </p>
<h2 id="queue">Queue</h2>
<p> CPU의 스케줄러는 큐를 이용해서 작업 순서를 정한다.
프로그램이 스레드를 구성하게 되면 실행코드를 담은 코드의 묶음인 Closure를 Queue에 싣고, 이 Queue를 Thread에 얹어서 실행(Dispatch) 하는 구조이다.  CPU 스케줄러의 Queue에는 하나씩 순서대로 진행되는 <strong>Serial Queue</strong>와 동시에 여러 작업이 수행되는 ** Concurrent Queue**가 있다. </p>
<h3 id="serial">Serial</h3>
<p>먼저 수행한 작업이 완전히 끝나야만 다음 작업이 진행된다. 
먼저 정의된 작업이 먼저 수행된다. (First In First Out, FIFO)
<img src="https://velog.velcdn.com/images/seo-faper/post/df0cd2db-fece-4a9f-b29f-e9fe94ee1f87/image.png" alt=""></p>
<h3 id="concurrency">Concurrency</h3>
<p>Serial과는 달리 여러 작업이 동시에 수행되는 것을 말한다. 
시작을 동시에 하는 것도 아니고 끝나는 것도 동시에 끝나는게 아니다. 
먼저 정의된 작업이 먼저 수행되기는 하지만 (FIFO) 먼저 수행되는 작업이 끝날 때까지 기다리는 것이 아니라 적절한 시점에서 동시에 실행된다.
Async, Multi Threading이 이에 해당된다. 
<img src="https://velog.velcdn.com/images/seo-faper/post/4a40d3bf-939e-4be0-8241-d805e9315c9b/image.png" alt=""></p>
<h3 id="parallelism">Parallelism</h3>
<p>여러 작업이 &#39;끊이지 않고&#39; 동시에 수행되는 것을 말한다. 
Concurrent 방식이 Parallel하게 수행될 수도 있고 아닐 수도 있다. 즉, 싱글 Core에서 Time Sharing 방식으로 Multi Threading을 할 때 처럼, 동시에 작업이 진행되더라도 한 작업 조금 하다가 바로 다른 작업을 하고, 다시 원래 작업을 처리하는 방식으로 진행될 수 있다. 이 경우는 Concurrent 하지만 Parallel하지는 않은 예시 이다. Concurrent가 작업들이 동시에 병렬적으로 수행되느냐의 구조(Structure)를 따지는 것 이라면, Parallel은 어떻게 수행되느냐의 수행(Execution) 관련인 것이다. </p>
<p><img src="https://velog.velcdn.com/images/seo-faper/post/901b4188-9ede-4f15-939b-a8ea9a5455fa/image.png" alt=""></p>
<p>즉, 멀티 프로세스는 연속성(Concurrency)와 병렬성(Parallelism)을 가진다는 특징이 있다.</p>
<h2 id="멀티-프로세스-vs-멀티-스레드">멀티 프로세스 vs 멀티 스레드</h2>
<p>멀티 스레드와 멀티 프로세스는 여러 흐름을 동시에 수행한다는 공통점이 있다.
그러나 멀티 프로세스는 각 프로세스가 독립적인 메모리를 별도로 가지지만 멀티 스레드는 각 스레드가 속한 프로세스 속에서 메모리를 공유한다는 차이점이 존재한다. </p>
<p>멀티 스레드는 쉽게 말해 프로그램의 로직에서 동시간에 다른 작업을 병행하는 기술이다.</p>
<p>멀티 스레드의 장점은 각 스레드가 자신이 속한 프로세스의 메모리를 공유하기 때문에 시스템의 자원 낭비가 적다는 것과 하나의 스레드가 작업할 때 다른 스레드가 별도의 작업을 할 수 있어서 사용자와의 응답성도 좋아진다. 
<img src="https://velog.velcdn.com/images/seo-faper/post/5227ea6b-30d1-4b5a-bb02-938731723c4b/image.png" alt=""></p>
<p>이렇게 main 함수에서 시작된 코드 흐름에서 동시에 어러개의 작업을 처리하는 방식이다.
즉 프로그램에서 병렬로 실행할 작업을 결정한 후, 각 작업별로 스레드를 나누어 실행해 주는 것이다. </p>
<h2 id="멀티-스레드의-메모리-관리-예제-코드">멀티 스레드의 메모리 관리 (예제 코드)</h2>
<p>앞서 말했듯 멀티 스레드는 하나의 프로세스 속에서 공통된 메모리를 공유한다.
자바에서 Thread와 Ruunable는 Thread Safey한 상태를 유지하게끔 도와주는 여러 메소드들이 있다. </p>
<p>우선 자바 프로그램에서 스레드의 안정성이 깨지는 상황에 대해 알아보자.</p>
<h3 id="스레드의-안정성이-깨지는-순간">스레드의 안정성이 깨지는 순간</h3>
<p>이것은 조회수 계산 프로그램이다.
특정 글을 조회하면 원래 조회수에 1을 더할 것이고, 여러 사용자가 동시에 접속할 수도 있기 때문에 멀티 스레드 환경에서 동작하게 만들었다.
이것은 100명의 사람이 100번 클릭했을 때를 가정한 코드이다.</p>
<pre><code class="language-java">public class CountingTest {
    public static void main(String[] args) {
        Count count = new Count();
        for (int i = 0; i &lt; 100; i++) {
            new Thread(){
                public void run(){
                    for (int j = 0; j &lt; 100; j++) {
                        System.out.println(count.view());
                    }
                }
            }.start();
        }
    }
}
class Count {
    private int count;
    public int view() {return count++;}
    public int getCount() {return count;}
}</code></pre>
<p>이렇게 되면 100 * 100이라서 10,000이 나올 것 같지만 실행해 보면 10,000이 되지 않는 모습을 볼 수 있다. 그 이유는 멀티스레드의 동시성(Concurrency) 때문이다.
view() 메소드는 count++을 수행하는데, count++는 사실 count = count + 1 과 같으므로
아래와 같은 로직을 거친다.</p>
<ol>
<li>count 변수의 값을 조회한다.</li>
<li>조회한 count 변수 값에 1을 더한 값을 저장한다. </li>
</ol>
<p>이러한 로직 사이에서 여러 스레드가 순서를 지키지 않고 count 변수에 접근을 하게 되면 그림과 같이** 동시성 이슈**가 발생한다.
<img src="https://velog.velcdn.com/images/seo-faper/post/b7503e9c-e6e6-4170-b440-7e91aaa0e086/image.png" alt=""></p>
<p>멀티 스레드가 과정 1을 동시에 실행하게 되면 실행된 스레드는 2개지만 최종적으로 저장되는 값은 101이 된다. </p>
<p>그래서 synchronized 키워드를 사용해 lock을 걸어 메소드에 스레드가 동시에 접근하는 것을 막는다. synchronized 키워드를 통해 동시에 접근하는 것을 막고 개발자가 의도한 대로 스케줄링되어 Queue에 의해 순차적으로 실행시킬 수 있다. 그러나, 여러 스레드가 하나의 자원에 동시에 read &amp; write를 할 때 항상 메모리에 접근하지 않는다.</p>
<h3 id="자원의-가시성-volatile">자원의 가시성 (volatile)</h3>
<p>운영체제는 여러 스레드가 하나의 자원에 동시에 read &amp; write를 할 때, 성능 향상을 위해 메모리가 아닌 CPU 캐시에 저장된 값을 사용한다. 그렇기 때문에 해당 데이터가 메모리에 저장된 실제 데이터와 항상 일치하는지 보장할 수 없다. 변수에 저장한 데이터를 읽었는데 이 데이터가 실제 데이터와 차이가 있을 수 있다는 것이다. 메인 메모리에 저장된 실제 자원의 값을 볼수 있는 개념을 자원의 가시성이라고 부르는데, 이 가시성을 확보하지 못한 경우, 앞서 말한 문제가 발생한다.</p>
<p>volatile 키워드는 이러한 CPU 캐시의 사용을 막는다. 해당 변수에 volatie를 붙여주면 이 변수는 캐시에 저장되는 대상에서 제외된다. 그러므로 매 번 메모리에 접근해서 실제 값을 읽어오도록 설정되어 캐시 사용으로 인한 데이터 불일치를 막아준다.</p>
<h3 id="불변-객체-immutable-instance">불변 객체 (Immutable Instance)</h3>
<p>스레드에 안전한 프로그래밍을 하는 방법 중 가장 안전한 방법은 바로 불변 객체를 만드는 것이다.
내부적인 상태가 변하지 않으니 여러 스레드가 동시에 참조해도 동시성 이슈가 발생하지 않는다.</p>
<p>아니면 그냥 내부 상태가 바뀌지 않도록 모든 변수를 final로 선언하면 된다. </p>
<h2 id="레퍼런스">레퍼런스</h2>
<p><a href="http://www.tcpschool.com/java/java_thread_concept">http://www.tcpschool.com/java/java_thread_concept</a>
<a href="https://blog.naver.com/jdub7138/220936452048">https://blog.naver.com/jdub7138/220936452048</a>
<a href="https://ko.wikipedia.org/wiki/%EB%A9%80%ED%8B%B0%EC%8A%A4%EB%A0%88%EB%94%A9">https://ko.wikipedia.org/wiki/%EB%A9%80%ED%8B%B0%EC%8A%A4%EB%A0%88%EB%94%A9</a>
<a href="https://webcache.googleusercontent.com/search?q=cache:dRhr1EFFiLIJ:https://imbf.github.io/computer-science(cs)/2020/10/18/CPU-Scheduling.html&amp;cd=4&amp;hl=ko&amp;ct=clnk&amp;gl=kr">https://webcache.googleusercontent.com/search?q=cache:dRhr1EFFiLIJ:https://imbf.github.io/computer-science(cs)/2020/10/18/CPU-Scheduling.html&amp;cd=4&amp;hl=ko&amp;ct=clnk&amp;gl=kr</a>
<a href="https://deveric.tistory.com/104">https://deveric.tistory.com/104</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[드림핵 리버싱 공부 - 퀴즈1]]></title>
            <link>https://velog.io/@seo-faper/%EB%93%9C%EB%A6%BC%ED%95%B5-%EB%A6%AC%EB%B2%84%EC%8B%B1-%EA%B3%B5%EB%B6%80-%ED%80%B4%EC%A6%881</link>
            <guid>https://velog.io/@seo-faper/%EB%93%9C%EB%A6%BC%ED%95%B5-%EB%A6%AC%EB%B2%84%EC%8B%B1-%EA%B3%B5%EB%B6%80-%ED%80%B4%EC%A6%881</guid>
            <pubDate>Wed, 08 Feb 2023 17:27:08 GMT</pubDate>
            <description><![CDATA[<h1 id="문제">문제</h1>
<p><img src="https://velog.velcdn.com/images/seo-faper/post/02d08032-ebd3-44f2-8c6a-5762b0644f2b/image.png" alt=""></p>
<pre><code class="language-c">[Register]
rcx = 0
rdx = 0
rsi = 0x400000
=======================
[Memory]
0x400000 | 0x67 0x55 0x5c 0x53 0x5f 0x5d 0x55 0x10
0x400008 | 0x44 0x5f 0x10 0x51 0x43 0x43 0x55 0x5d
0x400010 | 0x52 0x5c 0x49 0x10 0x47 0x5f 0x42 0x5c
0x400018 | 0x54 0x11 0x00 0x00 0x00 0x00 0x00 0x00
=======================
[code]
1: mov dl, BYTE PTR[rsi+rcx]
2: xor dl, 0x30
3: mov BYTE PTR[rsi+rcx], dl
4: inc rcx
5: cmp rcx, 0x19
6: jg end
7: jmp 1</code></pre>
<hr>
<h1 id="풀이">풀이</h1>
<pre><code class="language-c">cmp rcx, 0x19</code></pre>
<p>이 부분을 봤을 때 rcx가 19가 되면 아래의 <code>jg end</code>를 실행하고
아니라면 다시 1로 돌아가 반복하는 형태이다.</p>
<pre><code class="language-c">1: mov dl, BYTE PTR[rsi+rcx]</code></pre>
<p>BYTE는 1바이트를 말한다.
WORD는 2바이트, DWORD라면 4바이트를 가져온다. 
1바이트란 8Bit를 말하는데, 0x00로 표현할 수 있는 범위가 이에 해당된다.
그리고 PTR은 메모리 주소에 중괄호의 값을 대입에 가르키고 있는 값을 가져온다.
즉, <code>PTR[rsi + rcx]</code> 란 메모리에 rsi + rcx 의 주소값에 대입되어 있는 값을 가져온다는 뜻이다.</p>
<pre><code class="language-c">rcx = 0
rsi = 0x400000</code></pre>
<p>이므로 </p>
<pre><code class="language-c">0x400000 | 0x67 0x55 0x5c 0x53 0x5f 0x5d 0x55 0x10</code></pre>
<p>여기를 가져온다는 뜻이다. 하지만 BYTE이므로 1Byte만 가져온다.
그러므로 0x67만 가져올 것이다.
dl = <code>0x67</code></p>
<p>xor dl, 0x30 하면
xor 0x67, 0x30 
결과는 0x57, 문자로 바꾸면 W 이다.
그 후 <code>inc rcx</code> 를 만나 rcx는 0에서 1로 증감된다. 
<code>cmp rcx, 0x19</code>를 만나고 작기 때문에 다시 1로 돌아가 이를 반복한다.</p>
<p>0x67 xor 0x30 = 0x57(W)
0x55 xor 0x30 = 0x65(e)
0x5c xor 0x30 = 0x6c(l)</p>
<p>하나씩 계산기에 넣고 적기엔 너무 기니까 간단한 스크립트를 짜보자.</p>
<pre><code class="language-py">a = &quot;0x67 0x55 0x5c 0x53 0x5f 0x5d 0x55 0x10 0x44 0x5f 0x10 0x51 0x43 0x43 0x55 0x5d 0x52 0x5c 0x49 0x10 0x47 0x5f 0x42 0x5c 0x54 0x11 0x00 0x00 0x00 0x00 0x00 0x00&quot;
for i in a.split(&quot; &quot;):
    print(chr(int(i,16) ^ 0x30))</code></pre>
<p>0~19 까지 이므로 20개 만큼 잘라서 문자열로 넣어주고 연산해 주면 된다.</p>
<pre><code>W
e
l
c
o
m
e

t
o

a
s
s
e
m
b
l
y

w
o
r
l
d
!</code></pre><p>Welcome to assembly world!
이다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[이것이 자바다 18일차 - Chapter20 데이터베이스 입출력]]></title>
            <link>https://velog.io/@seo-faper/%EC%9D%B4%EA%B2%83%EC%9D%B4-%EC%9E%90%EB%B0%94%EB%8B%A4-18%EC%9D%BC%EC%B0%A8-Chapter20-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4-%EC%9E%85%EC%B6%9C%EB%A0%A5</link>
            <guid>https://velog.io/@seo-faper/%EC%9D%B4%EA%B2%83%EC%9D%B4-%EC%9E%90%EB%B0%94%EB%8B%A4-18%EC%9D%BC%EC%B0%A8-Chapter20-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4-%EC%9E%85%EC%B6%9C%EB%A0%A5</guid>
            <pubDate>Thu, 02 Feb 2023 20:51:56 GMT</pubDate>
            <description><![CDATA[<h2 id="jdbc-개요">JDBC 개요</h2>
<p>자바는 데이터베이스와 연결해서 데이터 입출력 작업을 할 수 있도록 JDBC(JavaDatabaseConnectivity)라이브러리를 제공한다.</p>
<p>JDBC는 데이터베이스 관리시스템 (DBMS)의 종류와 상관없이 동일하게 사용할 수 있는 클래스와 인터페이스로 구성되어있다.</p>
<p>DBMS의 종류에는 Oracle, MySql, MariaDB 등이 있는데, 여기선 MySql을 사용할 것이다.</p>
<p>JDBC에 포함되어 있는 클래스와 인터페이스들은 다음과 같다.</p>
<h3 id="drivermanager">DriverManager</h3>
<ul>
<li>JDBC Driver를 관리하며 DB와 연결해서 Connection 구현 객체를 생성한다.</li>
<li>여기서 JDBD Driver란 MySql을 쓰기 때문에 MySql이 된다.</li>
</ul>
<h3 id="connection">Connection</h3>
<ul>
<li>Connection 인터페이스는 Statement, PreparedStatement, CallableStatement 구현 객체를 생성하며, 트랜잭션 처리 및 DB 연결을 끊을 때 사용한다.</li>
</ul>
<h3 id="statement">Statement</h3>
<ul>
<li>SQL의 DDL과 DML을 실행할 때 사용한다. 주로 변경되지 않는 정적 SQl문을 실행할 때 사용한다.</li>
</ul>
<h3 id="preparedstatement">PreparedStatement</h3>
<ul>
<li>PreparedStatement는 Statement와 동일하게 SQL의 DDL, DML 문을 실행할 때 사용한다. 차이점은 매개변수화된 SQL문을 쓸 수 있기 때문에 편리성과 보안성이 좋다.</li>
</ul>
<h3 id="callablestatement">CallableStatement</h3>
<ul>
<li>DB에 저장되어 있는 프로시저와 함수를 호출할 때 사용한다.</li>
</ul>
<h3 id="resultset">ResultSet</h3>
<ul>
<li>DB에서 가져온 데이터를 읽을 때 사용한다.</li>
</ul>
<h2 id="db-연결">DB 연결</h2>
<pre><code class="language-java">package ch20.mysql.sec05;

import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.DriverManager;
import java.sql.SQLException;

public class ConnectionExample {
    public static void main(String[] args) {
        Connection conn = null;

        try {
//JDBC Driver 등록
            Class.forName(&quot;com.mysql.cj.jdbc.Driver&quot;);

            conn = DriverManager.getConnection(
                     &quot;jdbc:mysql://localhost:3306/thisisjava&quot;,
                     &quot;java&quot;,
                     &quot;mysql&quot;
                     );

             System.out.println(&quot;연결 성공&quot;);

             } catch (ClassNotFoundException e) {
             e.printStackTrace();
             } catch (SQLException e) {
             e.printStackTrace();
             } finally {
             if(conn != null) {
                 try {
                     //연결 끊기
                     conn.close();
                     System.out.println(&quot;연결 끊기&quot;);
                     } catch (SQLException e) {}
                 }
        }
    }
}
</code></pre>
<h2 id="데이터-저장">데이터 저장</h2>
<pre><code class="language-sql">INSERT INTO users (userid, username, userpassword, userage, useremail)
VALUES (&#39;winter&#39;, &#39;한겨울&#39;, &#39;12345&#39;, 25, &#39;winter@mycompany.com&#39;)</code></pre>
<p>이런 쿼리문이 있다.
이 것을 ?(물음표)로 대체한 매개변수화된 INSERT 문으로 변경하면 다음과 같다.</p>
<pre><code class="language-sql">INSERT INTO users (userid, username, userpassword, userage, useremail)
VALUES (?, ?, ?, ?, ?)</code></pre>
<p>그리고 이걸 String 타입으로 바꾸면 다음과 같다.</p>
<pre><code class="language-java">String sql = new StringBuilder()
.append(&quot;INSERT INTO users (userid, username, userpassword, userage, useremail) &quot;)
.append(&quot;VALUES (?, ?, ?, ?, ?)&quot;)
.toString();</code></pre>
<p>매개변수화 SQL을 실행하려면 PreparedStatement을 쓰면 된다.</p>
<pre><code class="language-java">PreparedStatement pstmt = conn.prepareStatement(sql);

pstmt.setString(1, &quot;winter&quot;);
pstmt.setString(2, &quot;한겨울&quot;);
pstmt.setString(3, &quot;12345&quot;);
pstmt.setInt(4, 25);
pstmt.setString(5, &quot;winter@mycompany.com&quot;);</code></pre>
<pre><code class="language-java">package ch20.mysql.sec06;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;

public class UserInsertExample {
    public static void main(String[] args) {
        Connection conn = null;

        try {
        //JDBC Driver 등록
            Class.forName(&quot;com.mysql.cj.jdbc.Driver&quot;);

            conn = DriverManager.getConnection(
                    &quot;jdbc:mysql://localhost:3306/thisisjava&quot;,
                    &quot;root&quot;,
                    &quot;mysql&quot;
            );

            System.out.println(&quot;연결 성공&quot;);
            String sql = &quot;&quot; +
                    &quot;INSERT INTO users (userid, username, userpassword, userage, useremail) &quot; +
                    &quot;VALUES (?, ?, ?, ?, ?)&quot;;

            PreparedStatement pstmt = conn.prepareStatement(sql);
            pstmt.setString(1,&quot;winter&quot;);
            pstmt.setString(2, &quot;한겨울&quot;);
            pstmt.setString(3, &quot;12345&quot;);
            pstmt.setInt(4, 25);
            pstmt.setString(5, &quot;winter@mycompany.com&quot;);

            int rows = pstmt.executeUpdate();
            System.out.println(&quot;저장된 행 수 : &quot;+rows);
            pstmt.close();

        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            if(conn != null) {
                try {
                    //연결 끊기
                    conn.close();
                    System.out.println(&quot;연결 끊기&quot;);
                } catch (SQLException e) {}
            }
        }
    }
}
</code></pre>
<h2 id="데이터-수정">데이터 수정</h2>
<p>게시판에 글을 썼다고 가정하면 다음과 같이 정보를 입력할 수 있다.</p>
<pre><code class="language-java">package ch20.mysql.sec06;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.sql.*;

public class BoardWithFileInsertExample {
    public static void main(String[] args) {
        Connection conn = null;

        try {
            //JDBC Driver 등록
            Class.forName(&quot;com.mysql.cj.jdbc.Driver&quot;);

            conn = DriverManager.getConnection(
                    &quot;jdbc:mysql://localhost:3306/thisisjava&quot;,
                    &quot;root&quot;,
                    &quot;mysql&quot;
            );

            String sql = &quot;&quot; +
                    &quot;INSERT INTO boards (btitle, bcontent, bwriter, bdate, bfilename, bfiledata) &quot; +
             &quot;VALUES (?, ?, ?, now(), ?, ?)&quot;;

            PreparedStatement pstmt = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
            pstmt.setString(1,&quot;눈 오는 날&quot;);
            pstmt.setString(2,&quot;함박눈이 내려요.&quot;);
            pstmt.setString(3,&quot;winter&quot;);
            pstmt.setString(4,&quot;snowman.jpg&quot;);
            pstmt.setBlob(5, new FileInputStream(&quot;src/ch20/mysql/sec06/snowman.jpg&quot;));
            int rows = pstmt.executeUpdate();
            System.out.println(&quot;수정된 행 수 : &quot;+rows);


             if(rows == 1) {
                ResultSet rs = pstmt.getGeneratedKeys();
                if(rs.next()) {
                     int bno = rs.getInt(1);
                     System.out.println(&quot;저장된 bno: &quot; + bno);
                     }
                 rs.close();
             }
        } catch (ClassNotFoundException | FileNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            if(conn != null) {
                try {
                    //연결 끊기
                    conn.close();
                    System.out.println(&quot;연결 끊기&quot;);
                } catch (SQLException e) {}
            }
        }
    }
}
</code></pre>
<p>기존에 데이터 저장 기능(INSERT)을 통해 테이블에 값이 담겨 있는 상태라면
UPDATE 키워드를 통해 수정 할 수 있다.</p>
<pre><code class="language-java">package ch20.mysql.sec07;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;

public class BoardUpdateExample {
    public static void main(String[] args) {
        Connection conn = null;

        try {
            //JDBC Driver 등록
            Class.forName(&quot;com.mysql.cj.jdbc.Driver&quot;);

            conn = DriverManager.getConnection(
                    &quot;jdbc:mysql://localhost:3306/thisisjava&quot;,
                    &quot;root&quot;,
                    &quot;mysql&quot;
            );

            String sql = new StringBuilder()
                    .append(&quot;UPDATE boards SET &quot;)
                    .append(&quot;btitle=?, &quot;)
                    .append(&quot;bcontent=?, &quot;)
                    .append(&quot;bfilename=?, &quot;)
                    .append(&quot;bfiledata=? &quot;)
                    .append(&quot;WHERE bno=?&quot;)
                    .toString();

            PreparedStatement pstmt = conn.prepareStatement(sql);
            pstmt.setString(1,&quot;눈사람&quot;);
            pstmt.setString(2,&quot;눈으로 만든 사람&quot;);
            pstmt.setString(3,&quot;snowman.jpg&quot;);
            pstmt.setBlob(4, new FileInputStream(&quot;src/ch20/mysql/sec07/snowman.jpg&quot;));
            pstmt.setInt(5,2);
            int rows = pstmt.executeUpdate();
            System.out.println(&quot;수정된 행 수 : &quot;+rows);
        } catch (ClassNotFoundException | FileNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            if(conn != null) {
                try {
                    //연결 끊기
                    conn.close();
                    System.out.println(&quot;연결 끊기&quot;);
                } catch (SQLException e) {}
            }
        }
    }

}
</code></pre>
<p>WHERE bno= ? 를 통해 특정 게시물 번호를 정하고 해당 번호의 칼럼을 지정된 값으로 바꾸는 코드 이다.</p>
<h2 id="데이터-삭제">데이터 삭제</h2>
<p>DELETE 키워드를 통해 해당 칼럼을 삭제 할 수 있다.</p>
<pre><code class="language-java">package ch20.mysql.sec08;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;

public class BoardDeleteExample {
    public static void main(String[] args) {
        Connection conn = null;

        try {
//JDBC Driver 등록
            Class.forName(&quot;com.mysql.cj.jdbc.Driver&quot;);

            conn = DriverManager.getConnection(
                    &quot;jdbc:mysql://localhost:3306/thisisjava&quot;,
                    &quot;root&quot;,
                    &quot;mysql&quot;
            );

            String sql = &quot;DELETE FROM boards WHERE bwriter=?&quot;;

            PreparedStatement psmt = conn.prepareStatement(sql);
            psmt.setString(1,&quot;winter&quot;);

            int rows = psmt.executeUpdate();
            System.out.println(&quot;삭제된 행 수 : &quot;+rows);

            psmt.close();

        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            if(conn != null) {
                try {
                    //연결 끊기
                    conn.close();
                    System.out.println(&quot;연결 끊기&quot;);
                } catch (SQLException e) {}
            }
        }
    }
}
</code></pre>
<h2 id="데이터-읽기">데이터 읽기</h2>
<p>SQl 문이 추가, 수정, 삭제 일 때는 executeUpdate() 메소드를 호출하지만, 데이터를 가져오는 SELECT 일 때는 executeQuery() 메소드를 써야 한다.</p>
<pre><code class="language-java">ResultSet rs = pstmt.executeQuery();</code></pre>
<h2 id="resultset-구조">ResultSet 구조</h2>
<p>ResultSet 이란 SELECT 문에 의해 선택된 칼럼으로 구성된 행(row)의 집합이다.</p>
<pre><code class="language-sql">SELECT userid, username, userage FROM users</code></pre>
<p>예를 들어 이 users라는 테이블에서 저 쿼리문을 실행하면 다음과 같이 구성된다.</p>
<p><img src="https://velog.velcdn.com/images/seo-faper/post/27b44cf1-5697-465d-bae5-48436fa388ed/image.png" alt=""></p>
<p>ResultSet의 특징은 커서cursor가 있는 행의 데이터만 읽을 수 있다는 것이다. 
여기서 커서는 행을 가리키는 포인터를 말한다. 
ResultSet은 실제 가져온 데이터 행의 앞과 뒤에 beforeFirst 행과 afterLast 행이 붙는데, 최초 커서는 beforeFirst를 가리킨다. 따라서 첫 번째 데이터 행인 first 행을 읽으려면 커서를 이동시켜야 한다. 이때 next() 메소드를 사용한다.</p>
<pre><code class="language-java">boolean result = rs.next();</code></pre>
<p>행이 하나만 있을 경우에는 다음과 같이 쓸 수 있다.</p>
<pre><code class="language-java">ResultSet rc = pstmt.executeQuery();
if(rs.next()) {
    //첫번째 데이터 행 처리
} else {
    //afterLast 행으로 이동했을 경우
}</code></pre>
<p>n개의 다이터 행을 가져올 경우</p>
<pre><code class="language-java">ResultSet rs = pstmt.executeQuery();
while(rs.next()) {
 //last 행까지 이동하면서 데이터 행 처리
}
 //afterLast 행으로 이동했을 경우</code></pre>
<p>SELECT문을 사용하고 난 뒤는 close()를 통해 사용한 메모리를 해제하는 것이 좋다.
SELECT문은 조건식에 따라 많은 데이터 행을 저장할 수 있기 때문이다.</p>
<pre><code class="language-java">package ch20.mysql.sec09.exam01;

import java.sql.*;

public class UserSelectExample {
    public static void main(String[] args) {
        Connection conn = null;

        try {
//JDBC Driver 등록
            Class.forName(&quot;com.mysql.cj.jdbc.Driver&quot;);

            conn = DriverManager.getConnection(
                    &quot;jdbc:mysql://localhost:3306/thisisjava&quot;,
                    &quot;root&quot;,
                    &quot;mysql&quot;
            );

            String sql = &quot;&quot;+
                    &quot;SELECT userid, username, userpassword, userage, useremail &quot;+
                    &quot;FROM users &quot;+
                    &quot;WHERE userid=?&quot;;

            PreparedStatement psmt = conn.prepareStatement(sql);
            psmt.setString(1,&quot;winter&quot;);

            ResultSet rs = psmt.executeQuery();
            if(rs.next()){
                User user = new User();
                user.setUserId(rs.getString(&quot;userid&quot;));
                user.setUserName(rs.getString(&quot;username&quot;));
                user.setUserPassword(rs.getString(&quot;userpassword&quot;));
                user.setUserAge(rs.getInt(4));
                user.setUserEmail(rs.getString(5));
                System.out.println(user);
            }else{
                System.out.println(&quot;사용자 아이디가 존재하지 않음&quot;);
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            if(conn != null) {
                try {
                    //연결 끊기
                    conn.close();
                    System.out.println(&quot;연결 끊기&quot;);
                } catch (SQLException e) {}
            }
        }
    }
}
</code></pre>
<p>SELECT로 데이터를 가져와서 자바 객체로 만드는 코드이다.</p>
<h2 id="트랜잭션-처리">트랜잭션 처리</h2>
<p>트랜잭션은 기능 처리의 최소 단위를 말한다. 
최소 단위란, 소작업들을 분리할 수 없으며 전체를 하나로 본다는 개념이다.
트랜잭션은 소작업들이 모두 성공하거나 모두 실패해야 한다.</p>
<p><img src="https://velog.velcdn.com/images/seo-faper/post/81de5a61-13ee-43be-99ff-2b1de603d82d/image.png" alt=""></p>
<p>예를 들어 계좌 이체는 출금 작업과 입금 과정으로 구성된 트랜잭션이다. 출금과 입금 작업 중 하나만 성공할 순 없으며 모두 성공하거나 모두 실패해야 한다.</p>
<pre><code class="language-sql">//출금 작업
UPDATE accounts SET balance=balance-이체금액 WHERE ano=출금계좌번호

//입금 작업
UPDATE accounts SET balance=balance+이체금액 WHERE ano=입금계좌번호</code></pre>
<p>이 2개의 UPDATE문은 모두 성공하거나 모두 실패해야 한다. 
트랜잭션을 위한 일반적인 코드 작성 패턴은 다음과 같다.</p>
<pre><code class="language-java">Connection conn = null;
try {
        //트랜잭션 시작 ----------------------------------------------------
          //자동 커밋 기능 끄기
         conn.setAutoCommit(false);
         //소작업 처리
             …
          //소작업 처리
             …
         //커밋 -&gt; 모두 성공 처리
         conn.commit();
        //트랜잭션 종료 ----------------------------------------------------
    } catch (Exception e) {
    try {
         //롤백 -&gt; 모두 실패 처리
         conn.rollback();
    } catch (SQLException e1) {
     e1.printStackTraced();
     finally {
        if(conn != null) {
             try {
             //원래대로 자동 커밋 기능 켜기
             conn.setAutoCommit(true);
             //연결 끊기
             conn.close();
         } catch (SQLException e) {}
    }
}</code></pre>
<h2 id="게시판-구현">게시판 구현</h2>
<pre><code class="language-java">package ch20.mysql.sec12;

import java.util.Scanner;

public class BoardExample1 {
    private Scanner sc = new Scanner(System.in);
    public void list(){
        System.out.println(&quot;[게시물 목록]&quot;);
        System.out.println(&quot;-------------------------------------------------------&quot;);
        System.out.printf(&quot;%-6s%-12s%-16s%-40s\n&quot;, &quot;no&quot;,&quot;writer&quot;,&quot;date&quot;,&quot;title&quot;);
        System.out.println(&quot;-------------------------------------------------------&quot;);
        System.out.printf(&quot;%-6s%-12s%-16s%-40s \n&quot;,
                &quot;1&quot;,&quot;winter&quot;,&quot;2022.01.27&quot;,&quot;게시판에 오신 것을 환영합니다.&quot;);
        System.out.printf(&quot;%-6s%-12s%-16s%-40s \n&quot;,
                &quot;2&quot;,&quot;winter&quot;,&quot;2022.01.27&quot;,&quot;올 겨울은 몹시 춥다.&quot;);
        mainMenu();
    }
    public void mainMenu(){
        System.out.println(&quot;------------------------------------------------------&quot;);
        System.out.println(&quot;메인 메뉴: 1.Create | 2.Read | 3.Clear | 4.Exit&quot;);
        System.out.print(&quot;메뉴 선택: &quot;);
        System.out.println(&quot;&quot;);
        String sel = sc.nextLine();
        switch (sel){
            case &quot;1&quot; -&gt; create();
            case &quot;2&quot; -&gt; read();
            case &quot;3&quot; -&gt; clear();
            case &quot;4&quot; -&gt; exit();
        }
    }

    private void exit() {
        System.out.println(&quot;***exit() 실행됨***&quot;);
        list();
    }

    private void clear() {
        System.out.println(&quot;***clear() 실행됨***&quot;);
        list();
    }

    private void read() {
        System.out.println(&quot;***read() 실행됨***&quot;);
        list();
    }

    private void create() {
        System.out.println(&quot;***create() 실행됨***&quot;);
        list();
    }

    public static void main(String[] args) {
        BoardExample1 boardExample1 = new BoardExample1();
        boardExample1.list();
    }
}</code></pre>
<p>기본적인 뼈대를 만들어 준다.</p>
<pre><code class="language-java">package ch20.mysql.sec12;

import java.sql.*;
import java.util.Scanner;

public class BoardExample3 {
    private Scanner sc = new Scanner(System.in);
    private Connection conn;

    public BoardExample3(){
        try {
//JDBC Driver 등록
            Class.forName(&quot;com.mysql.cj.jdbc.Driver&quot;);

            conn = DriverManager.getConnection(
                    &quot;jdbc:mysql://localhost:3306/thisisjava&quot;,
                    &quot;root&quot;,
                    &quot;mysql&quot;
            );

        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        } 
    }
    public void list(){
        System.out.println(&quot;[NEW 게시물 목록]&quot;);
        System.out.println(&quot;-------------------------------------------------------&quot;);
        System.out.printf(&quot;%-6s%-12s%-16s%-40s\n&quot;, &quot;no&quot;,&quot;writer&quot;,&quot;date&quot;,&quot;title&quot;);
        System.out.println(&quot;-------------------------------------------------------&quot;);
        try{
            String sql = &quot;&quot;+
                    &quot;SELECT bno, btitle, bcontent, bwriter, bdate &quot;+
                    &quot;FROM boards &quot;+
                    &quot;ORDER BY bno DESC&quot;;
            PreparedStatement pstmt = conn.prepareStatement(sql);
            ResultSet rs = pstmt.executeQuery();
            while (rs.next()){
                Board board = new Board();
                board.setBno(rs.getInt(&quot;bno&quot;));
                board.setBtitle(rs.getString(&quot;btitle&quot;));
                board.setBcontent(rs.getString(&quot;bcontent&quot;));
                board.setBwriter(rs.getString(&quot;bwriter&quot;));
                board.setBdate(rs.getDate(&quot;bdate&quot;));
                System.out.printf(&quot;%-6s%-12s%-16s%-40s \n&quot;,
                        board.getBno(),
                        board.getBwriter(),
                        board.getBdate(),
                        board.getBtitle());
            }
            rs.close();
            pstmt.close();
        }catch(SQLException e){
            e.printStackTrace();
        }
        mainMenu();
    }
    public void mainMenu(){
        System.out.println(&quot;------------------------------------------------------&quot;);
        System.out.println(&quot;메인 메뉴: 1.Create | 2.Read | 3.Clear | 4.Exit&quot;);
        System.out.print(&quot;메뉴 선택: &quot;);
        System.out.println(&quot;&quot;);
        String sel = sc.nextLine();
        switch (sel){
            case &quot;1&quot; -&gt; create();
            case &quot;2&quot; -&gt; read();
            case &quot;3&quot; -&gt; clear();
            case &quot;4&quot; -&gt; exit();
        }
    }

    private void exit() {
        System.out.println(&quot;***exit() 실행됨***&quot;);
        list();
    }

    private void clear() {
        System.out.println(&quot;***clear() 실행됨***&quot;);
        list();
    }

    private void read() {
        System.out.println(&quot;***read() 실행됨***&quot;);
        list();
    }

    private void create() {
        System.out.println(&quot;***create() 실행됨***&quot;);
        list();
    }

    public static void main(String[] args) {
        BoardExample3 boardExample3 = new BoardExample3();
        boardExample3.list();
    }
}
</code></pre>
<p>그 다음 DB를 연결해 주고 게시글 출력 부분을 DB에서 가져오는 걸로 바꿔준다.</p>
<pre><code class="language-java"> private void create() {
        Board board = new Board();
        System.out.println(&quot;[새 게시물 입력]&quot;);
        System.out.print(&quot;제목: &quot;);
        board.setBtitle(sc.nextLine());
        System.out.print(&quot;내용: &quot;);
        board.setBcontent(sc.nextLine());
        System.out.print(&quot;작성자: &quot;);
        board.setBwriter(sc.nextLine());

        System.out.println(&quot;------------------------------------------------------&quot;);
        System.out.println(&quot;보조 메뉴 : 1. OK | 2. Cancel&quot;);
        System.out.print(&quot;메뉴 선택 : &quot;);
        String menuNo = sc.nextLine();
        if(menuNo.equals(&quot;1&quot;)){
            try{
                String sql = &quot;&quot;+
                        &quot;INSERT INTO boards (btitle, bcontent, bwriter, bdate) &quot; +
                        &quot;VALUES (?, ?, ?, now())&quot;;
                PreparedStatement psmt = conn.prepareStatement(sql);
                psmt.setString(1,board.getBtitle());
                psmt.setString(2,board.getBcontent());
                psmt.setString(3,board.getBwriter());
                psmt.executeUpdate();
                psmt.close();
            }catch(Exception e){

            }
        }
        list();
    }</code></pre>
<p>글 쓰기 메소드를 완성해 준다.</p>
<pre><code class="language-java"> private void read() {
        System.out.println(&quot;[게시물 읽기]&quot;);
        System.out.print(&quot;bno: &quot;);
        int bno = Integer.parseInt(sc.nextLine());
        try{
            String sql = &quot;&quot; +
                     &quot;SELECT bno, btitle, bcontent, bwriter, bdate &quot; +
                     &quot;FROM boards &quot; +
                     &quot;WHERE bno= ?&quot;;
            PreparedStatement pstmt = conn.prepareStatement(sql);
            pstmt.setInt(1, bno);
             ResultSet rs = pstmt.executeQuery();
            if(rs.next()) {
                 Board board = new Board();
                 board.setBno(rs.getInt(&quot;bno&quot;));
                 board.setBtitle(rs.getString(&quot;btitle&quot;));
                 board.setBcontent(rs.getString(&quot;bcontent&quot;));
                 board.setBwriter(rs.getString(&quot;bwriter&quot;));
                 board.setBdate(rs.getDate(&quot;bdate&quot;));
                 System.out.println(&quot;#############&quot;);
                 System.out.println(&quot;번호: &quot; + board.getBno());
                 System.out.println(&quot;제목: &quot; + board.getBtitle());
                 System.out.println(&quot;내용: &quot; + board.getBcontent());
                 System.out.println(&quot;작성자: &quot; + board.getBwriter());
                 System.out.println(&quot;날짜: &quot; + board.getBdate());
                 System.out.println(&quot;#############&quot;);
                 System.out.println(&quot;----------------------&quot;);
                 System.out.println(&quot;보조 메뉴 : 1.Update | 2.Delete | 3.List&quot;);
                 System.out.print(&quot;메뉴 선택&quot;);
                 String menuNo = sc.nextLine();
                if (menuNo.equals(&quot;1&quot;)) {
                    update(board);
                }else if(menuNo.equals(&quot;2&quot;)){
                    delete(board);
                }else{

                }
            }
             rs.close();
             pstmt.close();
            }    catch(Exception e){}
        list();
    }
    private void delete(Board board){
        try{
            String sql = &quot;DELETE FROM boards WHERE bno=?&quot;;
            PreparedStatement pstmt = conn.prepareStatement(sql);
            pstmt.setInt(1,board.getBno());
            pstmt.executeUpdate();
            pstmt.close();
        }catch(Exception e){
            exit();
        }
        list();
    }
    private void update(Board board) {

        System.out.println(&quot;[수정 내용 입력]&quot;);
         System.out.print(&quot;제목: &quot;);
         board.setBtitle(sc.nextLine());
         System.out.print(&quot;내용: &quot;);
         board.setBcontent(sc.nextLine());
         System.out.print(&quot;작성자: &quot;);
         board.setBwriter(sc.nextLine());

         //보조 메뉴 출력
         System.out.println(&quot;------------------------------------------------------&quot;);
         System.out.println(&quot;보조 메뉴: 1.Ok | 2.Cancel&quot;);
         System.out.print(&quot;메뉴 선택: &quot;);
         String menuNo = sc.nextLine();
         if(menuNo.equals(&quot;1&quot;)) {

            try {
                String sql = &quot;&quot; +
                         &quot;UPDATE boards SET btitle= ?, bcontent= ?, bwriter= ? &quot; +
                         &quot;WHERE bno= ?&quot;;
                 PreparedStatement pstmt = conn.prepareStatement(sql);
                 pstmt.setString(1, board.getBtitle());
                 pstmt.setString(2, board.getBcontent());
                 pstmt.setString(3, board.getBwriter());
                 pstmt.setInt(4, board.getBno());
                 pstmt.executeUpdate();
                 pstmt.close();
            } catch (Exception e) {
                 e.printStackTrace();
                 exit();
                 }
             }
         list();
    }</code></pre>
<p>게시글 번호를 누르면 해당 게시글의 내용을 읽을 수 있게 만들어 준다.
또한 게시글을 읽었을 때 해당 게시글을 수정하거나 삭제하는 기능도 넣어 준다.</p>
<pre><code class="language-java"> private void clear() {
        System.out.println(&quot;[게시물 전체 삭제]&quot;);
        System.out.println(&quot;-------------------------------------------------------&quot;);
        System.out.println(&quot;보조 메뉴: 1.Ok | 2.Cancel&quot;);
        System.out.print(&quot;메뉴 선택: &quot;);
        String menuNo = sc.nextLine();
        if(menuNo.equals(&quot;1&quot;)) {
             try {
                 String sql = &quot;TRUNCATE TABLE boards&quot;;
                 PreparedStatement pstmt = conn.prepareStatement(sql);
                 pstmt.executeUpdate();
                 pstmt.close();
                 } catch (Exception e) {
                 e.printStackTrace();
                 exit();
                 }
             }
        list();
    }

    private void exit() {
        if(conn != null){
            try{
                conn.close();
            }catch(SQLException e){

            }
        }
        System.out.println(&quot;** 게시판 종료 **&quot;);
        System.exit(0);
    }</code></pre>
<p>나머지 clear()와 exit()를 구현해 주면 게시판 완성이다.</p>
<h2 id="연습문제">연습문제</h2>
<p><img src="https://velog.velcdn.com/images/seo-faper/post/ee9384fa-f88c-4d39-bdbd-bc96d3e7c6f6/image.png" alt=""></p>
<p>4번 
<img src="https://velog.velcdn.com/images/seo-faper/post/5fa71d4d-e82d-4bc5-9b78-2554778fa3c1/image.png" alt="">
3번 테이블 이름은 조회해서 보면 됩니다.
<img src="https://velog.velcdn.com/images/seo-faper/post/a3f4fe8f-1ab8-44b5-8c19-46489dd1cc02/image.png" alt="">
2-&gt;1-&gt;4-&gt;3</p>
<p><img src="https://velog.velcdn.com/images/seo-faper/post/f08ff458-6dfd-4f2f-bf8c-eb798dfe826c/image.png" alt="">
4번, 1번 부터 시작합니다.</p>
<p><img src="https://velog.velcdn.com/images/seo-faper/post/3e2ca4d6-eb41-4255-b76e-938a7edd86de/image.png" alt=""></p>
<p>2번 afterLast가 되면 false가 리턴됩니다.</p>
<p><img src="https://velog.velcdn.com/images/seo-faper/post/ed3f74f6-5bf0-41ba-9049-a224c23b0315/image.png" alt=""></p>
<p>3번</p>
<p><img src="https://velog.velcdn.com/images/seo-faper/post/aef9e2e0-7545-44bf-8c36-ecc5326bf7ae/image.png" alt="">
3번, 내부 작업은 모두 통일된 상태여야 합니다.</p>
<p><img src="https://velog.velcdn.com/images/seo-faper/post/ead02cca-c0c9-4874-b766-a62c3ae04023/image.png" alt=""></p>
<pre><code class="language-java">private void join() {
        User user = new User();
        System.out.println(&quot;[새로운 사용자 등록]&quot;);
        System.out.print(&quot;아이디: &quot;);
        user.setUserId(sc.nextLine());
        System.out.print(&quot;이름: &quot;);
        user.setUserName(sc.nextLine());
        System.out.print(&quot;비밀번호: &quot;);
        user.setUserPassword(sc.nextLine());
        System.out.println(&quot;나이: &quot;);
        user.setUserAge(Integer.parseInt(sc.nextLine()));
        System.out.println(&quot;이메일: &quot;);
        user.setUserEmail(sc.nextLine());
        System.out.println(&quot;------------------------------------------------------&quot;);
        System.out.println(&quot;보조 메뉴 : 1. OK | 2. Cancel&quot;);
        System.out.print(&quot;메뉴 선택 : &quot;);
        String menuNo = sc.nextLine();
        if(menuNo.equals(&quot;1&quot;)){
            try{
                String sql = &quot;&quot; +
                        &quot;INSERT INTO users (userid, username, userpassword, userage, useremail) &quot; +
                        &quot;VALUES (?, ?, ?, ?, ?)&quot;;
                PreparedStatement pstmt = conn.prepareStatement(sql);
                pstmt.setString(1,user.getUserId());
                pstmt.setString(2, user.getUserName());
                pstmt.setString(3, user.getUserPassword());
                pstmt.setInt(4, user.getUserAge());
                pstmt.setString(5, user.getUserEmail());
                pstmt.executeUpdate();
                pstmt.close();
            }catch(Exception e){

            }
        }
        list();
    }</code></pre>
<p><img src="https://velog.velcdn.com/images/seo-faper/post/73eb9ce6-07c0-48e9-956d-7d2e25b302dd/image.png" alt=""></p>
<p>앞서 만든 게시판 소스에서 조금만 수정해 주면 되는데, sql 쿼리문을 게시판이 아니라 사용자에 대한 걸로 해서 추가하면 됩니다. User.java도 새로 만들고..  로그인 세션을 담는 boolean도 깨작 선언해 준다음에</p>
<pre><code class="language-java">package ch20.mysql.sec12;

import java.sql.*;
import java.util.Scanner;

public class BoardExample3 {
    private Scanner sc = new Scanner(System.in);
    private Connection conn;
    private String name = &quot;&quot;;
    private boolean session = false;

    public BoardExample3(){
        try {
//JDBC Driver 등록
            Class.forName(&quot;com.mysql.cj.jdbc.Driver&quot;);

            conn = DriverManager.getConnection(
                    &quot;jdbc:mysql://localhost:3306/thisisjava&quot;,
                    &quot;root&quot;,
                    &quot;mysql&quot;
            );

        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    public void list(){
        if(session)  System.out.println(&quot;[게시물 목록] 사용자 :&quot;+name);
        else System.out.println(&quot;[게시물 목록]&quot;);
        System.out.println(&quot;-------------------------------------------------------&quot;);
        System.out.printf(&quot;%-6s%-12s%-16s%-40s\n&quot;, &quot;no&quot;,&quot;writer&quot;,&quot;date&quot;,&quot;title&quot;);
        System.out.println(&quot;-------------------------------------------------------&quot;);
        try{
            String sql = &quot;&quot;+
                    &quot;SELECT bno, btitle, bcontent, bwriter, bdate &quot;+
                    &quot;FROM boards &quot;+
                    &quot;ORDER BY bno DESC&quot;;
            PreparedStatement pstmt = conn.prepareStatement(sql);
            ResultSet rs = pstmt.executeQuery();
            while (rs.next()){
                Board board = new Board();
                board.setBno(rs.getInt(&quot;bno&quot;));
                board.setBtitle(rs.getString(&quot;btitle&quot;));
                board.setBcontent(rs.getString(&quot;bcontent&quot;));
                board.setBwriter(rs.getString(&quot;bwriter&quot;));
                board.setBdate(rs.getDate(&quot;bdate&quot;));
                System.out.printf(&quot;%-6s%-12s%-16s%-40s \n&quot;,
                        board.getBno(),
                        board.getBwriter(),
                        board.getBdate(),
                        board.getBtitle());
            }
            rs.close();
            pstmt.close();
        }catch(SQLException e){
            e.printStackTrace();
        }
        mainMenu();
    }
    public void mainMenu(){
        System.out.println(&quot;------------------------------------------------------&quot;);
        if(session) System.out.println(&quot;메인 메뉴: 1.Create | 2.Read | 3.Clear | 4.Join | 5.LogOut | 6.Exit &quot;);
        else System.out.println(&quot;메인 메뉴: 1.Create | 2.Read | 3.Clear | 4.Join | 5.Login | 6.Exit &quot;);
        System.out.print(&quot;메뉴 선택: &quot;);
        System.out.println(&quot;&quot;);
        String sel = sc.nextLine();
        switch (sel){
            case &quot;1&quot; -&gt; create();
            case &quot;2&quot; -&gt; read();
            case &quot;3&quot; -&gt; clear();
            case &quot;4&quot; -&gt; join();
            case &quot;5&quot; -&gt; {

                if(!session)login();
                else{
                    session = false;
                    name = &quot;&quot;;
                    list();
                }

            }
            case &quot;6&quot; -&gt; exit();
        }
    }

    private void join() {
        User user = new User();
        System.out.println(&quot;[새로운 사용자 등록]&quot;);
        System.out.print(&quot;아이디: &quot;);
        user.setUserId(sc.nextLine());
        System.out.print(&quot;이름: &quot;);
        user.setUserName(sc.nextLine());
        System.out.print(&quot;비밀번호: &quot;);
        user.setUserPassword(sc.nextLine());
        System.out.println(&quot;나이: &quot;);
        user.setUserAge(Integer.parseInt(sc.nextLine()));
        System.out.println(&quot;이메일: &quot;);
        user.setUserEmail(sc.nextLine());
        System.out.println(&quot;------------------------------------------------------&quot;);
        System.out.println(&quot;보조 메뉴 : 1. OK | 2. Cancel&quot;);
        System.out.print(&quot;메뉴 선택 : &quot;);
        String menuNo = sc.nextLine();
        if(menuNo.equals(&quot;1&quot;)){
            try{
                String sql = &quot;&quot; +
                        &quot;INSERT INTO users (userid, username, userpassword, userage, useremail) &quot; +
                        &quot;VALUES (?, ?, ?, ?, ?)&quot;;
                PreparedStatement pstmt = conn.prepareStatement(sql);
                pstmt.setString(1,user.getUserId());
                pstmt.setString(2, user.getUserName());
                pstmt.setString(3, user.getUserPassword());
                pstmt.setInt(4, user.getUserAge());
                pstmt.setString(5, user.getUserEmail());
                pstmt.executeUpdate();
                pstmt.close();
            }catch(Exception e){

            }
        }
        list();
    }

    private void login() {
        String id = &quot;&quot;;
        String pwd = &quot;&quot;;
        System.out.println(&quot;[로그인]&quot;);
        System.out.print(&quot;아이디: &quot;);
        id = sc.nextLine();
        System.out.print(&quot;비밀번호: &quot;);
        pwd = sc.nextLine();
        System.out.println(&quot;------------------------------------------------------&quot;);
        System.out.println(&quot;보조 메뉴 : 1. OK | 2. Cancel&quot;);
        System.out.print(&quot;메뉴 선택 : &quot;);
        String menuNo = sc.nextLine();
        if(menuNo.equals(&quot;1&quot;)){
            try{
                String sql = &quot;&quot;+
                        &quot;SELECT userid, username, userpassword, userage, useremail &quot;+
                        &quot;FROM users &quot;+
                        &quot;WHERE userid=?&quot;;

                PreparedStatement psmt = conn.prepareStatement(sql);
                psmt.setString(1,id);

                ResultSet rs = psmt.executeQuery();
                if(rs.next()){
                    if(id.equals(rs.getString(&quot;userid&quot;)) &amp;&amp; pwd.equals(rs.getString(&quot;userpassword&quot;))){
                        session = true;
                        name = rs.getString(&quot;username&quot;);
                        list();
                    }
                }else{
                    System.out.println(&quot;아이디가 존재하지 않습니다.&quot;);
                    join();
                }
            }catch(Exception e){

            }
        }
        list();

    }

    private void clear() {
        System.out.println(&quot;[게시물 전체 삭제]&quot;);
        System.out.println(&quot;-------------------------------------------------------&quot;);
        System.out.println(&quot;보조 메뉴: 1.Ok | 2.Cancel&quot;);
        System.out.print(&quot;메뉴 선택: &quot;);
        String menuNo = sc.nextLine();
        if(menuNo.equals(&quot;1&quot;)) {
             try {
                 String sql = &quot;TRUNCATE TABLE boards&quot;;
                 PreparedStatement pstmt = conn.prepareStatement(sql);
                 pstmt.executeUpdate();
                 pstmt.close();
                 } catch (Exception e) {
                 e.printStackTrace();
                 exit();
                 }
             }
        list();
    }

    private void exit() {
        if(conn != null){
            try{
                conn.close();
            }catch(SQLException e){

            }
        }
        System.out.println(&quot;** 게시판 종료 **&quot;);
        System.exit(0);
    }

    private void read() {
        System.out.println(&quot;[게시물 읽기]&quot;);
        System.out.print(&quot;bno: &quot;);
        int bno = Integer.parseInt(sc.nextLine());
        try{
            String sql = &quot;&quot; +
                     &quot;SELECT bno, btitle, bcontent, bwriter, bdate &quot; +
                     &quot;FROM boards &quot; +
                     &quot;WHERE bno= ?&quot;;
            PreparedStatement pstmt = conn.prepareStatement(sql);
            pstmt.setInt(1, bno);
             ResultSet rs = pstmt.executeQuery();
            if(rs.next()) {
                 Board board = new Board();
                 board.setBno(rs.getInt(&quot;bno&quot;));
                 board.setBtitle(rs.getString(&quot;btitle&quot;));
                 board.setBcontent(rs.getString(&quot;bcontent&quot;));
                 board.setBwriter(rs.getString(&quot;bwriter&quot;));
                 board.setBdate(rs.getDate(&quot;bdate&quot;));
                 System.out.println(&quot;#############&quot;);
                 System.out.println(&quot;번호: &quot; + board.getBno());
                 System.out.println(&quot;제목: &quot; + board.getBtitle());
                 System.out.println(&quot;내용: &quot; + board.getBcontent());
                 System.out.println(&quot;작성자: &quot; + board.getBwriter());
                 System.out.println(&quot;날짜: &quot; + board.getBdate());
                 System.out.println(&quot;#############&quot;);
                 System.out.println(&quot;----------------------&quot;);
                 System.out.println(&quot;보조 메뉴 : 1.Update | 2.Delete | 3.List&quot;);
                 System.out.print(&quot;메뉴 선택&quot;);
                 String menuNo = sc.nextLine();
                if (menuNo.equals(&quot;1&quot;)) {
                    update(board);
                }else if(menuNo.equals(&quot;2&quot;)){
                    delete(board);
                }else{

                }
            }
             rs.close();
             pstmt.close();
            }    catch(Exception e){}
        list();
    }
    private void delete(Board board){
        try{
            String sql = &quot;DELETE FROM boards WHERE bno=?&quot;;
            PreparedStatement pstmt = conn.prepareStatement(sql);
            pstmt.setInt(1,board.getBno());
            pstmt.executeUpdate();
            pstmt.close();
        }catch(Exception e){
            exit();
        }
        list();
    }
    private void update(Board board) {

        System.out.println(&quot;[수정 내용 입력]&quot;);
         System.out.print(&quot;제목: &quot;);
         board.setBtitle(sc.nextLine());
         System.out.print(&quot;내용: &quot;);
         board.setBcontent(sc.nextLine());
         System.out.print(&quot;작성자: &quot;);
         board.setBwriter(sc.nextLine());

         //보조 메뉴 출력
         System.out.println(&quot;------------------------------------------------------&quot;);
         System.out.println(&quot;보조 메뉴: 1.Ok | 2.Cancel&quot;);
         System.out.print(&quot;메뉴 선택: &quot;);
         String menuNo = sc.nextLine();
         if(menuNo.equals(&quot;1&quot;)) {

            try {
                String sql = &quot;&quot; +
                         &quot;UPDATE boards SET btitle= ?, bcontent= ?, bwriter= ? &quot; +
                         &quot;WHERE bno= ?&quot;;
                 PreparedStatement pstmt = conn.prepareStatement(sql);
                 pstmt.setString(1, board.getBtitle());
                 pstmt.setString(2, board.getBcontent());
                 pstmt.setString(3, board.getBwriter());
                 pstmt.setInt(4, board.getBno());
                 pstmt.executeUpdate();
                 pstmt.close();
            } catch (Exception e) {
                 e.printStackTrace();
                 exit();
                 }
             }
         list();
    }
    private void create() {
        Board board = new Board();
        System.out.println(&quot;[새 게시물 입력]&quot;);
        System.out.print(&quot;제목: &quot;);
        board.setBtitle(sc.nextLine());
        System.out.print(&quot;내용: &quot;);
        board.setBcontent(sc.nextLine());
        System.out.print(&quot;작성자: &quot;);
        board.setBwriter(sc.nextLine());

        System.out.println(&quot;------------------------------------------------------&quot;);
        System.out.println(&quot;보조 메뉴 : 1. OK | 2. Cancel&quot;);
        System.out.print(&quot;메뉴 선택 : &quot;);
        String menuNo = sc.nextLine();
        if(menuNo.equals(&quot;1&quot;)){
            try{
                String sql = &quot;&quot;+
                        &quot;INSERT INTO boards (btitle, bcontent, bwriter, bdate) &quot; +
                        &quot;VALUES (?, ?, ?, now())&quot;;
                PreparedStatement psmt = conn.prepareStatement(sql);
                psmt.setString(1,board.getBtitle());
                psmt.setString(2,board.getBcontent());
                psmt.setString(3,board.getBwriter());
                psmt.executeUpdate();
                psmt.close();
            }catch(Exception e){

            }
        }
        list();
    }

    public static void main(String[] args) {
        BoardExample3 boardExample3 = new BoardExample3();
        boardExample3.list();
    }
}
</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[이것이 자바다 17일차 - Chapter19 네트워크 입출력]]></title>
            <link>https://velog.io/@seo-faper/%EC%9D%B4%EA%B2%83%EC%9D%B4-%EC%9E%90%EB%B0%94%EB%8B%A4-17%EC%9D%BC%EC%B0%A8-Chapter19-%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-%EC%9E%85%EC%B6%9C%EB%A0%A5</link>
            <guid>https://velog.io/@seo-faper/%EC%9D%B4%EA%B2%83%EC%9D%B4-%EC%9E%90%EB%B0%94%EB%8B%A4-17%EC%9D%BC%EC%B0%A8-Chapter19-%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-%EC%9E%85%EC%B6%9C%EB%A0%A5</guid>
            <pubDate>Wed, 01 Feb 2023 16:03:51 GMT</pubDate>
            <description><![CDATA[<h2 id="ip-주소-얻기">IP 주소 얻기</h2>
<p>자바에서 네트워크 프로토콜을 다루는 법을 소개한다.
우선 집 주소라고 할 수 있는 IP를 얻는 방법은 다음과 같다.</p>
<pre><code class="language-java">package ch19.sec02;

import java.net.InetAddress;
import java.net.UnknownHostException;

public class InetAddressExample {
    public static void main(String[] args) {
        try{
            InetAddress local = InetAddress.getLocalHost();
            System.out.println(&quot;내 컴퓨터 IP 주소 : &quot;+local.getHostAddress());

            InetAddress[] iaArr = InetAddress.getAllByName(&quot;www.naver.com&quot;);
            for(InetAddress remote : iaArr){
                System.out.println(&quot;www.naver.com 주소 : &quot;+remote.getHostAddress());
            }
        }catch(UnknownHostException e){

        }

    }
}
</code></pre>
<p>java.net의 InetAddress를 통해 알아 낼 수 있다.</p>
<h2 id="tcp-네트워킹">TCP 네트워킹</h2>
<p>IP 주소로 프로그램들이 통신할 때는 약속된 데이터 전송 규약이 있다. 이것을 전송용 프로토콜이라고 부른다. 인터넷에서 전송용 프로토콜은 TCP와 UDP가 있다. </p>
<p>TCP는 3Way-handshaking으로 세션을 수립한다.</p>
<p><img src="https://velog.velcdn.com/images/seo-faper/post/1ab7006b-5552-4e9c-8693-9c1242473e67/image.png" alt=""></p>
<h3 id="tcp-서버-만드는-법">TCP 서버 만드는 법</h3>
<pre><code class="language-java">package ch19.sec03.exam01;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;

public class ServerExample {

    private static ServerSocket serverSocket = null;
    public static void main(String[] args) {
        System.out.println(&quot;----------------------------------------------------&quot;);
        System.out.println(&quot;서버를 종료하려면 q 또는 Q를 입력하고 Enter 키를 입력하세요.&quot;);
        System.out.println(&quot;----------------------------------------------------&quot;);

        startServer();

        Scanner scanner = new Scanner(System.in);
        while (true){
            String key = scanner.nextLine();
            if(key.toLowerCase().equals(&quot;q&quot;)) break;
        }

        scanner.close();

        stopServer();

    }


    public static void startServer() {
        Thread thread = new Thread(){
            @Override
            public void run(){
                try{
                    serverSocket = new ServerSocket(50001);
                    System.out.println(&quot;[서버] 시작됨&quot;);

                    while (true){
                        System.out.println(&quot;\n[서버] 연결 요청을 기다림\n&quot;);
                        Socket socket = serverSocket.accept();

                        InetSocketAddress isa =
                                (InetSocketAddress) socket.getRemoteSocketAddress();
                        System.out.println(&quot;[서버]&quot;+isa.getHostName()+&quot;의 연결 요청을 수락함&quot;);

                        socket.close();
                        System.out.println(&quot;[서버] &quot;+isa.getHostName()+&quot;의연결을 끊음&quot;);
                    }
                }catch(IOException e){
                        System.out.println(&quot;[서버] &quot;+e.getMessage());
                }
            }
        };
        thread.start();
    }
    private static void stopServer() {
        try{
            serverSocket.close();
            System.out.println(&quot;[서버] 종료됨.&quot;);
        }catch (IOException e){}
    }

}
</code></pre>
<h3 id="tcp-클라이언트-만드는-법">TCP 클라이언트 만드는 법</h3>
<pre><code class="language-java">package ch19.sec03.exam01;

import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;

public class ClientExample {
    public static void main(String[] args) {
        try{
            Socket socket = new Socket(&quot;localhost&quot;,50001);

            System.out.println(&quot;[클라이언트] 연결 성공&quot;);
            socket.close();
            System.out.println(&quot;[클라이언트] 연결 끊음&quot;);
        }catch(UnknownHostException e){
            //IP 표기 방법이 잘못되었을 때
        } catch (IOException e) {
            throw new RuntimeException(e);
            // 해당 포트의 서버에 연결할 수 없는 경우
        }

    }
}
</code></pre>
<h3 id="입출력-스트림으로-데이터-주고-받기">입출력 스트림으로 데이터 주고 받기</h3>
<p>클라이언트가 연결 요청 (connect())을 하고 서버가 연결 수락(accept())했다면 양쪽의 Socket 객체로부터 각각 입력 스트림과 출력 스트림을 얻을 수 있다.</p>
<h2 id="udp-네트워킹">UDP 네트워킹</h2>
<p>UDP는 TCP와 다르게 일방적인 데이터 수신 방식으로 안정성이 떨어지는 대신 빠른 속도가 장점이다.</p>
<p><img src="https://velog.velcdn.com/images/seo-faper/post/0f7c9c72-be1c-41c1-8328-038093f0ed58/image.png" alt=""></p>
<p>UDP는 말 그대로 User Datagram Protocal 이기 때문에 UDP 서버는 항상 클라이언트가 보낸 DatagramPacker를 받을 준비가 되어 있어야 한다. </p>
<pre><code class="language-java">package ch19.sec04;

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketAddress;
import java.util.Scanner;

public class NewsServer extends Thread{
    private static DatagramSocket datagramSocket = null;

    public static void main(String[] args) throws Exception{
        System.out.println(&quot;----------------------------------------&quot;);
        System.out.println(&quot;서버를 종료하려면 q를 입력하고 Enter 키를 입력하세요.&quot;);
        System.out.println(&quot;----------------------------------------&quot;);

        startServer();
        Scanner sc = new Scanner(System.in);
        while (true){
            String key = sc.nextLine();
            if(key.toLowerCase().equals(&quot;q&quot;)) break;
        }
        sc.close();

        stopServer();
    }

    private static void stopServer() {
        datagramSocket.close();
        System.out.println(&quot;[서버] 종료됨&quot;);
    }

    private static void startServer() {
        Thread thread = new Thread(){

                @Override
                public void run() {
                    try {
                        //Datagram 생성 및 포트 바인딩
                        datagramSocket = new DatagramSocket(50001);
                        System.out.println(&quot;[서버] 시작됨&quot;);

                        while (true){
                            //클라이언트가 구독하고 싶은 뉴스 주제 얻기
                            DatagramPacket receivePacket = new DatagramPacket
                                    (new byte[1024],1024);
                            datagramSocket.receive(receivePacket);
                            String newsKind =
                                    new String(receivePacket.getData(),0,
                                            receivePacket.getLength(), &quot;UTF-8&quot;);

                            //클라이언트의 IP와 PORT 얻기
                            SocketAddress socketAddress = receivePacket.getSocketAddress();

                            for(int i = 1; i&lt;=10; i++){

                                String data = newsKind + &quot;: 뉴스&quot;+i;
                                byte[] bytes = data.getBytes(&quot;UTF-8&quot;);
                                DatagramPacket sendPacket =
                                        new DatagramPacket(bytes, 0, bytes.length, socketAddress);
                                datagramSocket.send(sendPacket);
                            }
                        }
                    } catch (Exception e) {

                        System.out.println(&quot;[서버]&quot;+e.getMessage());
                    }
                }
        };
        thread.start();
    }
}
</code></pre>
<p>서버를 이렇게 세팅한다.</p>
<pre><code class="language-java">package ch19.sec04;

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;

public class NewsClient {
    public static void main(String[] args) {
        try{
            //구독하고 싶은 뉴스 주제 보내기
            DatagramSocket datagramSocket = new DatagramSocket();
            String data = &quot;정치&quot;;
            byte[] bytes = data.getBytes();
            DatagramPacket sendPacket = new DatagramPacket(bytes,bytes.length,
                new InetSocketAddress(&quot;localhost&quot;,50001));
            datagramSocket.send(sendPacket);

            while (true){
                //뉴스 받기
                DatagramPacket receivePacket = new DatagramPacket(new byte[1024],1024);
                datagramSocket.receive(receivePacket);

                //문자열로 변환
                String news = new String(receivePacket.getData(), 0,
                        receivePacket.getLength(), &quot;UTF-8&quot;);
                System.out.println(news);
                //10번째 뉴스를 받으면 while 문 종료
                if(news.contains(&quot;뉴스10&quot;)){
                    break;
                }
            }
            datagramSocket.close();
        }catch (Exception e){

        }
    }
}
</code></pre>
<h2 id="서버의-동시-요청-처리">서버의 동시 요청 처리</h2>
<p>일반적으로 서버는 다수의 클라이언트와 통신을 한다. 서버는 클라이언트들로부터 동시에 요청을 받아서 처리하고, 처리 결과를 개별 클라이언트로 보내 줘야 한다.
앞서 쓴 예제들은 모두 먼저 클라이언트의 요청을 처리한 후, 다음 클라이언트의 요청을 처리하도록 되어 있다.
이와 같은 방식은 먼저 연결한 클라이언트의 요청 처리 시간이 길어질수록 다음 클라이언트의 요청 처리 작업이 지연될 수 밖에 없다. 따라서 accept()와 receive()를 제외한 요청 처리 코드를 별도의 스레드에서 작업하는 것이 좋다. </p>
<pre><code class="language-java">package ch19.sec05.exam01;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class EchoServer {
    private static ServerSocket serverSocket = null;
    private static ExecutorService executorService =
            Executors.newFixedThreadPool(10);
    public static void main(String[] args) {

        System.out.println(&quot;----------------------------------------&quot;);
        System.out.println(&quot;서버를 종료하려면 q를 입력하고 Enter 키를 입력하세요.&quot;);
        System.out.println(&quot;----------------------------------------&quot;);

        startServer();
        Scanner sc = new Scanner(System.in);
        while (true){
            String key = sc.nextLine();
            if(key.toLowerCase().equals(&quot;q&quot;)) break;
        }
        sc.close();

        stopServer();
    }
    public static void startServer(){
        Thread thread = new Thread(){
            @Override
            public void run(){
                try{
                    serverSocket = new ServerSocket(50001);
                    System.out.println(&quot;[서버] 시작됨&quot;);

                    while (true){
                        Socket socket = serverSocket.accept();
                        executorService.execute(()-&gt;{ // 작업 큐에 처리 작업 넣는 코드 
                            try{
                                InetSocketAddress isa = (InetSocketAddress) socket.getRemoteSocketAddress();
                                System.out.println(&quot;[서버]&quot;+isa.getHostName()+&quot;의 연결 요청을 수락함 &quot;);

                                //데이터 받기
                                DataInputStream dis = new DataInputStream
                                        (socket.getInputStream());
                                String message = dis.readUTF();

                                //데이터 보내기
                                DataOutputStream dos =
                                        new DataOutputStream(socket.getOutputStream());
                                dos.writeUTF(message);
                                dos.flush();

                                System.out.println(&quot;[서버] 받은 데이터를 다시 보냄: &quot;+message);

                                socket.close();
                                System.out.println(&quot;[서버] &quot;+isa.getHostName()+&quot;의 연결을 끊음\n&quot;);

                            }catch(IOException e){

                            }
                        });
                    }
                }catch(Exception e){System.out.println(&quot;[서버] &quot;+e.getMessage());}
            }
        };
        thread.start();
    }
    public static void stopServer(){
        try{
            serverSocket.close();
            executorService.shutdownNow();
            System.out.println(&quot;[서버] 종료됨&quot;);
        }catch(IOException e){

        }
    }
}
</code></pre>
<h2 id="json-데이터-형식">json 데이터 형식</h2>
<p>네트워크로 전달하는 데이터가 복잡할 수록 구조화된 형식이 필요하다.
네트워크 통신에서 가장 많이 쓰는 형식은 JSON으로, 자바 뿐만 아니라 여러 곳에서 응용된다.</p>
<p>자바는 JSON을 편하게 만들기 위해 JSON 라이브러리를 제공한다.
<a href="https://search.maven.org/remotecontent?filepath=org/json/json/20220924/json-20220924.jar">다운로드</a></p>
<p>JSON 만드는 법 </p>
<pre><code class="language-java">package ch19.sec06;

import org.json.JSONArray;
import org.json.JSONObject;

import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.nio.charset.Charset;

public class CreateJsonExample {
    public static void main(String[] args) throws IOException {
        JSONObject root = new JSONObject();

        root.put(&quot;id&quot;,&quot;winter&quot;);
        root.put(&quot;name&quot;,&quot;한겨울&quot;);
        root.put(&quot;age&quot;,25);
        root.put(&quot;student&quot;,false);

        JSONObject tel = new JSONObject();
        tel.put(&quot;home&quot;,&quot;02-123-1234&quot;);
        tel.put(&quot;mobile&quot;,&quot;010-123-1234&quot;);
        root.put(&quot;tel&quot;,tel);

        JSONArray skill = new JSONArray();
        skill.put(&quot;java&quot;);
        skill.put(&quot;c&quot;);
        skill.put(&quot;c++&quot;);
        root.put(&quot;skill&quot;,skill);

        String json = root.toString();

        System.out.println(json);

        Writer writer = new FileWriter(&quot;C:/Temp/member.json&quot;, Charset.forName(&quot;UTF-8&quot;));
        writer.write(json);
        writer.flush();
        writer.close();
    }
}
</code></pre>
<p>지정된 경로에 member.json이 생성된다.</p>
<pre><code class="language-java">package ch19.sec06;

import org.json.JSONArray;
import org.json.JSONObject;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.nio.charset.Charset;

public class ParseJsonExample {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(
                new FileReader(&quot;C:/Temp/member.json&quot;, Charset.forName(&quot;UTF-8&quot;))
        );
        String json = br.readLine();
        br.close();

        JSONObject root = new JSONObject(json);

        System.out.println(&quot;id: &quot;+root.getString(&quot;id&quot;));
        System.out.println(&quot;name: &quot;+root.getString(&quot;name&quot;));
        System.out.println(&quot;age: &quot;+root.getInt(&quot;age&quot;));
        System.out.println(&quot;student: &quot;+root.getBoolean(&quot;student&quot;));

        JSONObject tel = root.getJSONObject(&quot;tel&quot;);
        System.out.println(&quot;home: &quot;+tel.getString(&quot;home&quot;));
        System.out.println(&quot;mobile: &quot;+tel.getString(&quot;mobile&quot;));

        JSONArray skill = root.getJSONArray(&quot;skill&quot;);
        System.out.print (&quot;skill: &quot;);
        for(int i = 0; i&lt;skill.length(); i++){
            System.out.print (skill.get(i)+&quot;, &quot;);
        }

    }
}
</code></pre>
<p>BufferReader로 읽어서 스트림을 만들고 JSON 라이브러리로 가공할 수 있다.</p>
<h2 id="tcp-채팅-프로그램-만들기">TCP 채팅 프로그램 만들기</h2>
<pre><code class="language-java">package ch19.sec07;

import java.net.ServerSocket;
import java.util.Collections;
import java.util.HashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ChatServer {
    ServerSocket serverSocket;
    ExecutorService threadPool = Executors.newFixedThreadPool(100);
    Map&lt;String, SocketClient&gt; chatRoom
            = Collections.synchronizedMap(new HashMap&lt;&gt;());

}
</code></pre>
<p>serverSocket은 클라이언트의 연결 요청을 수락하고, threadPool은 100개의 클라이언트가 동시에 채팅할 수 있도록 한다. cahtRoom은 통신용 SocketClient를 관리하는 동기화된 Map 이다.</p>
<pre><code class="language-java">    public void start() throws IOException{
        serverSocket = new ServerSocket(50001);
        System.out.println(&quot;[서버] 시작됨&quot;);

        Thread thread = new Thread(()-&gt;{
            try{
                while (true){
                    Socket socket = serverSocket.accept();
                    SocketClient sc = new SocketClient(this, socket);
                }
            }catch(Exception e){

            }

        });
        thread.start();</code></pre>
<p>그리고 이어서 start() 메소드를 만들어 준다.
이 코드는 서버를 열어주는 역할을 한다.</p>
<pre><code class="language-java">    public void addSocketClient(SocketClient socketClient){
        String key = socketClient.chatName+&quot;@&quot;+socketClient.clientIp;
        chatRoom.put(key,socketClient);
        System.out.println(&quot;입장: &quot;+key);
        System.out.println(&quot;현재 채팅자 수 : &quot;+chatRoom.size()+&quot;\n&quot;);
    }
    public void removeSocketClient(SocketClient socketClient){
        String key = socketClient.chatName+&quot;@&quot;+socketClient.clientIp;
        chatRoom.remove(key);
        System.out.println(&quot;나감: &quot;+key);
        System.out.println(&quot;현재 채팅자 수 : &quot;+chatRoom.size()+&quot;\n&quot;);
    }

</code></pre>
<p>이어서 클라이언트가 접속하고 나가는 기능을 구현한다.</p>
<pre><code class="language-java">    public void setToAll(SocketClient sender, String message){
        JSONObject root = new JSONObject();
        root.put(&quot;clientIP&quot;,sender.ClientIP);
        root.put(&quot;clientName&quot;,sender.chatName);
        root.put(&quot;message&quot;,message);
        String json = root.toString();

        Collection&lt;SocketClient&gt; socketClients = chatRoom.values();
        for(SocketClient sc : socketClients){
            if(sc == sender) continue;
            sc.send(json);
        }
    }</code></pre>
<p>그리고 접속한 모든 클라이언트에게 메세지를 보내는 메소드를 구현한다.</p>
<pre><code class="language-java">public void stop(){
        try{
            serverSocket.close();
            threadPool.shutdownNow();
            chatRoom.values().stream().forEach(e-&gt; e.close());
        }catch (IOException e){}
    }

    public static void main(String[] args) {
        try{
            ChatServer chatServer = new ChatServer();
            chatServer.start();

            System.out.println(&quot;----------------------------------------&quot;);
            System.out.println(&quot;서버를 종료하려면 q를 입력하고 Enter 키를 입력하세요.&quot;);
            System.out.println(&quot;----------------------------------------&quot;);

            Scanner sc = new Scanner(System.in);
            while (true){
                String key = sc.nextLine();
                if(key.toLowerCase().equals(&quot;q&quot;)) break;
            }
            sc.close();
            chatServer.stop();
        }catch(IOException e){
            System.out.println(&quot;[서버] &quot;+e.getMessage());
        }

    }</code></pre>
<p>그리고 서버의 main을 만들어 준다.
그 다음은 SocketClient를 만들어 보자.</p>
<pre><code class="language-java">package ch19.sec07;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;

public class SocketClient {
    ChatServer chatServer;
    Socket socket;
    DataInputStream dis;
    DataOutputStream dos;
    String clientIp;
    String chatName;
    public SocketClient(ChatServer chatServer, Socket socket){
        try{
            this.chatServer = chatServer;
            this.socket = socket;
            this.dis = new DataInputStream(socket.getInputStream());
            this.dos = new DataOutputStream(socket.getOutputStream());
            InetSocketAddress isa =
                    (InetSocketAddress) socket.getRemoteSocketAddress();
            this.clientIp = isa.getHostName();
            receive();
        }catch(IOException e){

        }


    }
}
</code></pre>
<p>기본적인 필드와 생성자를 만들어 준다. receive() 메소드는</p>
<pre><code class="language-java">    public void receive(){
        chatServer.threadPool.execute(()-&gt;{
            try{
                while (true){
                    String receiveJson = dis.readUTF();

                    JSONObject jsonObject = new JSONObject(receiveJson);
                    String command = jsonObject.getString(&quot;command&quot;);

                    switch (command){
                        case &quot;incoming&quot;:
                            this.chatName = jsonObject.getString(&quot;data&quot;);
                            chatServer.sendToAll(this,&quot;들어오셨습니다.&quot;);
                            chatServer.addSocketClient(this);
                            break;
                        case &quot;message&quot;:
                            String message = jsonObject.getString(&quot;data&quot;);
                            chatServer.sendToAll(this,message);
                            break;
                    }
                }
            }catch(IOException e){
                chatServer.sendToAll(this,&quot;나가셨습니다.&quot;);
                chatServer.removeSocketClient(this);
            }
        });
    }
    public void send(String json){
        try{
            dos.writeUTF(json);
            dos.flush();
        }catch (IOException e){

        }
    }
    public void close(){
        try{
            socket.close();
        }catch (IOException e){

        }
    }</code></pre>
<p>클라이언트가 보낸 JSON 메세지를 읽는 역할을 한다. dis.readUTF()로 JSON을 읽고 command 값을 얻어낸다.<br>그 다음은 채팅 클라이언트를 만들면 된다.</p>
<pre><code class="language-java">package ch19.sec07;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;

public class ChatClient {
    Socket socket;
    DataInputStream dis;
    DataOutputStream dos;
    String chatName;

    public void connect() throws IOException{
        socket = new Socket(&quot;localhost&quot;,50001);
        dis = new DataInputStream(socket.getInputStream());
        dos = new DataOutputStream(socket.getOutputStream());
        System.out.println(&quot;[클라이언트] 서버에 연결됨&quot;);
    }
}
</code></pre>
<p>receive() 메소드는 서버가 보낸 Json을 읽는 역할을 한다. </p>
<pre><code class="language-java">    public void receive(){
        Thread thread = new Thread(()-&gt;{
           try{
               while (true){
                   String json = dis.readUTF();
                   JSONObject root = new JSONObject(json);
                   String clientIp = root.getString(&quot;clientIp&quot;);
                   String chatName = root.getString(&quot;chatName&quot;);
                   String message = root.getString(&quot;message&quot;);
                   System.out.println(&quot;&lt;&quot;+chatName+&quot;@&quot;+clientIp+&quot;&gt; &quot;+message);

               }
           }catch (Exception e){
                System.out.println(&quot;[클라이언트] 서버 연결 끊김&quot;);
                System.exit(0);
           }
        });
        thread.start();
    }</code></pre>
<pre><code class="language-java"> public void send(String json) throws IOException{
        dos.writeUTF(json);
        dos.flush();
    }
    public void unconnect() throws IOException{
        socket.close();
    }

    public static void main(String[] args) {
        try{
            ChatClient chatClient = new ChatClient();
            chatClient.connect();
            Scanner sc = new Scanner(System.in);
            System.out.println(&quot;대화명 입력&quot;);
            chatClient.chatName = sc.nextLine();
            JSONObject jsonObject = new JSONObject();
            jsonObject.put(&quot;command&quot;,&quot;incoming&quot;);
            jsonObject.put(&quot;data&quot;,chatClient.chatName);
            String json = jsonObject.toString();
            chatClient.send(json);

            chatClient.receive();

            System.out.println(&quot;------------------------------------------&quot;);
            System.out.println(&quot;보낼 메세지를 입력하고 Enter&quot;);
            System.out.println(&quot;채팅을 종료하려면 q를 입력하고 Enter&quot;);
            System.out.println(&quot;------------------------------------------&quot;);
            while (true){
                String message = sc.nextLine();
                if(message.toLowerCase().equals(&quot;q&quot;)) break;
                else{
                    jsonObject = new JSONObject();
                    jsonObject.put(&quot;command&quot;,&quot;message&quot;);
                    jsonObject.put(&quot;data&quot;,message);
                    json = jsonObject.toString();
                    chatClient.send(json);

                }
            }
            sc.close();
            chatClient.unconnect();
        } catch (IOException e) {
            System.out.println(&quot;[클라이언트] 서버 연결 안됨&quot;);
        }
    }</code></pre>
<h2 id="연습문제">연습문제</h2>
<p><img src="https://velog.velcdn.com/images/seo-faper/post/5e615fb7-8f17-4af6-976a-12a5a6fb9827/image.png" alt=""></p>
<p>2번, Port도 있어야 합니다.
<img src="https://velog.velcdn.com/images/seo-faper/post/174ebcb0-00b9-41b2-a105-16d717a0ae11/image.png" alt=""></p>
<p>2번, 4번
TCP는 하나의 회선으로 통신하기 때문에 느리지만 정확하고 UDP는 여러 회선으로 전달하기 때문에 빠르지만 안정성이 떨어집니다.</p>
<p><img src="https://velog.velcdn.com/images/seo-faper/post/e4e0d9c0-ac54-4ee6-bdf0-b378bf2fd3f8/image.png" alt=""></p>
<pre><code class="language-java">new Socket(&quot;localhost&quot;,5001);


serverSocket.accept();</code></pre>
<p><img src="https://velog.velcdn.com/images/seo-faper/post/edad006d-700f-4885-aa48-73936431a59d/image.png" alt=""></p>
<p>왼쪽 위 : InputStream 
오른쪽 위 : OutputStream
왼쪽 아래 : OutputStream
오른쪽 아래 : InutStream</p>
<p><img src="https://velog.velcdn.com/images/seo-faper/post/4f6d4197-40de-4760-b308-23ffaa65bff1/image.png" alt=""></p>
<p>1: DatagramSocket
2: DatagramPacket
3: DatagramSocket
4: DatagramPacket
5: DatagramPacket</p>
<p>어차피 UDP는 DatagramSocket과 DatagramPacket으로 수발하기 때문에..</p>
<p><img src="https://velog.velcdn.com/images/seo-faper/post/bc74760a-3763-42a9-99ca-894d04dfb885/image.png" alt=""></p>
<p>4번, 발신할 때는 send() 입니다. </p>
<p><img src="https://velog.velcdn.com/images/seo-faper/post/3f32b5cd-1037-409d-8e4f-95a17d4f9777/image.png" alt=""></p>
<p>이거는 앞서한 TCP 채팅 서버 만들기에서 조금만 변형하면 됩니다. 원리는 같으니까요.
 서버 생성 -&gt; 소켓 클라이언트 생성 -&gt; receive()로 클라이언트한테 받은 JSON 처리하는 코드 작성 -&gt; 클라이언트 생성  이렇게 만들려 합니다.</p>
<p><code>Product.java</code></p>
<pre><code class="language-java">package ch19.homework;


public class Product {
    private int no;
    private String name;
    private int price;
    private int stock;


    public Product() {

    }

    public int getNo() {
        return no;
    }

    public void setNo(int no) {
        this.no = no;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getPrice() {
        return price;
    }

    public void setPrice(int price) {
        this.price = price;
    }

    public int getStock() {
        return stock;
    }

    public void setStock(int stock) {
        this.stock = stock;
    }



}</code></pre>
<p><code>ProductClient.java</code></p>
<pre><code class="language-java">package ch19.homework;

import org.json.JSONArray;
import org.json.JSONObject;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.util.Scanner;

public class ProductClient {
    private Socket socket;
    private DataInputStream dis;
    private DataOutputStream dos;
    private Scanner sc;

    public static void main(String[] args) {
        ProductClient productClient = new ProductClient();
        try{
            System.out.println(&quot;클라이언트 start()&quot;);
            productClient.start();
        }catch (IOException e){
            e.printStackTrace();
            productClient.stop();

        }
    }
    public void start() throws IOException {
        //서버 연결하기
        socket = new Socket(&quot;localhost&quot;, 50001);
        dis = new DataInputStream(socket.getInputStream());
        dos = new DataOutputStream(socket.getOutputStream());
        System.out.println(&quot;[클라이언트] 서버에 연결됨&quot;);

        sc = new Scanner(System.in);

        showProduct();

    }

    private void showProduct() throws IOException {
        System.out.println(&quot;[상품 목록]&quot;);
        System.out.println(&quot;-----------------------------------------------&quot;);
        System.out.println(&quot;no\t\tname\t\t\t\t\t\t\tprice\t\t\t\tstock&quot;);
        System.out.println(&quot;-----------------------------------------------&quot;);

        JSONObject request = new JSONObject();
        request.put(&quot;menu&quot;,0);
        request.put(&quot;data&quot;,new JSONObject());
        dos.writeUTF(request.toString());
        dos.flush();

        JSONObject response = new JSONObject(dis.readUTF());

        if(response.getString(&quot;status&quot;).equals(&quot;success&quot;)){
            JSONArray data = response.getJSONArray(&quot;data&quot;);
            for(int i = 0; i&lt;data.length();i++){
                JSONObject product = data.getJSONObject(i);
                System.out.printf(
                        &quot;%-6d%-30s%-15d%-10d\n&quot;,
                        product.getInt(&quot;no&quot;),
                        product.getString(&quot;name&quot;),
                        product.getInt(&quot;price&quot;),
                        product.getInt(&quot;stock&quot;)
                );
            }
        }

        showMenu();
    }

    private void showMenu() throws IOException {
        System.out.println(&quot;-----------------------------------------------&quot;);
        System.out.println(&quot;메뉴 : 1. Create | 2. Update | 3. Delete | 4.Exit&quot;);
        System.out.print(&quot;선택 &gt;&quot;);
        int sel = Integer.parseInt(sc.nextLine());

        if(sel==1)create();
        else if(sel==2) update();
        else if(sel==3) delete();
        else if(sel==4) exit();
    }

    private void exit() {
        stop();
    }

    private void delete() throws IOException {
        System.out.println(&quot;[상품 삭제]&quot;);
        System.out.print(&quot;상품 번호: &quot;);
        int no = Integer.parseInt(sc.nextLine());

        //상품 수정 요청하기
        JSONObject data = new JSONObject();
        data.put(&quot;no&quot;, no);

        JSONObject request = new JSONObject();
        request.put(&quot;menu&quot;, 3);
        request.put(&quot;data&quot;, data);

        dos.writeUTF(request.toString());
        dos.flush();

        //응답 받기
        JSONObject response = new JSONObject(dis.readUTF());
        if(response.getString(&quot;status&quot;).equals(&quot;success&quot;)) {
            showProduct();
        }
    }

    private void update() throws IOException {
        System.out.println(&quot;[상품 수정]&quot;);
        Product product = new Product();
        System.out.print(&quot;상품 번호: &quot;);
        product.setNo(Integer.parseInt(sc.nextLine()));
        System.out.print(&quot;이름 변경: &quot;);
        product.setName(sc.nextLine());
        System.out.print(&quot;가격 변경: &quot;);
        product.setPrice(Integer.parseInt(sc.nextLine()));
        System.out.print(&quot;재고 변경: &quot;);
        product.setStock(Integer.parseInt(sc.nextLine()));


        JSONObject data = new JSONObject();
        data.put(&quot;no&quot;, product.getNo());
        data.put(&quot;name&quot;, product.getName());
        data.put(&quot;price&quot;, product.getPrice());
        data.put(&quot;stock&quot;, product.getStock());

        JSONObject request = new JSONObject();
        request.put(&quot;menu&quot;, 2);
        request.put(&quot;data&quot;, data);

        dos.writeUTF(request.toString());
        dos.flush();

        JSONObject response = new JSONObject(dis.readUTF());

        if(response.getString(&quot;status&quot;).equals(&quot;success&quot;)) {
            showProduct();
        }
    }

    private void create() throws IOException {
        System.out.println(&quot;[상품 생성]&quot;);
        Product product = new Product();
        System.out.print(&quot;상품 이름 : &quot;);
        product.setName(sc.nextLine());
        System.out.print(&quot;상품 가격 : &quot;);
        product.setPrice(Integer.parseInt(sc.nextLine()));
        System.out.print(&quot;상품 재고 : &quot;);
        product.setStock(Integer.parseInt(sc.nextLine()));
        JSONObject data = new JSONObject();
        data.put(&quot;name&quot;, product.getName());
        data.put(&quot;price&quot;, product.getPrice());
        data.put(&quot;stock&quot;, product.getStock());

        JSONObject request = new JSONObject();
        request.put(&quot;menu&quot;, 1);

        request.put(&quot;data&quot;, data);

        dos.writeUTF(request.toString());
        dos.flush();

        JSONObject response = new JSONObject(dis.readUTF());
        if(response.getString(&quot;status&quot;).equals(&quot;success&quot;)) {
            showProduct();
        }
    }


    public void stop(){
        try{
            socket.close();
            sc.close();
        }catch(Exception e){
            System.out.println(&quot;[클라이언트] 종료&quot;);
        }
    }

}
</code></pre>
<p><code>ProductServer.java, SocketClient.java</code></p>
<pre><code class="language-java">package ch19.homework;

import org.json.JSONArray;
import org.json.JSONObject;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Iterator;
import java.util.List;
import java.util.Vector;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ProductServer {
    private ServerSocket serverSocket;
    private ExecutorService threadPool;
    private List&lt;Product&gt; products;
    private int number;

    public static void main(String[] args) {
        ProductServer productServer = new ProductServer();
        try{
            productServer.start();
        }catch(IOException e){
            productServer.stop();
        }
    }
    public void start() throws IOException{
        serverSocket = new ServerSocket(50001);
        threadPool = Executors.newFixedThreadPool(100);
        products = new Vector&lt;Product&gt;();

        System.out.println(&quot;[서버] 시작됨&quot;);

        while (true){
            Socket socket = serverSocket.accept();
            SocketClient sc = new SocketClient(socket);
        }

    }
    public void stop(){
        try{
            serverSocket.close();
            threadPool.shutdownNow();
            System.out.println(&quot;[서버] 종료됨&quot;);
        } catch (IOException e) {

        }

    }
    public class SocketClient{
        private Socket socket;
        private DataInputStream dis;
        private DataOutputStream dos;
        public SocketClient( Socket socket){
            try{
                this.socket = socket;
                this.dis = new DataInputStream(socket.getInputStream());
                this.dos = new DataOutputStream(socket.getOutputStream());
            }catch(IOException e){

            }
        }
        public void receive(){
            threadPool.execute(()-&gt;{
                try{
                    while (true){
                        String receiveJson = dis.readUTF();

                        JSONObject request = new JSONObject(receiveJson);
                        int sel = request.getInt(&quot;menu&quot;);

                        if(sel==0) list(request);
                        else if(sel==1) create(request);
                        else if(sel==2) update(request);
                        else if(sel==3) delete(request);
                    }
                }catch (IOException e){
                        close();
                }
            });
        }
        public void close(){
            try{
                socket.close();
            }catch (Exception e){

            }
        }

        private void update(JSONObject request) throws IOException {
            JSONObject data = request.getJSONObject(&quot;data&quot;);
            int no = data.getInt(&quot;no&quot;);
            for(int i= 0; i&lt;products.size(); i++) {
                Product product = products.get(i);
                if(product.getNo() == no) {
                    product.setName(data.getString(&quot;name&quot;));
                    product.setPrice(data.getInt(&quot;price&quot;));
                    product.setStock(data.getInt(&quot;stock&quot;));
                }
            }

            //응답 보내기
            JSONObject response = new JSONObject();
            response.put(&quot;status&quot;, &quot;success&quot;);
            response.put(&quot;data&quot;, new JSONObject());
            dos.writeUTF(response.toString());
            dos.flush();
        }

        private void delete(JSONObject request) throws IOException {
            JSONObject data = request.getJSONObject(&quot;data&quot;);
            int no = data.getInt(&quot;no&quot;);
            for(int i = 0; i&lt;products.size(); i++){
                if(products.get(i).getNo()== no) {
                    products.remove(i);
                    break;
                }
            }

            //응답 보내기
            JSONObject response = new JSONObject();
            response.put(&quot;status&quot;, &quot;success&quot;);
            response.put(&quot;data&quot;, new JSONObject());
            dos.writeUTF(response.toString());
            dos.flush();
        }

        private void create(JSONObject request) throws IOException {

            JSONObject data = request.getJSONObject(&quot;data&quot;);
            Product product = new Product();
            product.setNo(++number);
            product.setName(data.getString(&quot;name&quot;));
            product.setPrice(data.getInt(&quot;price&quot;));
            product.setStock(data.getInt(&quot;stock&quot;));
            products.add(product);

            JSONObject response = new JSONObject();
            response.put(&quot;status&quot;, &quot;success&quot;);
            response.put(&quot;data&quot;, new JSONObject());
            dos.writeUTF(response.toString());
            dos.flush();
        }

        private void list(JSONObject request) throws IOException {

            JSONArray data = new JSONArray();
            for (Product p : products) {
                JSONObject product = new JSONObject();
                product.put(&quot;no&quot;, p.getNo());
                product.put(&quot;name&quot;, p.getName());
                product.put(&quot;price&quot;, p.getPrice());
                product.put(&quot;stock&quot;, p.getStock());
                data.put(product);
            }
            JSONObject response = new JSONObject();
            response.put(&quot;status&quot;, &quot;success&quot;);
            response.put(&quot;data&quot;, data);
            dos.writeUTF(response.toString());
            dos.flush();
        }
    }
}
</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[이것이 자바다 16일차 - Chapter18 데이터 입출력]]></title>
            <link>https://velog.io/@seo-faper/%EC%9D%B4%EA%B2%83%EC%9D%B4-%EC%9E%90%EB%B0%94%EB%8B%A4-16%EC%9D%BC%EC%B0%A8-Chapter18-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%9E%85%EC%B6%9C%EB%A0%A5</link>
            <guid>https://velog.io/@seo-faper/%EC%9D%B4%EA%B2%83%EC%9D%B4-%EC%9E%90%EB%B0%94%EB%8B%A4-16%EC%9D%BC%EC%B0%A8-Chapter18-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%9E%85%EC%B6%9C%EB%A0%A5</guid>
            <pubDate>Tue, 31 Jan 2023 16:55:28 GMT</pubDate>
            <description><![CDATA[<h2 id="입출력-스트림">입출력 스트림</h2>
<p>데이터는 키보드를 통해 입력될 수도 있고, 파일 또는 프로그램으로부터 입력될 수도 있다.
반대로 데이터는 모니터를 통해 출력될 수도 있고 파일에 저장되거나 다른 프로그램으로 전송될 수 있다. 이것을 총칭해서 데이터 입출력이라고 한다.</p>
<p>자바는 입력 스트림과 출력 스트림을 통해 데이터를 입출력한다. 스트림은 단방향으로 데이터가 흐르는 것을 말하는데 키보드를 통해 입력한 데이터가 프로그램에 대입되고 다시 모니터를 통해 출력 되는 것을 구현하기 위해서 입력 스트림과 출력 스트림이 필요하다.  </p>
<p>어떤 데이터를 입출력하느냐에 따라 스트림은 두 종류로 구분할 수 있다.</p>
<ul>
<li>바이트 스트림 : 그림, 멀티미디어, 문자 등 모든 종류의 데이터를 입출력 할 때 사용</li>
<li>문자 스트림 : 문자만 입출력할 때 사용</li>
</ul>
<p>바이트 입출력 스트림의 최상위 클래스는 InputStream과 OutputStream이다. 이 클래스를 상속 받는 자식 클래스에는 접미사로 InputStream 또는 OutputStream이 붙는다. 예를 들어 이미지와 같은 바이너리 파일의 입출력 스트림 클래스는 FileInputStream과 FileOutputStream이다.
문자 입출력 스트림의 최상위 클래스는 Reader와 Writer가 붙는다. 텍스트 파일의 입출력 스트림 클래스는 FileReader와 FileWriter이다.</p>
<h2 id="바이트-출력-스트림">바이트 출력 스트림</h2>
<pre><code class="language-java">package ch18.sec02.exam01;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;

public class WriteExample {
    public static void main(String[] args) {
        try{
            OutputStream os = new FileOutputStream(&quot;C:/Temp/test1.db&quot;);

            byte a = 10;
            byte b = 20;
            byte c = 30;

            os.write(a);
            os.write(b);
            os.write(c);

            os.flush();
            os.close();

        }catch(IOException e){

        }

    }
}
</code></pre>
<p>이렇게 바이트를 하나씩 쓸 수도 있고</p>
<pre><code class="language-java">package ch18.sec02.exam02;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;

public class WriteExample {
    public static void main(String[] args) {
        try{
            OutputStream os = new FileOutputStream(&quot;C:/Temp/test2.db&quot;);

            byte[] array = {10,20,30};
            os.write(array);

            os.flush();
            os.close();

        }catch(IOException e){
            e.printStackTrace();
        }

    }
}
</code></pre>
<p>바이트 배열로 쓸 수도 있다.</p>
<pre><code class="language-java">package ch18.sec02.exam03;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;

public class WriteExample {
    public static void main(String[] args) {
        try{
            OutputStream os = new FileOutputStream(&quot;C:/Temp/test3.db&quot;);

            byte[] array = {10,20,30,40,50};
            os.write(array, 1, 3);

            os.flush();
            os.close();

        }catch(IOException e){
            e.printStackTrace();
        }

    }
}
</code></pre>
<p>범위 지정도 가능하다.</p>
<h2 id="바이트-입력-스트림">바이트 입력 스트림</h2>
<pre><code class="language-java">package ch18.sec03.exam01;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;

public class ReadExample {
    public static void main(String[] args) {
        try{
            InputStream is = new FileInputStream(&quot;C:/Temp/test1.db&quot;);

            while (true){
                int data = is.read();
                if(data == -1)break;
                System.out.println(data);
            }
            is.close();
        }catch(FileNotFoundException e){
            e.printStackTrace();
        }catch(IOException e) {
            e.printStackTrace();
        }
    }
}
</code></pre>
<p>이렇게 하면 일전에 만들어 둔 test1.db 파일을 읽어 10 20 30을 출력한다.</p>
<pre><code class="language-java">package ch18.sec03.exam03;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;

public class CopyExample {
    public static void main(String[] args) throws Exception{
        String originalFileName = &quot;C:/Temp/test.jpg&quot;;
        String targetFileName = &quot;C:/Temp/test2.jpg&quot;;

        InputStream is = new FileInputStream(originalFileName);
        OutputStream os = new FileOutputStream(targetFileName);

        byte[] data = new byte[1024];
        while (true){
            int num = is.read(data);
            if (num == -1) break;

            os.write(data,0,num);

        }
        os.flush(); // 내부 버퍼 잔류 바이트를 출력하고 버퍼를 비움
        os.close();
        is.close();
        System.out.println(&quot;복사가 완료되었습니다.&quot;);
    }
}
</code></pre>
<p>기존의 파일을 복사할 수도 있다.</p>
<h2 id="문자-입출력-스트림">문자 입출력 스트림</h2>
<p>파일에 문자를 쓰는 방법은 다음과 같다.</p>
<pre><code class="language-java">package ch18.sec04.exam01;

import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;

public class WriteExample {
    public static void main(String[] args) {
        try{
            Writer writer = new FileWriter(&quot;C:/Temp/test.txt&quot;);

            char a = &#39;A&#39;;
            writer.write(a);
            char b = &#39;B&#39;;
            writer.write(b);

            char[] arr = {&#39;C&#39;,&#39;D&#39;,&#39;E&#39;};
            writer.write(arr);

            writer.write(&quot;FGH&quot;);

            writer.flush();
            writer.close();

        }catch(IOException e){

        }
    }
}
</code></pre>
<p>Writer에서 write()는 char 배열과 문자열을 같은 것으로 취급한다.
저러고 나서 C:/Temp/test.txt 가 생성되는데, 열어보면 ABCDEFGH 가 적혀져 있다.</p>
<p>파일을 읽어서 출력하는 방법은 다음과 같다.</p>
<pre><code class="language-java">package ch18.sec04.exam02;

import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;

public class ReadExample {
    public static void main(String[] args) {
        try{

            // 방법 1 
            Reader reader = null;
            reader = new FileReader(&quot;C:/Temp/test.txt&quot;);
            while (true){
                int data = reader.read();
                if(data == -1) break;
                System.out.print((char)data);
            }
            System.out.println();


            //방법 2 
            reader = new FileReader(&quot;C:/Temp/test.txt&quot;);
            char[] data = new char[100];
            while (true){
                int num = reader.read(data);
                if(num == -1) break;
                for(int i = 0; i&lt;num; i++){
                    System.out.print(data[i]);
                }
            }
            reader.close();
        }catch (FileNotFoundException e){
            e.printStackTrace();
        }catch(IOException e){
            e.printStackTrace();
        }
    }
}
</code></pre>
<p>직접 한 글자씩 읽을 수도 있고 byte 배열을 대입해서 읽을 수도 있다.</p>
<h2 id="보조-스트림">보조 스트림</h2>
<p>보조 스트림이란 다른 스트림과 연결되어 여러 가지 편리한 기능을 제공해주는 스트림을 말한다.
보조 스트림은 자체적으로 입출력을 수행할 수 없기 때문에 입출력 소스로부터 직접 생성된 입출력 스트림에 연결해서 사용해야 한다. </p>
<p><strong>보조스트림 변수 = new 보조스트림(입출력스트림);</strong></p>
<p>이렇게 입출력 스트림에 보조 스트림을 연결하려면 보조 스트림을 생성할 때 생성자 매개값으로 입출력 스트림을 제공하면 된다. </p>
<p>예를 들어 바이트 입력 스트림인 FileInputStream에 InputStreamReader 보조 스트림을 연결하는 코드는 다음과 같다.</p>
<pre><code class="language-java">InputStream is = new FileInputStream(&quot;...&quot;);
InputStreamReader reader = new InputStreamReader(is);</code></pre>
<p>보조 스트림은 또 다른 보조 스트림과 연결되어 스트림 체인으로 구성할 수 있다.
예를 들어 문자 변환 보조 스트림인 InputStreamReader에 BufferedReader 보조 스트림을 연결하는 코드는 다음과 같다.</p>
<pre><code class="language-java">InputStream is = new FileInputStream(&quot;...&quot;);
InputStreamReader reader = new InputStreamReaer(is);
BufferedReader br = new BufferedReader(reader);</code></pre>
<h2 id="문자-변환-스트림">문자 변환 스트림</h2>
<pre><code class="language-java">package ch18.sec06;

import java.io.*;

public class CharacterConvertStreamExample {
    public static void main(String[] args) throws Exception {
        write(&quot;문자 변환 스트림을 사용합니다.&quot;);
        String data = read();
        System.out.println(data);
    }
    public static void write(String str) throws Exception{
        OutputStream os = new FileOutputStream(&quot;C:/Temp/test.txt&quot;);
        Writer writer = new OutputStreamWriter(os, &quot;UTF-8&quot;);
        writer.write(str);
        writer.flush();
        writer.close();
    }
    public static String read() throws Exception{
        InputStream is = new FileInputStream(&quot;C:/Temp/test.txt&quot;);
        Reader reader = new InputStreamReader(is,&quot;UTF-8&quot;);
        char[] data = new char[100];
        int num = reader.read(data);
        reader.close();
        String str = new String(data,0,num);
        return str;
    }
}
</code></pre>
<p>이건 test.txt에 &quot;문자 변환 스트림을 사용합니다.&quot;를 쓰고 그 다음 다시 test.txt를 읽어서 해당 문자열을 출력하는 예제이다. UTF-8 문자셋으로 파일에 문자를 저장하고 저장된 문자를 다시 읽는 것이다.</p>
<h2 id="성능-향상-스트림">성능 향상 스트림</h2>
<p>CPU와 메모리가 아무리 뛰어나도 하드 디스크의 입출력이 늦어지면 프로그램의 실행 성능은 하드디스크의 처리 속도에 맞춰진다. 향상된 버퍼 스트림을 사용하면 된다.</p>
<pre><code class="language-java">package ch18.sec07.exam01;

import java.io.*;

public class BufferExample {
    public static void main(String[] args) throws Exception {
        String originalFilePath1 =
                BufferExample.class.getResource(&quot;originalFile1.jpg&quot;).getPath();
        String targetFilePath1 = &quot;C:/Temp/targetFile1.jpg&quot;;
        FileInputStream fis = new FileInputStream(originalFilePath1);
        FileOutputStream fos = new FileOutputStream(targetFilePath1);

        String originalFilePath2 =
                BufferExample.class.getResource(&quot;originalFile2.jpg&quot;).getPath();
        String targetFilePath2 = &quot;C:/Temp/targetFile2.jpg&quot;;
        FileInputStream fis2 = new FileInputStream(originalFilePath2);
        FileOutputStream fos2 = new FileOutputStream(targetFilePath2);
        BufferedInputStream bis = new BufferedInputStream(fis2);
        BufferedOutputStream bos = new BufferedOutputStream(fos2);

        long nonBufferTime = copy(fis,fos);
        System.out.println(&quot;버퍼 미사용: \t&quot;+nonBufferTime+&quot;ns&quot;);

        long bufferTime = copy(bis, bos);
        System.out.println(&quot;버퍼 사용: \t&quot;+bufferTime+&quot;ns&quot;);
        fis.close();
        fos.close();
        bis.close();
        bos.close();
    }
    public static long copy(InputStream is, OutputStream os) throws Exception{
        long start = System.currentTimeMillis();
        while (true){
            int data = is.read();
            if(data == -1 ) break;
            os.write(data);
        }
        os.flush();
        long end = System.currentTimeMillis();
        return (end- start);
    }
}
</code></pre>
<p><img src="https://velog.velcdn.com/images/seo-faper/post/8db542f3-80d9-4cd3-9e3e-cf7763ff1a2d/image.png" alt=""></p>
<p>이렇게 파일을 2개 준비하고 실행해 보면 버퍼를 쓰는게 훨씬 빠른걸 볼 수 있다.</p>
<h2 id="기본-타입-스트림">기본 타입 스트림</h2>
<p>바이트 스트림에 DataInputStream과 DataOutputStream 보조 스트림을 연결하면 기본 타입인 boolean, char,short, int, long, float, double 값을 입출력 할 수 있다.</p>
<pre><code class="language-java">package ch18.sec08;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;

public class DataInputOutputStreamExample {
    public static void main(String[] args) throws Exception {
        FileOutputStream fos = new FileOutputStream(&quot;C:/Temp/primitive.db&quot;);
        DataOutputStream dos = new DataOutputStream(fos);

        dos.writeUTF(&quot;홍길동&quot;);
        dos.writeDouble(95.5);
        dos.writeInt(1);

        dos.writeUTF(&quot;김자바&quot;);
        dos.writeDouble(85.5);
        dos.writeInt(2);

        dos.flush();    dos.close();    fos.close();

        FileInputStream fis = new FileInputStream(&quot;C:/Temp/primitive.db&quot;);
        DataInputStream dis = new DataInputStream(fis);

        for(int i = 0; i&lt;2; i++){
            String name = dis.readUTF();
            double score = dis.readDouble();
            int order = dis.readInt();
            System.out.println(name+&quot; : &quot;+score+&quot; : &quot;+order);
        }
        dis.close();fis.close();
    }
}
</code></pre>
<p>굳이 쓰진 않을거 같다.</p>
<h2 id="프린트-스트림">프린트 스트림</h2>
<p>PrintStream과 PrintWriter는 프린터와 유사하게 출력하는 print(), println(), printf() 메소드를 가지고 있는 보조 스트림이다. 지금까지 우리는 콘솔에 출력하기 위해 System.out.println()을 사용했는데, 그 이유는 out이 printStream 타입이기 때문이다.</p>
<pre><code class="language-java">package ch18.sec09;

import java.io.FileOutputStream;
import java.io.PrintStream;

public class PrintStreamExample {
    public static void main(String[] args) throws Exception{
        FileOutputStream fos = new FileOutputStream(&quot;C:/Temp/printStream.txt&quot;);
        PrintStream ps = new PrintStream(fos);

        ps.print(&quot;마치 &quot;);
        ps.println(&quot;프린터가 출력하는 것 처럼&quot;);
        ps.println(&quot;데이터를 출력합니다.&quot;);
        ps.printf(&quot;| %6d | %-10s | %10s | \n&quot;,1,&quot;홍길동&quot;,&quot;도적&quot;);
        ps.printf(&quot;| %6d | %-10s | %10s | \n&quot;,2,&quot;김자바&quot;,&quot;학생&quot;);
    }
}</code></pre>
<p>그냥 System.out.println()을 쓰자.</p>
<h2 id="객체-스트림">객체 스트림</h2>
<p>자바는 메모리에 생성된 객체를 파일 또는 네트워크로 출력할 수 있다.
객체를 출력하려면 필드값을 일렬로 늘어선 바이트로 변경해야 하는데, 이를 직렬화(serialization)이라고 한다. 
반대로 직렬화된 바이트를 객체의 필드값으로 복원하는 것을 역직렬화(deserialization)라고 한다.</p>
<p>ObjectInputStream과 ObjectOutputStream을 통해 구현할 수 있다.</p>
<pre><code class="language-java">package ch18.sec10;

import java.io.Serializable;

public class Member implements Serializable {
    private static final long serialVersionUID = -622284561026719240L;
    private String id;
    private String name;

    public Member(String id, String name) {
        this.id = id;
        this.name = name;
    }

    @Override
    public String toString() {
        return &quot;Member{&quot; +
                &quot;id=&#39;&quot; + id + &#39;\&#39;&#39; +
                &quot;, name=&#39;&quot; + name + &#39;\&#39;&#39; +
                &#39;}&#39;;
    }
}
</code></pre>
<pre><code class="language-java">package ch18.sec10;

import java.io.Serializable;

public class Product implements Serializable {
    private static final long serialVersionUID = -621812868470078544L;
    private String name;
    private int price;

    public Product(String name, int price) {
        this.name = name;
        this.price = price;
    }

    @Override
    public String toString() {
        return &quot;Product{&quot; +
                &quot;name=&#39;&quot; + name + &#39;\&#39;&#39; +
                &quot;, price=&quot; + price +
                &#39;}&#39;;
    }
}
</code></pre>
<p>이렇게 객체를 만들고 Serializable을 구현하는 구현체를 만들면 된다.</p>
<pre><code class="language-java">package ch18.sec10;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.Arrays;

public class ObjectInputOutputStreamExample {
    public static void main(String[] args) throws Exception{
        FileOutputStream fos = new FileOutputStream(&quot;C:/Temp/object.dat&quot;);
        ObjectOutputStream oos = new ObjectOutputStream(fos);

        Member m1 = new Member(&quot;fall&quot;,&quot;단풍이&quot;);
        Product p1 = new Product(&quot;노트북&quot;,150000);
        int[] arr1 = {1,2,3};

        oos.writeObject(m1);
        oos.writeObject(p1);
        oos.writeObject(arr1);
        oos.flush();oos.flush();oos.flush();

        //FileInputStream에 ObjectInputStream 보조 스트림 연결
        FileInputStream fis = new FileInputStream(&quot;C:/Temp/object.dat&quot;);
        ObjectInputStream ois = new ObjectInputStream(fis);

        //파일을 읽고 역직렬화
        Member m2 = (Member) ois.readObject();
        Product p2 = (Product) ois.readObject();
        int[] arr2 = (int[]) ois.readObject();

        ois.close(); fis.close();

        System.out.println(m2);
        System.out.println(p2);
        System.out.println(Arrays.toString(arr2));
    }
}
</code></pre>
<h2 id="연습문제">연습문제</h2>
<p><img src="https://velog.velcdn.com/images/seo-faper/post/e1f99222-9cfb-4f0b-90bf-d6ab672212da/image.png" alt=""></p>
<p>1번</p>
<p><img src="https://velog.velcdn.com/images/seo-faper/post/0da4b937-351f-4955-ab45-ab464a89a0f2/image.png" alt=""></p>
<p>1번</p>
<p><img src="https://velog.velcdn.com/images/seo-faper/post/26b5e115-7a3d-4f47-b77e-2ea01df19e90/image.png" alt=""></p>
<p>3번
<img src="https://velog.velcdn.com/images/seo-faper/post/7e7ef7ad-52be-4d2c-b62b-51065da87887/image.png" alt=""></p>
<p>1번</p>
<p><img src="https://velog.velcdn.com/images/seo-faper/post/22a30ac6-507c-4454-bf41-98d1832a8e87/image.png" alt=""></p>
<p>3번</p>
<p><img src="https://velog.velcdn.com/images/seo-faper/post/cc61ceea-8e0d-4233-a841-15833268b008/image.png" alt=""></p>
<p>3번</p>
<p><img src="https://velog.velcdn.com/images/seo-faper/post/45f4c3e1-d458-49f7-b63b-1e6e868e2c4d/image.png" alt=""></p>
<pre><code class="language-java">new FileReader(filePath);
new BufferedReader(fr);

rowData = br.readLine();
if(rowData == null){
    break;
}
System.out.println(++rowNumber + &quot;: &quot;+rowData);</code></pre>
<p><img src="https://velog.velcdn.com/images/seo-faper/post/49b7810f-4c46-4185-8470-057c34eb535b/image.png" alt=""></p>
<p>4번 </p>
<p><img src="https://velog.velcdn.com/images/seo-faper/post/33c7d6d4-629d-4ecf-b134-524a4969f1e6/image.png" alt=""></p>
<p>2번 </p>
<p><img src="https://velog.velcdn.com/images/seo-faper/post/b124623c-3e26-4518-9e9f-f1dfa0741ab1/image.png" alt=""></p>
<pre><code class="language-java">import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.util.Scanner;
public class Example {
public static void main(String[] args) {
     try {
         Scanner scanner = new Scanner(System.in);


         System.out.print(&quot;원본 파일 경로: &quot;);
           String originalFilePath = scanner.nextLine();

         System.out.print(&quot;복사 파일 경로: &quot;);
         String targetFilePath = scanner.nextLine();

         File originalFile = new File(originalFilePath);
         if(!originalFile.exists()) {
         System.out.println(&quot;원본 파일이 존재하지 않습니다.&quot;);
         System.exit(0);
         }

         File targetFile = new File(targetFilePath);
         File parentFile = targetFile.getParentFile();
         if(!parentFile.exists()) {
         parentFile.mkdirs();
         }

         BufferedInputStream bis = new BufferedInputStream(
         new FileInputStream(originalFilePath));
         BufferedOutputStream bos = new BufferedOutputStream(
         new FileOutputStream(targetFilePath));

         byte[] data = new byte[1024];
         int num = -1;
         while(true) {
         num = bis.read(data);
         if(num = = -1) break;
         bos.write(data, 0, num);
         }

         System.out.println(&quot;복사가 성공되었습니다.&quot;);


         bis.close();
         bos.close();
         } catch(Exception e) {
             e.printStackTrace();
         }
    }
}
</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[이것이 자바다 15일차 - Chapter17 스트림 요소 처리]]></title>
            <link>https://velog.io/@seo-faper/%EC%9D%B4%EA%B2%83%EC%9D%B4-%EC%9E%90%EB%B0%94%EB%8B%A4-14%EC%9D%BC%EC%B0%A8-Chapter17-%EC%8A%A4%ED%8A%B8%EB%A6%BC-%EC%9A%94%EC%86%8C-%EC%B2%98%EB%A6%AC</link>
            <guid>https://velog.io/@seo-faper/%EC%9D%B4%EA%B2%83%EC%9D%B4-%EC%9E%90%EB%B0%94%EB%8B%A4-14%EC%9D%BC%EC%B0%A8-Chapter17-%EC%8A%A4%ED%8A%B8%EB%A6%BC-%EC%9A%94%EC%86%8C-%EC%B2%98%EB%A6%AC</guid>
            <pubDate>Mon, 30 Jan 2023 13:29:48 GMT</pubDate>
            <description><![CDATA[<h2 id="스트림이란">스트림이란?</h2>
<p>지금까지 컬렉션 및 배열에 저장된 요소를 반복 처리하기 위해 for문이나 iterator를 썼다. 
java8 부터는 또 다른 방법으로 컬렉션 및 배열의 요소를 반복 처리하기 위해 Stream을 사용할 수 있다. 스트림은 요소들이 하나씩 흘러가면서 처리된다는 의미를 가지고 있다.</p>
<pre><code class="language-java">Stream&lt;String&gt; stream = list.stream();
stream.forEach( item -&gt; //처리 
                ); </code></pre>
<pre><code class="language-java">package ch17.sec01.exam01;

import java.util.HashSet;
import java.util.Set;
import java.util.stream.Stream;

public class StreamExample {
    public static void main(String[] args) {
        Set&lt;String&gt; set = new HashSet&lt;&gt;();
        set.add(&quot;홍길동&quot;);
        set.add(&quot;신용권&quot;);
        set.add(&quot;김자바&quot;);

        Stream&lt;String&gt; stream = set.stream();
        stream.forEach(e-&gt;System.out.println(e));
    }
}
</code></pre>
<p>Stream은 Iterator와 비슷한 반복자이지만 차이점이 있다.</p>
<ul>
<li>내부 반복자이므로 처리 속도가 빠르고 병렬 처리에 효율적이다.</li>
<li>람다식으로 다양한 요소 처리를 정의할 수 있다.</li>
<li>중간 처리와 최종 처리를 수행하도록 파이프 라인을 형성할 수 있다.</li>
</ul>
<h2 id="내부-반복자">내부 반복자</h2>
<p>for문과 Iterator는 컬렉션의 요소를 컬렉션 바깥쪽으로 반복해서 가져와 처리하는데, 이것을 외부 반복자라고 한다. 반면 스트림은 요소 처리 방법을 컬렉션 내부로 주입시켜 요소를 반복 처리하는데, 이것을 내부 반복자라고 한다. </p>
<p>외부 반복자일 경우 컬렉션의 요소를 외부로 가져오는 코드와 처리하는 코드를 모두 개발자 코드가 가지고 있어야 한다. 반면 내부 반복자일 경우는 개발자 코드에서 제공한 데이터 처리 코드(람다식)을 가지고 컬렉션 내부에서 요소를 반복 처리한다.</p>
<p>이는 멀티 코어 CPU를 최대한 활용하기 위해 요소들을 분배시켜 병렬 작업을 할 수 있다.
하나씩 처리하는 순차적 외부 반복자보다 효율적으로 요소를 반복시킬 수 있다는 장점이 있다.</p>
<pre><code class="language-java">package ch17.sec02;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;

public class ParallelStreamExample {
    public static void main(String[] args) {
        List&lt;String&gt; list = new ArrayList&lt;&gt;();
        list.add(&quot;홍길동&quot;);
        list.add(&quot;신용권&quot;);
        list.add(&quot;김자바&quot;);
        list.add(&quot;람다식&quot;);
        list.add(&quot;박병렬&quot;);

        Stream&lt;String&gt; parallelStream = list.parallelStream();
        parallelStream.forEach(e-&gt;{
            System.out.println(e+&quot;: &quot;+Thread.currentThread().getName());
        });
    }
}
</code></pre>
<p>이렇게 중괄호를 넣어서 내부 반복자를 구현할 수 있다.</p>
<h2 id="중간-처리와-최종-처리">중간 처리와 최종 처리</h2>
<p>스트림은 하나 이상 연결될 수 있다. 스트림에 또 다른 스트림을 이어지게 하는 것을 파이프라인 이라고 한다.</p>
<p>주로 필터링, 매핑 같은 데이터 정재 작업을 하고 마지막에 집계를 내는 식으로 쓴다.</p>
<pre><code class="language-java">package ch17.sec03;

import java.util.Arrays;
import java.util.List;

public class StreamPipeLineExample {
    public static void main(String[] args) {
        List&lt;Student&gt; list = Arrays.asList(
                new Student(&quot;홍길동&quot;,10),
                new Student(&quot;신용권&quot;,20),
                new Student(&quot;유미선&quot;,30)
        );

        double avg = list.stream()
                .mapToInt(student -&gt; student.getScore())
                .average()
                .getAsDouble();
        System.out.println(avg);
    }
}
</code></pre>
<h2 id="리소스로부터-스트림-얻기">리소스로부터 스트림 얻기</h2>
<p>스트림은 기본적으로  BaseStream 인터페이스를 부모로 한 자식인터페이스들로 이루어진다.</p>
<p>Stream은 객체 요소를 처리하는 스트림, IntStream, DoubleStream, LongStream등 기본타입에 대한 스트림도 존재한다.</p>
<h3 id="array---stream">Array &lt;-&gt; stream</h3>
<pre><code class="language-java">        String[] strArray = {&quot;홍길동&quot;,&quot;신용권&quot;,&quot;김미나&quot;};
        Stream&lt;String&gt; strStream = Arrays.stream(strArray);</code></pre>
<p>String일 때는 String이 객체이므로 그냥 Stream을 써주면 되고</p>
<pre><code class="language-java">        int[] intArray = {1,2,3,4};
        IntStream intStream = Arrays.stream(intArray);</code></pre>
<p>int 배열은 IntStream을 통해 변환 할 수 있다. 다른 자료형도 마찬가지.</p>
<pre><code class="language-java">package ch17.sec04.sec03;

import java.util.stream.IntStream;

public class StreamExample {
    public static int sum;

    public static void main(String[] args) {
        IntStream stream = IntStream.rangeClosed(1,100);
        stream.forEach(e-&gt;sum +=e);
        System.out.println(sum);
    }
}
</code></pre>
<p>이렇게 숫자 범위의 스트림을 얻을 수도 있다.</p>
<h3 id="파일로부터-스트림-읽기">파일로부터 스트림 읽기</h3>
<p>java.nio.file.Files.의 lines() 메소드를 이용하면 텍스트 파일의 행 단위 스트림을 읽을 수 있다.
<code>data.txt</code></p>
<pre><code>{pno: 1, name: 상품1, company: 멋진 회사, price: 2209}
{pno: 2, name: 상품2, company: 멋진 회사, price: 4272}
{pno: 3, name: 상품3, company: 멋진 회사, price: 787}
{pno: 4, name: 상품4, company: 멋진 회사, price: 5593}
{pno: 5, name: 상품5, company: 멋진 회사, price: 6645}</code></pre><p>이런 식의  텍스트 파일이 존재한다고 치자.</p>
<pre><code class="language-java">package ch17.sec04.sec04;

import java.io.IOException;
import java.net.URISyntaxException;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.stream.Stream;

public class StreamExample {
    public static void main(String[] args) throws URISyntaxException, IOException {
        Path path = Paths.get(StreamExample.class.getResource(&quot;data.txt&quot;).toURI());
        Stream&lt;String&gt; stream = Files.lines(path, Charset.defaultCharset());
        stream.forEach(e-&gt;System.out.println(e));
        stream.close();
    }
}
</code></pre>
<p>이렇게 경로를 지정 후 파일 스트림을 읽을 수 있다.</p>
<h2 id="요소-걸러내기">요소 걸러내기</h2>
<p>스트림에서 쓰는 필터링 메소드는 크게 distinct()와 filter가 있다.
disctinct()는 중복 제거, filter()는 특정 조건을 만족하는 요소만 스트림으로 만든다.</p>
<p>객체스트림일 경우 disctinct()의 동일비교는 equals()만으로 진행한다.</p>
<pre><code class="language-java">package ch17.sec05;

import java.util.ArrayList;
import java.util.List;

public class FilteringExample {
    public static void main(String[] args) {
        List&lt;String&gt; list = new ArrayList&lt;&gt;();
        list.add(&quot;홍길동&quot;);    list.add(&quot;신용권&quot;);
        list.add(&quot;신도라애몽&quot;); list.add(&quot;신민철&quot;);
        list.add(&quot;김기동&quot;);    list.add(&quot;김기동&quot;);

        list.stream().distinct().forEach(e-&gt;System.out.println(e)); // 중복 제거

        list.stream().filter(e-&gt; e.startsWith(&quot;신&quot;))
        .forEach(e-&gt;System.out.println(e)); // 성씨가 신인 것만 

        list.stream()
        .distinct()
        .filter(e-&gt;e.startsWith(&quot;신&quot;))
        .forEach(e-&gt;System.out.println(e)); // 파이프라인 
    }
}</code></pre>
<p>이런 식으로 응용 할 수 있다.</p>
<h2 id="요소-변환매핑">요소 변환(매핑)</h2>
<p>매핑 스트림의 요소를 다른 요소로 변환하는 기능이 있다.</p>
<pre><code class="language-java">package ch17.sec06.exam02;

import java.util.Arrays;
import java.util.stream.IntStream;

public class MapExample {
    public static void main(String[] args) {
        int[] intArray = {1,2,3,4,5};

        IntStream intStream = Arrays.stream(intArray);
        intStream
                .asDoubleStream()
                .forEach(e-&gt; System.out.println(e));

        System.out.println();

        intStream = Arrays.stream(intArray);
        intStream.boxed().forEach(obj -&gt; System.out.println(obj.intValue()));
    }
}
</code></pre>
<h2 id="요소-정렬">요소 정렬</h2>
<pre><code class="language-java">package ch17.sec07.exam01;


public class Student implements Comparable&lt;Student&gt; {
    private String name;
    private int score;

    public Student(String name, int score) {
        this.name = name;
        this.score = score;
    }
    public String getName() {return name;}
    public int getScore(){return score;}

    @Override
    public int compareTo(Student o){
        return Integer.compare(score, o.score);
    }
}
</code></pre>
<pre><code class="language-java">package ch17.sec07.exam01;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;

public class SortingExample {
    public static void main(String[] args) {
        List&lt;Student&gt; studentList = new ArrayList&lt;&gt;();
        studentList.add(new Student(&quot;홍길동&quot;,30));
        studentList.add(new Student(&quot;신용권&quot;,10));
        studentList.add(new Student(&quot;유미선&quot;,20));

        studentList.stream()
                .sorted()
                .forEach(e-&gt;System.out.println(e.getName()+&quot; : &quot;+e.getScore()));
        System.out.println(&quot;&quot;);

        studentList.stream()
                .sorted(Comparator.reverseOrder())
                .forEach(e-&gt;System.out.println(e.getName()+&quot; : &quot;+e.getScore()));


    }
}
</code></pre>
<h2 id="요소를-하나씩-처리-루핑">요소를 하나씩 처리 (루핑)</h2>
<p>루핑은 스트림에서 요소를 하나씩 반복해서 가져와 처리하는 것을 말한다.
루핑 메소드에는 peek()과 forEach()가 있다.</p>
<pre><code class="language-java">package ch17.sec08;

import java.util.Arrays;

public class LoopingExample {
    public static void main(String[] args) {
        int[] intArr = {1,2,3,4,5};
        Arrays.stream(intArr)
                .filter(e-&gt;e%2==0)
                .peek(n-&gt;System.out.println(n)); // 최종 처리가 없으므로 작동하지 않음
        int total = Arrays.stream(intArr)
                .filter(e-&gt;e%2==0)
                .peek(n-&gt;System.out.println(n))
                .sum();
        System.out.println(total);

        Arrays.stream(intArr)
                .filter(e-&gt;e%2==0)
                .forEach(n-&gt;System.out.println(n));
    }
}
</code></pre>
<h2 id="요소-조건-만족-여부매칭">요소 조건 만족 여부(매칭)</h2>
<pre><code class="language-java">package ch17.sec09;

import java.util.Arrays;

public class MatchingExample {
    public static void main(String[] args) {
        int[] intArr = {2,4,6};

        boolean result = Arrays.stream(intArr).allMatch(a -&gt;a%2==0);// 짝수인 것만 존재하면 true
    }
}</code></pre>
<p>allMatch() : 모든 요소가 조건을 만족해야함</p>
<pre><code class="language-java">        result = Arrays.stream(intArr).anyMatch(a -&gt; a%3 ==0); // 하나라도 3의 배수가 존재하면 true
        System.out.println(result);</code></pre>
<p>anyMatch() : 하나라도 조건을 만족하면 true</p>
<pre><code class="language-java">        result = Arrays.stream(intArr).noneMatch(a -&gt; a%3==0); // 3의 배수가 하나도 존재하지 않으면 true
        System.out.println(result);</code></pre>
<p>nonMatch() 단 하나도 만족하는 조건이 없으면 true</p>
<h2 id="요소-기본-집계">요소 기본 집계</h2>
<p>스트림은 카운팅, 최대, 최소, 평균, 합계 등을 구할 수 있다.</p>
<pre><code class="language-java">package ch17.sec10;

import java.util.Arrays;

public class AggregateExample {
    public static void main(String[] args) {
        int[] arr = {1,2,3,4,5};

        long count = Arrays.stream(arr).filter(e -&gt; e%2==0).count();
        long sum = Arrays.stream(arr).filter(e-&gt;e%2==0).sum();
        double avg = Arrays.stream(arr).filter(e-&gt;e%2==0).average().getAsDouble();
        int max = Arrays.stream(arr).filter(e -&gt; e%2==0).max().getAsInt();
        int min = Arrays.stream(arr).filter(e -&gt; e%2==0).min().getAsInt();
    }
}
</code></pre>
<h2 id="요소-커스텀-집계">요소 커스텀 집계</h2>
<p>합, 평균, 최소, 최대 등 이것 말고도 다양한 집계 결과물을 볼 수 있도록 reduce(); 메소드를 지원한다.</p>
<p>reduce()는 스트림에 요소가 없을 경우 예외가 발생하지만 indentity 매개값이 주어지면 이 값을 디폴트 값으로 리턴한다. 다음 중 왼쪽 코드는 스트림에 요소가 없을 경우 NoSuchElementException을 발생시키지만, 기본값을 넣어주면 해당 디폴트값을 리턴한다.</p>
<pre><code class="language-java">int sum = stream.reduce(0, (a,b) -&gt; a+b).getAsInt();</code></pre>
<pre><code class="language-java">package ch17.sec11;

import java.util.Arrays;
import java.util.List;

public class ReductionExample {
    public static void main(String[] args) {
        List&lt;Student&gt; studentList = Arrays.asList(
                new Student(&quot;홍길동&quot;,92),
                new Student(&quot;신용권&quot;,95),
                new Student(&quot;김자바&quot;,88)
        );
        int sum1 = studentList.stream()
                .mapToInt(Student::getScore).sum();

        int sum2 = studentList.stream()
                .map(Student::getScore)
                .reduce(0, (a,b)-&gt;a+b);

        System.out.println(sum1);
        System.out.println(sum2);
    }
}
</code></pre>
<h2 id="요소-수집">요소 수집</h2>
<pre><code class="language-java">package ch17.sec12.exam01;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class CollectExample {
    public static void main(String[] args) {
        List&lt;Student&gt; totalList = new ArrayList&lt;&gt;();
        totalList.add(new Student(&quot;홍길동&quot;,&quot;남&quot;,92));
        totalList.add(new Student(&quot;김수영&quot;,&quot;여&quot;,87));
        totalList.add(new Student(&quot;김자바&quot;,&quot;남&quot;,95));

        List&lt;Student&gt; maleList = totalList.stream().filter(e-&gt;e.getSex().equals(&quot;남&quot;)).toList();
        System.out.println();
        Map&lt;String, Integer&gt; map = totalList.stream()
                .collect(
                        Collectors.toMap(s -&gt;s.getName(),s-&gt;s.getScore())
                );
        System.out.println(map);
    }
}
</code></pre>
<h2 id="요소-그룹핑">요소 그룹핑</h2>
<pre><code class="language-java">package ch17.sec12.exam02;

import ch17.sec12.exam01.Student;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class CollectExample {
    public static void main(String[] args) {
        List&lt;Student&gt; totalList = new ArrayList&lt;&gt;();
        totalList.add(new Student(&quot;홍길동&quot;,&quot;남&quot;,92));
        totalList.add(new Student(&quot;김수영&quot;,&quot;여&quot;,87));
        totalList.add(new Student(&quot;김자바&quot;,&quot;남&quot;,95));

        Map&lt;String, List&lt;Student&gt;&gt; map = totalList.stream().collect(
                Collectors.groupingBy(s -&gt; s.getSex())
        );

        List&lt;Student&gt; maleList = map.get(&quot;남&quot;);
        maleList.stream().forEach(s-&gt;System.out.println(s.getName()));
        System.out.println();

        List&lt;Student&gt; famaleList = map.get(&quot;여&quot;);
        famaleList.forEach(s-&gt;System.out.println(s.getName()));


    }
}
</code></pre>
<pre><code class="language-java">package ch17.sec12.exam03;

import ch17.sec12.exam01.Student;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class CollectExample {
    public static void main(String[] args) {
        List&lt;ch17.sec12.exam01.Student&gt; totalList = new ArrayList&lt;&gt;();
        totalList.add(new ch17.sec12.exam01.Student(&quot;홍길동&quot;,&quot;남&quot;,92));
        totalList.add(new ch17.sec12.exam01.Student(&quot;김수영&quot;,&quot;여&quot;,87));
        totalList.add(new Student(&quot;김자바&quot;,&quot;남&quot;,95));

        Map&lt;String,Double&gt; map = totalList.stream()
                .collect(
                        Collectors.groupingBy(
                                s-&gt;s.getSex(),
                                Collectors.averagingDouble(s-&gt;s.getScore())
                        )
                );
        System.out.println(map);
    }
}</code></pre>
<h2 id="요소-병렬-처리">요소 병렬 처리</h2>
<p>요소 병렬 처리란 멀티 코어 CPU 환경에서 전체 요소를 분할해서 각각의 코어가 병렬적으로 처리하는 것을 말한다. 요소 병렬 처리의 목적은 작업 처리 시간을 줄이기 위한 것에 있따. 자바를 요소 병렬 처리를 위해 병렬 스트림을 제공한다.</p>
<h3 id="동시성과-병렬성">동시성과 병렬성</h3>
<p>멀티 스레드는 동시성 또는 병렬성으로 실행된다. 동시성을 멀티 작업을 위해 멀티 스레드가 하나의 코어에서 번갈아 가며 실행하는 것을 말하고, 병렬성은 멀티 작업을 위해 멀티 코어를 각각 이용해서 병렬로 실행하는 것을 말한다.</p>
<p>동시성은 한 시점에 하나의 작업만 실행한다. 번갈아 작업을 실행하는 것이 워낙 빠르다보니 동시에 처리되는 것 처럼 보일 뿐이다. 병렬성은 한 시점에 여러 개의 작업을 병렬로 실행하기 때문에 동시성보다 좋은 성능을 낸다.</p>
<p><strong>데이터 병렬성</strong>
데이터 병렬성은 전체 데이터를 분할해서 서브 데이터셋으로 만들고 이 서브 데이터셋들을 병렬 처리해서 작업을 빨리 끝내는 것을 말한다. 자바 병렬 스트림은 데이터 병렬성을 구현한 것이다.</p>
<p><strong>작업 병렬성</strong>
작업 병렬성은 서로 다른 작업을 병렬 처리하는 것을 말한다. 작업 병렬성의 대표적인 예는 서버 프로그램이다. 서버는 각각의 클라이언트에서 요청한 내용을 개별 스레드에서 병렬로 처리한다.</p>
<h3 id="포크조인-프레임워크">포크조인 프레임워크</h3>
<p>자바 병렬 스트림은 요소들을 병렬 처리하기 위해 포크조인 프레임워크를 사용한다. 포크조인 프레임워크는 포크 단계에서 전체 요소들을 서브 요소셋으로 분할하고, 각각의 서브 요소셋을 멀티 코어에서 병렬로 처리한다. 조인 단계에서는 서브 결과를 결합해서 최종 결과를 만들어낸다.
예를 들어 쿼드 코어 CPU에서 병렬 스트림으로 요소들을 처리할 경우 먼저 포크 단계에서 스트림의 전체 요소들을 4개의 서버 요소셋으로 분할한다.  그리고 각각의 서브 요소셋을 개별 코어에서 처리하고, 조인 단게에서는 3번의 결합 과정을 거쳐 최종 결과를 산출한다.</p>
<h3 id="병렬-스트림-사용">병렬 스트림 사용</h3>
<p>자바에서는 스트림을 쓰면 쉽게 병렬처리를 할 수 있다.</p>
<p>parallelStream()과 parallel()을 이용해 자동으로 포크조인 프레임워크를 쓰는 기능을 사용할 수 있다. </p>
<pre><code class="language-java">package ch17.sec13;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.stream.Stream;

public class ParallelExample {
    public static void main(String[] args) {
        Random random = new Random();
        List&lt;Integer&gt; scores = new ArrayList&lt;&gt;();
        for(int i = 0; i&lt;100000000; i++){
            scores.add(random.nextInt(101));
        }
        double avg = 0.0;
        long startTime = 0;
        long endTime = 0;
        Stream&lt;Integer&gt; stream = scores.stream();
        startTime = System.nanoTime();
        avg = stream.mapToInt(i-&gt;i.intValue()).average().getAsDouble();
        endTime = System.nanoTime();
        System.out.println(&quot;일반 스트림 처리 시간 : &quot;+(endTime-startTime)+&quot;ns&quot;);

        Stream&lt;Integer&gt; parallelStream = scores.parallelStream();
        startTime = System.nanoTime();
        avg = parallelStream.mapToInt(i-&gt;i.intValue()).average().getAsDouble();
        endTime = System.nanoTime();
        System.out.println(&quot;병렬 스트림 처리 시간 : &quot;+(endTime-startTime)+&quot;ns&quot;);
    }
}
</code></pre>
<pre><code class="language-bash">일반 스트림 처리 시간 : 106322100ns
병렬 스트림 처리 시간 : 48725000ns

Process finished with exit code 0
</code></pre>
<p>속도 차이는 엄청나다.</p>
<h2 id="연습문제">연습문제</h2>
<p><img src="https://velog.velcdn.com/images/seo-faper/post/5095e07c-5e48-4f5d-9334-ba07dcd73db9/image.png" alt="">
4번 요소를 모두 처리하고 나면 스트림이 끝납니다.
<img src="https://velog.velcdn.com/images/seo-faper/post/7ddae8ea-35c2-48c4-9810-9cb78e206b6d/image.png" alt=""></p>
<p>2번, 범위를 배열로 바꿔줘야 합니다.</p>
<p><img src="https://velog.velcdn.com/images/seo-faper/post/6dbf4cbb-c064-4f95-a673-b36f4bd81d1f/image.png" alt=""></p>
<p>4번 중간처리는 최종 처리가 있을 때만 가능합니다.</p>
<p><img src="https://velog.velcdn.com/images/seo-faper/post/475e9061-897f-4f39-a75d-b30902480b23/image.png" alt=""></p>
<p>3번 항상 빠른건 아닙니다. </p>
<p><img src="https://velog.velcdn.com/images/seo-faper/post/8f5250b1-6260-4515-a9fa-e7ed421c0c39/image.png" alt="">
<img src="https://velog.velcdn.com/images/seo-faper/post/c915542b-4eed-42f4-b8f5-b3153f92b659/image.png" alt="">
.filter(e-&gt;e.toLowerCase().contains(&quot;java&quot;).forEach(e-&gt;System.out.println(e));</p>
<p><img src="https://velog.velcdn.com/images/seo-faper/post/e8b193e0-fc5a-4ad6-9524-60eb989f0026/image.png" alt="">
<img src="https://velog.velcdn.com/images/seo-faper/post/3b73a2ea-70d3-41b3-923a-2933e9d373a1/image.png" alt="">
.mapToInt(Member::getAge).average().getAsDouble();</p>
<p><img src="https://velog.velcdn.com/images/seo-faper/post/bf7a71c3-f06f-4b71-8aec-88f7686535b1/image.png" alt="">
<img src="https://velog.velcdn.com/images/seo-faper/post/0f7df313-ce7d-4627-899a-5ffe509c45aa/image.png" alt=""></p>
<p>.filter(m-&gt;m.getJob().equals(&quot;개발자&quot;)).collect(Collectors.toList());</p>
<p><img src="https://velog.velcdn.com/images/seo-faper/post/9d93f310-1e72-448f-861b-025583194e8d/image.png" alt="">
<img src="https://velog.velcdn.com/images/seo-faper/post/fecd5d31-ba9e-4806-a465-973705e78b5e/image.png" alt=""></p>
<pre><code class="language-java">.collect(Collectors.groupingBy(m -&gt; m.getJob()));
</code></pre>
<pre><code class="language-java">groupingMap.get(&quot;개발자&quot;).stream()
 .forEach(m -&gt; System.out.println(m));</code></pre>
<pre><code class="language-java">groupingMap.get(&quot;디자이너&quot;).stream()
 .forEach(m -&gt; System.out.println(m));</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Java] 프로그래머스 기능 개발 풀이]]></title>
            <link>https://velog.io/@seo-faper/Java-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EA%B8%B0%EB%8A%A5-%EA%B0%9C%EB%B0%9C-%ED%92%80%EC%9D%B4</link>
            <guid>https://velog.io/@seo-faper/Java-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EA%B8%B0%EB%8A%A5-%EA%B0%9C%EB%B0%9C-%ED%92%80%EC%9D%B4</guid>
            <pubDate>Fri, 27 Jan 2023 16:30:44 GMT</pubDate>
            <description><![CDATA[<h2 id="문제">문제</h2>
<p>프로그래머스 팀에서는 기능 개선 작업을 수행 중입니다. 각 기능은 진도가 100%일 때 서비스에 반영할 수 있습니다.</p>
<p>또, 각 기능의 개발속도는 모두 다르기 때문에 뒤에 있는 기능이 앞에 있는 기능보다 먼저 개발될 수 있고, 이때 뒤에 있는 기능은 앞에 있는 기능이 배포될 때 함께 배포됩니다.</p>
<p>먼저 배포되어야 하는 순서대로 작업의 진도가 적힌 정수 배열 progresses와 각 작업의 개발 속도가 적힌 정수 배열 speeds가 주어질 때 각 배포마다 몇 개의 기능이 배포되는지를 return 하도록 solution 함수를 완성하세요.</p>
<h2 id="제한-사항">제한 사항</h2>
<ul>
<li>작업의 개수(progresses, speeds배열의 길이)는 100개 이하입니다.</li>
<li>작업 진도는 100 미만의 자연수입니다.</li>
<li>작업 속도는 100 이하의 자연수입니다.</li>
<li>배포는 하루에 한 번만 할 수 있으며, 하루의 끝에 이루어진다고 가정합니다. 예를 들어 진도율이 95%인 작업의 개발 속도가 하루에 4%라면 배포는 2일 뒤에 이루어집니다.</li>
</ul>
<h2 id="테스트-케이스">테스트 케이스</h2>
<table>
<thead>
<tr>
<th>progresses</th>
<th>speeds</th>
<th>return</th>
</tr>
</thead>
<tbody><tr>
<td>[93, 30, 55]</td>
<td>[1, 30, 5]</td>
<td>[2, 1]</td>
</tr>
<tr>
<td>[95, 90, 99, 99, 80, 99]</td>
<td>[1, 1, 1, 1, 1, 1]</td>
<td>[1, 3, 2]</td>
</tr>
</tbody></table>
<p>입출력 예 #1
첫 번째 기능은 93% 완료되어 있고 하루에 1%씩 작업이 가능하므로 7일간 작업 후 배포가 가능합니다.
두 번째 기능은 30%가 완료되어 있고 하루에 30%씩 작업이 가능하므로 3일간 작업 후 배포가 가능합니다. 하지만 이전 첫 번째 기능이 아직 완성된 상태가 아니기 때문에 첫 번째 기능이 배포되는 7일째 배포됩니다.
세 번째 기능은 55%가 완료되어 있고 하루에 5%씩 작업이 가능하므로 9일간 작업 후 배포가 가능합니다.</p>
<p>따라서 7일째에 2개의 기능, 9일째에 1개의 기능이 배포됩니다.</p>
<h2 id="풀이">풀이</h2>
<p>[7,3,9] -&gt; [2,1]</p>
<p>[1,2,3,4,1,2,3] -&gt; [1,1,1,4] </p>
<p>이렇게 보니 증가할때는 계속 1을 추가하고 이후에 작은게 나오면 더 큰 수가 나올 때 까지 그 수만큼 더해주면 된다. 이게 처음 떠오른 풀이고 코드로 그대로 옮기면 다음과 같다.</p>
<ol>
<li>progresses와 speeds를 적절히 연산해서 스택 L을 하나 만든다.
첫 번째 테스트 케이스에서 [7,3,9] 이라서 [2, 1]이 나오므로
(100 - 진행률 ) / 속도 + 나머지가 있으면 1 없으면 0
그런데 7부터 뺄거기 때문에 스택은 역순으로 만들어준다.</li>
</ol>
<ol start="2">
<li><p>빈 스택 sk를 하나 선언, 스택 L이 빌 때 까지 반복한다.</p>
</li>
<li><p>만약 sk가 비어있으면 L을 pop 해 push, init에 sk의 peek를 저장한다.</p>
</li>
<li><p>비어있지 않을 시, init이 L의 peek보다 크거나 같은지 비교한다.</p>
</li>
<li><p>같다면 sk에 L을 pop해 넣는다.</p>
</li>
<li><p>init이 L의 peek보다 작다면 ans에 현재 sk의 사이즈를 더하고 init을 sk의 peek로 저장한다.</p>
</li>
<li><p>sk를 비운다.</p>
</li>
</ol>
<pre><code class="language-java">
import java.util.ArrayList;
import java.util.Stack;

class Solution {
    public int[] solution(int[] progresses, int[] speeds) {

        Stack&lt;Integer&gt; L = new Stack&lt;&gt;();

        for(int i = progresses.length-1; i&gt;=0; i--){
            int daily = (100 - progresses[i]) / speeds[i];
            if ((100 - progresses[i]) % speeds[i] != 0) daily++;
            L.push(daily);
        }

        System.out.println(L.toString());
        Stack&lt;Integer&gt; sk = new Stack&lt;&gt;();
        ArrayList&lt;Integer&gt; ans = new ArrayList&lt;&gt;();
        int init = 0;
        while (!L.isEmpty()){
            if(sk.isEmpty()) {
                sk.push(L.pop());
                init = sk.peek();
            }else{

                if(init &gt;= L.peek()){
                    sk.push(L.pop());
                }else{
                    ans.add(sk.size());
                    init = sk.peek();
                    sk.clear();
                }
            }

        }
        ans.add(sk.size());
        int[] answer = ans.stream().mapToInt(i -&gt; i).toArray();
        return answer;
    }
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[이것이 자바다 14일차 - Chapter16 람다식]]></title>
            <link>https://velog.io/@seo-faper/%EC%9D%B4%EA%B2%83%EC%9D%B4-%EC%9E%90%EB%B0%94%EB%8B%A4-13%EC%9D%BC%EC%B0%A8-Chapter16-%EB%9E%8C%EB%8B%A4%EC%8B%9D</link>
            <guid>https://velog.io/@seo-faper/%EC%9D%B4%EA%B2%83%EC%9D%B4-%EC%9E%90%EB%B0%94%EB%8B%A4-13%EC%9D%BC%EC%B0%A8-Chapter16-%EB%9E%8C%EB%8B%A4%EC%8B%9D</guid>
            <pubDate>Fri, 27 Jan 2023 11:12:20 GMT</pubDate>
            <description><![CDATA[<h2 id="람다식이란">람다식이란</h2>
<p>함수형 프로그래밍에서 나온 기법으로 함수를 정의하고 함수를 데이터 처리부로 보내 데이터를 처리하는 기법을 말한다. 데이터 처리부는 데이터만 가지고 있을 뿐, 처리 방법이 정해져 있지 않아 외부에서 제공된 함수에 의존한다.</p>
<p>람다식은 다음과 같이 쓸 수 있다.</p>
<pre><code class="language-java">(매개변수, ...) -&gt; { 처리 내용 }</code></pre>
<p>자바는 람다식을 익명 객체로 변환한다. 
익명 객체란 이름이 없는 인터페이스 구현 객체를 말한다.</p>
<pre><code class="language-java">public interface Calculable {
    void calculate(int x, int y);
}</code></pre>
<p>Calculable 인터페이스의 익명 구현 객체는 다음과 같이 생성할 수 있다.</p>
<pre><code class="language-java">new Calculable(){
    @Override
    public void calculate(int x, int y) { ... 처리 내용 }
}</code></pre>
<p>그리고 이것을 람다식으로 표현하면 다음과 같다.</p>
<pre><code class="language-java">(x, y) -&gt; { 처리 내용 }</code></pre>
<p>람다식은 인터페이스의 익명 구현 객체이므로 인터페이스 타입의 매개변수에 대입될 수 있다.
예를 들어 다음과 같이 Calculable 매개변수를 가지고 있는 action() 메소드가 있다고 가정해보자.</p>
<pre><code class="language-java">public void action(Calculable calculable){
    int x = 10;
    int y = 4;
    calculable.calculate(x,y); //ㅔ이터를 제공하고 추상 메소드를 호출
}</code></pre>
<p>action() 메소드를 호출할 때 매개값으로 다음과 같이 람다식을 제공할 수 있다.</p>
<pre><code class="language-java">action( (x,y) -&gt; { 
    int result = x + y;
    System.out.println(result);
});</code></pre>
<p>또한 인터페이스가 단 하나의 추상 메소드를 가지는 것을 함수형 인터페이스라고 한다.
그걸 람다식으로 줄여서 쓸 수 있는 것이다.</p>
<p>인터페이스가 함수형 인터페이스임을 보장하기 위해선 @FunctionalInterface 어노테이션을 붙이면 된다. 이걸 적으면 컴파일 과정에서 추상 메소드가 하나인지 검사하기 때문에 더욱 정확한 함수형 인터페이스를 작성할 수 있다.</p>
<h2 id="매개변수가-없는-람다식">매개변수가 없는 람다식</h2>
<pre><code class="language-java">package ch16.sec02.exam01;

@FunctionalInterface
public interface Workable {
    void work();
}
</code></pre>
<pre><code class="language-java">package ch16.sec02.exam01;

public class Person {
    public void action(Workable workable){
        workable.work();
    }
}
</code></pre>
<pre><code class="language-java">package ch16.sec02.exam01;

public class LamdaExample {
    public static void main(String[] args) {
        Person person = new Person();

        person.action(()-&gt;{
            System.out.println(&quot;출근을 합니다.&quot;);
            System.out.println(&quot;프로그래밍을 합니다.&quot;);
        });

        person.action(()-&gt;System.out.println()); //실행문이 한개면 중괄호 생략 가능
    }
}
</code></pre>
<p>걍 이렇게 쓰면 된다.</p>
<h2 id="매개변수가-있는-람다식">매개변수가 있는 람다식</h2>
<p>추상 메소드에 매개변수가 있을 시 람다식에서 매개변수 타입을 생략할 수 있고 구체적인 타입 대신에 var를 사용할 수도 있다. </p>
<pre><code class="language-java">(타입 매개변수, ...) -&gt;{
    실행문;
    실행문;
}
</code></pre>
<pre><code class="language-java">(var 매개변수, ...) -&gt;{
    실행문;
    실행문;
}</code></pre>
<pre><code class="language-java">package ch16.sec03;

@FunctionalInterface
public interface Speakable {
    void speak(String content);
}
</code></pre>
<pre><code class="language-java">package ch16.sec03;

@FunctionalInterface
public interface Workable {
    void work(String name, String job);
}
</code></pre>
<pre><code class="language-java">package ch16.sec03;

public class Person {
    public void action1(Workable workable){
        workable.work(&quot;홍길동&quot;,&quot;프로그래밍&quot;);
    }
    public void action2(Speakable speakable){
        speakable.speak(&quot;안녕하세요.&quot;);
    }
}
</code></pre>
<pre><code class="language-java">package ch16.sec03;

public class LambdaExample {
    public static void main(String[] args) {
        Person person = new Person();

        person.action1((name, job) -&gt; {
            System.out.println(name+&quot;이&quot;);
            System.out.println(job+&quot;을 합니다.&quot;);
        });

        person.action2(word-&gt;{
            System.out.println(word+&quot;라고 말합니다.&quot;);
        });
    }
}
</code></pre>
<p>이렇게 매개변수를 람다식에 넣어 쓰면 된다.</p>
<h2 id="리턴값이-있는-람다식">리턴값이 있는 람다식</h2>
<p>함수형 인터페이스의 추상 메소드에 리턴값이 있을 경우 람다식은 다음과 같이 작성할 수 있다.</p>
<pre><code class="language-java">(매개변수, ...) -&gt;{
    실행문;
    return 값;
}</code></pre>
<p>그리고 return문이 하나뿐일 경우 값 하나로 대채할 수 있다. </p>
<pre><code class="language-java">(매개변수, ...) -&gt; 값</code></pre>
<pre><code class="language-java">package ch16.sec04;

public class LambdaExample {
    public static void main(String[] args) {
        Person person = new Person();

        person.action((x, y) -&gt; {
            double result = x + y;
            return result;
        });

        person.action((x, y) -&gt; (x+y));
    }
}
</code></pre>
<p>이런식으로 쓸 수 있다.</p>
<h2 id="메소드-참조">메소드 참조</h2>
<p>메소드 참조는 매개변수의 정보 및 리턴 타입을 알아내 람다식에서 불필요한 매개변수를 제거하는 것을 목적으로 한다. </p>
<p>예를 들어 두 개의 값을 받아 큰 수를 리턴하는 Math 클래스의 max() 정적 메소드를 호출하는 람다식은 다음과 같다.</p>
<pre><code class="language-java">(left,right) -&gt; Math.left,right);</code></pre>
<p>이 표현식을 람다의 메소드 참조를 이용해 표현하면 다음과 같다.</p>
<pre><code class="language-java">Math :: max;</code></pre>
<p>기존의 쓰던 방식을 줄여서 쓸 수 있기 때문에 편리하다.</p>
<p>매개변수의 메소드 참조 역시 가능한데,</p>
<pre><code class="language-java">(a,b) -&gt; { a.methodA(b); }</code></pre>
<p>이것을 메소드 참조로 표현하면 다음과 같다. a의 클래스 이름 뒤에 :: 기호를 붙이고 메소드 이름을 기술한다. 작성 방법은 정적 메소드 참조와 동일하지만, a의 인스턴스 메소드가 사용된다는 점에서 다르다.</p>
<pre><code class="language-java">클래스 :: methodA</code></pre>
<h2 id="생성자-참조">생성자 참조</h2>
<p>생성자를 참조한다는 것은 객체를 생성하는 것을 의미한다. 람다식이 단순히 객체를 생성하고 리턴하도록 구성된다면 람다식을 생성자 참조로 대치할 수 있다. 다음 코드를 보면 람다식은 단순히 객체를 생성한 후 리턴만 한다.</p>
<pre><code class="language-java">(a,b) -&gt; {return new 클래스(a,b);}</code></pre>
<p>이것을 생성자 참조로 표현하면 다음과 같다. 클래스 이름 뒤에 :: 기호를 붙이고 new 연산자를 기술하면 된다.</p>
<pre><code class="language-java">클래스 :: new </code></pre>
<pre><code class="language-java">package ch16.sec05.exam03;

@FunctionalInterface
public interface Creatable1 {
    public Member create(String id);
}
</code></pre>
<pre><code class="language-java">package ch16.sec05.exam03;

@FunctionalInterface
public interface Creatable2 {
    public Member create(String id, String name);
}
</code></pre>
<pre><code class="language-java">package ch16.sec05.exam03;

public class Member {
    private String id;
    private String name;
    public Member(String id){
        this.id = id;
        System.out.println(&quot;Member(String id)&quot;);
    }

    public Member(String id, String name){
        this.id = id;
        this.name = name;
        System.out.println(&quot;Member(String id, String name)&quot;);
    }

    @Override
    public String toString() {
        String info = &quot;{id: }&quot;+id+&quot;, name: &quot;+name+&quot;}&quot;;
        return info;
    }
}
</code></pre>
<pre><code class="language-java">package ch16.sec05.exam03;

public class Person {
    public Member getMember1(Creatable1 creatable){
        String id = &quot;winter&quot;;
        Member member = creatable.create(id);
        return member;
    }
    public Member getMember2(Creatable2 creatable){
        String id = &quot;winter&quot;;
        String name = &quot;한겨울&quot;;
        Member member = creatable.create(id,name);
        return member;
    }

}
</code></pre>
<pre><code class="language-java">package ch16.sec05.exam03;

public class ConstructorReferenceExample {
    public static void main(String[] args) {
        Person person =  new Person();

        Member m1 = person.getMember1(Member::new);
        System.out.println(m1);
        System.out.println();

        Member m2 = person.getMember2(Member::new);
        System.out.println(m2);
    }
}
</code></pre>
<p>이렇게 쓸 수 있다.</p>
<h2 id="연습문제">연습문제</h2>
<p><img src="https://velog.velcdn.com/images/seo-faper/post/198f1b33-52a3-4e96-a36d-83064dbb91a9/image.png" alt=""></p>
<p>4번 어노테이션은 참고만 하는 겁니다.
<img src="https://velog.velcdn.com/images/seo-faper/post/b41cd354-58b8-48ce-9251-52fb0e4113df/image.png" alt="">
4번 매개변수 있어도 할 수 있습니다.</p>
<p><img src="https://velog.velcdn.com/images/seo-faper/post/422774dc-c42d-47b6-b332-c62933c96c70/image.png" alt="">
2번 2개 이상이면 ()로 묶어야합니다.</p>
<p><img src="https://velog.velcdn.com/images/seo-faper/post/089fa072-69b2-48aa-b5a5-74408e6432dc/image.png" alt=""></p>
<pre><code class="language-java"> Thread thread = new Thread(() -&gt; {
            for (int i = 0; i &lt; 3; i++) {
                System.out.println(&quot;작업 스레드가 실행됩니다.&quot;);
            }
        });</code></pre>
<p><img src="https://velog.velcdn.com/images/seo-faper/post/9e8178f9-499c-4fb6-8198-e0da96e87d88/image.png" alt=""></p>
<pre><code class="language-java">(()-&gt; System.out.println(&quot;Ok 버튼을 눌렀습니다.);})
(()-&gt; System.out.println(&quot;Cancel 버튼을 눌렀습니다.);})
</code></pre>
<p><img src="https://velog.velcdn.com/images/seo-faper/post/b0597c81-bbee-412d-8172-7a87222d1920/image.png" alt=""></p>
<pre><code class="language-java">@FunctionalInterface
public interface Function{

    public double apply(double a, double b);
}</code></pre>
<p><img src="https://velog.velcdn.com/images/seo-faper/post/606c18d8-cb99-456f-8f21-1e3b485d161e/image.png" alt="">
<img src="https://velog.velcdn.com/images/seo-faper/post/fd2d2d7d-fc88-47da-937c-88ba69c3b0c1/image.png" alt=""></p>
<pre><code class="language-java">(x,y) -&gt; Math.max(x,y)
(x,y) -&gt; Math.min(x,y)</code></pre>
<p><img src="https://velog.velcdn.com/images/seo-faper/post/0ebdbef9-c7a1-45dd-ad55-bdc146e529a8/image.png" alt="">
<img src="https://velog.velcdn.com/images/seo-faper/post/e3789342-94d7-4670-bb87-5d8de43c665e/image.png" alt=""></p>
<pre><code class="language-java">public static double avg(Function&lt;Student&gt; function){
    int sum = 0;
    for(Student i : students) sum += function.apply(student);
    return (double) sum / students.length;
}</code></pre>
<p><img src="https://velog.velcdn.com/images/seo-faper/post/fbbf3c91-6834-4555-b202-a5f31d0300e8/image.png" alt=""></p>
<pre><code class="language-java">Student::getEnglishScore
Student::getMathScore</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[이것이 자바다 12일차 - Chapter14 멀티 스레드]]></title>
            <link>https://velog.io/@seo-faper/%EC%9D%B4%EA%B2%83%EC%9D%B4-%EC%9E%90%EB%B0%94%EB%8B%A4-12%EC%9D%BC%EC%B0%A8-Chapter14-%EB%A9%80%ED%8B%B0-%EC%8A%A4%EB%A0%88%EB%93%9C</link>
            <guid>https://velog.io/@seo-faper/%EC%9D%B4%EA%B2%83%EC%9D%B4-%EC%9E%90%EB%B0%94%EB%8B%A4-12%EC%9D%BC%EC%B0%A8-Chapter14-%EB%A9%80%ED%8B%B0-%EC%8A%A4%EB%A0%88%EB%93%9C</guid>
            <pubDate>Wed, 25 Jan 2023 17:52:19 GMT</pubDate>
            <description><![CDATA[<h2 id="멀티-스레드란">멀티 스레드란?</h2>
<p>운영체제는 실행 중인 프로그램을 프로세스(process)로 관리한다.
멀티 태스킹은 두 가지 이상의 작업을 동시에 처리하는 것을 말하는데, 이 때 운영체제는 멀티 프로세스를 생성하여 처리한다. </p>
<p>하나의 프로세스에서 멀티 스레드를 이용하여 두 가지 이상의 작업을 처리할 수 있다.
스레드는 코드의 실행 흐름을 말하는데, 프로세스 내에 스레드가 두 개라면 두 개의 코드 실행 흐름이 생긴다는 의미이다.</p>
<p>멀티 프로세스들은 서로 독립적이므로 하나의 프로세스에서 오류가 발생해도 다른 프로세스에게 영향을 미치지 않는다.
하지만 멀티 스레드는 프로세스 내부에서 생성되기 때문에 하나의 스레드가 예외를 발생시키면 프로세스가 종료되므로 다른 스레드에게 영향을 미친다.</p>
<h2 id="thread-클래스-생성">Thread 클래스 생성</h2>
<pre><code class="language-java">package ch14.sec03.exam01;

import java.awt.*;

public class BeepPrintExample {
    public static void main(String[] args) {
        Toolkit toolkit = Toolkit.getDefaultToolkit();
        for(int i = 0; i&lt;5; i++){
            toolkit.beep();
            try{Thread.sleep(500);} catch (Exception e){}
        }
        for(int i = 0; i&lt;5; i++){
            System.out.println(&quot;띵&quot;);
            toolkit.beep();
            try{Thread.sleep(500);} catch (Exception e){}
        }
    }
}
</code></pre>
<p>이렇게 만들면 싱글 스레드의 흐름으로, main() 함수 안에 단일 스레드가 비프음을 5번 실행 하고 그 후에 프린팅을 진행한다.</p>
<pre><code class="language-java">package ch14.sec03.exam02;

import java.awt.*;

public class BeepPrintExample {
    public static void main(String[] args) {
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                Toolkit toolkit = Toolkit.getDefaultToolkit();
                for(int i = 0; i&lt;5; i++){
                    toolkit.beep();
                    try{Thread.sleep(500);} catch (Exception e){}
                }
            }
        });
        thread.start();
        for(int i = 0 ; i&lt;5; i++){
        System.out.println(&quot;띵&quot;);
        try{Thread.sleep(500);} catch (Exception e){}
        }
    }
}
</code></pre>
<p>Thread객체를 생성할 때 매개값으로 Runnable() 구현체를 넣으면 이렇게 run() 함수를 구현할 수 있는데, run() 함수는 멀티 스레드 환경에서 돌아가며 start();에 의해 비프음을 5번 실행함과 동시에 프린팅을 동시에 수행해 비프음과 프린팅이 뒤죽박죽으로 실행되는걸 볼 수 있다. </p>
<h2 id="쓰레드-이름-변경">쓰레드 이름 변경</h2>
<p>스레드는 자신의 이름을 가진다. 메인 스레드는 &#39;main&#39;이라는 이름을 가지고 작업 스레드는 자동적으로 &#39;Thread-n&#39;이라는 이름을 가진다. 자동 설정된 이름 말고 다른 이름으로 설정하고 싶으면 setName() 메소드를 써보자.</p>
<pre><code class="language-java">package ch14.sec04;

public class ThreadNameExample {
    public static void main(String[] args) {
        Thread mainThread = Thread.currentThread();
        System.out.println(mainThread.getName()+&quot;실행&quot;);

        for(int i = 0; i&lt;3; i++){
            Thread threadA = new Thread(){
                @Override
                public void run() {
                    System.out.println(getName()+&quot; 실행&quot;);
                }
            };
            threadA.start();

            Thread chatThread = new Thread(){
                @Override
                public void run() {
                    System.out.println(getName()+&quot; 실행&quot;);
                }
            };
            chatThread.setName(&quot;chat-Thread&quot;);
            chatThread.start();
        }
    }
}
</code></pre>
<h2 id="스레드-상태">스레드 상태</h2>
<p>스레드 객체를 생성하고 start() 메소드를 호출하면 곧바로 스레드가 실행되는 것이 아니라 실행 대기 상태(RUNNABLE)가 된다.
실행 대기 상태란 실행을 기다리고 있는 상태를 말한다.</p>
<p>실행 대기하는 스레드는 CPU 스케줄링에 따라 CPU를 점유하고 run() 메소드를 실행한다. 이때를 실행(RUNNING) 상태라고 한다.</p>
<p>실행 스레드는 run() 메소드를 모두 실행하기 전에 스케줄링에 의해 다시 실행 대기 상태로 돌아갈 수 있다. 그리고 다른 스레드가 실행상태가 된다.</p>
<p>이렇게 스레드는 실행 대기 상태와 실행 상태를 번갈아 가면서 자신의 run() 메소드를 조금씩 실행한다.
실행 상태에서 run() 메소드가 종료되면 더 이상 실행할 코드가 없기 때문에 스레드의 실행은 멈추게 된다. 이 상태를 종료상태(TERMINATED) 라고 한다.</p>
<p>실행 상태에서 일시 정지 상태로 가기도 하는데, 일시 정지 상태는 스레드가 실행할 수 없는 상태를 말한다.</p>
<h3 id="주어진-시간-동안-일시-정지">주어진 시간 동안 일시 정지</h3>
<pre><code class="language-java">package ch14.sec05.exam01;

import java.awt.*;

public class SleepExample {
    public static void main(String[] args) {
        Toolkit toolkit = Toolkit.getDefaultToolkit();
        for(int i = 0; i&lt;10; i++){
            toolkit.beep();
            try{
                Thread.sleep(3000);
            }catch (Exception e){

            }
        }
    }
}
</code></pre>
<p>sleep() 으로 3초동안 정지했다가 다시 실행된다.</p>
<h3 id="다른-스레드의-종료를-기다림">다른 스레드의 종료를 기다림</h3>
<pre><code class="language-java">package ch14.sec05.exam02;

public class SumThread extends Thread{
    private long sum;

    public long getSum() {
        return sum;
    }

    public void setSum(long sum) {
        this.sum = sum;
    }

    @Override
    public void run() {
        for(int i = 1; i&lt;=100; i++){
            sum+=i;
        }
    }
}
</code></pre>
<pre><code class="language-java">package ch14.sec05.exam02;

public class JoinExample {
    public static void main(String[] args) {
        SumThread sumThread = new SumThread();
        sumThread.start();
        try{
            sumThread.join();
        }catch (Exception e){}
        System.out.println(&quot;1~100 합 : &quot;+sumThread.getSum());
    }
}
</code></pre>
<p>join() 으로 다른 스레드의 종료까지 기다린 후 다음 스레드를 실행 할 수 있다.</p>
<h2 id="스레드-동기화">스레드 동기화</h2>
<p>멀티스레드는 하나의 객체를 공유해서 작업할 수도 있다. 이경우 다른 스레드에 의해 객체 내부 데이터가 쉽게 변경될 수 있기 때문에 의도했던 것과는 다른 결과가 나올 수 있다. 이를 방지하기 위해 동기화 메소드 및 블록을 지원한다.</p>
<pre><code class="language-java">public synchronized void method(){
    // 단 하나의 스레드만 실행하는 영역
}</code></pre>
<pre><code class="language-java">public void method(){
    synchronized(공유객체){ ... /*단 하나의 스레드만 실행하는 영역*/  }

    // 여러 스레드를 실행 할 수 있는 영역
}</code></pre>
<h3 id="wait과-notify를-이용한-스레드-제어">wait()과 notify()를 이용한 스레드 제어</h3>
<p>wait()과 notify()는 말 그대로 정지와 재생이다. 경우에 따라 두 개의 스레드를 교대로 번갈아 가며 실행 할 때가 있는데, wait()으로 일시정지를 걸고 nofity()로 정지된 스레드를 다시 실행하는 기능을 한다.</p>
<pre><code class="language-java">public class WorkObject {
    public synchronized void methodA(){
        Thread thread = Thread.currentThread();
        System.out.println(thread.getName()+&quot; : methodA 작업 실행&quot;);
        notify();
        try{
            wait();
        }catch (Exception e){}
    }

    public synchronized void methodB(){
        Thread thread = Thread.currentThread();
        System.out.println(thread.getName()+&quot; : methodB 작업 실행&quot;);
        notify();
        try{
            wait();
        }catch (Exception e){}
    }


}</code></pre>
<h2 id="스레드-안전종료">스레드 안전종료</h2>
<p>스레드는 run() 메소드가 모두 실행되면 자동으로 종료되지만 경우에 따라 실행 중인 스레드를 즉시 종료할 필요가 있다. stop() 메소드가 존재하긴 하나 이는 deprecated(더이상 사용하지 않음) 처리 되었다. 그 이유는 갑자기 스레드를 종료하면 리소스들이 불안정한 상태로 남겨지기 때문이다.</p>
<h3 id="조건문-이용">조건문 이용</h3>
<pre><code class="language-java">package ch14.sec07;

public class PrintThread extends Thread{
    private boolean stop;

    public void setStop(boolean stop) {
        this.stop = stop;
    }

    @Override
    public void run() {
        while (!stop){
            System.out.println(&quot;실행 중&quot;);
        }
        System.out.println(&quot;리소스 정리&quot;);
        System.out.println(&quot;실행 종료&quot;);
    }
}
</code></pre>
<p>스레드가 while문으로 반복 실행 될 경우, 조건문을 통해 run() 메소드의 종료를 유도할 수 있다.</p>
<h3 id="interrupt-메소드-이용">interrupt() 메소드 이용</h3>
<p>interrupt()는 스레드가 일시정지 상태일 때 InterruptedException 예외를 발생시키는 역할을 한다. 이걸 이용해 예외처리로 넘겨 run()을 정상종료 할 수 있다.</p>
<pre><code class="language-java">package ch14.sec07.exam02;

public class PrintThread extends Thread{
    @Override
    public void run() {
        try{
            while (true){
                System.out.println(&quot;실행 중&quot;);
                Thread.sleep(1);
            }
        }catch (InterruptedException e){
            System.out.println(&quot;리소스 정리&quot;);
            System.out.println(&quot;정상 종료&quot;);
        }
    }
}
</code></pre>
<pre><code class="language-java">package ch14.sec07.exam02;

public class InterruptExample {
    public static void main(String[] args) {
        Thread thread = new PrintThread();
        thread.start();

        try{
            Thread.sleep(1000);
        }catch (InterruptedException e){

        }
        thread.interrupt();
    }
}
</code></pre>
<h2 id="데몬-스레드">데몬 스레드</h2>
<p>데몬 스레드는 주 스레드의 작업을 돕는 보조 스레드이이다.
주 스레드가 종료되면 데몬 스레드도 자동으로 종료된다.</p>
<p>데몬 스레드를 적용한 예로는 워드프로세서의 자동 저장, 가비지 컬렉터 등이 있다.</p>
<p>스레드를 데몬으로 만들기 위해서는 주 스레드가 데몬이 될 스레드의 setDaemon(true)를 호출하면 된다.</p>
<pre><code class="language-java">package ch14.sec08;

public class AutoSaveThread extends Thread{
    public void save(){
        System.out.println(&quot;작업 내용을 저장함&quot;);
    }

    @Override
    public void run() {
        while (true){
            try{
                Thread.sleep(1000);
            }catch (Exception e){ break;}
            save();
        }

    }
}

</code></pre>
<pre><code class="language-java">package ch14.sec08;

public class DaemonExample {
    public static void main(String[] args) {
        AutoSaveThread autoSaveThread = new AutoSaveThread();
        autoSaveThread.setDaemon(true);
        autoSaveThread.start();

        try{
            Thread.sleep(3000);
        }catch (Exception e){

        }
        System.out.println(&quot;메인 스레드 종료&quot;);
    }
}
</code></pre>
<h2 id="스레드-풀">스레드 풀</h2>
<p>병렬 작업 처리가 많아지면 스레드의 개수가 폭증하여 CPU가 바빠지고 메모리 사용량이 늘어난다.
이에 따라 애플리케이션의 성능 또한 급격히 저하된다. 이렇게 병렬 작업 증가로 인한 스레드의 폭증을 막으려면 스레드풀을 사용하는 것이 좋다.</p>
<p>스레드풀은 작업 처리에 사용되는 스레드를 제한된 개수만큼 정해놓고 작업 큐에 들어오는 작업들을 스레드가 하나씩 맡아 처리하는 방식이다.</p>
<p>작업 처리가 끝난 스레드는 다시 작업 큐에서 새로운 작업을 가져와 처리한다.</p>
<p>솔직히 무슨 말인지 이해가 안된다. 여긴 더 공부해야겠다. 
어떻게 쓰는지 알고 문법을 이해하는 것은 사실 아무것도 아니다.
본질적인 개념에 대한 이해가 없으면 아무리 코드 짜봤자 실전에서 못쓴다.</p>
<pre><code class="language-java">package ch14.sec09.exam03;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class CallableSubmitExample {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(5);

        for(int i = 1; i&lt;=100; i++){
            final int idx = i;
            Future&lt;Integer&gt; future = executorService.submit(new Callable&lt;Integer&gt;() {
                @Override
                public Integer call() throws Exception {
                    int sum = 0;
                    for(int i = 1; i&lt;=idx; i++){
                      sum+=i;
                    }
                    Thread thread = Thread.currentThread();
                    System.out.println(&quot;[&quot;+thread.getName()+&quot;] 1~&quot;+idx+&quot; 합 계산&quot;);
                    return sum;
                }
            });
            try{
                int result = future.get();
                System.out.println(&quot;\t리턴값: &quot;+result);
            }catch (Exception e){}
        }
    executorService.shutdown();
    }

}
</code></pre>
<p>1에서 100까지의 합을 어섬하게 셀 수 있는 코드다.</p>
<h2 id="연습문제">연습문제</h2>
<p><img src="https://velog.velcdn.com/images/seo-faper/post/c115e837-2459-4e08-bd9d-a67694b939e1/image.png" alt="">
4번</p>
<p><img src="https://velog.velcdn.com/images/seo-faper/post/20d5c9e5-fd65-4558-b751-2f50b1307fa6/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/seo-faper/post/c282c8ad-49ea-4112-87c6-55e7930a4463/image.png" alt="">
new Runnable() { ... }
extends Thread
implements Runnable
<img src="https://velog.velcdn.com/images/seo-faper/post/22c4b3d6-331d-4967-8e60-243f65fc97bc/image.png" alt="">
2번
<img src="https://velog.velcdn.com/images/seo-faper/post/7c82cbd6-8731-4f09-950e-3ea8fd0675de/image.png" alt="">
4번
<img src="https://velog.velcdn.com/images/seo-faper/post/b8fd1ae3-c9bd-4a7d-ba53-46bf9bb5ac2f/image.png" alt="">
2번
<img src="https://velog.velcdn.com/images/seo-faper/post/46c65c85-f3f2-4fda-b85c-600316b3bdf7/image.png" alt=""></p>
<pre><code class="language-java">if(this.isInterrupted()) break;</code></pre>
<p><img src="https://velog.velcdn.com/images/seo-faper/post/9fbe5ce5-18d8-4a9a-bd87-6fc77393b258/image.png" alt="">
3번
<img src="https://velog.velcdn.com/images/seo-faper/post/1c0df679-08af-4b62-9263-02c9f782cd8f/image.png" alt=""></p>
<pre><code class="language-java">thread.setDaemon(true);</code></pre>
<p><img src="https://velog.velcdn.com/images/seo-faper/post/3e0f3b75-1d49-4645-ae1b-a3a1ccd4c1ca/image.png" alt="">
4번
<img src="https://velog.velcdn.com/images/seo-faper/post/a3f28cde-596b-4eaa-9eaf-cf7a2fbe48b9/image.png" alt="">
4번 </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[이것이 자바다 11일차 - Chapter13 제네릭]]></title>
            <link>https://velog.io/@seo-faper/%EC%9D%B4%EA%B2%83%EC%9D%B4-%EC%9E%90%EB%B0%94%EB%8B%A4-11%EC%9D%BC%EC%B0%A8-Chapter13-%EC%A0%9C%EB%84%A4%EB%A6%AD</link>
            <guid>https://velog.io/@seo-faper/%EC%9D%B4%EA%B2%83%EC%9D%B4-%EC%9E%90%EB%B0%94%EB%8B%A4-11%EC%9D%BC%EC%B0%A8-Chapter13-%EC%A0%9C%EB%84%A4%EB%A6%AD</guid>
            <pubDate>Tue, 24 Jan 2023 08:28:02 GMT</pubDate>
            <description><![CDATA[<h2 id="제네릭이란">제네릭이란?</h2>
<p>결정되지 않은 타입을 파리미터로 처리하고 실제 사용할 때 파라미터를 구체적인 타입으로 대체시키는 기능이다.</p>
<pre><code class="language-java">package ch13.sec01;

public class Box&lt;T&gt; {
    public T content;
}
</code></pre>
<p>이렇게 클래스 옆에 <code>&lt;T&gt;</code> 를 통해 제네릭을 선언 할 수 있으며 객체 생성 시 정해지는 자료형에 의해 content가 담을 수 있는 종류가 정해지는 것이다.</p>
<pre><code class="language-java">package ch13.sec01;

public class GenericExample {
    public static void main(String[] args) {
        Box&lt;String&gt; box1 = new Box&lt;&gt;();
        box1.content = &quot;안녕하세요.&quot;;
        String str = box1.content;
        System.out.println(str);

        Box&lt;Integer&gt; box2 = new Box&lt;&gt;();
        box2.content = 100;
        int value = box2.content;
        System.out.println(value);
    }
}
</code></pre>
<p>처음에는 <code>&lt;String&gt;</code>으로 선언했기 때문에 문자열이 담기고 그 다음은 Integer로 선언해 정수가 담기는 모습을 볼 수 있다.</p>
<h2 id="제네릭-타입">제네릭 타입</h2>
<p>제네릭 타입은 결정되지 않는 타입을 파라미터로 가지는 클래스와 인터페이스를 말한다.</p>
<pre><code class="language-java">package ch13.sec02;

public class Product &lt;K,M&gt;{
    private K kind;
    private M model;

    public K getKind() {return this.kind;}
    public M getModel(){return this.model;}
    public void setKind(K kind){this.kind = kind;}
    public void setModel(M model){this.model = model;}
}
</code></pre>
<p>이렇게 2개 이상의 파라미터는 쉼표로 구별한다.</p>
<h2 id="제네릭-메소드">제네릭 메소드</h2>
<p>제네릭 메소드는 타입 파라미터를 가지고 있는 메소드를 말한다. 타입 파라미터가 메소드 선언부에 정의된다는 점에서 제네릭 타입과 차이가 있다. </p>
<pre><code class="language-java">public &lt;A, B, ... &gt; 리턴타입 메소드명(매개변수, ...) { ... }</code></pre>
<pre><code class="language-java">package ch13.sec03.exam01;

public class Box&lt;T&gt; {
    private T t;

    public T getT() {
        return t;
    }

    public void setT(T t) {
        this.t = t;
    }
}
</code></pre>
<pre><code class="language-java">package ch13.sec03.exam01;

public class GenericExample {
    public static &lt;T&gt; Box&lt;T&gt; boxing(T t){
        Box&lt;T&gt; box = new Box&lt;T&gt;();
        box.setT(t);
        return box;
    }

    public static void main(String[] args) {
        Box&lt;Integer&gt; box1 = boxing(100);
        int intValue = box1.getT();
        System.out.println(intValue);

        Box&lt;String&gt; box2 = boxing(&quot;홍길동&quot;);
        String strValue = box2.getT();
        System.out.println(strValue);
    }
}
</code></pre>
<h2 id="제한된-타입-파라미터">제한된 타입 파라미터</h2>
<p>경우에 따라서는 타입 파라미터를 대처하는 구체적인 타입을 제한할 필요가 있다. 예를 들어 숫자를 연산ㄴ하는 제네릭 메소드는 대체 타입으로 Number 또는 자식 클래스 (Byte, Short, Integer, Long, Double)로 제한할 필요가 있다. </p>
<pre><code class="language-java">public &lt;T extends 상위타입&gt; 리턴타입 메소드(매개변수, ...) { ... }</code></pre>
<pre><code class="language-java">package ch13.sec04;

public class GenericExample {
    public static &lt;T extends Number&gt; boolean compare(T t1, T t2){
        System.out.println(&quot;compare(&quot;+t1.getClass().getSimpleName()+&quot;, &quot;+t2.getClass().getSimpleName()+&quot;)&quot;);

        double v1 = t1.doubleValue();
        double v2 = t2.doubleValue();
        return (v1 == v2);
    }

    public static void main(String[] args) {
        boolean result1 = compare(10,20);
        System.out.println(result1);

        boolean result2 = compare(4.5,4.5);
        System.out.println(result2);
    }
}</code></pre>
<h2 id="와일드카드-타입-파라미터">와일드카드 타입 파라미터</h2>
<p>제네릭 타입을 매개값이나 리턴 타입으로 사용할 때 타입 파라미터로 ?(와일드카드)를 사용할 수 있다. ?는 범위에 있는 모든 타입으로 대체할 수 있다는 표시이다. </p>
<pre><code class="language-java">package ch13.sec05;

public class Person {

}
class Worker extends Person{

}
class Student extends Person{

}
class HighStudent extends Student{

}
class MiddleStudent extends Student{

}
</code></pre>
<pre><code class="language-java">package ch13.sec05;

public class Applicant&lt;T&gt; {
    public T kind;
    public Applicant(T kind){
        this.kind = kind;
    }
}
</code></pre>
<pre><code class="language-java">package ch13.sec05;

public class Course {
    public static void registerCourse1(Applicant&lt;?&gt; applicant){
        System.out.println(applicant.kind.getClass().getSimpleName());
    }
    public static void registerCourse2(Applicant&lt;? extends Student&gt; applicant){
        System.out.println(applicant.kind.getClass().getSimpleName());
    }
    public static void registerCourse3(Applicant&lt;? super  Worker&gt; applicant){
        System.out.println(applicant.kind.getClass().getSimpleName());
    }
}
</code></pre>
<h2 id="연습문제">연습문제</h2>
<p><img src="https://velog.velcdn.com/images/seo-faper/post/69faaca3-2404-48d5-acbf-2a31ddf0bea0/image.png" alt=""></p>
<p>4번, 가질 수 있지요.</p>
<p><img src="https://velog.velcdn.com/images/seo-faper/post/367b167f-ad80-4466-9c0b-b644ab9fc11c/image.png" alt=""></p>
<pre><code class="language-java">public class Container&lt;T&gt;{
    private T t;
    public T get(){
           return t;
    } 
    public void set(T t){
        this.t = t;
    }
}</code></pre>
<p><img src="https://velog.velcdn.com/images/seo-faper/post/b93fa5d2-3955-4bd2-ac77-0f170889cc39/image.png" alt=""></p>
<pre><code class="language-java">public class Container&lt;K, V&gt;{
    private K key;
    private V value;

    public K getKey(){
        return this.key;
    }

    public V getValue(){
        return this.value;
    }

    public void set(K key, V value){
        this.key = key;
        this.vaule = value;
    }
}</code></pre>
<p><img src="https://velog.velcdn.com/images/seo-faper/post/15344a0c-0aec-4328-bfd7-ba64202ec39d/image.png" alt="">
<img src="https://velog.velcdn.com/images/seo-faper/post/eb5a1385-bb7a-4e34-873a-ac49d58f22e8/image.png" alt="">
<img src="https://velog.velcdn.com/images/seo-faper/post/4f233d15-4b7a-49b2-8e4e-6184f2fc6867/image.png" alt=""></p>
<pre><code class="language-java">public class Util{
    public static &lt;K,V&gt; V getValue(Pair&lt;K, V&gt; p, K l){
        if(p.getKey() == k){
            return p.getValue();
        }else{
            return null;
        }
    }
}

</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[이것이 자바다 10일차 - Chapter12 java.base 모듈]]></title>
            <link>https://velog.io/@seo-faper/%EC%9D%B4%EA%B2%83%EC%9D%B4-%EC%9E%90%EB%B0%94%EB%8B%A4-10%EC%9D%BC%EC%B0%A8-Chapter12-java.base-%EB%AA%A8%EB%93%88</link>
            <guid>https://velog.io/@seo-faper/%EC%9D%B4%EA%B2%83%EC%9D%B4-%EC%9E%90%EB%B0%94%EB%8B%A4-10%EC%9D%BC%EC%B0%A8-Chapter12-java.base-%EB%AA%A8%EB%93%88</guid>
            <pubDate>Fri, 20 Jan 2023 08:45:39 GMT</pubDate>
            <description><![CDATA[<h2 id="자바의-도큐먼트">자바의 도큐먼트</h2>
<p><a href="https://docs.oracle.com/javase/7/docs/api/overview-summary.html">자바 공식 문서</a></p>
<p>이 모든 클래스를 알 필요는 없지만 자주 쓰는 것 위주로 정리하겠다.</p>
<h2 id="object-클래스">Object 클래스</h2>
<p>동등 비교를 할 때 쓰는 equals는 object의 소속이다.
자바의 모든 클래스는 암묵적으로 java.lang.Object를 상속 받는다.
그래서 모든 객체는 equals, hashCode, toString을 메소드로 가진다.</p>
<pre><code class="language-java">public class Member {
    public String id;

    public Member(String id) {
        this.id = id;
    }

    @Override
    public boolean equals(Object o) {
        if(o instanceof Member target){
            if(id.equals(target.id)){
                return true;
            }
        }
        return false;
    }

}
</code></pre>
<p>이렇게 Member에서 equals를 재정의 해서 Member끼리 동등비교를 할 수 있다.</p>
<p>그런데 equals는 값을 비교하는 것이고 객체가 진짜 동등한 객체인지 확인 할 때는 equals와 hashCode를 같이 쓴다.</p>
<pre><code class="language-java">        if(s1.hashCode() == s2.hashCode()){
            if(s1.equals(s2)){
                System.out.println(&quot;동등 객체&quot;);
            }</code></pre>
<p>이렇게 hashCode와 equals가 모두 같아야 동등하다고 보는 것이다.</p>
<pre><code class="language-java">public class Student {
    private int no;
    private String name;

    public Student(int no, String name){
        this.no = no;
        this.name = name;
    }
    public int getNo(){ return no;}
    public String getName(){return name;}

    @Override
    public int hashCode(){
        int hashCode = no + name.hashCode();
        return hashCode;
    }
    @Override
    public boolean equals(Object obj){
        if(obj instanceof Student target){
            if(no == target.getNo() &amp;&amp; name.equals(target.getName())){
                return true;
            }
        }
        return false;
    }

    @Override
    public String toString() {
        return &quot;Student{&quot; +
                &quot;no=&quot; + no +
                &quot;, name=&#39;&quot; + name + &#39;\&#39;&#39; +
                &#39;}&#39;;
    }
}</code></pre>
<p>이렇게 hashCode, equals, toString을 모두 재정의 해주면 다음과 같이 응용이 가능하다.</p>
<pre><code class="language-java">package ch12.sec03.exam02;

import java.util.HashSet;

public class HashSetExample {
    public static void main(String[] args) {
        HashSet hashSet = new HashSet();
        Student s1 = new Student(1,&quot;홍길동&quot;);
        hashSet.add(s1);

        Student s2 = new Student(1,&quot;홍길동&quot;);
        hashSet.add(s2);

        System.out.println(hashSet.toString());

    }
}</code></pre>
<p>HashSet의 동등비교는 equals와 hashCode로 하기 때문에 동등한 객체라 중복 저장하지 않고
출력할 때도 toString()을 통해 미리 정의된 출력 포멧으로 볼 수 있다.</p>
<h2 id="system-클래스">System 클래스</h2>
<p>주로 키보드 입출력에서 활용된다.</p>
<pre><code class="language-java">public class InExample {
    public static void main(String[] args) throws Exception{
        int speed = 0;
        int keyCode = 0;

        while (true){
            if(keyCode != 13 &amp;&amp; keyCode != 10){
                if(keyCode == 49){
                    speed++;
                }else if(keyCode == 50){
                    speed--;
                }else if(keyCode == 51){
                    break;
                }
                System.out.println(&quot;-------------------&quot;);
                System.out.println(&quot;1. 증속 | 2. 감속 | 3. 중지&quot;);
                System.out.println(&quot;-------------------&quot;);

                System.out.println(&quot;현재 속도 : &quot;+speed);
                System.out.println(&quot;선택 : &quot;);
            }
            keyCode = System.in.read();

        }
        System.out.println(&quot;프로그램 종료&quot;);
    }
}</code></pre>
<p>이렇게 System.in을 통해 읽을 수 있다.</p>
<p> System.exit(); 는 프로그램을 종료할 때 쓴다.</p>
<h2 id="문자열-관련-클래스">문자열 관련 클래스</h2>
<p>프로그램을 개발하다 보면 특히 네트워크쪽에서는 byte를 주로 쓰게 되는데, 이를 String으로 다뤄야 하는 경우가 많다.</p>
<pre><code class="language-java">String str = new String(byte[] bytes);</code></pre>
<p>이런 식으로!</p>
<pre><code class="language-java">        String data = &quot;자바&quot;;

        byte[] arr1 = data.getBytes(); // 아무것도 적지 않을 시 UTF-8
        System.out.println(Arrays.toString(arr1));

        String str1 = new String(arr1);
        System.out.println(str1);

        byte[] arr2 = data.getBytes(&quot;EUC-KR&quot;); // EUC-KR 하면 2바이트로 변환됨
        System.out.println(Arrays.toString(arr2));

        String str2 = new String(arr2);
        System.out.println(str2);</code></pre>
<p><code>String.getBytes()</code>로 변환하고, 생성자를 통해 다시 String으로 만들 수 있다.</p>
<p>StringBuilder는 효율적인 문자열 관리를 위해 만들어진 클래스이다.
StringBuilder를 쓰는 것이 메모리 측면에서 더욱 효율적이다.</p>
<table>
<thead>
<tr>
<th>리턴타입</th>
<th>메소드(매개변수)</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>StringBuilder</td>
<td><code>append(문자열)</code></td>
<td>문자열을 끝에 추가</td>
</tr>
<tr>
<td>StringBuilder</td>
<td><code>insert(위치, 문자열)</code></td>
<td>문자열을 위치에 추가</td>
</tr>
<tr>
<td>StringBuilder</td>
<td><code>delete(시작위치, 끝위치)</code></td>
<td>문자열 일부를 삭제</td>
</tr>
<tr>
<td>StringBuilder</td>
<td><code>replace(시작위치, 끝위치, 문자열)</code></td>
<td>문자열 일부를 대체</td>
</tr>
<tr>
<td>String</td>
<td><code>toString()</code></td>
<td>완성된 문자열 리턴</td>
</tr>
</tbody></table>
<pre><code class="language-java">        String data = new StringBuilder()
                .append(&quot;DEF&quot;)
                .insert(0,&quot;ABC&quot;)
                .delete(3,4)
                .toString();
        System.out.println(data);</code></pre>
<p>이렇게 활용할 수 있다.
그리고 특정 구분자로 연결되어 있을 때 분리해 배열로 만들어 주는 split()과 비슷한 StringTokenizer도 있다.
이 둘의 차이점은 split()은 비교의 기준이 정규식이고 StringTokenizer는 문자열을 기준으로 자른다는 것이다.</p>
<pre><code class="language-java">import java.util.StringTokenizer;

public class StringTokenizerExample {
    public static void main(String[] args) {
        String data1 = &quot;홍딜동&amp;이수홍,박연수&quot;;
        String[] arr = data1.split(&quot;&amp;|,&quot;);
        for(String token : arr){
            System.out.println(token);
        }
        String data2 = &quot;홍길동/바언수/샌즈&quot;;
        StringTokenizer st = new StringTokenizer(data2,&quot;/&quot;);
        while (st.hasMoreTokens()){
            String token = st.nextToken();
            System.out.println(token);
        }
    }
}
</code></pre>
<p>이런 차이점이 있다.</p>
<h2 id="포장-클래스">포장 클래스</h2>
<p>자바의 기본 타입(int, short, int, long, float, double, boolean)은 모두 어떤 값을 갖는 객체를 생성할 수 있다.
문자열로된 숫자를 정수로 바꿀 때 Integer를 쓴 것 처럼, 기본 타입들은 모두 포장 클래스를 가지고 있다.</p>
<table>
<thead>
<tr>
<th>기본 타입</th>
<th>포장 클래스</th>
</tr>
</thead>
<tbody><tr>
<td>byte</td>
<td>Byte</td>
</tr>
<tr>
<td>char</td>
<td>Character</td>
</tr>
<tr>
<td>short</td>
<td>Short</td>
</tr>
<tr>
<td>int</td>
<td>Integer</td>
</tr>
<tr>
<td>long</td>
<td>Long</td>
</tr>
<tr>
<td>float</td>
<td>Float</td>
</tr>
<tr>
<td>double</td>
<td>Double</td>
</tr>
<tr>
<td>boolean</td>
<td>Boolean</td>
</tr>
</tbody></table>
<p>이걸 쓰는 이유는 컬렉션 프레임워크에서 기본 타입의 객체의 값은 저장할 수 없고 객체만 저장할 수 있기 때문에 객체 형태의 기본 타입이 존재하는 것이다.</p>
<p>이렇게 객체로 만들면 당연하게도 값 비교 ==, !=는 쓸 수 없다. 객체이므로 equals를 통해 비교해야 한다.</p>
<pre><code class="language-java">        Integer obj = 100;
        System.out.println(&quot;value: &quot;+obj.intValue());

        int value = obj;
        System.out.println(&quot;value: &quot;+value);

        int result = obj + 100;
        System.out.println(&quot;result: &quot;+result);</code></pre>
<h2 id="수학-클래스">수학 클래스</h2>
<p>Math 클래스는 각종 수학 연산을 해준다. 
제곱, 루트, 절대값을 주로 쓴다.</p>
<p>Math.random(); 
Math.sqrt();
Math.pow();
Math.abs();</p>
<p>이런 것들이 있다.</p>
<h2 id="날짜와-시간-클래스">날짜와 시간 클래스</h2>
<p>자바는 컴퓨터의 날짜 및 시각을 읽을 수 있도록 java.util 패키지에서 Date와 Calendar 클래스를 제공하다 있다.</p>
<table>
<thead>
<tr>
<th>클래스</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>Date</td>
<td>날짜 정보를 전달하기 위해 사용</td>
</tr>
<tr>
<td>Calendar</td>
<td>다양한 시간대별로 날짜와 시간을 얻을 때 사용</td>
</tr>
<tr>
<td>LocalDateTime</td>
<td>날짜와 시간을 조작할 때 사용</td>
</tr>
</tbody></table>
<pre><code class="language-java">public class DateExample {
    public static void main(String[] args) {
        Date now = new Date();
        String strNow1 = now.toString();
        System.out.println(strNow1);

        SimpleDateFormat sdf =  new SimpleDateFormat(&quot;yyyy.MM.dd HH:mm:ss&quot;);
        String strNow2 = sdf.format(now);
        System.out.println(strNow2);

    }
}
</code></pre>
<p>이런 식으로 활용할 수 있다.</p>
<pre><code class="language-java">public class CalendarExample {
    public static void main(String[] args) {
        Calendar now = Calendar.getInstance();

        int year = now.get(Calendar.YEAR);
        int month = now.get(Calendar.MONTH)+1;
        int day = now.get(Calendar.DAY_OF_MONTH);
        int week = now.get(Calendar.DAY_OF_WEEK);
        String strWeek = null;
        switch(week){
            case Calendar.MONDAY -&gt; strWeek = &quot;월&quot;;
            case Calendar.TUESDAY -&gt; strWeek = &quot;화&quot;;
            case Calendar.WEDNESDAY -&gt; strWeek = &quot;수&quot;;
            case Calendar.THURSDAY -&gt; strWeek = &quot;목&quot;;
            case Calendar.FRIDAY -&gt; strWeek = &quot;금&quot;;
            case Calendar.SATURDAY -&gt; strWeek = &quot;토&quot;;
            default -&gt; strWeek = &quot;일&quot;;
        }
        int amPm = now.get(Calendar.AM_PM);
        String strAmPm = null;
        if(amPm == Calendar.AM){
            strAmPm = &quot;오전&quot;;
        }else{
            strAmPm = &quot;오후&quot;;
        }

        int hour = now.get(Calendar.HOUR);
        int minute = now.get(Calendar.MINUTE);
        int second = now.get(Calendar.SECOND);

        System.out.print(year+&quot;년 &quot;);
        System.out.println(month +  &quot;월 &quot;);
        System.out.println(day + &quot;일 &quot;);
        System.out.print(strWeek+&quot;요일 &quot;);
        System.out.println(strAmPm+&quot; &quot;);
        System.out.print(hour+&quot;시 &quot;);
        System.out.print(minute+&quot;분 &quot;);
        System.out.println(second+&quot;초&quot;);
    }
}</code></pre>
<p>캘린더 클래스로 이런 것도 된다!</p>
<h2 id="정규-표현식-클래스">정규 표현식 클래스</h2>
<p>정규 표현식은 자바 뿐만 아니라 컴퓨터밥좀 먹은 사람이라면 누구나 알고 있어야 하는 문자 검열식 이다. 정규표현식은 정확히 말하자면 무수한 가능성을 지닌 사용자의 입력을 정해져 있는 형식으로 가공하기 위한 식이다. 전화번호, 이메일 등 형식에 맞는지 확인하기 위한 규칙인 것이다.</p>
<p><a href="https://regexr.com/">정규 표현식 연습 사이트</a>
문법 자체는 여기서 연습하면 된다.</p>
<pre><code class="language-java">Patten.matches(resExp, data)</code></pre>
<p>이렇게 쓰면 된다~!</p>
<h2 id="리플렉션">리플렉션</h2>
<p>자바는 클래스와 인터페이스의 메타 정보를 Class 객체로 관리한다. 여기서 메타 정보란 패키지 정보, 타입 정보, 멤버(생서자, 필드, 메소드) 정보 등을 말한다. 이러한 메타 정보를 프로그램에서 읽고 수정하는 행위를 리플렉션(relection)이라고 한다.</p>
<pre><code class="language-java">1) Class claszz = 클래스이름.class;
2) Class clazz = Class.forName(&quot;패키지 ... 클래스이름&quot;);
3) Class clazz = 객체참조변수.getClass();</code></pre>
<p>이렇게 쓰면 된다.
1과 2는 클래스 이름만 가지고 Class 객체를 얻는 방법이고, 3은 객체로부터 Class 객체를 얻는 방법이다. </p>
<pre><code class="language-java">package ch12.sec11.exam01;

public class Car {
}</code></pre>
<p>이렇게 객체를 하나 만들고 </p>
<pre><code class="language-java">package ch12.sec11.exam01;

public class GetClassExample {
    public static void main(String[] args) throws Exception {
        Class clazz = Car.class;

        System.out.println(&quot;패키지 : &quot;+clazz.getPackage().getName());
        System.out.println(&quot;클래스 간단 이름 : &quot;+clazz.getSimpleName());
        System.out.println(&quot;클래스 전체 이름 : &quot;+clazz.getName());
    }
}
</code></pre>
<p>리플렉션으로 알아볼 수 있다.</p>
<h2 id="어노테이션">어노테이션</h2>
<p>코드중 @로 시작하는 요소를 어노테이션이라고 한다. 
어노테이션의 용도는 다음과 같다.</p>
<ol>
<li>컴파일 시 사용하는 정보 전달</li>
<li>빌드 툴이 코드를 자동으로 생성할 때 사용하는 정보 전달</li>
<li>실행 시 특정 기능을 처리할 때 사용하는 정보 전달 </li>
</ol>
<p>대표적인 예로 @Override가 있다. </p>
<p>어노테이션은 자바 프로그램을 개발할 때 필수 요소가 되었다. 특히 웹 개발에 많이 사용되는 Spring Framework 또는 Spring Boot는 다양한 종류의 어노테이션을 활용해 웹 어플리케이션을 설정 하는데 사용된다.</p>
<h3 id="어노테이션-타입-정의와-적용">어노테이션 타입 정의와 적용</h3>
<p>어노테이션도 하나의 타입이므로 어노테이션을 사용하기 위해서는 먼저 정의 부터 해야 한다.</p>
<pre><code class="language-java">public @interface AnnotationName {
}</code></pre>
<p>이렇게 정의한 어노테이션은 코드에서 다음과 같이 사용된다.</p>
<pre><code class="language-java">@AnnotationName</code></pre>
<p>어노테이션은 속성을 가질 수 있따. 속성은 타입과 이름으로 구성되며 이름 뒤에 괄호를 붙인다.
속성의 기본값은 default 키워드로 지정할 수 있다. 예를 들어 String 타입 prop1과 int 타입의 prop2 속성은 다음과 같이 선언할 수 있다.</p>
<pre><code class="language-java">public @interface AnnotationName{
    String prop1();
    int prop2() defaultt 1;
}
</code></pre>
<p>이렇게 정의한 어노테이션은 코드에서 다음과 같이 사용할 수 있다. 
prop1은 기본값이 없기 때문에 반드시 값을 기술해야 하고, prop2는 기본값이 있기 때문에 생략 가능하다. </p>
<pre><code class="language-java">@AnnotationName(prop1=&quot;값&quot;);
@AnnotationName(prop1= &quot;값&quot;, prop2=3);
</code></pre>
<p>어노테이션은 기본 속성인 value를 다음과 같이 가질 수 있다.</p>
<pre><code class="language-java">public @interface AnnotationName{
    String value();
    int prop2() defalut 1;
}</code></pre>
<p>value 속성을 가진 어노테이션 코드에서 사용할 때는 다음과 같이 값만 기술할 수 있다. 
이 값은 value 속성에 자동으로 대입된다.</p>
<pre><code class="language-java">@AnnotationName(&quot;값&quot;);</code></pre>
<p>하지만 value 속성과 다른 속성의 값을 동시에 주고 싶다면 value 속성 이름을 반드시 언급해야 한다.</p>
<pre><code class="language-java">@AnnotationName(value=&quot;값&quot;, prop2=3);</code></pre>
<h3 id="어노테이션-적용-대상">어노테이션 적용 대상</h3>
<p>자바에서 어노테이션은 설정 정보라고 했다. 그렇다면 어떤 대상에 설정 정보를 적용할 것인지, 즉 클래스에 적용할 것인지, 메소드에 적용할 것인지를 명시해야 한다. 적용할 수 있는 대상의 종류는 ElementType 열거 상수로 정의되어 있다.</p>
<table>
<thead>
<tr>
<th>ElementType 열거 상수</th>
<th>적용 요소</th>
</tr>
</thead>
<tbody><tr>
<td>TYPE</td>
<td>클래스,인터페이스,열거 타입</td>
</tr>
<tr>
<td>ANNOTATION_TYPE</td>
<td>어노테이션</td>
</tr>
<tr>
<td>FIELD</td>
<td>필드</td>
</tr>
<tr>
<td>CONSTRUCTOR</td>
<td>생성자</td>
</tr>
<tr>
<td>METHOD</td>
<td>메소드</td>
</tr>
<tr>
<td>LOCAL_VARIABLE</td>
<td>로컬 변수</td>
</tr>
<tr>
<td>PACKAGE</td>
<td>패키지</td>
</tr>
</tbody></table>
<p>적용 대상을 지정할 때에는 @Target 어노테이션을 사용한다. @Target의 기본 속성인 value는 ElementType 배열을 값으로 가진다. 이것은 적용 대상을 복수 개로 지정하기 위해서이다. 예를 들어 다음과 같이 적용 대상을 지정했다고 가정해보자.</p>
<pre><code class="language-java">@Target( {ElementType.TYPE, ElementType.FILED, ElementType.METHOD} )
public @interface AnnotationName{
}</code></pre>
<p>이 어노테이션은 다음과 같이 클래스, 필드, 메소드에 적용할 수 있고 생성자는 적용할 수 없다.</p>
<pre><code class="language-java">@AnnotationName
public class ClassName{
    @AnnotationName
    private String fieldName;

    //@AnnotationName &lt;- @Target에 CONSTRUCT가 없으므로 생성자에는 적용 못함
    public ClassName() { }

    @AnnotationName
    public void methodName() { }
}</code></pre>
<h3 id="어노테이션-유지-정책">어노테이션 유지 정책</h3>
<p>어노테이션을 정의할 떄 한 가지 더 추가해야 할 내용은 @AnnotationName을 언제까지 유지할 것인지를 지정하는 것이다. 어노테이션 유지 정책은 RetentionPolicy 열거 상수로 다음과 같이 정의 되어 있다.</p>
<table>
<thead>
<tr>
<th>RetentionPolicy 열거 상수</th>
<th>어노테이션 적용 시점</th>
<th>어노테이션 제거 시점</th>
</tr>
</thead>
<tbody><tr>
<td>SOURCE</td>
<td>컴파일할 때 적용</td>
<td>컴파일</td>
</tr>
<tr>
<td>CLASS</td>
<td>메모리로 로딩할 때 적용</td>
<td>메모리로 로딩된 후에 제거됨</td>
</tr>
<tr>
<td>RUNTIME</td>
<td>실행할 때 적용</td>
<td>계속 유지됨</td>
</tr>
</tbody></table>
<p>유지 정책을 지정할 때에는 @Retention 어노테이션을 사용한다. @Retention의 기본 속성인 value는 RetentionPolicy 열거 상수 값을 가진다. 다음은 실행 시에도 어노테이션을 설정 정보를 이용할 수 있도록 유지 정책을 RUNTIME으로 지정한 예이다.</p>
<pre><code class="language-java">@Target( {ElementType.TYPE, ElementType.FILED, ElementType.METHOD} )
@Retention( RetentionPolicy.RUNTIME )
public @interface AnnotationName{
}</code></pre>
<h2 id="연습문제">연습문제</h2>
<p><img src="https://velog.velcdn.com/images/seo-faper/post/7ce4de5c-0333-4428-afc2-669c3e7fa168/image.png" alt="">
4번 
<img src="https://velog.velcdn.com/images/seo-faper/post/7b636c7d-b786-4855-9341-6769587718b9/image.png" alt="">
3번
<img src="https://velog.velcdn.com/images/seo-faper/post/e66f737a-d9a2-457b-bfd2-b68501105d25/image.png" alt="">
4번
<img src="https://velog.velcdn.com/images/seo-faper/post/d3aad65f-63f9-4a20-b369-b6056dddc389/image.png" alt=""></p>
<p>equals, hashCode</p>
<p><img src="https://velog.velcdn.com/images/seo-faper/post/b01a01ce-e63e-47f4-9246-eec4e49d7ee6/image.png" alt=""></p>
<pre><code class="language-java">@Override
public boolean equals(Object obj) {
     if(obj instanceof Student) {
         Student student = (Student) obj;

     if(studentNum.equals(student.getStudentNum())) {
         return true;
         }
     }
 return false;
}
@Override
public int hashCode() {
     return studentNum.hashCode();
}
</code></pre>
<p><img src="https://velog.velcdn.com/images/seo-faper/post/6069c5cc-b438-4d91-8499-abb67920d1d5/image.png" alt=""></p>
<pre><code class="language-java">@Override
public String toString() {
     return id + &quot;: &quot; + name;
}</code></pre>
<p><img src="https://velog.velcdn.com/images/seo-faper/post/d11deab9-25fa-4160-ba0e-a52f2996eb25/image.png" alt=""></p>
<p>3번
<img src="https://velog.velcdn.com/images/seo-faper/post/43b80ee4-d94d-4460-bef0-522a0d24a1bd/image.png" alt="">
long start = System.nanoTime() 으로 처음에 측정하고
long end = System.nanoTime() 로 마지막줄에 측정해서 
end - start 를 출력하면 됩니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[이것이 자바다 9일차 - Chapter11 예외 처리]]></title>
            <link>https://velog.io/@seo-faper/%EC%9D%B4%EA%B2%83%EC%9D%B4-%EC%9E%90%EB%B0%94%EB%8B%A4-9%EC%9D%BC%EC%B0%A8-Chapter11-%EC%98%88%EC%99%B8-%EC%B2%98%EB%A6%AC</link>
            <guid>https://velog.io/@seo-faper/%EC%9D%B4%EA%B2%83%EC%9D%B4-%EC%9E%90%EB%B0%94%EB%8B%A4-9%EC%9D%BC%EC%B0%A8-Chapter11-%EC%98%88%EC%99%B8-%EC%B2%98%EB%A6%AC</guid>
            <pubDate>Thu, 19 Jan 2023 07:23:52 GMT</pubDate>
            <description><![CDATA[<h2 id="예외와-예외-클래스">예외와 예외 클래스</h2>
<p>컴퓨터 하드웨어의 고장으로 인해 응용프로그램 실행 오류가 발생하는 것을 자바에서는 에러 라고 한다. 프로그램을 아무리 견고하게 만들어도 개발자는 이런 에러를 대처할 방법이 없다. </p>
<p>자바에는 에러 이외에 예외(Exception)이라고 불리우는 오류가 있다. 예외란 잘못된 사용 또는 코딩으로 인한 오류를 말한다. 예외가 발생하면 프로그램이 곧바로 종료된다는 점에서 에러와 동일하지만 예외 처리를 통해 계속 실행 상태를 유지할 수 있다. </p>
<ul>
<li><strong>일반 예외</strong> : 컴파일러가 예외 처리 코드 여부를 검사하는 예외(체크 예외)를 말한다.</li>
<li><strong>실행 예외</strong> : 컴파일러가 예외 처리 코드 여부를 검사하지 않는 예외(언체크 예외)를 말한다.</li>
</ul>
<p>자바는 예외가 발생하면 예외 클래스로부터 객체를 생성한다. 이 객체는 예외 처리 시 사용된다.
자바의 모든 에러와 예외 클래스는 Throwable을 상속받아 만들어지고, 추가적으로 예외 클래스는 java.lang.Exception 클래스를 상속받는다.</p>
<p>실행 예외는 RuntimeException과 그 자식 클래스에 해당한다. 그 밖의 예외 클래스는 모두 일반 예외이다. 자바는 자주 사용되는 예외 클래스를 표준 라이브러리로 제공한다.
<img src="https://velog.velcdn.com/images/seo-faper/post/6f683dfd-b249-4e53-be4a-6fe91c4a735e/image.png" alt=""></p>
<p>즉 정리하자면 체크 예외는 컴파일 하기 전에 반드시 throws든 try-catch든 예외처리를 해줘야 실행이 가능한 예외들이고 그 외에 RuntimeException 하위 예외들은 언체크 예외로, 필요하면 그 때 throws나 try-catch로 잡아주면 된다.</p>
<h2 id="예외-처리-코드">예외 처리 코드</h2>
<pre><code class="language-java">package ch11.sec02.exam01;

public class ExceptionHandlingExample {
    public static void printLength(String data){
        int result = data.length(); //data가 null일 경우 예외 발생
        System.out.println(&quot;문자 수 : &quot;+result);
    }
    public static void main(String[] args) {
           System.out.println(&quot;[프로그램 시작]&quot;);
        printLength(&quot;ThisIsJava&quot;);
        printLength(null);
        System.out.println(&quot;[프로그램 종료]&quot;);
    }
}
</code></pre>
<p>이런 경우에 null이 들어왔을 때 프로그램은 실행 예외인 NullPointerException을 발생시키고 예외가 발생한 시점에서 프로그램이 종료된다. 그래서 아래의 [프로그램 종료]는 출력되지 않는다.
이 때 예외가 발생 해도 종료되지 않게 하려면 try - catch 문을 활용하면 된다.</p>
<pre><code class="language-java">package ch11.sec02.exam02;

public class ExceptionHandlingExample2 {
    public static void printLength(String data){
        try{
            int result = data.length();
            System.out.println(&quot;문자 수 : &quot;+result);
        }catch (NullPointerException e){
            System.out.println(e.getMessage()); // 예외 출력 방식 1
            //System.out.println(e.toString()); 예외 출력 방식 2
            //e.printStackTrace();                예외 출력 방식 3
        } finally {
            System.out.println(&quot;마무리 실행&quot;);
        }

    }
    public static void main(String[] args) {
        System.out.println(&quot;[프로그램 시작]&quot;);
        printLength(&quot;ThisIsJava&quot;);
        printLength(null);
        System.out.println(&quot;[프로그램 종료]&quot;);
    }
}
</code></pre>
<p>try 블록에서 NullPointerException이 발생하면 catch 블럭을 실행해 예외를 처리하고, 
finally블럭을 통해 예외와 관계 없이 코드를 실행해 마무리 작업을 할 수 있다.</p>
<p> e.toString(), e.getMessage(), e.printStackTrace() 의 차이점은 다음과 같다.</p>
<ol>
<li>e.toString()은 에러의 Exception 내용과 원인을 출력.</li>
<li>e.getMessage()는 에러의 원인을 간단하게 출력.</li>
<li>e.printStackTrace()는 에러의 발생 근원지를 찾아 단계별로 에러를 출력.</li>
</ol>
<p>finally 블록은 예외에 관계 없이 실행되는 부분인데, 여기에 return을 넣게 되면 
try-catch는 return을 만났을 때 finally 블록을 거쳐 정상 종료 되는 반면 finally 블록은 try안에 발생한 Exception이 무시되고 finally 블록으로 제어가 전달되어 결국 정상종료가 되지 않을 가능성이 있다. </p>
<h2 id="예외-종류에-따른-처리">예외 종류에 따른 처리</h2>
<p>try 블럭에는 다양한 종류의 예외가 발생할 수 있다. 이 경우 다중 catch를 이용하면 예외에 따라 예외 처리 코드를 다르게 작성할 수 있다.</p>
<pre><code class="language-java">package ch11.sec03.exam01;

public class ExceptionHandlingExample {
    public static void main(String[] args) {
        String[] array = {&quot;100&quot;,&quot;1oo&quot;};

        for(int i = 0; i&lt;array.length; i++){
            try{
                int value = Integer.parseInt(array[i]);
            }catch (ArrayIndexOutOfBoundsException e){
                e.printStackTrace();
                System.out.println(&quot;배열 길이 초과됨&quot;);
            }catch (NumberFormatException e){
                e.printStackTrace();
                System.out.println(&quot;숫자로 변환할 수 없음&quot;);
            }
        }
    }
}
</code></pre>
<h2 id="리소스-자동-닫기">리소스 자동 닫기</h2>
<p>리소스란 데이터를 제공하는 객체를 말한다. 리소스는 사용하기 위해 열어야(open) 하며, 사용이 끝난 다음에는 닫아야(close)한다. 예를 들어 파일 내용을 읽기 위해서는 파일을 열어야 하며, 다 읽고 난 후에는 파일을 닫아야 다른 프로그램에서 사용할 수 있다. </p>
<p>불안정한 리소스 사용을 막기 위해 try-catch-finally 문을 사용할 수 있다. </p>
<pre><code class="language-java">package ch11.sec04;

public class MyResource implements AutoCloseable{

    private String name;

    public MyResource(String name){
        this.name = name;
        System.out.println(&quot;[MyResource(&quot;+name+&quot;)열기&quot;);
    }
    public String read1(){
        System.out.println(&quot;[MyResource(&quot;+name+&quot;)읽기&quot;);
        return &quot;100&quot;;
    }
    public String read2(){
        System.out.println(&quot;[MyResource(&quot;+name+&quot;)열기&quot;);
        return &quot;abc&quot;;
    }

    @Override
    public void close() throws Exception {
        System.out.println(&quot;[MyResource(&quot;+name+&quot;)닫기&quot;);
    }
}
</code></pre>
<p>AutoCloseable의 구현체를 만들어서 close()를 오버라이딩 하면 되는데,
이후에 finally 블록에서 close()를 호출해 안전하게 닫는 방법이 있다.</p>
<pre><code class="language-java">FileInputStream fis = null;
try{
    fis = new FileInputStream(&quot;file.txt&quot;);
    ...
}catch(IOException e){
    ...
}finally{
    fis.close();
}</code></pre>
<p>좀 더 편한 방법으로 try-with-resources 문이 있다.
 try 안에 리소스를 여는 코드를 작성하면 try 블록이 정상 종료 되거나 예외가 발생하면 자동으로 리소스의 close() 메소드가 호출된다.</p>
<pre><code class="language-java">try(FileInputStream fis = new FileInputStream(&quot;file.txt&quot;)){
    ...
} catch(IOException e){
    ...
}</code></pre>
<h2 id="예외-떠넘기기">예외 떠넘기기</h2>
<p>try-catch 블록 이외에도 throws 를 이용한 예외처리가 가능하다.</p>
<pre><code class="language-java">리턴타입 메소드명(매개변수, ...) throws 예외클래스1, 예외클래스2, ... {

}</code></pre>
<p>throws 키워드가 붙어있는 메소드에서 해당 예외를 처리하지 않고 떠넘겼기 때문에 이 메소드를 호출하는 곳에서 예외를 받아 처리해야 한다. </p>
<pre><code class="language-java">package ch11.sec05;

public class ThrowsExample1 {
    public static void main(String[] args) {
        try {
            findClass();
        } catch(ClassNotFoundException e){
            System.out.println(&quot;예외처리: &quot;+e.getMessage());
        }
    }
    public static void findClass() throws ClassNotFoundException{
        Class.forName(&quot;java.lang.String2&quot;);
    }
}
</code></pre>
<p>나열해야 할 예외 클래스가 많으면 throws Exception 또는 throws Throwable만으로 모든 예외를 떠넘길 수 있다.
main()에서도 throws를 쓸 수 있는데, 이 때는 JVM에서 예외처리를 진행한다.</p>
<pre><code class="language-java">package ch11.sec05;

public class ThrowsExample2 {
    public static void main(String[] args) throws Exception {
        findClass();
    }
    public static void findClass() throws ClassNotFoundException{
        Class.forName(&quot;java.lang.String2&quot;);
    }
}
</code></pre>
<h2 id="사용자-정의-예외">사용자 정의 예외</h2>
<p>상황에 따라 개발자가 사용자 정의 예외를 만들어야 할 때도 있다.
통상적으로 일반 예외는 Exception의 자식 클래스로 선언하고, 실행 예외는 RuntimeException의 자식 클래스로 선언한다.</p>
<pre><code class="language-java">package ch11.sec06;

public class InsufficientException extends Exception{
    public InsufficientException(){

    }
    public InsufficientException(String message){
        super(message);
    }
}
</code></pre>
<p>이렇게 Exception을 상속받은 후에</p>
<pre><code class="language-java">package ch11.sec06;

public class Account {
    private long balance;

    public Account(){}

    public long getBalance(){
        return balance;
    }
    public void deposit(int money){
        balance += money;
    }
    public void withdraw(int money) throws InsufficientException{
        if(balance &lt; money){
            throw new InsufficientException(&quot;잔고 부족: &quot;+(money-balance)+&quot; 모자람&quot;);
        }
        balance -= money;
    }
}
</code></pre>
<p>이렇게 만들어 주면 된다.
일반 예외의 경우 컴파일러가 예외 처리 코드 여부를 체크하기 때문에 </p>
<pre><code class="language-java">public void withdraw(int money) throws InsufficientException{
        if(balance &lt; money){
            throw new InsufficientException(&quot;잔고 부족: &quot;+(money-balance)+&quot; 모자람&quot;);
        }
        balance -= money;
    }</code></pre>
<p>이렇게 사용자 정의 예외를 만들 때 catch를 하지 않고 throw new InsufficientException로 넘겼기 때문에 예외를 메소드 선언부에 throws InsufficientException 를 선언해 줘야 한다.</p>
<p>체크 예외는 이렇게 모든 예외를 throws하거나 catch 해줘야 하기 때문에 의존관계에 문제가 생길 수 있다. 어차피 체크 예외들은 거의 대부분 복구 불가능한 예외들이 많은데 처리하지 못할 예외에 throws가 계속 붙으면 강한 의존상태가 되어 코드 수정이 어려워 진다.</p>
<p>그래서 throws Exception 이렇게 극단적으로 쓸 수도 있는데 이렇게 하기 보다는 그냥 런타임 예외를 구체적으로 잘 짜는게 더 좋다. </p>
<pre><code class="language-java">package ch11.sec06;

public class AccountExample {
    public static void main(String[] args) {
        Account account = new Account();

        account.deposit(10000);

        System.out.println(&quot;예금액: &quot;+account.getBalance());

        try{
            account.withdraw(30000);
        }catch (InsufficientException e){
            String message = e.getMessage();
            System.out.println(message);
        }
    }
}
</code></pre>
<h2 id="연습문제">연습문제</h2>
<p><img src="https://velog.velcdn.com/images/seo-faper/post/f5d4563b-b37e-47bd-957f-cdca9896d042/image.png" alt=""></p>
<p>4번 사설 예외도 처리할 수 있죠.
<img src="https://velog.velcdn.com/images/seo-faper/post/3bc2dadc-e5d9-4e8a-86e1-2afc379e0f1c/image.png" alt=""></p>
<p>3번, finally는 예외 발생 여부와 상관없이 무조건 실행된다. try안에서 return을 해도 finally는 실행된다.</p>
<p><img src="https://velog.velcdn.com/images/seo-faper/post/8c9ebe1f-aeb4-4022-b415-077cf15ebb54/image.png" alt=""></p>
<p>4번, throws는 말 그대로 예외를 전달하는데 목적이 있습니다.</p>
<p><img src="https://velog.velcdn.com/images/seo-faper/post/21379d75-be44-492c-a525-4ce3ad80c170/image.png" alt=""></p>
<p>2번, 생성자에는 불가능하고 메소드만 됩니다.</p>
<p><img src="https://velog.velcdn.com/images/seo-faper/post/471e6e8c-a19d-4400-a1ba-b3747720f29f/image.png" alt=""></p>
<p>3번, method1()에서 throws 하는 저 2개의 예외에 대한 catch를 해줘야 합니다.</p>
<p><img src="https://velog.velcdn.com/images/seo-faper/post/842aa01b-bfd5-4cf5-98b3-7be8220ef8a8/image.png" alt=""></p>
<pre><code>10
숫자로 변환할 수 없음
10
인덱스를 초과했음.
10</code></pre><p><img src="https://velog.velcdn.com/images/seo-faper/post/660fbd7c-fb27-4173-9814-9d7f1abef1eb/image.png" alt="">
<img src="https://velog.velcdn.com/images/seo-faper/post/9ca4486a-9025-4268-8b3d-fee7676aebd0/image.png" alt="">
<img src="https://velog.velcdn.com/images/seo-faper/post/1b9068f9-c81d-4f48-abe3-4a1f15f0b4d5/image.png" alt=""></p>
<pre><code class="language-java">1번 괄호 super(message); 
2번 괄호 super(message); 
3번 괄호 throws NotExistIdException
4번 괄호 throws new NotExistIdException(&quot;아이디가 존재하지 않습니다.&quot;);
5번 괄호 throws new WrongPasswordException(&quot;패스워드가 틀립니다.&quot;);</code></pre>
<p><img src="https://velog.velcdn.com/images/seo-faper/post/6b6f0730-4238-4768-a3de-06e9be297379/image.png" alt=""></p>
<pre><code class="language-java">try (FileWriter fw = new FileWriter(&quot;file.txt&quot;) ){
    fw.write(&quot;java&quot;);
} catch (IOException e){
    e.printStackTrace();
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[이것이 자바다 8일차 - Chapter10 라이브러리와 모듈]]></title>
            <link>https://velog.io/@seo-faper/%EC%9D%B4%EA%B2%83%EC%9D%B4-%EC%9E%90%EB%B0%94%EB%8B%A4-8%EC%9D%BC%EC%B0%A8-Chapter10-%EB%9D%BC%EC%9D%B4%EB%B8%8C%EB%9F%AC%EB%A6%AC%EC%99%80-%EB%AA%A8%EB%93%88</link>
            <guid>https://velog.io/@seo-faper/%EC%9D%B4%EA%B2%83%EC%9D%B4-%EC%9E%90%EB%B0%94%EB%8B%A4-8%EC%9D%BC%EC%B0%A8-Chapter10-%EB%9D%BC%EC%9D%B4%EB%B8%8C%EB%9F%AC%EB%A6%AC%EC%99%80-%EB%AA%A8%EB%93%88</guid>
            <pubDate>Wed, 18 Jan 2023 11:36:58 GMT</pubDate>
            <description><![CDATA[<h2 id="라이브러리-만드는-법">라이브러리 만드는 법</h2>
<p>라이브러리란? 프로그램 개발 시 활용할 수 있는 클래스와 인터페이스들을 모아놓은 것이다.
<code>*.jar</code> 파일 형태로 존재하며 클래스와 인터페이스의 바이트코드 파일이 압축되어 있다.</p>
<p>인텔리제이에서 jar 추출 하는 법
<img src="https://velog.velcdn.com/images/seo-faper/post/147157ce-2ddb-4fa7-9b4d-5cfc2484125b/image.png" alt=""></p>
<p>대충 이렇게 추출할 코드를 만들고</p>
<p>File -&gt; Project Settings -&gt; Artifacts </p>
<p><img src="https://velog.velcdn.com/images/seo-faper/post/0680e0e6-5ec0-4a20-98b0-14eb2cb0a478/image.png" alt=""></p>
<p>저기 + 표시 눌러주고 From module with depencies 눌러서 만들고 Apply
그 후 Build -&gt; Build Artifacts 
그럼 저 경로에 jar파일이 생긴다.</p>
<h2 id="라이브러리-추가하는-법">라이브러리 추가하는 법</h2>
<p><img src="https://velog.velcdn.com/images/seo-faper/post/a6695346-9330-40ac-b2d2-e3ca3a54fbb5/image.png" alt="">
File -&gt; Project Settings -&gt; Libraries</p>
<p>그리고 + 표시 눌러서 추가하면 됨</p>
<h2 id="모듈-만드는-법">모듈 만드는 법</h2>
<p>모듈이란 패키지 관리 기능이 포함된 라이브러리를 말한다.
일반 라이브러리는 내부에 포함된 모든 패키지에 외부 프로그램에서의 접근이 가능하지만
모듈은 패키지를 은닉하여 접근할 수 없게끔 할 수 있다.</p>
<p>또한 모듈 기술자(module-info.java)를 통해 모듈간의 의존 관계를 쉽게 파악할 수 있다.</p>
<p>응용프로그램의 규모가 커질수록 협업과 유지보수 측면에서 서브 모듈로 쪼개서 개발하는 것이 유리하다. </p>
<h3 id="module-info-만드는-법">module-info 만드는 법</h3>
<pre><code class="language-java">exports &lt;패키지 명&gt;</code></pre>
<p>exports 키워드는 해당 모듈이 가지고 있는 패키지를 외부에서 사용할 수 있도록 열어주는 역할을 한다.</p>
<pre><code class="language-java">requires &lt;모듈 명&gt;</code></pre>
<p>requires 는 모듈을 이식받는 모듈이나 프로젝트에서 필요한 의존 모듈을 설정하는 키워드이다.
즉, 어떤 모듈을 컴파일 하거나 실행 할 때 반드시 먼저 필요한 의존 모듈을 설정하는 것이다.</p>
<p>그런데 여기서 의존 모듈을 의존 모듈로 쓰고 싶을 때가 있다.
즉, <code>MyApplication</code> -&gt; <code>module_a</code> -&gt; <code>module_b</code> 이런 구조로 의존성을 가지고 싶을 때가 있는데, MyApplication에서 requires로 module_a를 의존 설정 해도 module_a는 module_b를 의존 모듈로 설정하고 있으면 MyApplication에서는 module_b를 모르기 때문에 설정을 할 수가 없다. 그 때 사용하는 키워드가 전의 의존 (requires transtivie) 이다.</p>
<pre><code class="language-java">requires transitive module_b</code></pre>
<p>module_a에서 이렇게 쓰면 의존을 받는 MyApplication에서도 module_b를 쓸 수 있다.</p>
<p><code>module-info.java</code></p>
<pre><code class="language-java">module my.module.a {
    exports pack1;
    requires transitive my.module.b;
}</code></pre>
<p>즉, module_a의 모듈기술자를 이렇게 작성하면 MyApplication에서 module_a를 requires로 받았을 때 정상적으로 module_b 까지 사용할 수 있는 것이다.</p>
<h2 id="모듈-추가하는-법">모듈 추가하는 법</h2>
<p><img src="https://velog.velcdn.com/images/seo-faper/post/5ec0ec6a-80e7-4d1b-91e6-1605e6f5c671/image.png" alt="">
File -&gt; Project Settings -&gt; Modules
여기서 추가하면 된다.</p>
<h2 id="연습문제">연습문제</h2>
<p><img src="https://velog.velcdn.com/images/seo-faper/post/8c997db6-f5a2-4abb-8ea1-7d849ab45843/image.png" alt="">
2번 소스파일이 아니라 .class 파일로 존재합니다.</p>
<p><img src="https://velog.velcdn.com/images/seo-faper/post/4fb0dae4-6928-4513-b377-096c0058830b/image.png" alt=""></p>
<p>3번 없으면 모듈로 취급 못받습니다.</p>
<p><img src="https://velog.velcdn.com/images/seo-faper/post/a89ed604-d5c4-4c63-86cc-286eb5a24391/image.png" alt="">
3번 할 수 있습니다. 내가 의존을 설정하고 또 다른 모듈에게 전의 할 수 있죠. </p>
<p><img src="https://velog.velcdn.com/images/seo-faper/post/453dc29c-9378-41c1-9790-f97fbcdd5ca4/image.png" alt=""></p>
<p>4번 집합 모듈을 의존 설정해도 별개로 다른 모듈의 의존 설정을 할 수 있습니다.</p>
<p><img src="https://velog.velcdn.com/images/seo-faper/post/a04a0226-c10b-42be-89bf-1de274a31c24/image.png" alt=""></p>
<p>2번 아무리 기본 모듈이라도 쓸려면 import 해야 합니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Java] 프로그래머스 방문 길이 풀이]]></title>
            <link>https://velog.io/@seo-faper/Java-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EB%B0%A9%EB%AC%B8-%EA%B8%B8%EC%9D%B4-%ED%92%80%EC%9D%B4</link>
            <guid>https://velog.io/@seo-faper/Java-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EB%B0%A9%EB%AC%B8-%EA%B8%B8%EC%9D%B4-%ED%92%80%EC%9D%B4</guid>
            <pubDate>Tue, 17 Jan 2023 15:26:37 GMT</pubDate>
            <description><![CDATA[<p><a href="https://school.programmers.co.kr/learn/courses/30/lessons/49994">문제 링크</a></p>
<h2 id="풀이">풀이</h2>
<p>중복으로 왔다 갔다 하는걸 어떻게 셀 수 있을까 생각하다가 자바의 컬렉션 프레임워크 중 중복을 허용하지 않는 Set이 있다는 것을 떠올렸다.</p>
<p><code>(지금 좌표, 움직일 좌표)</code> 이걸 Set에 넣으면 같은 곳을 또 방문했을 때는 중복 저장이 되지 않으므로 Set의 크기가 방문 길이가 될 것이다. 라고 생각했다.</p>
<p>그런데 예제 중 </p>
<pre><code>입력값 〉    &quot;UDUDUDUD&quot;
기댓값 〉    1</code></pre><p>이걸 보고 이 상태로 짜면 2가 나오는데, 고민한 결과 추가로 <code>(지금 좌표, 움직일 좌표)</code> 뿐만 아니라 <code>(움직일 좌표, 지금 좌표)</code>를 기록한 후 2를 나누면 된다는 결론이 나왔다. 왜냐하면 예제 그대로 왔던 곳을 다시 돌아가는 것은 한번 움직인 것이기 때문이다. 그래서 모든 구간을 왔다 갔다 두 번 한 걸로 기록하면 여러곳을 빙빙 돌다가 다시 왔을 때, 양방향으로 문질러 놨으므로(?) 기록 되지 않는다. 그리고 모두 두 번씩 간 걸로 더했기 때문에 마지막엔 2로 나눠주면 원하는 길이가 나온다는 결론이였다. </p>
<p>즉, </p>
<pre><code class="language-java">set.add(지금좌표, 움직일좌표)
set.add(움직일좌표, 지금좌표)</code></pre>
<p>이렇게 추가하고 2로 나눠주면 된다는 것이다.</p>
<pre><code class="language-java">
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

public class Solution
{
    public int solution(String dirs) {
        int answer = 0;
        char[] command = dirs.toCharArray();
        int x = 0;
        int y = 0;
        int[] dx = {0,0,-1,1};
        int[] dy = {1,-1,0,0};
        Set&lt;String&gt; movement = new HashSet&lt;String&gt;();
        for(int i = 0; i&lt; command.length; i++) {

            if (command[i] == &#39;U&#39; &amp;&amp; y &lt; 5) {
                movement.add(Arrays.toString(new int[]{x,y,x+dx[0],y+dy[0]}));
                movement.add(Arrays.toString(new int[]{x+dx[0],y+dy[0],x,y}));
                y++;

            } else if (command[i] == &#39;D&#39; &amp;&amp; y &gt; -5) {
                movement.add(Arrays.toString(new int[]{x,y,x+dx[1],y+dy[1]}));
                movement.add(Arrays.toString(new int[]{x+dx[1],y+dy[1],x,y}));
                y--;


            } else if (command[i] == &#39;L&#39; &amp;&amp; x &gt; -5 ) {
                movement.add(Arrays.toString(new int[]{x,y,x+dx[2],y+dy[2]}));
                movement.add(Arrays.toString(new int[]{x+dx[2],y+dy[2],x,y}));
                x--;

            } else if (command[i] == &#39;R&#39; &amp;&amp; x &lt; 5) {
                movement.add(Arrays.toString(new int[]{x,y,x+dx[3],y+dy[3]}));
                movement.add(Arrays.toString(new int[]{x+dx[3],y+dy[3],x,y}));
                x++;

            }

        }
        return movement.size()/2;

    }

}
</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[이것이 자바다 7일차 - Chapter9 중첩 선언과 익명 객체 ]]></title>
            <link>https://velog.io/@seo-faper/%EC%9D%B4%EA%B2%83%EC%9D%B4-%EC%9E%90%EB%B0%94%EB%8B%A4-7%EC%9D%BC%EC%B0%A8-Chapter9-%EC%A4%91%EC%B2%A9-%EC%84%A0%EC%96%B8%EA%B3%BC-%EC%9D%B5%EB%AA%85-%EA%B0%9D%EC%B2%B4</link>
            <guid>https://velog.io/@seo-faper/%EC%9D%B4%EA%B2%83%EC%9D%B4-%EC%9E%90%EB%B0%94%EB%8B%A4-7%EC%9D%BC%EC%B0%A8-Chapter9-%EC%A4%91%EC%B2%A9-%EC%84%A0%EC%96%B8%EA%B3%BC-%EC%9D%B5%EB%AA%85-%EA%B0%9D%EC%B2%B4</guid>
            <pubDate>Tue, 17 Jan 2023 08:43:16 GMT</pubDate>
            <description><![CDATA[<h2 id="중첩-클래스란">중첩 클래스란</h2>
<p>중첩 클래스란 클래스 내부에 선언한 클래스를 말한다.
중첩 클래스를 사용하면 클래스의 멤버를 쉽게 사용할 수 있고 외부에는 중첩 관계 클래스를 감춤으로써 코드의 복잡성을 줄일 수 있다. </p>
<p>중첩 클래스는 선언하는 위치에 따라 두 가지로 분류된다. 클래스의 멤버러소 선언되는 중첩 클래스를 멤버 클래스라고 하고 메소드 내부에서 선언되는 중첩 클래스를 로컬 클래스라고 한다. </p>
<table>
<thead>
<tr>
<th>선언 위치</th>
<th>객체 생성 조건</th>
</tr>
</thead>
<tbody><tr>
<td><code>class A { class B {...} }</code></td>
<td>A 객체를 생성해야함 B 객체 생성 가능</td>
</tr>
<tr>
<td><code>class A { static class B {...} }</code></td>
<td>A 객체를 생성하지 않아도 B 객체를 생성할 수 있음</td>
</tr>
<tr>
<td><code>class A { void method() { class B {...} } }</code></td>
<td>method가 실행할 때만 B 객체를 생성할 수 있음</td>
</tr>
</tbody></table>
<p>중첩 클래스도 하나의 클래스이기 때문에 컴파일 시 바이트코드 파일(<code>*.class</code>)이 별도로 생성된다. 멤버 클래스일 경우 바이트 코드 파일은 다음과 같이 결정된다.</p>
<pre><code>A $ B . class
|   |
|   ㄴ 멤버 클래스
ㄴ 바깥 클래스 </code></pre><p>로컬 클래스일 경우는 다음과 같이 $1이 포함된 바이트코드 파일이 생성된다.</p>
<pre><code>A $1 B . class</code></pre><h2 id="인스턴스-멤버-클래스">인스턴스 멤버 클래스</h2>
<p>인스턴스 멤버 클래스는 다음과 같이 A 클래스의 멤버로 선언된 B 클래스를 말한다.</p>
<pre><code class="language-java">[public] class A {
    [public | private] class B { ... } //&lt;- 인스턴스 멤버 클래스 
}</code></pre>
<p>접근 제한자에 따른 인스턴스 멤버 클래스의 접근 범위는 다음과 같다.</p>
<table>
<thead>
<tr>
<th>구분</th>
<th>접근 범위</th>
</tr>
</thead>
<tbody><tr>
<td><code>public class B{ ... }</code></td>
<td>다른 패키지에서 B 클래스를 사용할 수 있다.</td>
</tr>
<tr>
<td><code>class B { ... }</code></td>
<td>같은 패키지에서만 B 클래스를 사용할 수 있다.</td>
</tr>
<tr>
<td><code>private class B { ... }</code></td>
<td>A 클래스내부에서만 B 클래스를 사용할 수 있다.</td>
</tr>
</tbody></table>
<p>인스턴스 멤버 클래스 B는 주로 A 클래스 내부에 사용되므로 private로 선언하는게 일반적이다. B 객체는 A 클래스 내부 어디에서나 생성할 수 없고, 인스턴스 필드값, 생성자, 인스턴스 메소드에서 생성할 수 있다. A 객체가 있어야 B 객체도 생성될 수 있기 때문이다.</p>
<pre><code class="language-java">package ch09.sec02.exam01;

public class A {

    //인스턴스 멤버 클래스
    class B{}

    // 인스턴스 필드 값으로 B 객체 대입
    B field = new B();

    //생성자
    A(){
        B b = new B();
    }

    // 인스턴스 메소드
    void method(){
        B b = new B();
    }
}
</code></pre>
<pre><code class="language-java">package ch09.sec02.exam01;

public class AExample {
    public static void main(String[] args) {
        A a = new A();

        A.B b = a.new B();
    }
}
</code></pre>
<h2 id="정적-멤버-클래스">정적 멤버 클래스</h2>
<pre><code class="language-java">[public] class A {
    [public | private] static class B { ... } //&lt;- 정적 멤버 클래스 
}</code></pre>
<p>이렇게 내부 클래스에 static 이 붙어있으면 정적 맴버로 선언된다.</p>
<table>
<thead>
<tr>
<th>구분</th>
<th>접근 범위</th>
</tr>
</thead>
<tbody><tr>
<td><code>public static class B{ ... }</code></td>
<td>다른 패키지에서 B 클래스를 사용할 수 있다.</td>
</tr>
<tr>
<td><code>static class B { ... }</code></td>
<td>같은 패키지에서만 B 클래스를 사용할 수 있다.</td>
</tr>
<tr>
<td><code>private static class B { ... }</code></td>
<td>A 클래스내부에서만 B 클래스를 사용할 수 있다.</td>
</tr>
</tbody></table>
<p>정적 멤버 클래스는 주로 클래스 외부에서 함께 사용되기 때문에 주로 default 또는 public 접근 제한자를 주로 가진다.
B 객체는 A 캘래스 내부 어디든 생성 할 수 있기 때문이다.</p>
<h2 id="로컬-클래스">로컬 클래스</h2>
<p>생성자 또는 메소드 내부에서 선언된 클래스를 로컬 클래스라고 한다.</p>
<pre><code class="language-java">[public] class A{
    //생성자
    public A() {
        class B { ... } // 로컬 클래스
    }

    //메소드
    public void method(){
        class B {...} // 로컬 클래스
    }
}</code></pre>
<p>로컬 변수를 로컬 클래스에서 사용할 경우 로컬 변수는 final의 특성을 가지게되어 읽는 것만 가능하다.</p>
<h2 id="바깥-멤버-클래스">바깥 멤버 클래스</h2>
<h3 id="바깥-클래스의-멤버-접근">바깥 클래스의 멤버 접근</h3>
<p>중첩 클래스는 바깥 클래스와 긴밀한 관계를 맺으면서 바깥 클래스의 멤버(필드, 메소드)에 접근할 수 있다. 하지만 중첩 클래스가 어떻게 선언되었느냐에 따라 접근 제한이 있을 수 있다.</p>
<pre><code class="language-java">package ch09.sec05.exam01;

public class A {
    int field1;
    void method1(){}
    static int field2;
    static void method2(){}

    class B{
        void method(){
            field1 = 10;
            method1();

            field2 = 10;
            method2();
        }
    }
    static class C{
        void method(){
            //field1 = 10; 정적 멤버에서는 바깥 객체가 없어도 사용가능해야 하므로 사용 불가
            //method1();

            //static으로 선언된 필드와 메소드는 가능
            field2 = 10;
            method2();;
        }
    }
}</code></pre>
<p>여기 보면 static으로 선언된 내부 클래스는 바깥 클래스의 필드와 메소드를 쓸 수 없는 반면 같은 static으로 선언된 바깥 클래스의 필드와 메소드는 사용 가능하다.
당연하다. 컴파일 과정에서 static으로 선언된 애들이 무조건 먼저 메소드 영역에 박제 당할 테니까. 그래서 무분별한 static 선언은 프로그램 메모리를 많이 잡아 먹게 된다. 어디에서도 호출 가능하니 관리도 힘들어지고.</p>
<h3 id="바깥-클래스의-객체-접근">바깥 클래스의 객체 접근</h3>
<pre><code class="language-java">바깥클래스이름.this // 바깥 객체</code></pre>
<p>중첩 클래스에서 this는 해당 줒첩 클래스의 객체를 말한다. 만약 중첩 클래스 내부에서 바깥 클래스의객체를 얻으려면 바깥 클래스 이름에 this를 붙여주면 된다.</p>
<h2 id="중첩-인터페이스">중첩 인터페이스</h2>
<p>중첩 인터페이스란 클래스의 멤버로 선언된 인터페이스를 말한다. 인터페이스를 클래스 내부에 선언하는 이유는 해당 클래스와 긴밀한 관계를 맺는 구현 객체를 만들기 위해서다.</p>
<pre><code class="language-java">class A {
    [public | private] [static] interface B{
        //상수 필드
        //추상 메소드
        //디폴트 메소드
        //정적 메소드
    }
}</code></pre>
<p>외부의 접근을 막지 않으려면 public, A 클래스 내부에서만 사용하려면 private를 쓰고 접근 제한자를 붙이지 않을 시 같은 패키지 안에서만 접근이 가능하다. 그리고 A 객체 없이 B인터페이스를 사용할 수 있도록 하기 위해 static을 추가할 수 있다.</p>
<p>중첩 인터페이스는 안드로이드와 같은 UI 프로그램에서 이벤트를 처리할 목적으로 많이 활용된다.</p>
<pre><code class="language-java">package ch09.sec06.exam01;

public class Button {
    public static interface ClickListener{

        void onClick();
    }
}
</code></pre>
<p>onClick() 메소드는 버튼이 클릭되었을 때 호출될 메소드이다. 
Button 클래스에 ClickListener 타입의 필드와 Setter를 추가해서 외부에서 Setter를 통해 ClickListener구현 객체를 필드에 저장할 수 있도록 하자.</p>
<pre><code class="language-java">package ch09.sec06.exam03;

public class Button {
    public static interface ClickListener{
        void onClick();
    }

    private ClickListener clickListener;

    public void setClickListener(ClickListener clickListener){
        this.clickListener = clickListener;
    }
    public void click(){
        this.clickListener.onClick();
    }
}
</code></pre>
<p>Button이 클릭되었을 때 실행할 메소드를 click()이라고 하고 선언한다. 실행 내용은 ClickListener 인터페이스 필드를 이용해서 onClick() 추상 메소드를 호출한다. </p>
<pre><code class="language-java">package ch09.sec06.exam03;

public class ButtonExample {
    public static void main(String[] args) {
        Button btnOk = new Button();

        //Ok 버튼 클릭 이벤트를 처리할 ClickListener 구현 클래스 (로컬 클래스)
        class OkListener implements Button.ClickListener{

            @Override
            public void onClick() {
                System.out.println(&quot;Ok 버튼을 클릭했습니다.&quot;);
            }
        }
        //Ok 버튼 객체에 ClickListener 구현 객체 주입
        btnOk.setClickListener(new OkListener());

        btnOk.click();
        //---------------------------

        Button btnCancel = new Button();
        class CancelListener implements Button.ClickListener{

            @Override
            public void onClick() {
                System.out.println(&quot;Cancel 버튼을 클릭했습니다.&quot;);
            }
        }
        btnCancel.setClickListener(new CancelListener());
        btnCancel.click();
    }
}
</code></pre>
<p>이렇게 하나의 Button 클래스에서 어떤 구현 객체를 주입하는지에 따라 인터페이스를 이용한 다형성을 구현할 수 있다. </p>
<h2 id="익명-객체">익명 객체</h2>
<p>말 그대로 이름이 없는 객체를 말한다. 명시적으로 클래스를 선언하지 않기 때문에 쉽게 객체를 생성할 수 있다는 장점이 있다. 익명 객체는 필드값, 로컬 변수값, 매개변수값으로 주로 사용된다. 익명 객체는 클래스를 상속하거나 인터페이스를 구현해야만 생성할 수 있다. 클래스를 상속해서 만들경우 익명 자식 객체라고 하고, 인터페이스를 구현해서 만들 경우 익명 구현 객체라고 한다.</p>
<h3 id="익명-자식-객체">익명 자식 객체</h3>
<pre><code class="language-java">new 부모생성자(매개값, ...)
{
 ...
}</code></pre>
<p>부모 클래스를 상속받아 다음과 같이 생성된다. 이렇게 생성된 객체는 부모 타임의 필드, 로컬 변수, 매개변수의 값으로 대입할 수 있다.</p>
<pre><code class="language-java">package ch09.sec07.exam01;

public class Tire {
    public void roll(){
        System.out.println(&quot;일반 타이어가 굴러갑니다.&quot;);
    }
}
</code></pre>
<pre><code class="language-java">package ch09.sec07.exam01;

public class Car {
    private Tire tire1 = new Tire();

    private Tire tire2 = new Tire(){
        @Override
        public void roll() {
            System.out.println(&quot;익명 자식 Tire 객체 1이 굴러갑니다.&quot;);
        };
    };

    public void run1(){
        tire1.roll();
        tire2.roll();
    }

    public void run2(){
        Tire tire = new Tire(){
            @Override
            public void roll() {
                System.out.println(&quot;익명 자식 Tire 객체 2가 굴러갑니다.&quot;);
            }
        };
        tire.roll();
    }
    public void run3(Tire tire){
        tire.roll();
    }
}
</code></pre>
<pre><code class="language-java">package ch09.sec07.exam01;

public class CarExample {
    public static void main(String[] args) {
        Car car = new Car();

        car.run1();

        car.run2();

        car.run3(new Tire(){
            @Override
            public void roll() {
                System.out.println(&quot;익명 자식 Tire 객체 3이 굴러갑니다.&quot;);
            }
        });
    }
}
</code></pre>
<p>이렇게 필드에 익명 자식 객체를 대입할 수도 있고 로컬 변수에 대입 할수도 있고 익명 자식 객체가 대입된 매개변수를 사용할 수도 있다.</p>
<h3 id="익명-구현-객체">익명 구현 객체</h3>
<pre><code class="language-java">new 인터페이스(){
    //필드
    //메소드
}</code></pre>
<pre><code class="language-java">package ch09.sec07.exam02;

public interface RemoteControl {
    void turnOn();
    void turnOff();
}</code></pre>
<pre><code class="language-java">package ch09.sec07.exam02;

public class Home {
    private RemoteControl rc = new RemoteControl() {
        @Override
        public void turnOn() {
            System.out.println(&quot;TV를 킵니다.&quot;);
        }

        @Override
        public void turnOff() {
            System.out.println(&quot;TV를 끕니다.&quot;);
        }
    };

    public void use1(){
        rc.turnOn();
        rc.turnOff();
    }

    public void use2(){
        RemoteControl rc = new RemoteControl() {
            @Override
            public void turnOn() {
                System.out.println(&quot;에어컨을 킵니다.&quot;);
            }

            @Override
            public void turnOff() {
                System.out.println(&quot;에어컨을 끕니다.&quot;);
            }
        };
    }
    public void use3(RemoteControl rc){
        rc.turnOn();
        rc.turnOff();
    }
}
</code></pre>
<pre><code class="language-java">package ch09.sec07.exam02;

public class HomeExample {
    public static void main(String[] args) {
        Home home = new Home(); //Home 객체 생성

        //익명 구현 객체가 대입된 필드 사용
        home.use1();

        //익명 구현 객체가 대입된 로컬 변수 사용
        home.use2();

        // 익명 구현 객체가 대입된 매개변수 사용
        home.use3(new RemoteControl() {
            @Override
            public void turnOn() {
                System.out.println(&quot;난방을 킵니다.&quot;);
            }

            @Override
            public void turnOff() {
                System.out.println(&quot;난방을 끕니다.&quot;);
            }
        });
    }
}
</code></pre>
<pre><code class="language-java">package ch09.sec07.exam03;

import ch09.sec06.exam03.Button;

public class ButtonExample {
    public static void main(String[] args) {
        Button btnOk = new Button();
        btnOk.setClickListener(new Button.ClickListener() {
            @Override
            public void onClick() {
                System.out.println(&quot;Ok 버튼을 클릭했습니다.&quot;);
            }
        });

        btnOk.click();

        Button btnCancel = new Button();
        btnCancel.setClickListener(new Button.ClickListener() {
            @Override
            public void onClick() {
                System.out.println(&quot;Cancel 버튼을 클릭했습니다.&quot;);
            }
        });

        btnCancel.click();
    }
}
</code></pre>
<p>일전에 만들었던 중첩 인터페이스 예제를 수정한 것이다.
버튼 이벤트 처리 객체를 익명 구현 객체로 대채한 것인데, Setter를 호출할 때 매개값으로 ClickListener익명 구현 객체를 대입했다.
명시적인 구현 클래스를 생성하지 않기 때문에 코드가 간결해 진 것을 볼 수 있다. </p>
<h2 id="연습문제">연습문제</h2>
<p><img src="https://velog.velcdn.com/images/seo-faper/post/e736f931-0aef-4e69-9d2c-891ee51274ed/image.png" alt=""></p>
<p>4번, 바깥 클래스의 정적 필드만 쓸 수 있습니다. 같은 static 집안끼리만 쓸 수 있죠.
<img src="https://velog.velcdn.com/images/seo-faper/post/baf2fd4c-374d-4046-bb4a-311e791aaf24/image.png" alt=""></p>
<p>3번, 로컬 클래스는 메소드 내부에 선언된 클래스인데 static으로 선언하면 메소드가 호출되지도 않았는데 쓸 수 있게 되겠죠.. </p>
<p><img src="https://velog.velcdn.com/images/seo-faper/post/e35f7dad-c5d2-4df6-aa38-f939c568043a/image.png" alt="">
3번, 익명 객체는 명시적으로 클래스를 선언하지 않기 때문에 생성자를 선언할 수 없습니다.</p>
<p><img src="https://velog.velcdn.com/images/seo-faper/post/ae4d620a-02c0-4a54-938a-041aade4cc7c/image.png" alt="">
<img src="https://velog.velcdn.com/images/seo-faper/post/6e2d06c4-fd12-4840-abb9-c10d23f499d0/image.png" alt=""></p>
<pre><code class="language-java">myCar.new Tire();</code></pre>
<pre><code class="language-java">new Car.Engine(); </code></pre>
<p>Car 객체의 내부 클래스기 때문에 myCar.new 로 생성하는 반면 
static으로 선언되어 있으면 바로 접근 할 수 있지요.</p>
<p><img src="https://velog.velcdn.com/images/seo-faper/post/24462a45-422c-44b7-acdf-362b0798896e/image.png" alt=""></p>
<pre><code class="language-java">new( Action(){
    @Override
    public void work(){
        System.out.println(&quot;복사를 합니다.);
      }
});</code></pre>
<p>코드가 간결해 지네요.</p>
<p><img src="https://velog.velcdn.com/images/seo-faper/post/a5556e65-a952-45d5-9596-c5d6395b48d7/image.png" alt="">
<img src="https://velog.velcdn.com/images/seo-faper/post/631dcc6b-a57c-430f-b63c-b08c66ef1081/image.png" alt=""></p>
<p>필드에서 하는 법 </p>
<pre><code class="language-java">Vehicle field = new Vehicle() {
    @Override
    public void run(){
        System.out.println(&quot;자전거가 달립니다.&quot;);
    }
}</code></pre>
<p>로컬 변수의 초기값으로 하는 법</p>
<pre><code class="language-java">Vehicle localVar = new Vehicle() {
    @Override
    public void run(){
        System.out.println(&quot;승용차가 달립니다.&quot;);
    }
}</code></pre>
<p>메소드의 매개값으로 대입하는법</p>
<pre><code class="language-java">anony.method2( new Vehicle(){
    @Override
    public void run(){
        System.out.println(&quot;트럭이 달립니다.&quot;);
    }
});</code></pre>
<p><img src="https://velog.velcdn.com/images/seo-faper/post/405edbc8-bb76-4550-8bb0-98f22a6f19cd/image.png" alt=""></p>
<p>로컬 변수인 nickName을 로컬 클래스인 Chat에서 사용하게 될 경우 nickName은 final의 특성을 갖게 되기에 nickName에 null이 들어간 이상 변경할 수 없게되죠. 그래서 String nickName = chatId; 이렇게 처음에 대입해야 합니다.  </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[이것이 자바다 6일차 - Chapter8 인터페이스]]></title>
            <link>https://velog.io/@seo-faper/%EC%9D%B4%EA%B2%83%EC%9D%B4-%EC%9E%90%EB%B0%94%EB%8B%A4-6%EC%9D%BC%EC%B0%A8-Chapter8-%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4</link>
            <guid>https://velog.io/@seo-faper/%EC%9D%B4%EA%B2%83%EC%9D%B4-%EC%9E%90%EB%B0%94%EB%8B%A4-6%EC%9D%BC%EC%B0%A8-Chapter8-%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4</guid>
            <pubDate>Mon, 16 Jan 2023 06:57:17 GMT</pubDate>
            <description><![CDATA[<h2 id="인터페이스-역할">인터페이스 역할</h2>
<p>인터페이스(Interface)는 다형성 구현의 주된 기술로 상속을 통한 다형성 구현보다 인터페이스를 활용하여 다형성을 구현하는 경우가 더 많다. 인터페이스의 사전적 정의는 두 장치를 연결하는 접속기로, 자바에서 인터페이스는 두 객체를 연결하는 역할을 한다. </p>
<p>객체 A가 인터페이스의 메소드를 호출하면 인터페이스는 객체 B의 메소드를 호출하고 그 결과를 객체 A로 전달해 준다. 객체 B 뿐만 아니라 인터페이스 뒤에는 여러 다형성을 구현하는 객체가 올 수 있다. 예를 들어 객체 B의 메소드와 객체 C의 메소드 실행 결과가 다르다면 객체 A는 객체 교체로 인해 다른 결과를 얻게 된다.  </p>
<h2 id="인터페이스와-구현-클래스-선언">인터페이스와 구현 클래스 선언</h2>
<p>인터페이스는 <code>*.java</code> 형태의 파일로 작성하고 컴파일 시 <code>*.class</code> 형태로 되기 때문에 물리적인 형태는 클래스와 같지만 소스를 작성할 땐 선언하는 방법과 구성 멤버가 클래스와는 다르다.</p>
<h3 id="인터페이스-선언">인터페이스 선언</h3>
<p>인터페이스 선언은 <code>class</code> 키워드 대신 <code>interface</code> 키워드를 사용한다. </p>
<pre><code class="language-java">interface 인터페이스명 { ... }          // default 접근 제한
public interface 인터페이스명 { ... }  // public 접근 제한</code></pre>
<p>접근 제한자는 클래스와 동일하게 default와 public이 있다.</p>
<p>중괄호 안에는 인터페이스가 가지는 멤버들을 선언할 수 있다.</p>
<pre><code class="language-java">public interface 인터페이스명
{
    public 상수 필드
    public 추상 메소드
    public 디폴트 메소드
    public 정적 메소드
    private 메소드
    private 정적 메소드 
}</code></pre>
<p>추상 메소드란 이렇게 선언부만 있고 실행부인 중괄호가 없는 메소드를 말한다.
<code>RemoteContorl.java (interface)</code></p>
<pre><code class="language-java">package ch08.sec02;

public interface RemoteControl
{
    public void turnOn();
}
</code></pre>
<h3 id="구현-클래스-선언">구현 클래스 선언</h3>
<p> <img src="https://velog.velcdn.com/images/seo-faper/post/21b20f4e-94a9-4b45-a6e0-08185eda2ee5/image.png" alt=""></p>
<p>이런 구조로 인터페이스가 돌아가는데, 객체 A가 인터페이스의 추상 메소드를 호출하면 인터페이스는 객체 B의 메소드를 실행한다. 그 때 객체 B는 인터페이스에 선언된 추상 메소드와 동일한 선언부를 가진 (즉, 재정의를 해야 한다.) 메소드를 가지고 있어야 한다.
이 때 객체 B를 인터페이스를 구현한(implement) 객체라고 부른다. </p>
<pre><code class="language-java">public class B implements 인터페이스명 { ... }</code></pre>
<p>이렇게 <code>implements</code> 키워드를 통해 구현체를 만들 수 있다.
기렇게 implements가 선언된 클래스는 해당 클래스가 인터페이스를 통해 사용될 수 있다는 표시이며, 인터페이스의 추상 메소드를 재정의(Overriding) 해줘야 한다.</p>
<p><code>Television.java</code></p>
<pre><code class="language-java">package ch08.sec02;

public class Television implements RemoteControl{

    @Override
    public void turnOn() {
        System.out.println(&quot;TV를 켭니다.&quot;);
    }
}
</code></pre>
<h3 id="변수-선언과-구현-객체-대입">변수 선언과 구현 객체 대입</h3>
<p>인터페이스도 하나의 타입이므로 변수의 타입으로 사용 할 수 있다. 인터페이스는 참조 타입에 속하므로 null을 대입할 수 있다. </p>
<pre><code class="language-java">RemoteControl rc;
RemoteControl rc = null;</code></pre>
<p>인터페이스를 통해 구현 객체를 사용하려면 인터페이스 변수에 구현 객체를 대입해야 한다. 
정확히는 Heap영역에 존재하는 구현 객체의 주소값을 인터페이스 변수에 대입하는 것 이다.
다음은 Television 객체를 생성하고 주소값을 대입하는 코드이다.</p>
<pre><code class="language-java">rc = new Television();</code></pre>
<p>만약 Television이 implements RemoteControl로 선언되지 않았다면 RemoteControl 타입으로 선언할 수 없다. 인터페이스 변수에 구현 객체가 대입이 되어 있다면 변수를 통해 인터페이스의 추상 메소드를 호출할 수 있게 된다. </p>
<pre><code class="language-java">package ch08.sec02;

public class RemoteControlExample
{
    public static void main(String[] args) {
        RemoteControl rc;
        rc = new Television();
        rc.turnOn();
    }
}
</code></pre>
<p>이 rc 변수에는 RemoteControl을 구현한 그 어떠한 객체든 대입할 수 있다. 예시로 또 다른 객체인 Audio객체를 만들고 호출해 보자.</p>
<pre><code class="language-java">package ch08.sec02;

public class Audio implements RemoteControl{
    @Override
    public void turnOn() {
        System.out.println(&quot;Audio를 켭니다.&quot;);
    }
}
</code></pre>
<pre><code class="language-java">package ch08.sec02;

public class RemoteControlExample
{
    public static void main(String[] args) {
        RemoteControl rc;
        // rc 변수에 Television 객체를 대입
        rc = new Television();
        rc.turnOn();

        // rc 변수에 Audio 객체를 대입 (교체시킴)
        rc = new Audio();
        rc.turnOn();
    }
}
</code></pre>
<p>출력 결과는 TV를 켭니다, Audio를 켭니다가 순서대로 출력된다. </p>
<h2 id="상수-필드">상수 필드</h2>
<p>인터페이스에는 public static final로 불변의 상수 필드를 멤버로 가질 수 있다.
인터페이스에서 선언된 필드는 모두 상수의 특성을 가지기 때문에 앞의 수식어를 붙이지 않아도 컴파일 과정에서 자동으로 public static final이 붙게 된다. 즉, 인터페이스는 컴파일 과정 시 정적 영역으로 모든 필드를 넘기는 특징이 있다. </p>
<pre><code class="language-java">package ch08.sec03;

public interface RemoteControl {
    int MAX_VOLUME = 10;
    int MIN_VOLUME = 0;
}
</code></pre>
<pre><code class="language-java">package ch08.sec03;

public class RemoteControlExample {
    public static void main(String[] args) {
        System.out.println(&quot;리모콘 최대 볼륨 : &quot;+RemoteControl.MAX_VOLUME);
        System.out.println(&quot;리모콘 최소 볼륨 : &quot;+RemoteControl.MIN_VOLUME);
    }
}
</code></pre>
<h2 id="추상-메소드">추상 메소드</h2>
<p>인터페이스는 구현 클래스가 재정의 해야 하는 <code>public abstract</code> 으로 시작하는 추상 메소드를 멤버로 가질 수 있다. 메소드의 선언부만 작성하면 된다.
이것 역시 필드 처럼 컴파일 과정에서 자동으로 public abstract이  붙게 된다. </p>
<pre><code class="language-java">package ch08.sec04;

public interface RemoteControl {
    int MAX_VOLUME = 10;
    int MIN_VOLUME = 0;

    void turnOn();
    void turnOff();
    void setVolume(int volume); // 메소드 선언부만 작성 
}
</code></pre>
<p>이렇게 추상 메소드를 구현할 수 있다.</p>
<pre><code class="language-java">package ch08.sec04;


public class Television implements RemoteControl {
    private int volume;


    @Override
    public void turnOn() {
        System.out.println(&quot;TV를 켭니다.&quot;);
    }

    @Override
    public void turnOff() {
        System.out.println(&quot;TV를 끕니다.&quot;);
    }

    @Override
    public void setVolume(int volume) {
        if(volume&gt;RemoteControl.MAX_VOLUME){
            this.volume = RemoteControl.MAX_VOLUME;
        }else if(volume &lt; RemoteControl.MIN_VOLUME){
            this.volume = RemoteControl.MIN_VOLUME;
        } else {
            this.volume = volume;
        }
        System.out.println(&quot;현재 TV 볼륨 : &quot;+volume);
    }
}
</code></pre>
<p>그리고 추상 메소드를 구체화 하는 코드는 implements를 받는 객체에서 재정의로 만들어 주면 된다. </p>
<h2 id="디폴트-메소드">디폴트 메소드</h2>
<p>인터페이스에는 완전한 실행 코드를 가진 디폴트 메소드를 선언할 수 있다.
추상 메소드와 다른 점은 실행부 (중괄호)가 존재한다는 점이다.</p>
<pre><code class="language-java">[public] default 리턴타입 메소드명(매개변수,...) { ... }</code></pre>
<p>이런 형태로 구현 할 수 있다.</p>
<pre><code class="language-java">package ch08.sec05;

public interface RemoteControl
{
    int MAX_VOLUME = 10;
    int MIN_VOLUME = 0;

    void turnOn();
    void turnOff();
    void setVolume(int volume);

    default void setMute(boolean mute){
        if(mute){
            System.out.println(&quot;무음 처리합니다.&quot;);
            setVolume(MIN_VOLUME);
        } else {
          System.out.println(&quot;무음 해제 합니다. &quot;);
        }
    }
}
</code></pre>
<p>default로 선언된 setMute()는 인터페이스임에도 실행부를 가진다. 그래서 인터페이스를 구현한 구현체에 없는 메소드지만 인터페이스 변수에 담았을 때 메소드 호출이 가능하다.</p>
<pre><code class="language-java">package ch08.sec05;

public class RemoteControlExample {
    public static void main(String[] args) {
        RemoteControl rc;
        rc = new Television();
        rc.turnOn();
        rc.setVolume(5);
        rc.setMute(true); // Television.java에 존재하지 않는 메소드
        rc.setMute(false); //Television.java에 존재하지 않는 메소드 
    }
}
</code></pre>
<p>또한 이 디폴트 메소드는 구현 클래스에서 재정의 할 수 있는데, 이 때는 default 키워드를 생략 하고 반드시 public 접근 제한자를 붙여야 한다. </p>
<pre><code class="language-java">package ch08.sec05;

public class Audio implements RemoteControl{
    private int volume;
    @Override
    public void turnOn() {
        System.out.println(&quot;Audio를 켭니다.&quot;);
    }

    @Override
    public void turnOff() {
        System.out.println(&quot;Audio를 끕니다.&quot;);
    }

    @Override
    public void setVolume(int volume) {
        if(volume&gt;RemoteControl.MAX_VOLUME){
            this.volume = RemoteControl.MAX_VOLUME;
        }else if(volume &lt; RemoteControl.MIN_VOLUME){
            this.volume = RemoteControl.MIN_VOLUME;
        } else {
            this.volume = volume;
        }
        System.out.println(&quot;현재 TV 볼륨 : &quot;+volume);
    }

    private int memoryVolume;

    @Override
    public void setMute(boolean mute) {
        if(mute){
            this.memoryVolume = this.volume;
            System.out.println(&quot;무음 처리합니다.&quot;);
            setVolume(RemoteControl.MIN_VOLUME);
        } else {
            System.out.println(&quot;무음 해제합니다.&quot;);
            setVolume(this.memoryVolume); // 원래 볼륨을 복구 하는 코드
        }
    }


}
</code></pre>
<pre><code class="language-java">package ch08.sec05;

public class RemoteControlExample {
    public static void main(String[] args) {
        RemoteControl rc;
        rc = new Television();
        rc.turnOn();
        rc.setVolume(5);

        //기존 인터페이스에 존재하는 디폴트 메소드
        rc.setMute(true);
        rc.setMute(false);

        System.out.println();

        rc = new Audio();
        rc.turnOn();
        rc.setVolume(5);

        //재정의한 디폴트 메소드 호출
        rc.setMute(true);
        rc.setMute(false);
    }
}
</code></pre>
<p>이렇게 원래 볼륨을 복구하는 코드로 재정의 할 수 있다. </p>
<h2 id="정적-메소드">정적 메소드</h2>
<p>또한 인터페이스는 정적 메소드도 선언이 가능하다. 추상 메소드와 디폴트 메소드는 구현 객체가 필요하지만 정적 메소드는 구현 객체가 없어도 인터페이스만으로 호출할 수 있다. 선언 방법은 클래스 정적 메소드와 완전 동일하다. 단, public을 생략하더라도 자동으로 컴파일 과정에서 붙는다. </p>
<pre><code class="language-java">[public | private] static 리턴타입 메소드명(매개변수, ...) { ... }</code></pre>
<pre><code class="language-java">package ch08.sec06;

public interface RemoteControl
{
    //상수 필드
    int MAX_VOLUME = 10;
    int MIN_VOLUME = 0;

    //추상 메소드 
    void turnOn();
    void turnOff();
    void setVolume(int volume);

    //디폴트 메소드
    default void setMute(boolean mute){
        ... 
    }

    // 정적 메소드 
    static void changeBattery(){
        System.out.println(&quot;리모콘 건전지를 교환합니다.&quot;);
    }
}
</code></pre>
<p>이 정적 메소드는 상수 필드를 호출하는 것 처럼 쓸 수 있다.</p>
<pre><code class="language-java">    public static void main(String[] args) {

        RemoteControl rc;

        rc = new Television();
        rc.turnOn();
        rc.setVolume(5);
        rc.setMute(true);
        rc.setMute(false);

        System.out.println();

        rc = new Audio();
        rc.turnOn();
        rc.setVolume(5);

        //재정의한 디폴트 메소드 호출
        rc.setMute(true);
        rc.setMute(false);

        //정적 메소드 호출 
        RemoteControl.changeBattery();
    }</code></pre>
<h2 id="private-메소드">private 메소드</h2>
<p>인터페이스의 상수 필드, 추상 메소드, 디폴트 메소드, 정적 메소드는 모두 public 접근 제한을 갖기 때문에 외부에서 접근이 가능하다. 그래서 인터페이스에도 private로 외부에서 접근할 수 없는 private 메소드 선언이 가능하다. </p>
<p>private 메소드의 용도는 디폴트와 정적 메소드들의 중복 코드를 줄이기 위함이다.
즉 인터페이스 내부에서 동작하는 메소드가 있을 때 사용한다.</p>
<pre><code class="language-java">package ch08.sec07;

public interface Service
{
    default void defaultMethod1(){
        System.out.println(&quot;defaultMethod1 종속 코드&quot;);
        defaultCommon();
    }
    default void defaultMethod2(){
        System.out.println(&quot;defaultMethod2 종속 코드&quot;);
        defaultCommon();
    }
    // private 메소드
    private void defaultCommon(){
        System.out.println(&quot;defaultMethod 중복 코드 A&quot;);
        System.out.println(&quot;defaultMethod 중복 코드 B&quot;);
    }

    static void staticMethod1(){
        System.out.println(&quot;staticMethod1 종속 코드&quot;);
        staticCommon();
    }
    static void staticMethod2(){
        System.out.println(&quot;staticMethod2 종속 코드&quot;);
        staticCommon();
    }
    private static void staticCommon(){
        System.out.println(&quot;staticMethod 중복 코드 C&quot;);
        System.out.println(&quot;staticMethod 중복 코드 D&quot;);
    }
}
</code></pre>
<pre><code class="language-java">package ch08.sec07;

public class ServiceImpl implements Service{ }
</code></pre>
<pre><code class="language-java">package ch08.sec07;

public class ServiceExample {
    public static void main(String[] args) {
        Service service = new ServiceImpl();

        service.defaultMethod1();
        System.out.println();
        service.defaultMethod2();
        System.out.println();

        Service.staticMethod1();
        System.out.println();
        Service.staticMethod2();
        System.out.println();
    }
}
</code></pre>
<p>출력하면 </p>
<pre><code>defaultMethod1 종속 코드
defaultMethod 중복 코드 A
defaultMethod 중복 코드 B

defaultMethod2 종속 코드
defaultMethod 중복 코드 A
defaultMethod 중복 코드 B

staticMethod1 종속 코드
staticMethod 중복 코드 C
staticMethod 중복 코드 D

staticMethod2 종속 코드
staticMethod 중복 코드 C
staticMethod 중복 코드 D</code></pre><p>이렇게 나온다. </p>
<h2 id="다중-인터페이스-구현">다중 인터페이스 구현</h2>
<p>구현 객체는 여러개의 인터페이스를 implements 할 수 있다. 
구현 클래스는 선언부에 implements 뒤에 쉼표로 구분된 인터페이스명으로 여러개의 인터페이스를 받을 수 있다. 그리고 여러개의 모든 인터페이스의 추상 메소드를 재정의 하면 된다.</p>
<pre><code class="language-java">public class 구현클래스명 implements 인터페이스A, 인터페이스B</code></pre>
<pre><code class="language-java">인터페이스A 변수 = new 구현클래스명(...);
인터페이스B 변수 = new 구현클래스명(...);</code></pre>
<p>이렇게 구현 객체가 어떤 인터페이스 변수에 대입되느냐에 따라 변수를 통해 호출할 수 있는 추상 메소드가 정해진다.</p>
<pre><code class="language-java">package ch08.sec08;

public interface RemoteControl {
    void turnOn();
    void turnOff();
}</code></pre>
<pre><code class="language-java">package ch08.sec08;

public interface Searchable {
    void search(String url);
}
</code></pre>
<pre><code class="language-java">package ch08.sec08;

public class SmartTelevision implements RemoteControl, Searchable{
    @Override
    public void turnOn() {
        System.out.println(&quot;TV를 켭니다.&quot;);
    }

    @Override
    public void turnOff() {
        System.out.println(&quot;TV를 끕니다.&quot;);
    }

    @Override
    public void search(String url) {
        System.out.println(url+&quot;을 검색합니다.&quot;);
    }
}
</code></pre>
<p>이렇게 2개의 인터페이스에 대한 모든 추상 메소드를 재정의 한다.</p>
<pre><code class="language-java">package ch08.sec08;

public class MultiInterfaceImplExample {
    public static void main(String[] args) {
        RemoteControl rc = new SmartTelevision();
        rc.turnOn(); //RemoteControl 인터페이스에 선언된 추상 메소드만 호출 가능
        rc.turnOff(); //RemoteControl 인터페이스에 선언된 추상 메소드만 호출 가능

        Searchable searchable = new SmartTelevision();
        //Searchable 인터페이스에 선언된 추상 메소드만 호출 가능
        searchable.search(&quot;https://www.youtube.com&quot;);
    }
}
</code></pre>
<p>그 후 사용할 때는 어떤 인터페이스 변수에 대입할지 결정한 후 사용하면 된다. </p>
<h2 id="인터페이스-상속">인터페이스 상속</h2>
<p>인터페이스도 다른 인터페이스를 상속할 수 있다. (어질..)
심지어 클래스와는 달리 다중 상속을 허용한다.
extends 키워드 뒤에 상속할 인터페이스들을 나열하면 된다.</p>
<pre><code class="language-java">public interface 자식인터페이스 extends 부모인터페이스1, 부모인터페이스2 { ... }</code></pre>
<p>자식인터페이스를 구현한 구현 클래스는 자식 인터페이스의 메소드 뿐만 아니라 상속받는 모든 부모인터페이스의 추상 메소드를 재정의 해야 한다. 그리고 그 구현 객체는 자식 및 부모 인터페이스 변수에 대입될 수 있다.</p>
<pre><code class="language-java">자식인터페이스 변수 = new 구현클래스(...);
부모인터페이스1 변수 = new 부모인터페이스1(...);
부모인터페이스2 변수 = new 부모인터페이스2(...);</code></pre>
<p>자식인터페이스를 담는 변수는 부모와 자식의 모든 추상 메소드를 호출할 수 있지만 부모인터페이스를 담는 변수는 해당 부모인터페이스에 선언된 추상 메소드만 호출할 수 있다.</p>
<pre><code class="language-java">package ch08.sec09;

public interface InterfaceA {
    void methodA();
}
</code></pre>
<pre><code class="language-java">package ch08.sec09;

public interface InterfaceB {
    void methodB();
}
</code></pre>
<pre><code class="language-java">package ch08.sec09;

public interface InterfaceC extends InterfaceA, InterfaceB
{
    void methodC();
}
</code></pre>
<p>InterfaceC는 두 부모 인터페이스를 상속 받는다.</p>
<pre><code class="language-java">package ch08.sec09;

public class InterfaceCImpl implements InterfaceC{


    @Override
    public void methodA() {
        System.out.println(&quot;InterfaceCImpl-methodA() 실행&quot;);
    }

    @Override
    public void methodB() {
        System.out.println(&quot;InterfaceCImpl-methodB() 실행&quot;);
    }

    @Override
    public void methodC() {
        System.out.println(&quot;InterfaceCImpl-methodC() 실행&quot;);
    }

}
</code></pre>
<p>그리고 이 상속 카르텔(?)의 구현 클래스에는 모든 추상 메소드에 대한 재정의가 있어야 한다.</p>
<pre><code class="language-java">package ch08.sec09;

public class ExtendsExample
{
    public static void main(String[] args) {
        InterfaceCImpl impl = new InterfaceCImpl();

        InterfaceA ia = impl;
        ia.methodA();
        //ia.methodB(); InterfaceA와 InterfaceB는 상속관계가 아니므로 불가능

        InterfaceB ib = impl;
        //ia.methodA(); InterfaceA와 InterfaceB는 상속관계가 아니므로 불가능
        ib.methodB();

        InterfaceC ic = impl;
        ic.methodA();
        ic.methodB();
        ic.methodC();
    }
}
</code></pre>
<h2 id="타입-변환">타입 변환</h2>
<p>상속이 있으니 당연히 타입변환도 있다. 인터페이스의 타입 변환은 인터페이스와 구현 클래스 간에 발생한다. 인터페이스 변수에 구현 객체를 대입하면 구현 객체는 인터페이스 타입으로 자동 변환된다. 그리고 상속과 마찬가지로 변환된 인터페이스 타입을 구현 클래스 타입으로 바꿀려면 강제 타입 변환이 필요하다. </p>
<pre><code class="language-java">인터페이스 변수 = 구현객체;</code></pre>
<p>부모 클래스가 인터페이스를 구현하고 있다면 자식 클래스도 인터페이스 타입으로 자동 타입 변환될 수 있다. </p>
<pre><code class="language-java">package ch08.sec10.exam02;

public interface Vehicle {
    void run();
}
</code></pre>
<pre><code class="language-java">package ch08.sec10.exam02;

public class Bus implements Vehicle{

    @Override
    public void run() {
        System.out.println(&quot;버스가 달립니다.&quot;);
    }

    public void checkFare(){
        System.out.println(&quot;승차요금을 체크합니다.&quot;);
    }
}
</code></pre>
<p>이렇게 인터페이스와 구현 클래스를 만들어두고 </p>
<pre><code class="language-java">package ch08.sec10.exam02;

public class CastingExample {
    public static void main(String[] args) {
        Vehicle vehicle = new Bus();

        vehicle.run();

        Bus bus = (Bus) vehicle;
        bus.run();
        bus.checkFare();
    }
}
</code></pre>
<p>이렇게 강제 타입 변환도 할 수 있다. 
인터페이스인 vechile을 구현 클래스 타입인 Bus로 강제 타입 변환이 일어난 것이다.</p>
<h2 id="다형성">다형성</h2>
<p>다형성이란 사용 방법은 동일하지만 다양한 결과가 나오는 성질을 뜻한다.
그리고 인터페이스는 앞서 배운 상속보다 현업에서 더 많이 다형성을 구현하기 위해 사용된다.</p>
<h3 id="필드의-다형성">필드의 다형성</h3>
<pre><code class="language-java">package ch08.sec11.exam01;

public interface Tire {
     void roll();
}
</code></pre>
<p>이렇게 하나의 인터페이스에</p>
<pre><code class="language-java">package ch08.sec11.exam01;

public class HankookTire implements Tire{
    @Override
    public void roll() {
        System.out.println(&quot;한국 타이어가 굴러갑니다.&quot;);
    }
}
</code></pre>
<pre><code class="language-java">package ch08.sec11.exam01;

public class KumhoTire implements Tire{
    @Override
    public void roll() {
        System.out.println(&quot;금호 타이어가 굴러갑니다.&quot;);
    }
}
</code></pre>
<p>서로 다른 구현체가 있다고 가정하면</p>
<pre><code class="language-java">package ch08.sec11.exam01;

public class Car {
    Tire tire1 = new HankookTire();
    Tire tire2 = new HankookTire();

    void run(){
        tire1.roll();
        tire2.roll();
    }
}
</code></pre>
<p>초기값은 한국타이어로 정해져 있지만</p>
<pre><code class="language-java">package ch08.sec11.exam01;

public class CarExample {
    public static void main(String[] args) {
        Car myCar = new Car();

        myCar.run();

        myCar.tire1 = new KumhoTire();
        myCar.tire2 = new KumhoTire();

        myCar.run();
    }
}
</code></pre>
<p>필드의 다형성에 의해 금호타이어로 바꿔 다른 결과를 출력시킬 수 있다.</p>
<h3 id="메개변수의-다형성">메개변수의 다형성</h3>
<pre><code class="language-java">package ch08.sec11.exam02;

public interface Vehicle {
    void run();
}
</code></pre>
<pre><code class="language-java">package ch08.sec11.exam02;

public class Driver {
    void drive(Vehicle vehicle){
        vehicle.run();
    }
}
</code></pre>
<p>Driver 클래스에 인터페이스인 Vehicle를 메개변수로 가지는 drive() 메소드를 만든다.
그 때 Bus와 Taxi가 Vechicle을 구현하는 구현 클래스라면 객체를 생성해서 전달하는 것이 가능하다. </p>
<pre><code class="language-java">package ch08.sec11.exam02;

public class Bus implements Vehicle{
    @Override
    public void run() {
        System.out.println(&quot;버스가 달립니다.&quot;);
    }
}
</code></pre>
<pre><code class="language-java">package ch08.sec11.exam02;

public class Taxi implements Vehicle{

    @Override
    public void run() {
        System.out.println(&quot;택시가 달립니다.&quot;);
    }
}
</code></pre>
<pre><code class="language-java">package ch08.sec11.exam02;

public class DriverExample
{
    public static void main(String[] args) {
        Driver driver = new Driver();

        Bus bus = new Bus();
        Taxi taxi = new Taxi();

        driver.drive(bus); // 자동 타입 변환 -&gt; 오버라이딩 메소드 호출 -&gt; 다형성
        driver.drive(taxi);
    }

}
</code></pre>
<h2 id="객체-타입-확인">객체 타입 확인</h2>
<p>상속 때랑 똑같다. <code>instanceof</code> 연산자로 확인 할 수 있다.</p>
<pre><code class="language-java">    public static void ride(Vehicle vehicle){
        if(vehicle instanceof Bus bus){
            bus.checkFare();
        }
        vehicle.run();
    }</code></pre>
<h2 id="봉인된-인터페이스">봉인된 인터페이스</h2>
<p>상속 파트랑 Dejavu를 느끼고 있는데, 암튼 똑같다.</p>
<pre><code class="language-java">public sealed interface InterfaceA permits InterfaceB { ... }</code></pre>
<p>무분별한 자식 인터페이스 생성을 방지하기 위해 만들어 졌다.
저렇게 쓰면 InterfaceA의 자식 인터페이스는 InterfaceB만 가능하고 그 외에는 불가능해진다.</p>
<pre><code class="language-java">public non-sealed interface InterfaceB extends InterfaceA { ... }</code></pre>
<p>그리고 InterfaceB에서는 non-sealed 키워드로 선언해 오직 상속 받는 부모 인터페이스는 InterfaceA임을 명시하면 된다. </p>
<h2 id="연습문제">연습문제</h2>
<p><img src="https://velog.velcdn.com/images/seo-faper/post/f8572749-39fe-48b8-9c87-c0f1ad14362b/image.png" alt=""></p>
<p>1번 인터페이스는 클래스가 아니기 때문에 new로 생성할 수 없습니다.</p>
<p><img src="https://velog.velcdn.com/images/seo-faper/post/b932fd21-f5d0-4641-9a9a-30cc49154a20/image.png" alt=""></p>
<p>3번 할 수 있습니다. 정적으로 만들어 놓은 것들만이 컴파일 과정에 메소드 영역으로 넘어가 고정이 되어 버리지요.</p>
<p><img src="https://velog.velcdn.com/images/seo-faper/post/457bf756-62d9-437d-a9ae-7ebc7b4cd288/image.png" alt=""></p>
<p>4번 애초에 만들 때 강제 타입 변환을 할 필요 없이 InterfaceA ia = new 구현객채(); 이렇게 받죠..? 그리고 3번 같은 경우는 변수에 구현 객체의 메모리 주소가 담기는 거라 배열로도 가능합니다. 그냥 인터페이스 타입의 변수가 여러 개 있는 것과 동일하다. </p>
<p><img src="https://velog.velcdn.com/images/seo-faper/post/67caa16f-64ba-49b1-80a4-fc2314213675/image.png" alt=""></p>
<p>제일 조상 격인 인터페이스 A를 가지고 있기 때문에 모두 가능하다. </p>
<p><img src="https://velog.velcdn.com/images/seo-faper/post/2f1189f2-ddaf-4574-9f46-d80edfc01442/image.png" alt=""></p>
<pre><code class="language-java">public class TV implements Remocon{
    @Override
    public void powerOn() { System.out.println(&quot;TV를 켰습니다.&quot;);
    ...
}</code></pre>
<p>재정의를 통해 만들면 되죠.
<img src="https://velog.velcdn.com/images/seo-faper/post/3c1e6f60-4452-4521-a84c-fa703c880dd6/image.png" alt=""></p>
<pre><code class="language-java">public class Cat implements Soundable(){
    @override
    public String sound(){
        return &quot;야옹&quot;;
    }
}</code></pre>
<pre><code class="language-java">public class Dog implements Soundable(){
    @override
    public String sound(){
        return &quot;멍멍&quot;;
    }
}</code></pre>
<p><img src="https://velog.velcdn.com/images/seo-faper/post/ee4baa31-c3eb-44d7-b29a-005130c26156/image.png" alt=""></p>
<pre><code class="language-java">package ch08.homework;

public interface DataAccessObject
{
    void select();
    void insert();
    void update();
    void delete();
}
</code></pre>
<pre><code class="language-java">package ch08.homework;

public class MysqlDao implements DataAccessObject{
    @Override
    public void select() {
        System.out.println(&quot;MySql DB에서 검색&quot;);
    }

    @Override
    public void insert() {
        System.out.println(&quot;MySql DB에서 삽입&quot;);
    }

    @Override
    public void update() {
        System.out.println(&quot;MySql DB를 수정&quot;);
    }

    @Override
    public void delete() {
        System.out.println(&quot;MySql DB에서 삭제&quot;);
    }
}
</code></pre>
<pre><code class="language-java">package ch08.homework;

public class OracleDao implements DataAccessObject{
    @Override
    public void select() {
        System.out.println(&quot;Oracle DB에서 검색&quot;);
    }

    @Override
    public void insert() {
        System.out.println(&quot;Oracle DB에서 삽입&quot;);
    }

    @Override
    public void update() {
        System.out.println(&quot;Oracle DB를 수정&quot;);
    }

    @Override
    public void delete() {
        System.out.println(&quot;Oracle DB에서 삭제&quot;);
    }
}
</code></pre>
<p>다형성은 신인가? </p>
<p><img src="https://velog.velcdn.com/images/seo-faper/post/641dfa92-64fd-4a45-af5f-e948a2327533/image.png" alt="">
<img src="https://velog.velcdn.com/images/seo-faper/post/3bd92f7f-5d06-4e72-b033-7e88c08ba4c4/image.png" alt=""></p>
<p>a instanceof C c 죠 자바가 12버전 이상이면!</p>
]]></description>
        </item>
    </channel>
</rss>