<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>춘식이가 개발자라면?</title>
        <link>https://velog.io/</link>
        <description>춘식이를 너무 좋아하는 주니어 백엔드 개발자입니다.</description>
        <lastBuildDate>Fri, 11 Apr 2025 14:33:33 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>춘식이가 개발자라면?</title>
            <url>https://velog.velcdn.com/images/choonsik_dev/profile/5f77207f-1aa7-433d-9b98-7df99ecf4941/image.jpg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. 춘식이가 개발자라면?. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/choonsik_dev" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[99클럽 코테 스터디 10일차 TIL + 해시]]></title>
            <link>https://velog.io/@choonsik_dev/99%ED%81%B4%EB%9F%BD-%EC%BD%94%ED%85%8C-%EC%8A%A4%ED%84%B0%EB%94%94-10%EC%9D%BC%EC%B0%A8-TIL-%ED%95%B4%EC%8B%9C</link>
            <guid>https://velog.io/@choonsik_dev/99%ED%81%B4%EB%9F%BD-%EC%BD%94%ED%85%8C-%EC%8A%A4%ED%84%B0%EB%94%94-10%EC%9D%BC%EC%B0%A8-TIL-%ED%95%B4%EC%8B%9C</guid>
            <pubDate>Fri, 11 Apr 2025 14:33:33 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/choonsik_dev/post/ba099f91-1011-4ea0-ba34-d0207675a922/image.png" alt=""></p>
<h1 id="문제">문제</h1>
<p><a href="https://www.acmicpc.net/problem/2358">백준 2358번-평행선</a></p>
<p>평면에 n개의 점이 있다. 그중 두 개 이상의 점을 지나면서 x축 또는 y축에 평행한 직선이 몇 개인지 알아내는 프로그램을 작성하시오.</p>
<p>입력:
첫째 줄에 n(1 ≤ n ≤ 100,000)이 주어진다. 다음 n개의 줄에는 각 점의 좌표가 주어진다. 같은 좌표가 여러 번 주어질 수 있으며, 그런 경우 서로 다른 점으로 간주한다. 좌표는 절댓값이 231보다 작은 정수이다.</p>
<p>출력:
첫째 줄에 답을 출력한다.</p>
<h2 id="예시">예시</h2>
<p>입력:</p>
<pre><code class="language-text">4
0 0
10 10
0 10
10 0</code></pre>
<p>출력:</p>
<pre><code class="language-text">4</code></pre>
<h1 id="풀이">풀이</h1>
<pre><code class="language-java">import java.util.*;
import java.io.*;

public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
        StringTokenizer st;

        int n = Integer.parseInt(br.readLine());

        Map&lt;Integer, Integer&gt; y = new HashMap&lt;&gt;(), x = new HashMap&lt;&gt;();
        while (n-- &gt; 0) {
            st = new StringTokenizer(br.readLine());
            int input = Integer.parseInt(st.nextToken());
            if (y.containsKey(input))
                y.put(input, y.get(input) + 1);
            else
                y.put(input, 0);

            input = Integer.parseInt(st.nextToken());
            if (x.containsKey(input))
                x.put(input, x.get(input) + 1);
            else
                x.put(input, 0);
        }

        int cnt = 0;
        for (int key : y.keySet())
            if (y.get(key) &gt; 0)
                cnt++;

        for (int key : x.keySet())
            if (x.get(key) &gt; 0)
                cnt++;

        bw.write(String.valueOf(cnt));
        bw.flush();
    }
}</code></pre>
<h1 id="회고">회고</h1>
<p>참고: <a href="https://graycode.tistory.com/350">https://graycode.tistory.com/350</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[99클럽 코테 스터디 9일차 TIL + 문자열/해시]]></title>
            <link>https://velog.io/@choonsik_dev/99%ED%81%B4%EB%9F%BD-%EC%BD%94%ED%85%8C-%EC%8A%A4%ED%84%B0%EB%94%94-8%EC%9D%BC%EC%B0%A8-TIL-%EB%AC%B8%EC%9E%90%EC%97%B4%ED%95%B4%EC%8B%9C</link>
            <guid>https://velog.io/@choonsik_dev/99%ED%81%B4%EB%9F%BD-%EC%BD%94%ED%85%8C-%EC%8A%A4%ED%84%B0%EB%94%94-8%EC%9D%BC%EC%B0%A8-TIL-%EB%AC%B8%EC%9E%90%EC%97%B4%ED%95%B4%EC%8B%9C</guid>
            <pubDate>Thu, 10 Apr 2025 14:31:49 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/choonsik_dev/post/199ee09c-dfd0-47bf-9baf-59b9d7527b9f/image.png" alt=""></p>
<h1 id="문제">문제</h1>
<p><a href="https://leetcode.com/problems/design-hashmap/">LeetCode 705-Design HashMap</a>
Design a HashMap without using any built-in hash table libraries.</p>
<p>Implement the MyHashMap class:</p>
<ul>
<li>MyHashMap() initializes the object with an empty map.</li>
<li>void put(int key, int value) inserts a (key, value) pair into the HashMap. If the key already exists in the map, update the corresponding value.</li>
<li>int get(int key) returns the value to which the specified key is mapped, or -1 if this map contains no mapping for the key.</li>
<li>void remove(key) removes the key and its corresponding value if the map contains the mapping for the key.</li>
</ul>
<h2 id="예시">예시</h2>
<p>Input</p>
<pre><code>[&quot;MyHashMap&quot;, &quot;put&quot;, &quot;put&quot;, &quot;get&quot;, &quot;get&quot;, &quot;put&quot;, &quot;get&quot;, &quot;remove&quot;, &quot;get&quot;]
[[], [1, 1], [2, 2], [1], [3], [2, 1], [2], [2], [2]]</code></pre><p>Output</p>
<pre><code>[null, null, null, 1, -1, null, 1, null, -1]</code></pre><p>Explanation</p>
<pre><code class="language-java">MyHashMap myHashMap = new MyHashMap();
myHashMap.put(1, 1); // The map is now [[1,1]]
myHashMap.put(2, 2); // The map is now [[1,1], [2,2]]
myHashMap.get(1);    // return 1, The map is now [[1,1], [2,2]]
myHashMap.get(3);    // return -1 (i.e., not found), The map is now [[1,1], [2,2]]
myHashMap.put(2, 1); // The map is now [[1,1], [2,1]] (i.e., update the existing value)
myHashMap.get(2);    // return 1, The map is now [[1,1], [2,1]]
myHashMap.remove(2); // remove the mapping for 2, The map is now [[1,1]]
myHashMap.get(2);    // return -1 (i.e., not found), The map is now [[1,1]]</code></pre>
<h1 id="풀이">풀이</h1>
<pre><code class="language-java">class ListNode {
    int key, val;  // 각각 저장된 키와 값
    ListNode next; // 체이닝 방식으로 다음 노드를 가리키는 링크
    public ListNode(int key, int val, ListNode next) {
        this.key = key;
        this.val = val;
        this.next = next;
    }
}

class MyHashMap {
    static final int size = 19997; // 해시 테이블의 크기 (소수 사용 -&gt; 충돌 줄이기)
    static final int mult = 12582917; // 해시 함수 성능 향상용 큰 소수 (곱셈 해시)
    ListNode[] data; // 각 해시 인덱스에 연결리스트를 저장하는 배열

    public MyHashMap() {
        this.data = new ListNode[size];
    }

    //곱셈 후 모듈러 연산으로 해시 인덱스 계산
    //일반적인 key % size보다 충돌이 훨씬 적은 고성능 해시 함수
    private int hash(int key) {
        // long으로 캐스팅한 이유는 key * mult가 int 범위를 넘을 수 있어서
        return (int)((long)key * mult % size);
    }

    public void put(int key, int val) {
        remove(key); // 같은 키 있으면 먼저 지움
        int h = hash(key);
        ListNode node = new ListNode(key, val, data[h]); // 새로운 노드를 헤드에 삽입(속도 빠름)
        data[h] = node;
    }

    public int get(int key) {
        int h = hash(key);
        ListNode node = data[h];
        //해당 인덱스(버킷)에 연결된 노드들 순회
        for(; node != null; node = node.next) {
            if (node.key == key) {
                return node.val;
            }
        }
        return -1;
    }

    //헤드 노드가 삭제 대상이면 바로 다음 노드를 넣음
    //중간 노드는 node.next로 탐색하면서 삭제
    public void remove(int key) {
        int h = hash(key);
        ListNode node = data[h];
        if (node == null) {
            return;
        }
        if (node.key == key) {
            data[h] = node.next;
        } else for (; node.next != null; node = node.next) {
            if (node.next.key == key) {
                node.next = node.next.next;
                return;
            }
        }
    }
}</code></pre>
<h2 id="회고">회고</h2>
<p>처음에는 output을 보고 배열로 풀었으나 HashMap을 구현하라는 문제의 취지와는 어긋나는 것 같아서 제일 좋아요 많이 받은 코드를 대신한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[99클럽 코테 스터디 8일차 TIL + 문자열/해시]]></title>
            <link>https://velog.io/@choonsik_dev/99%ED%81%B4%EB%9F%BD-%EC%BD%94%ED%85%8C-%EC%8A%A4%ED%84%B0%EB%94%94-7%EC%9D%BC%EC%B0%A8-TIL-%EB%AC%B8%EC%9E%90%EC%97%B4%ED%95%B4%EC%8B%9C</link>
            <guid>https://velog.io/@choonsik_dev/99%ED%81%B4%EB%9F%BD-%EC%BD%94%ED%85%8C-%EC%8A%A4%ED%84%B0%EB%94%94-7%EC%9D%BC%EC%B0%A8-TIL-%EB%AC%B8%EC%9E%90%EC%97%B4%ED%95%B4%EC%8B%9C</guid>
            <pubDate>Wed, 09 Apr 2025 13:24:15 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/choonsik_dev/post/4763ed03-c322-4fb0-b21c-aa3759b81076/image.png" alt=""></p>
<h1 id="문제">문제</h1>
<p><a href="https://leetcode.com/problems/check-if-number-has-equal-digit-count-and-digit-value/">LeetCode 2283-Check if Number Has Equal Digit Count and Digit Value</a></p>
<p>You are given a 0-indexed string num of length n consisting of digits.</p>
<p>Return true if for every index i in the range 0 &lt;= i &lt; n, the digit i occurs num[i] times in num, otherwise return false.</p>
<p>Constraints:</p>
<ul>
<li>n == num.length</li>
<li>1 &lt;= n &lt;= 10</li>
<li>num consists of digits.</li>
</ul>
<h2 id="예시">예시</h2>
<p>Example 1:</p>
<ul>
<li>Input: num = &quot;1210&quot;</li>
<li>Output: true</li>
<li>Explanation:
num[0] = &#39;1&#39;. The digit 0 occurs once in num.
num[1] = &#39;2&#39;. The digit 1 occurs twice in num.
num[2] = &#39;1&#39;. The digit 2 occurs once in num.
num[3] = &#39;0&#39;. The digit 3 occurs zero times in num.
The condition holds true for every index in &quot;1210&quot;, so return true.</li>
</ul>
<p>즉, 표로 정리하면 다음과 같다.</p>
<table>
<thead>
<tr>
<th>자리(i)</th>
<th>num[i]</th>
<th>의미</th>
</tr>
</thead>
<tbody><tr>
<td>0번 자리</td>
<td>&#39;1&#39;</td>
<td>숫자 &#39;0&#39;은 1번 나와야 함</td>
</tr>
<tr>
<td>1번 자리</td>
<td>&#39;2&#39;</td>
<td>숫자 &#39;1&#39;은 2번 나와야 함</td>
</tr>
<tr>
<td>2번 자리</td>
<td>&#39;1&#39;</td>
<td>숫자 &#39;2&#39;는 1번 나와야 함</td>
</tr>
<tr>
<td>3번 자리</td>
<td>&#39;0&#39;</td>
<td>숫자 &#39;3&#39;은 0번 나와야 함</td>
</tr>
<tr>
<td>만족하므로 true이다.</td>
<td></td>
<td></td>
</tr>
</tbody></table>
<p>Example 2:</p>
<ul>
<li>Input: num = &quot;030&quot;</li>
<li>Output: false</li>
<li>Explanation:
num[0] = &#39;0&#39;. The digit 0 should occur zero times, but actually occurs twice in num.
num[1] = &#39;3&#39;. The digit 1 should occur three times, but actually occurs zero times in num.
num[2] = &#39;0&#39;. The digit 2 occurs zero times in num.
The indices 0 and 1 both violate the condition, so return false.</li>
</ul>
<table>
<thead>
<tr>
<th>자리(i)</th>
<th>num[i]</th>
<th>의미</th>
</tr>
</thead>
<tbody><tr>
<td>0번 자리</td>
<td>&#39;0&#39;</td>
<td>숫자 &#39;0&#39;은 0번 나와야 함</td>
</tr>
<tr>
<td>1번 자리</td>
<td>&#39;3&#39;</td>
<td>숫자 &#39;1&#39;은 3번 나와야 함</td>
</tr>
<tr>
<td>2번 자리</td>
<td>&#39;0&#39;</td>
<td>숫자 &#39;2&#39;는 0번 나와야 함</td>
</tr>
<tr>
<td>즉, false이다.</td>
<td></td>
<td></td>
</tr>
</tbody></table>
<h1 id="풀이">풀이</h1>
<pre><code class="language-java">class Solution {
    public boolean digitCount(String num) {
        int[] count = new int[10]; // 숫자 0~9의 등장 횟수를 저장할 배열

        // 1단계: num 문자열을 순회하면서 각 숫자의 등장 횟수를 센다
        for (char c : num.toCharArray()) {
            int digit = c - &#39;0&#39;;  // &#39;3&#39; 같은 문자 → 숫자 3으로 바꿔줌
            count[digit]++;       // 해당 숫자 등장 횟수 증가
        }

        // 2단계: num[i]와 실제 count[i]가 같은지 확인
        for (int i = 0; i &lt; num.length(); i++) {
            int expected = num.charAt(i) - &#39;0&#39;; // num[i] → 기대되는 등장 횟수
            if (count[i] != expected) {
                return false; // 하나라도 틀리면 false
            }
        }

        return true; // 전부 통과하면 true
    }
}</code></pre>
<h1 id="회고">회고</h1>
<p>이번 문제는 문제 자체가 뭔 소리를 하나 싶어서 문제 이해에만 5분 넘게 사용한 것 같다.. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[99클럽 코테 스터디 7일차 TIL + 스택/큐]]></title>
            <link>https://velog.io/@choonsik_dev/99%ED%81%B4%EB%9F%BD-%EC%BD%94%ED%85%8C-%EC%8A%A4%ED%84%B0%EB%94%94-7%EC%9D%BC%EC%B0%A8-TIL-%EC%8A%A4%ED%83%9D%ED%81%90</link>
            <guid>https://velog.io/@choonsik_dev/99%ED%81%B4%EB%9F%BD-%EC%BD%94%ED%85%8C-%EC%8A%A4%ED%84%B0%EB%94%94-7%EC%9D%BC%EC%B0%A8-TIL-%EC%8A%A4%ED%83%9D%ED%81%90</guid>
            <pubDate>Tue, 08 Apr 2025 14:16:36 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/choonsik_dev/post/a6e0c5e4-37dd-4861-95df-998b3e1348b9/image.png" alt=""></p>
<h1 id="문제">문제</h1>
<p><a href="https://www.acmicpc.net/problem/3986">백준 3986번-좋은 단어</a>
평석이는 단어 위로 아치형 곡선을 그어 같은 글자끼리(A는 A끼리, B는 B끼리) 쌍을 짓기로 하였다. 만약 선끼리 교차하지 않으면서 각 글자를 정확히 한 개의 다른 위치에 있는 같은 글자와 짝 지을수 있다면, 그 단어는 &#39;좋은 단어&#39;이다. 평석이가 &#39;좋은 단어&#39; 개수를 세는 것을 도와주자.</p>
<p>입력:
첫째 줄에 단어의 수 N이 주어진다. (1 ≤ N ≤ 100)</p>
<p>다음 N개 줄에는 A와 B로만 이루어진 단어가 한 줄에 하나씩 주어진다. 단어의 길이는 2와 100,000사이이며, 모든 단어 길이의 합은 1,000,000을 넘지 않는다.</p>
<p>출력:
첫째 줄에 좋은 단어의 수를 출력한다.</p>
<h2 id="예시">예시</h2>
<p>입력:</p>
<pre><code class="language-text">3
ABAB
AABB
ABBA</code></pre>
<p>출력:</p>
<pre><code class="language-text">2</code></pre>
<h1 id="풀이">풀이</h1>
<pre><code class="language-java">import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int result = 0;    // 좋은 단어 갯수
        int num = sc.nextInt();
        sc.nextLine();

        for (int i=0; i&lt;num; i++) {
            String line = sc.nextLine();

            if (isGoodWord(line)) {
                result++;
            }
        }
        System.out.print(result);
    }

    private static boolean isGoodWord(String line) {
        Stack&lt;Character&gt; stack = new Stack&lt;&gt;();

        for (char ch:line.toCharArray()) {
            if (!stack.isEmpty() &amp;&amp; stack.peek() == ch) {
                stack.pop();    // 짝이 맞으면 제거
            } else {
                stack.push(ch);    // 짝이 아니면 추가
            }
        }

        return stack.isEmpty();    // 비어 있어야 좋은 단어
    }
}</code></pre>
<ol>
<li><code>line.toCharArray()</code>는 문자열을 문자 배열로 바꾸는 메소드이다.</li>
<li>for-each 문으로 line의 각 문자를 하나씩 <code>ch</code>에 꺼내서 반복한다.</li>
<li>만약 스택이 비어있지 않고 스택의 맨 위 문자와 지금 문자가 같으면 <code>pop()</code>을 통해 제거한다.</li>
<li>그렇지 않다면 <code>push()</code>를 통해 스택에 값을 추가한다.</li>
<li>스택 마지막이 비어있으면 좋은 단어로 간주하고 <code>result++</code>를 진행한다.</li>
</ol>
<h1 id="회고">회고</h1>
<p>시간이 없다.. 시간을 내자</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[99클럽 코테 스터디 6일차 TIL + 스택/큐]]></title>
            <link>https://velog.io/@choonsik_dev/99%ED%81%B4%EB%9F%BD-%EC%BD%94%ED%85%8C-%EC%8A%A4%ED%84%B0%EB%94%94-6%EC%9D%BC%EC%B0%A8-TIL-%EC%8A%A4%ED%83%9D%ED%81%90-32tn9nvc</link>
            <guid>https://velog.io/@choonsik_dev/99%ED%81%B4%EB%9F%BD-%EC%BD%94%ED%85%8C-%EC%8A%A4%ED%84%B0%EB%94%94-6%EC%9D%BC%EC%B0%A8-TIL-%EC%8A%A4%ED%83%9D%ED%81%90-32tn9nvc</guid>
            <pubDate>Mon, 07 Apr 2025 13:29:20 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/choonsik_dev/post/fb4c50f9-fa07-4ac3-98fa-119a645bdb36/image.png" alt=""></p>
<h1 id="문제">문제</h1>
<p><a href="https://leetcode.com/problems/climbing-stairs/description/">LeetCode 70-Climbing Stairs</a>
You are climbing a staircase. It takes n steps to reach the top.</p>
<p>Each time you can either climb 1 or 2 steps. In how many distinct ways can you climb to the top?</p>
<h2 id="예시">예시</h2>
<p><strong>Example 1:</strong></p>
<ul>
<li>Input: n = 2</li>
<li>Output: 2</li>
<li>Explanation: There are two ways to climb to the top.<ol>
<li>1 step + 1 step</li>
<li>2 steps</li>
</ol>
</li>
</ul>
<p><strong>Example 2:</strong></p>
<ul>
<li>Input: n = 3</li>
<li>Output: 3</li>
<li>Explanation: There are three ways to climb to the top.<ol>
<li>1 step + 1 step + 1 step</li>
<li>1 step + 2 steps</li>
<li>2 steps + 1 step</li>
</ol>
</li>
</ul>
<h1 id="풀이">풀이</h1>
<pre><code class="language-java">class Solution {
    public int climbStairs(int n) {
        if (n &lt;= 2) {
            return n;
        }

        int first = 1;
        int second = 2;

        for (int i = 3; i &lt;= n; i++) {
            int thrid = first + second;
            first = second;
            second = thrid;
        }

        return second;
    }
}</code></pre>
<ol>
<li>계단이 1칸이면 1가지 방법, 2칸이면 2가지 방법(1+1, 2) 밖에 없으므로 n이 2 이하일 때는 그대로 반환한다.</li>
<li><code>first</code>는 1칸일 때의 방법 수, <code>second</code>은 2칸일 때의 방법 수를 의미한다.</li>
<li>계단이 3칸일 때부터 n칸 까지 for문을 반복한다.</li>
<li><code>thrid</code>에 현재 계단의 경우의 수를 저장하고, <code>first</code>, <code>second</code>를 다음 계산을 위해 한 칸씩 앞으로 옮긴다.</li>
<li>반복문이 끝나면 <code>second</code>에는 n칸 계단을 올라가는 총 방법 수가 들어 있다.</li>
</ol>
<h1 id="회고">회고</h1>
<p>피보나치 수열에 관련된 문제들을 몇 개 풀어서 응용해봐야겠다는 생각이 든다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[99클럽 코테 스터디 5일차 TIL + 스택/큐]]></title>
            <link>https://velog.io/@choonsik_dev/99%ED%81%B4%EB%9F%BD-%EC%BD%94%ED%85%8C-%EC%8A%A4%ED%84%B0%EB%94%94-N%EC%9D%BC%EC%B0%A8-TIL-%EC%8A%A4%ED%83%9D%ED%81%90</link>
            <guid>https://velog.io/@choonsik_dev/99%ED%81%B4%EB%9F%BD-%EC%BD%94%ED%85%8C-%EC%8A%A4%ED%84%B0%EB%94%94-N%EC%9D%BC%EC%B0%A8-TIL-%EC%8A%A4%ED%83%9D%ED%81%90</guid>
            <pubDate>Fri, 04 Apr 2025 14:17:18 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/choonsik_dev/post/bfb90293-1894-4575-91b4-468ec86ab2d2/image.png" alt=""></p>
<h1 id="문제">문제</h1>
<p><a href="https://leetcode.com/problems/implement-stack-using-queues/">LeetCode 225-ImplementStackUsingQueues</a></p>
<p>Implement a last-in-first-out (LIFO) stack using only two queues. The implemented stack should support all the functions of a normal stack (<code>push</code>, <code>top</code>, <code>pop</code>, and <code>empty</code>).</p>
<p>Implement the <code>MyStack</code> class:</p>
<ul>
<li><code>void push(int x)</code> Pushes element x to the top of the stack.</li>
<li><code>int pop()</code> Removes the element on the top of the stack and returns it.</li>
<li><code>int top()</code> Returns the element on the top of the stack.</li>
<li><code>boolean empty()</code> Returns true if the stack is empty, false otherwise.</li>
</ul>
<p>Notes:</p>
<ul>
<li>You must use only standard operations of a queue, which means that only <code>push to back</code>, <code>peek/pop from front</code>, <code>size</code> and <code>is empty</code> operations are valid.</li>
<li>Depending on your language, the queue may not be supported natively. You may simulate a queue using a list or deque (double-ended queue) as long as you use only a queue&#39;s standard operations.</li>
</ul>
<h2 id="예시">예시</h2>
<p>입력:</p>
<pre><code class="language-text">[&quot;MyStack&quot;, &quot;push&quot;, &quot;push&quot;, &quot;top&quot;, &quot;pop&quot;, &quot;empty&quot;]
[[], [1], [2], [], [], []]</code></pre>
<p>출력:</p>
<pre><code>[null, null, null, 2, 2, false]</code></pre><h1 id="풀이">풀이</h1>
<pre><code class="language-java">class MyStack {
    Queue&lt;Integer&gt; q1;
    Queue&lt;Integer&gt; q2;

    public MyStack() {
        q1 = new LinkedList&lt;&gt;();
        q2 = new LinkedList&lt;&gt;();
    }

    public void push(int x) {
        q2.offer(x);

        while(!q1.isEmpty()) {
            q2.offer(q1.poll());
        }

        Queue&lt;Integer&gt; temp = q1;
        q1 = q2;
        q2 = temp;
    }

    public int pop() {
        return q1.poll();
    }

    public int top() {
        return q1.peek();
    }

    public boolean empty() {
        return q1.isEmpty();
    }
}</code></pre>
<h1 id="회고">회고</h1>
<p>Queue의 기본 메서드들을 잘 봐야겠다..!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[99클럽 코테 스터디 4일차 TIL + 스택/큐]]></title>
            <link>https://velog.io/@choonsik_dev/99%ED%81%B4%EB%9F%BD-%EC%BD%94%ED%85%8C-%EC%8A%A4%ED%84%B0%EB%94%94-4%EC%9D%BC%EC%B0%A8-TIL-%EC%8A%A4%ED%83%9D%ED%81%90</link>
            <guid>https://velog.io/@choonsik_dev/99%ED%81%B4%EB%9F%BD-%EC%BD%94%ED%85%8C-%EC%8A%A4%ED%84%B0%EB%94%94-4%EC%9D%BC%EC%B0%A8-TIL-%EC%8A%A4%ED%83%9D%ED%81%90</guid>
            <pubDate>Thu, 03 Apr 2025 04:24:41 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/choonsik_dev/post/d0861309-5b14-439b-b5b8-b8af8cdb4759/image.png" alt=""></p>
<h1 id="문제">문제</h1>
<p><a href="https://leetcode.com/problems/implement-queue-using-stacks/">LeetCode 232번-Implement Queue using Stacks</a></p>
<p>Implement a first in first out (FIFO) queue <strong>using only two stacks</strong>. The implemented queue should support all the functions of a normal queue (<code>push</code>, <code>peek</code>, <code>pop</code>, and <code>empty</code>).</p>
<p>Implement the <code>MyQueue</code> class:</p>
<ul>
<li><code>void push(int x)</code>: Pushes element x to the back of the queue.</li>
<li><code>int pop()</code>: Removes the element from the front of the queue and returns it.</li>
<li><code>int peek()</code>: Returns the element at the front of the queue.</li>
<li><code>boolean empty()</code>: Returns true if the queue is empty, false otherwise.</li>
</ul>
<h2 id="예시">예시</h2>
<p>입력:</p>
<pre><code class="language-text">[&quot;MyQueue&quot;, &quot;push&quot;, &quot;push&quot;, &quot;peek&quot;, &quot;pop&quot;, &quot;empty&quot;]
[[], [1], [2], [], [], []]</code></pre>
<p>출력:</p>
<pre><code class="language-text">[null, null, null, 1, 1, false]</code></pre>
<p>설명:</p>
<pre><code class="language-java">MyQueue myQueue = new MyQueue();
myQueue.push(1); // queue is: [1]
myQueue.push(2); // queue is: [1, 2] (leftmost is front of the queue)
myQueue.peek(); // return 1
myQueue.pop(); // return 1, queue is [2]
myQueue.empty(); // return false</code></pre>
<h1 id="풀이">풀이</h1>
<pre><code class="language-java">class MyQueue {

    private Stack&lt;Integer&gt; inStack;
    private Stack&lt;Integer&gt; outStack;

    public MyQueue() {
        inStack = new Stack&lt;&gt;();
        outStack = new Stack&lt;&gt;();
    }

    // 큐에 집어넣는다.
    public void push(int x) {
        inStack.push(x);
    }

    // 첫번째 요소를 제거하고 리턴한다.
    public int pop() {
        shiftStacks();
        return outStack.pop();
    }

    // 첫번째 요소를 리턴한다.
    public int peek() {
        shiftStacks();
        return outStack.peek();
    }

    // 큐가 비어있는지 확인한다.
    public boolean empty() {
        return inStack.isEmpty() &amp;&amp; outStack.isEmpty();
    }

    // inStack에 있는 요소들을 outStack에 집어넣는다.
    private void shiftStacks() {
        if (outStack.isEmpty()) {
            while (!inStack.isEmpty()) {
                outStack.push(inStack.pop());
            }
        }
    }
}</code></pre>
<p>Stack은 <strong>FIFO</strong> 구조이고, Queue는 <strong>LIFO</strong>이다. 따라서 두 개의 Stack을 갖고 구현해야 하는 문제이다.</p>
<ol>
<li>2개의 스택 <code>inStack</code>과 <code>outStack</code>을 선언하고, 생성자에서 초기화해주었다.</li>
<li><code>push()</code> 메소드를 통해서 <code>inStack</code>에 값을 추가한다.</li>
<li><code>pop()</code> 메소드와 <code>peek()</code> 메소드 둘 다 inStack에 있는 값을 outStack에 넣는 과정(LIFO -&gt; FIFO)이 필요하다. -&gt; <code>shiftStacks()</code>
3-1. 만약 outStack이 비어있다면 inStack에서 <code>pop</code>한 요소를 outStack에 <code>push</code>한다. outStack이 비어있지 않다면 굳이 추가하지 않고도 outStack에 있는 요소를 반환하면 된다.
3-2. 스택에서의 <strong>pop</strong>은 맨 위에 있는 요소(가장 마지막에 추가한 요소)를 리턴하고 삭제한다.
3-3. inStack에 가장 마지막에 들어온 요소를 outStack 가장 아래에 추가한다.</li>
<li><code>pop()</code> 메소드는 outStack에 가장 위에 있는 요소를 리턴하고 삭제한다.</li>
<li><code>peek()</code> 메소드는 outStack에 가장 위에 있는 요소를 <strong>리턴만</strong> 한다.</li>
<li><code>empty()</code> 메소드는 inStack과 outStack 모두 비어있는지의 여부를 리턴한다.</li>
</ol>
<h1 id="회고">회고</h1>
<p>Stack을 갖고 Queue를 구현할 수 있다는 점이 흥미롭다. 이제 자료구조 코테 문제를 좀 푸나보다. 주말동안 자료구조를 다시 한 번 공부해야겠다..</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[99클럽 코테 스터디 3일차 TIL + 문자열]]></title>
            <link>https://velog.io/@choonsik_dev/99%ED%81%B4%EB%9F%BD-%EC%BD%94%ED%85%8C-%EC%8A%A4%ED%84%B0%EB%94%94-3%EC%9D%BC%EC%B0%A8-TIL-%EB%AC%B8%EC%9E%90%EC%97%B4</link>
            <guid>https://velog.io/@choonsik_dev/99%ED%81%B4%EB%9F%BD-%EC%BD%94%ED%85%8C-%EC%8A%A4%ED%84%B0%EB%94%94-3%EC%9D%BC%EC%B0%A8-TIL-%EB%AC%B8%EC%9E%90%EC%97%B4</guid>
            <pubDate>Wed, 02 Apr 2025 06:08:41 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/choonsik_dev/post/0cd2b3fc-1f29-48e8-819c-2c80c4c4c1e6/image.png" alt=""></p>
<h1 id="문제">문제</h1>
<p><a href="https://www.acmicpc.net/problem/31458">백준 31458번-!!초콜릿 중독 주의!!</a>
이 문제에서 계산할 수식은 정수 하나와  $0$개 이상의 느낌표로 이루어져 있다. 정수는 $0$ 또는 $1$이며, 느낌표는 정수의 앞이나 뒤에 올 수 있다. 이 수식을 계산하는 규칙은 다음과 같다.</p>
<p> 
$n!$은 $n$의 팩토리얼이다. 
$0!=1$, 
$1!=1$로 정의된다.
 
$!n$은 $n$의 논리 반전(logical not)이다. 
$!0=1$, 
$!1=0$으로 정의된다.</p>
<p>팩토리얼이나 논리 반전이 중첩되어 있으면 중첩된 횟수만큼 계산하며, $!n!$과 같이 둘 다 사용된 경우에는 팩토리얼을 먼저 계산한다. 예를 들어, $!!n!!=!(!((n!) !))$이다.</p>
<h2 id="예시">예시</h2>
<p>입력:</p>
<pre><code class="language-text">6
0!
1!
!0
!1
!!0!!
!!1!!</code></pre>
<p>출력:</p>
<pre><code class="language-text">1
1
1
0
1
1</code></pre>
<h1 id="풀이">풀이</h1>
<h2 id="1-첫번째-풀이">1. 첫번째 풀이</h2>
<pre><code class="language-java">import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        StringBuilder sb = new StringBuilder();
        int T = sc.nextInt();
        sc.nextLine();

        while (T-- &gt; 0) {
            String str = sc.nextLine();
            int left = 0;

            while ((left &lt; str.length()) &amp;&amp; (str.charAt(left) == &#39;!&#39;)) {
                left++;
            }

            int right = str.length() - left - 1;
            int num = str.charAt(left) - &#39;0&#39;;

            if (right &gt; 0) {
                num = 1;
            }

            sb.append((num + left) % 2).append(&quot;\n&quot;);
        }


        System.out.print(sb);
        sc.close();
    }
}</code></pre>
<p>맨 처음에는 if-else문을 통해서 0!이거나 1!이면 둘 다 1로 출력하도록 하고, !1이면 0, !0이면 1을 출력하도록 하려고 했다. 그런데 중첩된 거는 어떻게 해결해야할지 너무 막막했다. 결국 구글링을 통해 C++로 되어있는 코드를 자바에 맞게 작성하였다.</p>
<ol>
<li>먼저 왼쪽 느낌표의 개수를 구한다. <code>left</code>가 <code>str</code>보다 작으면서 <code>charAt(left) == &#39;!&#39;</code>라면 왼쪽 느낌표가 있는거다. <code>++</code>를 해준다.</li>
<li>오른쪽 느낌표의 개수는 전체 문자열의 길이(<code>str.length()</code>) - 왼쪽 느낌표의 개수(<code>left</code>) - 정수(<code>1</code>)을 빼준 값이다.</li>
<li>정수는 <code>str.charAt(left) - &#39;0&#39;</code>을 해준 값이다.
3-1. <code>charAt()</code>은 0부터 시작한다. 따라서 만약 left가 1개가 있다면, <code>charAt(1)</code>은 왼쪽 느낌표의 뒤의 정수를 나타낼 것이다.
3-2. <code>- &#39;0&#39;</code>은(는) ASCII 코드 활용으로 문자를 정수로 변환하는 방식이다. 해당 과정을 생략하면 실제 left가 저장된 ASCII 코드값이 반환됨로 &#39;0&#39;을 빼줌으로써 0 또는 1을 반환한다.</li>
<li>패러다임 계산: 오른쪽 느낌표는 정수값이 0이든 1이든 둘 다 1을 반환해주므로 오른쪽 느낌표가 있다면 1을 반환해주도록 한다.</li>
<li>논리부정 계산: <code>(num + left) % 2</code>를 통해 나머지 값을 반환해준다.</li>
</ol>
<h2 id="2-두번째-풀이">2. 두번째 풀이</h2>
<pre><code class="language-java">num ^= (left % 2);

sb.append(num).append(&quot;\n&quot;);</code></pre>
<p>그리고 나는 문득 비트 연산자를 사용해서 계산할 수도 있지 않을까? 라는 생각을 했다. <code>^</code>는 <strong>XOR 비트 연산자</strong>이다. 두 비트가 서로 다르면 1, 같으면 0을 반환한다.</p>
<ol>
<li>위에서 오른쪽 느낌표가 있다면 1을, 없다면 0을 <code>num</code>에 저장했을 것이다.</li>
<li>XOR 계산
2-1. <code>left % 2</code>가 0이라면, <code>0 ^ 0</code>이므로 같아서 0을 반환한다.
2-2. <code>left % 2</code>가 1이라면, <code>0 ^ 1</code>이므로 다르기 때문에 1을 반환한다.</li>
</ol>
<blockquote>
<p>💡
<code>left % 2</code>가 짝수라면, 부정-&gt;부정 이므로 다시 원래의 숫자를 반환하는 원리이다.</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/choonsik_dev/post/d69c3e89-0f64-4583-ab01-cc2544b8fcae/image.png" alt="">
맨 위가 비트 연산자를 사용한 결과로, 나머지 두 개는 첫번째 풀이 방법이다. <del>(백준이 익숙하지 않아서 같은 코드로 두 번 제출되었다.)</del> 비트 연산자를 통해서 확실히 메모리 사용량이 줄어든 것을 볼 수 있다!</p>
<h1 id="회고">회고</h1>
<p><code>int num = str.charAt(left) - &#39;0&#39;;</code>에서 &#39;0&#39;을 빼줌으로써 문자를 숫자로 변환할 수 있음을 새로 깨달았다. 즉, &#39;0&#39;을 빼지 않으면 숫자가 아니라 ASCII 코드값이 저장된다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[99클럽 코테 스터디 2일차 TIL + 문자열]]></title>
            <link>https://velog.io/@choonsik_dev/99%ED%81%B4%EB%9F%BD-%EC%BD%94%ED%85%8C-%EC%8A%A4%ED%84%B0%EB%94%94-2%EC%9D%BC%EC%B0%A8-TIL-%EB%AC%B8%EC%9E%90%EC%97%B4-7m1znmg6</link>
            <guid>https://velog.io/@choonsik_dev/99%ED%81%B4%EB%9F%BD-%EC%BD%94%ED%85%8C-%EC%8A%A4%ED%84%B0%EB%94%94-2%EC%9D%BC%EC%B0%A8-TIL-%EB%AC%B8%EC%9E%90%EC%97%B4-7m1znmg6</guid>
            <pubDate>Tue, 01 Apr 2025 12:57:38 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/choonsik_dev/post/63b3d124-5ad7-46d4-bb8a-7be4c59a2371/image.png" alt=""></p>
<h1 id="문제">문제</h1>
<p><a href="https://www.acmicpc.net/problem/10820">백준 10280번-문자열 분석</a>
문자열 N개가 주어진다. 이때, 문자열에 포함되어 있는 소문자, 대문자, 숫자, 공백의 개수를 구하는 프로그램을 작성하시오.</p>
<p>각 문자열은 알파벳 소문자, 대문자, 숫자, 공백으로만 이루어져 있다.</p>
<h2 id="예시">예시</h2>
<p>입력:</p>
<pre><code class="language-text">This is String
SPACE    1    SPACE
 S a M p L e I n P u T     
0L1A2S3T4L5I6N7E8</code></pre>
<p>출력:</p>
<pre><code class="language-text">10 2 0 2
0 10 1 8
5 6 0 16
0 8 9 0</code></pre>
<h1 id="풀이">풀이</h1>
<h2 id="1-첫번째-풀이">1. 첫번째 풀이</h2>
<pre><code class="language-java">public static void main(String[] args) {
    Scanner sc = new Scanner(System.in);
    String str = &quot;&quot;;

    while (sc.hasNextLine()) {
        str = sc.nextLine();
        int lowerCase = 0;
        int upperCase = 0;
        int num = 0;
        int space = 0;
        char[] charArr = str.toCharArray();
        for (int i=0; i&lt;charArr.length; i++) {
            if (charArr[i] == 32) { //공백
                space++;
            } else if ((48 &lt;= charArr[i]) &amp;&amp; (charArr[i] &lt;= 57)) { //숫자
                num++;
            } else if ((65 &lt;= charArr[i]) &amp;&amp; (charArr[i] &lt;= 90)) { //대문자
                upperCase++;
            } else { //소문자
                lowerCase++;
            }
        }
        System.out.println(lowerCase + &quot; &quot; + upperCase + &quot; &quot; + num + &quot; &quot; + space);
    }

    sc.close();
}</code></pre>
<p>처음에 생각했던 방법은 char 배열로 바뀌어서 해당 영소문자, 대문자, 숫자, 공백에 맞는 ASCII 코드와 동일하면 거기에 해당되는 정수형 변수의 값을 늘리는 것으로 생각을 했다.
자주 사용되는 ASCII 코드는 쓸모가 있어서 영문자는 알겠지만 숫자와 공백은 몇 번째인지 몰라서 구글링해서 풀었다.</p>
<h2 id="2-두번째-풀이">2. 두번째 풀이</h2>
<pre><code class="language-java">while (sc.hasNextLine()) {
    str = sc.nextLine();
    int lowerCase = 0;
    int upperCase = 0;
    int num = 0;
    int space = 0;

    for (char c : str.toCharArray()) {
        if (Character.isDigit(c)) {
            num++;
        } else if (Character.isUpperCase(c)) {
            upperCase++;
        } else if (Character.isLowerCase(c)) {
            lowerCase++;
        } else {
            space++;
        }
}
    System.out.println(lowerCase + &quot; &quot; + upperCase + &quot; &quot; + num + &quot; &quot; + space);</code></pre>
<p>두번째 방법은 다른 사람들은 어떻게 풀었는지 구경하다보니 알게된 방법이다. <code>Character</code> 클래스에 있는 <code>isDigit()</code>, <code>isUpperCase()</code>, <code>isLowerCase()</code>를 통해서 첫번째 방법을 대체할 수 있다. 이는 ASCII 코드를 몰라도 손쉽게 풀 수 있는 방법이다. 래퍼 클래스의 메소드들을 다시 한 번 훑어봐야겠다!</p>
<h2 id="3-세번째-풀이">3. 세번째 풀이</h2>
<pre><code class="language-java">public static void main(String[] args) {
    Scanner sc = new Scanner(System.in);
    String str = &quot;&quot;;
    StringBuilder sb = new StringBuilder();

    while (sc.hasNextLine()) {
        str = sc.nextLine();
        int lowerCase = 0;
        int upperCase = 0;
        int num = 0;
        int space = 0;

        for (char c : str.toCharArray()) {
            if (Character.isDigit(c)) {
                num++;
            } else if (Character.isUpperCase(c)) {
                upperCase++;
            } else if (Character.isLowerCase(c)) {
                lowerCase++;
            } else {
                space++;
            }
        }

        sb.append(lowerCase).append(&quot; &quot;)
          .append(upperCase).append(&quot; &quot;)
          .append(num).append(&quot; &quot;)
          .append(space).append(&quot;\n&quot;);
    }
       System.out.print(sb);
    sc.close();
}</code></pre>
<p>마지막은 <code>String</code> 대신 <code>StringBuilder</code>를 사용해서 <code>append()</code>를 통해서 문자열을 붙여나가는 방법이다. 다 담았다가 while문을 빠져나가면 그 때 <code>System.out.print()</code>를 통해 Builder에 담아둔 문자열들을 출력해준다.</p>
<p><img src="https://velog.velcdn.com/images/choonsik_dev/post/e8f80f9d-cfff-467e-80bc-5f430af8e13f/image.png" alt="">
위부터 세번째-&gt;두번째-&gt;첫번째 방법으로 푼 메모리와 시간이다. StringBuilder를 통해 메모리와 시간을 유의미하게 줄일 수 있었다.</p>
<h1 id="회고">회고</h1>
<p>래퍼 클래스의 메소드들을 다시 한 번 훑어보자. 그리고 StringBuilder를 사용하자!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[99클럽 코테 스터디 1일차 TIL + 문자열]]></title>
            <link>https://velog.io/@choonsik_dev/99%ED%81%B4%EB%9F%BD-%EC%BD%94%ED%85%8C-%EC%8A%A4%ED%84%B0%EB%94%94-1%EC%9D%BC%EC%B0%A8-TIL-%EB%AC%B8%EC%9E%90%EC%97%B4</link>
            <guid>https://velog.io/@choonsik_dev/99%ED%81%B4%EB%9F%BD-%EC%BD%94%ED%85%8C-%EC%8A%A4%ED%84%B0%EB%94%94-1%EC%9D%BC%EC%B0%A8-TIL-%EB%AC%B8%EC%9E%90%EC%97%B4</guid>
            <pubDate>Mon, 31 Mar 2025 12:59:43 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/choonsik_dev/post/b936eae4-dcfb-4dc8-b5ec-a4ec889a90d1/image.png" alt=""></p>
<h1 id="문제">문제</h1>
<p><a href="https://www.acmicpc.net/problem/1032">백준 1032번:명령 프롬프트</a>
이 문제는 검색 결과가 먼저 주어졌을 때, 패턴으로 뭘 쳐야 그 결과가 나오는지를 출력하는 문제이다. 패턴에는 알파벳과 &quot;.&quot; 그리고 &quot;?&quot;만 넣을 수 있다. 가능하면 ?을 적게 써야 한다. 그 디렉토리에는 검색 결과에 나온 파일만 있다고 가정하고, 파일 이름의 길이는 모두 같다.</p>
<h3 id="예시">예시</h3>
<p>입력:</p>
<pre><code class="language-text">3
config.sys
config.inf
configures</code></pre>
<p>출력:</p>
<pre><code class="language-text">config????</code></pre>
<h1 id="풀이">풀이</h1>
<pre><code class="language-java">import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int cnt = sc.nextInt();
        sc.nextLine(); //개행 처리

        String[] files = new String[cnt];
        for(int i=0; i&lt;cnt; i++) {
            files[i] = sc.nextLine();
        }

        String firstFile = files[0];
        StringBuilder pattern = new StringBuilder();

        for (int i=0; i&lt;firstFile.length(); i++) {
            boolean isSame = true;
            char currentChar = firstFile.charAt(i);

            for (int j=1; j&lt;cnt; j++) {
                if (currentChar != files[j].charAt(i)) {
                    isSame = false;
                    break;
                }
            }

            if (isSame) {
                pattern.append(currentChar);
            } else {
                pattern.append(&quot;?&quot;);
            }
        }

        System.out.println(pattern);
        sc.close();
    }
}</code></pre>
<ol>
<li>첫번째 줄에 입력받은 파일의 개수만큼 String 배열 생성</li>
<li><code>sc.nextLine();</code>을 통해 for문을 돌면서 파일 배열 목록에 값 입력</li>
<li>첫번째 파일 이름의 개수만큼 for문을 돌면서 다음 파일 배열 목록들의 이름과 비교</li>
<li>만약 첫번째 파일 이름의 char i번째와 다음 파일 이름의 char i번째가 같지 않다면, <code>isSame</code> 변수를 false로 설정하고 <code>break;</code>를 통해 해당 for문을 빠져나감</li>
<li><code>isSame</code>이 true이면 현재 char를 StringBuilder에 추가하고, 그렇지 않다면 &#39;?&#39; 문자열을 추가</li>
<li>for문을 다 돌고 나면 해당 패턴을 출력하고 스캐너 close</li>
</ol>
<h1 id="회고">회고</h1>
<p>오랜만에 코딩테스트를 준비하려고 하니까 처음에는 막막했다. 45분이 권장사항 이었지만, 훨씬 오래 걸렸다. 처음에는 백준에 주어진 조건(파일이름 알파벳 소문자와 &#39;.&#39;로 이루어져있는지, N이 50보다 작거나 같은 자연수인지)을 모두 구현해야하는 줄 알고 복잡하게 생각해 더 어렵게 느껴졌다. 하지만 그게 아니었다..
그 외에도 오늘 정기모임 스터디에서 추가 문제로 <a href="https://www.acmicpc.net/problem/10988">백준 10988번:팰린드롬인지 확인하기</a>도 풀었다. 처음에는 이중for문으로 풀었는데, 챗gpt에서는 그냥 for문을 돌면서 <code>if (str.charAt(i) != str.charAt(str.length()-1-i))</code> 조건문을 추가하는 방식도 있었다. 그런데, 정기모임 팀장(?)님의 풀이 방법 중 하나인 바로 <code>StringBuilder sb = new StringBuilder().append(str).reverse();</code>를 해서 바로 입력받은 문자열과 비교를 하는 방식도 있었는데 저런 방법도 있구나 생각이 많이 들었다.
오늘이 1일차인데, 내일 이사를 해서 당분간 정신 없을 것 같지만 끝까지 매일 미션을 완수해보자!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[자바] Thread-Unsafe 케이스]]></title>
            <link>https://velog.io/@choonsik_dev/%EC%9E%90%EB%B0%94-Thread-Unsafe-%EC%BC%80%EC%9D%B4%EC%8A%A4</link>
            <guid>https://velog.io/@choonsik_dev/%EC%9E%90%EB%B0%94-Thread-Unsafe-%EC%BC%80%EC%9D%B4%EC%8A%A4</guid>
            <pubDate>Sat, 29 Jul 2023 11:51:44 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>자바에서 Thread-Unsafe한 케이스를 알아봅시다.</p>
</blockquote>
<h2 id="1-static-변수를-사용할-경우">1. static 변수를 사용할 경우</h2>
<p>static 변수는 해당 클래스의 모든 인스턴스들 간에 공유되는 변수입니다. 즉, 클래스의 모든 객체들이 이 변수를 같은 메모리 위치에 공유하여 사용하게 됩니다.</p>
<h2 id="2-singleton일-경우">2. singleton일 경우</h2>
<p>Singleton이란 메모리 상에 오직 하나의 인스턴스만 있는 경우인 디자인 패턴 중 하나입니다. 인스턴스 생성을 외부에서 할 수 없고, 접근 제어자를 이용해서 클래스 내부에서만 생성되도록 제한합니다. 싱글톤에서 사용되는 getInstance() 메소드는 쓰레드에 안전하지 않기 때문에 synchronized 키워드를 붙이거나 다른 방법을 사용해야 합니다.</p>
<h2 id="3-collection">3. Collection</h2>
<h3 id="3-1-arraylist">3-1. ArrayList</h3>
<blockquote>
<p><strong>Note that this implementation is not synchronized.</strong> ...(중략)... If no such object exists, the list should be &quot;wrapped&quot; using the Collections.synchronizedList method. This is best done at creation time, to prevent accidental unsynchronized access to the list:
    <code>List list = Collections.synchronizedList(new ArrayList(...));</code></p>
</blockquote>
<p>자바 API를 보면 ArrayList는 thread unsafe하다고 나와있고, 만약 thread safe하게 사용하려면 <code>Collections.synchronizedList()</code> 메소드를 사용하라고 나와있습니다.</p>
<h3 id="3-2-hashset-hashmap">3-2. HashSet, HashMap</h3>
<p>HashSet과 HashMap 또한 thread-unsafe 하기 때문에 아래와 같은 Collections의 메소드를 통하며 생성할 수 있습니다.</p>
<pre><code class="language-java">Set s = Collections.synchronizedSet(new HashSet(...));
Map m = Collections.synchronizedMap(new HashMap(...));</code></pre>
<h2 id="4-stingbuilder">4. StingBuilder</h2>
<p>멤버 변수로 사용하느냐 아니면 로컬 변수로 사용하냐에 의해서 결정하면 됩니다.
멤버 변수로 사용할 경우에는 StringBuffer를 사용해야 하고, 로컬 변수로 사용하는 경우에는 StringBuilder를 사용해도 됩니다.</p>
<hr>
<p><strong>참고</strong>
<a href="https://tecoble.techcourse.co.kr/post/2021-11-26-hashmap-hashtable-concurrenthashmap/">HashMap vs HashTable vs ConcurrentHashMap</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[자바] 8. Nested Class]]></title>
            <link>https://velog.io/@choonsik_dev/%EC%9E%90%EB%B0%94-8.-Nested-Class</link>
            <guid>https://velog.io/@choonsik_dev/%EC%9E%90%EB%B0%94-8.-Nested-Class</guid>
            <pubDate>Sun, 23 Jul 2023 10:54:14 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>도서 자바의신을 참고하였습니다.</p>
</blockquote>
<h2 id="1-nested-class">1. Nested Class</h2>
<blockquote>
<p>nested : n. 집합 또는 구간의 순서를 지닌 계열(系列)에서, 각기 그 앞의 것에 포함되고, 또한 집합의 직경이나 구간의 길이가 0으로 수속(收束)하는 것.</p>
</blockquote>
<p>Nested Class는 클래스 안의 클래스를 뜻합니다. 자바 기반의 UI를 처리할 때 사용자의 입력이나, 외부의 이벤트에 대한 처리를 하는 곳에서 주로 사용됩니다. 파일 이름은 반드시 public 클래스의 이름이어야 합니다. 그렇지 않으면 컴파일 에러가 발생합니다.
Nested Class를 사용하는 이유는 다음과 같습니다.</p>
<ul>
<li>한 곳에서만 사용되는 클래스를 논리적으로 묶어서 처리할 필요가 있을 때 -&gt; static nested class</li>
<li>캡슐화가 필요할 때. 즉, 내부 구현을 감추고 싶을 때 -&gt; inner class</li>
<li>소스의 가독성과 유지보수성을 높이고 싶을 때</li>
</ul>
<hr>
<h2 id="2-static-nested-class">2. Static Nested Class</h2>
<p>Nested class는 감싸고 있는 외부 클래스의 어떤 변수든 접근 가능합니다. 단, <span style="color:red">static nested class는 불가능</span>합니다. static nested class는 <span style="background-color:#F7E600">클래스를 <strong>묶기 위해 *<em>사용되며, 겉으로 보기에는 유사하지만 *</em>내부적으로 구현이 달라야할 때</strong> 사용</span>됩니다. </p>
<pre><code class="language-java">public class University {
    static class Student {
    }
}

public class School {
    static class Student {
    }
}</code></pre>
<p>위와 같이 University클래스의 Student 클래스와 School의 Student 클래스가 보기에는 유사하지만 내부적으로 구현이 달라야할 때 사용됩니다.</p>
<pre><code class="language-java">public class OutClass {
    static class StaticNested {
    }
}</code></pre>
<p>이런식으로 되어 있다면 컴파일은 <code>OutClass.class</code>와 <code>OutClass$StaticNested.class</code>으로 나누어집니다. 접근을 할 때는 <code>OutClass.StaticNested staticNested = new OutClass.StaticNested();</code>로 접근하면 됩니다.</p>
<hr>
<h2 id="3-inner-class">3. Inner Class</h2>
<p>Inner Class는 캡슐화를 목적으로 하는 클래스입니다. 하나의 클래스에서 어떤 공통적인 작업을 수행하는 클래스가 필요한데 다른 클래스에서는 그 클래스가 전혀 필요없을 때 Inner class를 만들어 사용합니다. Inner 클래스의 객체를 생성하기 위해서는 먼저 Outer 클래스 객체를 생성해야 하고, 이 객체를 통해서 Inner 객체 생성이 가능합니다. 주로 GUI 프로그램을 만들 때 사용됩니다.</p>
<hr>
<h2 id="4-anonymous-class">4. Anonymous Class</h2>
<p>Anonymous Class는 이름이 없는 일회용 클래스로, <span style="color:red">정의와 생성을 동시에 수행</span>합니다.</p>
<pre><code class="language-java">class MyClass extends ParentClass {
    //...
}
MyClass mc = new MyClass();</code></pre>
<p>보통 객체를 생성할 때 위와 같이 생성하지만 익명 클래스는 다음과 같이 생성합니다.</p>
<pre><code class="language-java">new ParentClass {
    //...
}

//또는

new 구현인터페이스이름 {
    //...
}</code></pre>
<p>컴파일하고 난 후에는 클래스명이 없기 때문에 <code>클래스명$1.class</code>처럼 숫자로 붙습니다. 클래스를 만들고 클래스를 호출하면 그 정보는 메모리에 올라가게 되고, 클래스를 많이 만들수록 메모리는 많이 필요해지고 애플리케이션을 시작할 때 더 많은 시간이 소요됩니다. 따라서 이런 익명 클래스를 통해서 간단한 방법으로 객체를 생성할 수 있습니다.</p>
<hr>
<blockquote>
<p>지속적으로 수정해나갈 예정입니다.
2023-07-23 V1.0</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[자바] 7. String 클래스]]></title>
            <link>https://velog.io/@choonsik_dev/%EC%9E%90%EB%B0%94-7.-String-%ED%81%B4%EB%9E%98%EC%8A%A4</link>
            <guid>https://velog.io/@choonsik_dev/%EC%9E%90%EB%B0%94-7.-String-%ED%81%B4%EB%9E%98%EC%8A%A4</guid>
            <pubDate>Sun, 23 Jul 2023 10:37:53 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>도서 자바의신을 참고하였습니다.</p>
</blockquote>
<h2 id="1-string-클래스">1. String 클래스</h2>
<pre><code class="language-java">public final class String
extends Object
implements Serializable, Comparable&lt;String&gt;, CharSequence</code></pre>
<p>String 클래스는 위와 같이 <code>Serializable</code>, <code>Comparable&lt;String&gt;</code>, <code>CharSequence</code>를 구현하고 있습니다.</p>
<ul>
<li><code>Serializable</code> : 구현해야하는 메소드가 없습니다. 그냥 선언하면 해당 객체를 파일로 저장하거나 다른 서버에 전송 가능한 상태가 됩니다.</li>
<li><code>Comparable&lt;String&gt;</code> : compareTo()라는 메소드 하나만 있는데, 객체 비교에 쓰이고 int값을 리턴합니다.</li>
<li><code>CharSequence</code>: 해당 클래스가 문자열을 다루기 위한 클래스라는 것을 명시적으로 나타냅니다.</li>
</ul>
<p>String 클래스는 생성자가 굉장히 많은데, 그 중 가장 잘 쓰이는 것은 아래 2가지가 있습니다.</p>
<ul>
<li><code>String(byte[] bytes)</code> : 현재 사용중인 플랫폼의 캐릭터 셋을 사용하여 제공된 byte배열을 디코딩한 String 객체를 생성합니다.</li>
<li><code>String(byte[] bytes, String charsetName)</code> : 지정한 이름을 갖는 캐릭터 셋을 사용하여 지정한 byte배열을 디코딩한 String 객체를 생성합니다.</li>
</ul>
<hr>
<h2 id="2-string---byte">2. String -&gt; byte</h2>
<p>String에서 byte로 변환하는 방법은 getBytes() 메소드를 사용하는 것입니다.</p>
<ul>
<li><code>byte[] getBytes()</code> : 기본 캐릭터 셋의 바이트 배열을 생성합니다.</li>
<li><code>byte[] getBytes(Charset charset)</code> : 지정한 캐릭터 셋 객체 타입으로 바이트 배열을 생성합니다.</li>
<li><code>byte[] getBytes(String charsetName)</code> : 지정한 이름의 캐릭터 셋으로 바이트 배열을 생성합니다.</li>
</ul>
<p>여기서 매개변수가 있는 getBytes() 메소드는 다른 시스템에서 전달받은 문자열을 byte 배열로 변환할 때 사용합니다. 이때, <code>UnsupportedEncodingException</code> 발생할 가능성이 있으므로 예외를 잡거나 던져주어야 합니다.</p>
<hr>
<h2 id="3-charset">3. Charset</h2>
<p>java.nio.charset.Charset 클래스 API에 정해진 표준 Charset은 다음과 같습니다.</p>
<ul>
<li>US-ASCII : 7bit ASCII</li>
<li>ISO-8859-1 : ISO 라틴 알파벳</li>
<li>UTF-8 : 8bit UCS(Unicode Character Set) 변환 포맷</li>
<li>UTF-16BE : 16bit UCS 변환 포맷. big-endian byte 순서</li>
<li>UTF-16LE : 16bit UCS 변환 포맷. little-endian byte 순서</li>
<li>UTF-16 : 16bit UCS 변환 포맷. byte-order mark byte 순서</li>
</ul>
<p>추가적으로 EUC-KR과 MS949라는 캐릭터 셋이 있는데, EUC-KR은 8bit 문자 인코딩으로, EUC의 일종이며 대표적인 한글 완성형 인코딩입니다. MS949는 Microsoft에서 만든 한글 확장 완성형 인코딩입니다.</p>
<p>byte배열로 생성할 때 사용한 캐릭터 셋을 문자열로 다시 전환할 때도 동일하게 사용해야 합니다.</p>
<pre><code class="language-java">byte[] arr = str.getBytes(&quot;UTF-16&quot;);
String str2 = new String(arr, &quot;UTF-16&quot;);</code></pre>
<hr>
<h2 id="4-string-methods">4. String Methods</h2>
<h3 id="1-length">1) length()</h3>
<p><code>public int length()</code>는 문자열의 길이를 리턴합니다. 이 때, <span style="color:red">공백도 길이에 포함</span>됩니다.</p>
<pre><code class="language-java">String strEn = &quot;hello&quot;;
String strKr = &quot;안녕하세요&quot;;
String blank = &quot; &quot;;

System.out.println(&quot;strEn.length() = &quot; + strEn.length()); //5
System.out.println(&quot;strKr.length() = &quot; + strKr.length()); //5
System.out.println(&quot;blank.length() = &quot; + blank.length()); //1</code></pre>
<h3 id="2-isempty">2) isEmpty()</h3>
<p><code>public boolean isEmpty()</code>는 문자열이 비어있는지 확인하는 메소드입니다. 만약 문자열이 비어있을 경우 true를 리턴합니다. <span style="color:red">공백도 문자열에 포함되므로 <strong>false</strong>를 리턴</span>합니다.</p>
<pre><code class="language-java">String strEn = &quot;hello&quot;;
String blank = &quot; &quot;;
String empty = &quot;&quot;;

System.out.println(&quot;strEn.isEmpty() = &quot; + strEn.isEmpty()); //false
System.out.println(&quot;blank.isEmpty() = &quot; + blank.isEmpty()); //false
System.out.println(&quot;empty.isEmpty() = &quot; + empty.isEmpty()); //true</code></pre>
<p>만약 공백도 true로 리턴하게끔 하려면 Java11부터 추가된 <code>isBlank()</code>을 사용하면 됩니다.</p>
<pre><code class="language-java">String blank = &quot; &quot;;

System.out.println(&quot;blank.isBlank() = &quot; + blank.isBlank()); //true</code></pre>
<h3 id="3-equals">3) equals()</h3>
<ul>
<li><code>boolean equals(Object anObject)</code></li>
<li><code>boolean equalsIgnoreCase(String anotherStr)</code></li>
<li><blockquote>
<p>위 두 메소드는 동일한 값인지 비교할 때 사용됩니다. 여기서 IgnoreCase가 붙은 메소드들은 대소문자 구분을 하지 않습니다.</p>
</blockquote>
</li>
</ul>
<pre><code class="language-java">String str1 = &quot;Hi&quot;;
String str2 = &quot;Hi&quot;;
String str3 = new String(&quot;Hi&quot;);
String str4 = new String(&quot;hi&quot;);

System.out.println(str1 == str2); //true
System.out.println(&quot;str1.equals(str2) = &quot; + str1.equals(str2)); //true
System.out.println(str1 == str3); //false
System.out.println(&quot;str1.equals(str3) = &quot; + str1.equals(str3)); //true
System.out.println(&quot;str1.equalsIgnoreCase(str4) = &quot; + str1.equalsIgnoreCase(str4)); //true</code></pre>
<p>자바에서는 객체들을 재사용하기 위해 <code>Constant Pool(상수 풀)</code>이 있고, String의 경우 동일한 값을 갖는 객체가 있으면 이미 만든 객체를 재사용합니다. 따라서 <code>str1==str2</code>가 <code>true</code>로 나올 수 있었으나, 이는 지양하는 것이 좋습니다. 재사용하지 않으려면 str3과 같이 new 연산자를 사용해 String 객체를 새로 생성을 해주어야 합니다. <code>str1==str3</code>이 <code>false</code>가 나올 수 있던 이유입니다.</p>
<ul>
<li><code>int compareTo(String anotherStr)</code></li>
<li><code>int compareToIgnoreCase(String str)</code></li>
<li><blockquote>
<p>compareTo() 메소드는 보통 정렬할 때 많이 사용됩니다. 매개변수가 알파벳순으로 앞에 있으면 양수, 뒤에 있으면 음수를 리턴합니다.</p>
</blockquote>
<pre><code class="language-java">String textA = &quot;a&quot;;
String textB = &quot;b&quot;;
String textC = &quot;c&quot;;
</code></pre>
</li>
</ul>
<p>System.out.println(&quot;textB.compareTo(textA) = &quot; + textB.compareTo(textA)); //1
System.out.println(&quot;textB.compareTo(textC) = &quot; + textB.compareTo(textC)); //-1
System.out.println(&quot;textA.compareTo(textC) = &quot; + textA.compareTo(textC)); //-2</p>
<pre><code>
- boolean contentEquals(CharSequence cs)
- boolean contentEquals(StringBuffer sb)
-&gt; 매개변수로 넘어오는 CharSequence를 구현한 클래스의 객체가 String 객체와 동일한지 비교합니다.
```java
String str = &quot;hi&quot;;
StringBuffer sb = new StringBuffer(&quot;hi&quot;);
StringBuilder builder = new StringBuilder(&quot;hi&quot;);

System.out.println(&quot;str.equals(sb) = &quot; + str.equals(sb)); //false
System.out.println(&quot;str.contentEquals(sb) = &quot; + str.contentEquals(sb)); //true
System.out.println(&quot;str.contentEquals(builder) = &quot; + str.contentEquals(builder)); //true</code></pre><h3 id="4-특정-조건에-문자열이-있는지-확인하는-메소드">4) 특정 조건에 문자열이 있는지 확인하는 메소드</h3>
<ul>
<li><code>boolean startsWith(String prefix)</code> : 가장 많이 사용하는 메소드로 prefix로 시작하는지 확인.</li>
<li><code>boolean startsWith(String prefix, int toffset)</code> : 위 메소드와 동일하나 어디서부터 찾을지 지정 가능.</li>
<li><code>boolean endsWith(String suffix)</code> : suffix로 끝나는지 확인.</li>
<li><code>boolean contains(CharSequence s)</code> : 매개변수로 들어온 s값이 문자열에 존재하는지 확인. 중간에 있는 값 확인할 때 사용.</li>
<li><code>boolean matches(String regex)</code> : 정규표현식에 맞는 문자열인지 확인.</li>
</ul>
<ul>
<li><code>boolean regionMatches(boolean ignoreCase, int toffset, String other, int ooffset, int len)</code></li>
<li><code>boolean regionMatches(int toffset, String other, int ooffset, int len)</code></li>
<li><blockquote>
<p>문자열 중에서 특정 영역이 매개변수로 넘어온 문자열과 동일한지 확인.</p>
</blockquote>
<ul>
<li>ignoreCase : true일 경우 대소문자 구분 없이 값 비교</li>
<li>toffset : 비교 대상 문자열의 확인 시작 위치 지정</li>
<li>other : 존재하는지를 확인할 문자열</li>
<li>ooffset : other 객체의 확인 시작 위치 지정</li>
<li>len : 비교할 char의 개수 지정</li>
</ul>
</li>
</ul>
<h3 id="5-indexof">5) indexOf()</h3>
<ul>
<li><code>int indexOf(int ch)</code> : char는 정수형으로 자동으로 형변환해줍니다.</li>
<li><code>int indexOf(int ch, int fromIndex)</code></li>
<li><code>int indexOf(String str)</code></li>
<li><code>int indexOf(String str, int fromIndex)</code></li>
<li><blockquote>
<p>앞에서부터 문자열이나 char를 찾는 메소드입니다. 0부터 시작하며, 찾고자 하는 값이 없으면 -1을 리턴합니다.</p>
</blockquote>
</li>
</ul>
<ul>
<li><code>int lastIndexOf(int ch)</code></li>
<li><code>int lastIndexOf(int ch, int fromIndex)</code></li>
<li><code>int lastIndexOf(String str)</code></li>
<li><code>int lastIndexOf(String str, int fromIndex)</code></li>
<li><blockquote>
<p>뒤에서부터 찾는 메소드입니다.</p>
</blockquote>
</li>
</ul>
<h3 id="6-char단위-값-추출하는-메소드">6) char단위 값 추출하는 메소드</h3>
<ul>
<li><code>char charAt(int index)</code> : 특정 위치의 char값을 리턴함.</li>
<li><code>void getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin)</code> : dst라는 char배열 내에 srcBegin에서 srcEnd 사이에 있는 char를 저장하는데 이때, dst배열의 시작 위치는 dstBegin입니다.</li>
<li><code>int codePointAt(int index)</code> : 특정 위치의 유니코드 값 리턴. 리턴 타입은 int지만 char로 형 변환도 가능.</li>
<li><code>int codePointBefore(int index)</code> : 특정 위치 앞에 있는 char의 유니코드 값 리턴. 리턴 타입은 int지만 char로 형변환도 가능.</li>
<li><code>int codePointCount(int beginIndex, int endIndex)</code> : 지정한 범위에 있는 유니코드 개수 리턴.</li>
<li><code>int offsetByCodePoints(int index, int codePointOffset)</code> : 지정된 index부터 offset이 설정된 인덱스를 리턴. 문자열 인코딩과 관련된 문제를 해결하기 위해서 사용됨.</li>
</ul>
<h3 id="7-char---string-string---char">7) Char -&gt; String, String -&gt; Char</h3>
<h4 id="1-char---string">(1) Char -&gt; String</h4>
<ul>
<li><code>static String copyValueOf(char[] data)</code> : char배열에 있는 값을 문자열로 변환함.</li>
<li><code>static String copyValueOf(char[] data, int offset, int count)</code> : char배열에 있는 값을 문자열로 변환함. 단, offset 위치부터 count까지의 개수만큼만 문자열로 변환함.<pre><code class="language-java">char[] values = new char[]{&#39;H&#39;, &#39;e&#39;, &#39;l&#39;, &#39;l&#39;, &#39;o&#39;};
String str = String.copyValueOf(values);
System.out.println(&quot;str = &quot; + str); //Hello
</code></pre>
</li>
</ul>
<p>str = String.copyValueOf(values, 1, 2);
System.out.println(&quot;str = &quot; + str); //el</p>
<pre><code>

#### (2) String -&gt; Char
- `char[] toCharArray()` : 문자열을 char 배열로 변환함.

![Choonsik을 디버깅했을 때](https://velog.velcdn.com/images/choonsik_dev/post/118d584a-8154-43b4-9c62-77251f560533/image.png)

어떤 String 객체를 만들더라도, 그 객체 내부에는 char 배열을 포함합니다.


### 8) substring()
- `String substring(int beginIndex)` : beginIndex부터 끝까지 대상 문자열을 잘라 String으로 리턴함.
- `String substring(int beginIndex, int endIndex)` : beginIndex부터 endIndex까지 대상 문자열을 잘라 String으로 리턴함.
- `CharSequence subSequence(int beginIndex, int endIndex)` : beginIndex부터 endIndex까지 대상 문자열을 잘라 CharSequence로 리턴함.

### 9) split()
- `String[] split(String regex)` : regex(정규표현식)에 맞추어 문자열을 잘라 String 배열로 리턴함.
- `String[] split(String regex, int limit)` : 위 메소드와 동일하나 limit보다 커서는 안됨.

자바에서 문자열을 나누는 방법에는 split()메소드와 StringTokenizer를 활용한 방법 2가지가 있습니다.
&lt;table border=&quot;1&quot;&gt;
  &lt;th&gt;&lt;/th&gt;
  &lt;th&gt;split()&lt;/th&gt;
  &lt;th&gt;StringTokenizer&lt;/th&gt;
  &lt;tr&gt;
    &lt;td&gt;정규 표현식&lt;/td&gt;
    &lt;td&gt;⭕️&lt;/td&gt;
    &lt;td&gt;❌&lt;/td&gt;
  &lt;/tr&gt;
    &lt;tr&gt;
    &lt;td&gt;특정 String&lt;/td&gt;
    &lt;td&gt;❌&lt;/td&gt;
    &lt;td&gt;⭕️&lt;/td&gt;
  &lt;/tr&gt;
    &lt;tr&gt;
    &lt;td&gt;특정 알파벳이나 기호 하나&lt;/td&gt;
    &lt;td&gt;⭕️&lt;/td&gt;
    &lt;td&gt;⭕️&lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;
정규표현식을 사용하여 문자열을 나눌 때는 split() 메소드를 사용하고, 특정 String을 사용하여 문자열을 나눌 경우 StringTokenizer를 사용하면 됩니다. 특정 알파벳이나 기호 하나로 나눌 때는 둘 다 사용해도 됩니다.

### 10) replace()
- `String replace(char oldChar, char newChar)` : 해당 문자열에 있는 oldChar의 값을 newChar로 대치함.
- `String replace(CharSequence target, CharSequence replacement)` : 해당 문자열에 있는 target과 같은 값을 replacement로 대치함.
- `String replaceAll(String regex, String replacement)` : regex(정규표현식)에 포함되는 모든 내용을 replacement로 대치함.
- `String replaceFirst(String regex, String replacement)` : regex(정규표현식)에 포함되는 첫번째 내용을 replacement로 대치함.

하지만 위 메소드를 사용한다고 해서 &lt;span style=&quot;color:red&quot;&gt;기존 문자열의 값은 바뀌지 않습니다&lt;/span&gt;. 또한 &lt;span style=&quot;color:red&quot;&gt;대소문자를 구분&lt;/span&gt;합니다.

```java
String str = &quot;Choonsik&quot;;
System.out.println(&quot;str.replace(&#39;o&#39;, &#39;z&#39;) = &quot; + str.replace(&#39;o&#39;, &#39;z&#39;)); //Chzznsik
System.out.println(&quot;str.replace(&#39;c&#39;, &#39;a&#39;) = &quot; + str.replace(&#39;c&#39;, &#39;a&#39;)); //Choonsik
System.out.println(&quot;str = &quot; + str); //Choonsik</code></pre><h3 id="11-format">11) format()</h3>
<ul>
<li><code>static String format(String format, Object...args)</code> : format에 있는 문자열의 내용 중 변환해야 하는 부분을 args의 내용으로 변경함.</li>
<li><code>static String format(Locale l, String format, Object...args)</code> : 위 메소드와 동일하며, Locale 타입 l에 선언된 지역에 맞추어 출력함. 만약 Locale을 지정하지 않으면 자바 프로그램이 수행되는 OS의 지역 정보 제공함.</li>
</ul>
<pre><code class="language-java">String str = &quot;제 이름은 %s 입니다. 나이는 %d살 입니다.&quot;;
String realStr = String.format(str, &quot;춘식이&quot;, 3);
System.out.println(&quot;realStr = &quot; + realStr); //제 이름은 춘식이 입니다. 나이는 3살 입니다.</code></pre>
<p>대치해야할 문자열이 n개일 때, format뒤에 n+m개가 오는 것은 상관없지만 n개보다 적은 변수를 명시하면 <code>MissingFormatArgumentException</code>이 발생합니다.</p>
<pre><code class="language-java">String realStr = String.format(str, &quot;춘식이&quot;);

Exception in thread &quot;main&quot; java.util.MissingFormatArgumentException: Format specifier &#39;%d&#39;</code></pre>
<h3 id="12-대소문자-바꾸는-메소드">12) 대소문자 바꾸는 메소드</h3>
<ul>
<li><code>String toLowerCase()</code></li>
<li><code>String toLowerCase(Locale locale)</code></li>
<li><blockquote>
<p>모든 문자열을 소문자로 변경하는데, 지정한 지역 정보에 맞추어 변경할 수도 있습니다.</p>
</blockquote>
</li>
</ul>
<ul>
<li><code>String toUpperCase()</code></li>
<li><code>String toUpperCase(Locale locale)</code></li>
<li><blockquote>
<p>모든 문자열을 대문자로 변경하는데, 지정한 지역 정보에 맞추어 변경할 수도 있습니다.</p>
</blockquote>
</li>
</ul>
<h3 id="13-기본자료형---문자열">13) 기본자료형 -&gt; 문자열</h3>
<ul>
<li><code>static String valueOf(boolean b)</code></li>
<li><code>static String valueOf(char c)</code></li>
<li><code>static String valueOf(char[] data)</code></li>
<li><code>static String valueOf(char[] data, int offset, int count)</code></li>
<li><code>static String valueOf(double d)</code></li>
<li><code>static String valueOf(float f)</code> </li>
<li><code>static String valueOf(int i)</code> </li>
<li><code>static String valueOf(long l)</code> </li>
<li><code>static String valueOf(Object obj)</code> : <span style="background-color:#F7E600">toString()을 구현한 객체나 정상적인 객체를 매개변수로 넘기면 toString()의 결과를 리턴</span>합니다. 만약 null이 넘어오면 &quot;null&quot;이라는 문자열을 리턴해줍니다.</li>
</ul>
<pre><code class="language-java">String str = null;
System.out.println(str); //null</code></pre>
<h3 id="14-intern">14) intern()</h3>
<p>intern() 메소드는 절대로 사용해서는 안되는 메소드입니다. 자바로 구현되지 않고 C로 구현되어 있는 native 메소드 중 하나입니다.</p>
<hr>
<h2 id="5-stringbuffer-stringbuilder">5. StringBuffer, StringBuilder</h2>
<p>String은 immutable 객체입니다. String 문자열을 더하면 새로운 String 객체가 생서되고, 기존 객체는 GC의 대상이 됩니다. 따라서 이 점을 보완하기 위해 StringBuffer와 StringBuilder가 탄생하게 됩니다. 둘 다 모두 CharSequence 인터페이스의 구현 클래스입니다.매개변수로 받는 작업할 때 String이나 StringBuilder보다는 CharSequence 타입으로 받는 것이 좋습니다.</p>
<ul>
<li>StringBuffer : Thread Safe. 인스턴스 변수가 선언되고 여러 thread에서 이 변수를 동시에 접근할 때 사용</li>
<li>StringBuilder : 빠름. JDK5부터 String에서 더하기 연산을 사용할 경우 컴파일할 때 자동으로 해당 연산은 StringBuilder로 변환함. 하나의 메소드 내에서 문자열을 생성하여 더할 떄 사용.</li>
</ul>
<p>StringBuffer와 StringBuilder는 문자열을 더하더라도 새로운 객체를 새로 생성하지 않습니다. 또한 더할 때는 <code>append()</code>라는 메소드를 사용하는데, 매개변수로 모든 기본 자료형과 참조 자료형이 포함됩니다. </p>
<hr>
<blockquote>
<p>지속적으로 수정해나갈 예정입니다.
2023-07-23 v1.0</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[자바] 6. 예외(Exception)]]></title>
            <link>https://velog.io/@choonsik_dev/%EC%9E%90%EB%B0%94-6.-%EC%98%88%EC%99%B8Exception</link>
            <guid>https://velog.io/@choonsik_dev/%EC%9E%90%EB%B0%94-6.-%EC%98%88%EC%99%B8Exception</guid>
            <pubDate>Sat, 22 Jul 2023 05:03:26 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>도서 자바의신을 참고하였습니다.</p>
</blockquote>
<h2 id="1-예외-종류">1. 예외 종류</h2>
<p><img src="https://velog.velcdn.com/images/choonsik_dev/post/aa2bc006-97ba-48dc-9483-2b312407caa2/image.png" alt="Throwable 계층"></p>
<p><code>java.lang.Throwable</code>은 <code>Error</code>와 <code>Exception</code>을 포함한 최상위 클래스입니다. <span style="background-color:#F7E600">Error는 자바 프로그램 밖에서 발생한 예외로, 프로세스에 영향을 미치지만 Exception은 해당 쓰레드에만 영향</span>을 줍니다.
Exception은 다시 <code>RuntimeException(=UncheckedException)</code>과 <code>CheckedException</code>으로 나뉩니다. RuntimeException은 컴파일 시 체크되지 않으므로 <span style="color:red">예외가 발생한 것을 미리 감지하지 못할 때 발생</span>합니다. 이와 반대로 CheckedException은 <code>java.lang.Exception</code>을 확장한 클래스들로 <span style="color:red">컴파일 시 체크</span> 가능합니다.</p>
<hr>
<h2 id="2-throwable-클래스">2. Throwable 클래스</h2>
<p>Exception과 Error의 공통 부모 클래스는 <code>java.lang.Throwable</code>클래스입니다. Exception와 Error의 성격은 다르지만, 모두 동일한 이름의 메소드를 사용하여 처리하기 위해 동일한 부모 클래스를 상속받으며, Exception과 Error를 처리할 때 <code>Throwable</code>로 처리해도 됩니다.</p>
<h3 id="1-throwable-생성자">1) Throwable 생성자</h3>
<ul>
<li>Throwable()</li>
<li>Throwable(String message)</li>
<li>Throwable(String message, Throwable cause)</li>
<li>Throwable(Throwable cause)</li>
</ul>
<p>Throwable 생성자에는 위 4가지 생성자가 있으며, 매개변수인 <code>Throwable cause</code>를 통해 별도의 예외 원인을 객체로 넘겨줄 수 있습니다.</p>
<h3 id="2-thowable-메소드">2) Thowable 메소드</h3>
<ul>
<li><code>public String getMessage()</code> : 예외 메시지를 String 형태로 제공받음. 어떤 예외가 발생했는지 확인할 때 유용함.</li>
<li><code>public String getLocalizedMessage()</code> : 로컬 예외 메시지를 받지만, 특정 클래스에서 이 메소드가 재정의되어 있지 않은 경우 getMessage()와 동일한 결과값을 리턴함.</li>
<li><code>public String toString()</code> : getMessage()보다 더 자세하게 예외 클래스명도 제공함.</li>
<li><code>public void printStackTrace()</code> : 매우 자세하게 어디서 무슨 예외가 발생했는지 알려주기 때문에 운영에 배포될 소스에는 사용하면 안됨.</li>
</ul>
<pre><code class="language-java">public class ExceptionMain {
    public static void main(String[] args) {
        try {
            crunch(null);
        } catch (NullPointerException e) {
            //System.out.println(e.getMessage()); //null
            //System.out.println(e.getLocalizedMessage()); //null
            //System.out.println(e.toString());   //java.lang.NullPointerException
            e.printStackTrace();
            //java.lang.NullPointerException
            //    at com.test.exception.ExceptionMain.mash(ExceptionMain.java:19)
            //    at com.test.exception.ExceptionMain.crunch(ExceptionMain.java:16)
            //    at com.test.exception.ExceptionMain.main(ExceptionMain.java:6)
        }
    }

    static void crunch(int[] a) {
        mash(a);
    }
    static void mash(int[] b) {
        System.out.println(b[0]);
    }
}</code></pre>
<hr>
<h2 id="3-throws">3. throws</h2>
<p><code>throws</code> 키워드는 예외가 발생된 메소드를 다시 호출한 메소드로 던지는 것을 말합니다. 이미 throws한 것을 다시 throws 하는 것은 별로 좋지 않습니다. 가장 좋은 방법은 throws하는 메소드를 호출하는 메소드에서 <code>try~catch</code>로 처리하는 방법입니다. 메소드를 throws할 때는 2가지 이상의 예외를 던질 수도 있습니다.</p>
<p>try 블록 내에서 예외를 발생시킬 경우에는 다음과 같이 <code>throw</code>예약어를 적어둔 뒤 예외를 객체를 생성하거나 예외 클래스명을 명시해야 합니다.</p>
<pre><code class="language-java">public static void main(String[] args) {
    try {
        throw new MyException(&quot;m&quot;);
    } catch (MyException e) {
        e.printStackTrace();
    }
 }</code></pre>
<p>만약 throw한 예외 클래스가 catch 블록에 선언되어 있지 않거나, throws에 선언되어 있지 않다면 컴파일 에러가 발생합니다. catch 블록에서 예외를 throw할 경우에도 메소드 선언의 throws 구문에 해당 예외가 정의되어 있어야 합니다.</p>
<hr>
<h2 id="4-커스텀-예외">4. 커스텀 예외</h2>
<p>Throwable이나 Exception 클래스를 상속 받으면 커스텀 예외 클래스를 생성할 수 있습니다. 하지만, Throwable은 Exception말고도 Error를 자식클래스로 포함하기 때문에, <span style="color:red">Exception 클래스 상속받는 것을 더 추천</span>합니다.</p>
<hr>
<h2 id="5-예외-처리-전략">5. 예외 처리 전략</h2>
<p>만약 커스텀 예외 클래스에서 <code>extends Exception</code>을 통해서 만들었지만, 런타임 시에 예외가 발생할 확률이 더 높다면 <code>extends RuntimeException</code>으로 선언하는 것이 좋습니다. 이렇게 변경하면 해당 예외를 throw하는 메소드를 사용하더라도 try<del>catch문으로 묶지 않아도 컴파일 에러가 발생하지 않습니다. 하지만, 이 경우에는 예외 발생 시 해당 클래스를 호출하는 다른 클래스에서 예외를 처리하도록 구조적인 안전 장치(호출하는 메소드에서 try</del>catch문이 있어야 함)가 되어 있어야 합니다.</p>
<pre><code class="language-java">public void methodCaller() {
    try {
        methodCallee();
    } catch(Exception e) {
        //예외 처리
    }
}

public void methodCallee() {
    //RuntimeException 발생 가능성 있는 부분
    //여기서 try~catch문이 없다면 methodCaller에 있어야 함.
}</code></pre>
<hr>
<blockquote>
<p>지속적으로 수정해나갈 예정입니다.
2023-07-22 v1.0</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[자바] 5. 모든 자바 클래스의 부모 Object 클래스]]></title>
            <link>https://velog.io/@choonsik_dev/5.-%EB%AA%A8%EB%93%A0-%EC%9E%90%EB%B0%94-%ED%81%B4%EB%9E%98%EC%8A%A4%EC%9D%98-%EB%B6%80%EB%AA%A8-Object-%ED%81%B4%EB%9E%98%EC%8A%A4</link>
            <guid>https://velog.io/@choonsik_dev/5.-%EB%AA%A8%EB%93%A0-%EC%9E%90%EB%B0%94-%ED%81%B4%EB%9E%98%EC%8A%A4%EC%9D%98-%EB%B6%80%EB%AA%A8-Object-%ED%81%B4%EB%9E%98%EC%8A%A4</guid>
            <pubDate>Sat, 22 Jul 2023 03:03:29 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>도서 자바의신, 이것이 자바다를 참고하였습니다.</p>
</blockquote>
<h2 id="1-object">1. Object</h2>
<p>모든 자바 클래스의 부모는 <code>java.lang.Object</code> 클래스입니다. 아무런 상속을 받지 않으면, Object 클래스를 확장합니다.
Object 클래스에 선언되어 있는 메소드는 두 가지로 분류될 수 있습니다.</p>
<ul>
<li>객체를 처리하기 위한 메소드</li>
<li>쓰레드를 위한 메소드</li>
</ul>
<h3 id="1-객체를-처리하기-위한-메소드">1) 객체를 처리하기 위한 메소드</h3>
<ul>
<li><code>public boolean equals(Object obj)</code> : 현재 객체와 파라미터로 넘어온 obj 객체가 같은지 확인함.</li>
<li><code>public int hashCode()</code> : 객체에 대한 해시코드값(16진수로 제공되는 객체의 메모리 주소)을 리턴함.</li>
<li><code>public String toString()</code> : 객체를 문자열로 표현한 값 리턴함.</li>
<li><code>public Class&lt;?&gt; getClass()</code> : 현재 객체의 Class 클래스 객체를 리턴함.</li>
<li><code>protected void finalize()</code> : 현재 객체가 더 이상 쓸모없을 때 GC에 의해 호출됨.</li>
<li><code>protected Object clone()</code> : 객체의 복사본을 만들어 리턴함.</li>
</ul>
<h3 id="2-쓰레드-처리를-위한-메소드">2) 쓰레드 처리를 위한 메소드</h3>
<ul>
<li><code>public void notify()</code>: 이 객체의 모니터에 대기하고 있는 단일 쓰레드를 깨움.</li>
<li><code>public void notifyAll()</code>: 이 객체의 모니터에 대기하고 있는 모든 쓰레드를 깨움.</li>
<li><code>public void wait()</code> : 다른 쓰레드가 현재 객체에 대한 notify()나 notifyAll()을 호출할 때 까지 현재 쓰레드가 대기하고 있도록 함.</li>
<li><code>public void wait(long timeout)</code> : wait()와 동일한 기능을 제공하며, timeout만큼 대기함. 즉, timeout을 넘을 경우 현재 쓰레드는 다시 깨어나고, 시간은 밀리세컨드(1/1,000)초 단위임.</li>
<li><code>public void wait(long timeout, int nanos)</code> : 위 메소드보다 더 자세한 밀리초+나노초(1/10억초) 만큼 대기함. nanos값은 0~999,999 사이의 값만 지정 가능함.</li>
</ul>
<hr>
<h2 id="2-tostring">2. toString()</h2>
<p><code>toString()</code> 메소드는 System.out.println()과 같은 출력 메소드에 매개 변수로 들어가는 경우 혹은 객체에 더하기 연산을 하는 경우 자동 호출됩니다. 재정의를 해주지 않았을 경우 다음과 같은 결과값을 리턴합니다.
<code>getClass().getName() + &#39;@&#39; + Integer.toHexString(hashCode())</code></p>
<ul>
<li>getClass().getName() : 현재 클래스의 패키지명과 클래스명을 리턴함.</li>
<li>hashCode() : int 타입으로 반환되는 객체 해시코드 값</li>
<li>Integer.toHexString() : int값을 16진수로 변환함.</li>
</ul>
<hr>
<h2 id="3-equals">3. equals()</h2>
<p>객체의 값을 비교할 때는 <code>==</code> 또는 <code>!=</code>를 사용하는 것은 지양하고, equals() 메소드를 사용하는 것이 좋습니다. <code>==</code>는 주소값을 비교하기 때문에 기본자료형만 사용 가능합니다.
<span style="background-color:#F7E660">equals() 메소드를 오버라이딩하지 않으면 hashCode() 값을 비교하기 때문에, equals() 메소드를 오버라이딩할 때 hashCode()도 같이 오버라이딩해 주어야 합니다. 만약 둘 다 함께 재정의하지 않으면 Collection(HashSet, HashMap, HashTable)을 사용할 때 문제가 발생</span>합니다.
equals() 메소드를 오버라이딩할 때 지켜야 할 5가지는 다음과 같습니다. (모두 null이 아니라는 전제 하)</p>
<ul>
<li><strong>재귀(reflexive)</strong> : X 객체의 X.equals(X)는 항상 같아야 함.</li>
<li><strong>대칭(symmetric)</strong> : X, Y 객체에서 Y.equals(X)가 true를 리턴하면 X.equals(Y)도 true를 리턴해야 함.</li>
<li><strong>타동적(transitive)</strong> : X, Y, Z 객체에서 X.equals(Y)가 true이고, Y.equals(Z)가 true면 X.equals(Z)도 true를 리턴해야 함.</li>
<li><strong>일관(consistent)</strong> : X, Y 객체에서 객체가 변경되지 않은 상황에서는 몇 번을 호출하더라도 X.equals(Y)는 항상 true이거나 false여야 함.</li>
<li><strong>null과의 비교</strong> : X 객체의 X.equals(null)은 항상 false여야 함.</li>
</ul>
<hr>
<h2 id="4-hashcode">4. hashCode()</h2>
<p>hashCode() 메소드는 기본적으로 <span style="background-color:#F7E600">객체의 메모리 주소를 16진수로 리턴</span>합니다. 만약, 어떤 2개의 객체가 서로 동일하다면, hashCode()는 무조건 동일해야 합니다.
hashCode()를 오버라이딩시 지켜야 할 조건은 다음과 같습니다.</p>
<ul>
<li><span style="color:red">자바 애플리케이션이 수행되는 동안, 어떤 객체에 대해서 이 메소드가 호출될 때에는 항상 동일한 int값을 리턴</span>해야 합니다. 단, 자바를 실행할 때마다 같은 값일 필요는 없습니다.</li>
<li>어떤 두 객체에 대하여 <span style="color:red">equals()가 true일 경우, 두 객체의 hashCode()는 동일한 int값을 리턴해야</span> 합니다.</li>
<li>두 객체의 equals()가 <span style="color:red">false를 리턴했다고 해서, hashCode()값이 무조건 달라야 할 필요는 없지만</span>, 서로 다른 int값을 제공하면 hashtable의 성능 향상에 도움이 됩니다.</li>
</ul>
<hr>
<blockquote>
<p>지속적으로 수정해나갈 예정입니다.
2023-07-22 v1.0</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[자바] 4. 클래스와 인터페이스]]></title>
            <link>https://velog.io/@choonsik_dev/%EC%9E%90%EB%B0%94-4.-%ED%81%B4%EB%9E%98%EC%8A%A4%EC%99%80-%EC%83%81%EC%86%8D</link>
            <guid>https://velog.io/@choonsik_dev/%EC%9E%90%EB%B0%94-4.-%ED%81%B4%EB%9E%98%EC%8A%A4%EC%99%80-%EC%83%81%EC%86%8D</guid>
            <pubDate>Sat, 22 Jul 2023 02:41:06 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>도서 자바의신, 이것이 자바다를 참고하였습니다.</p>
</blockquote>
<h2 id="1-패키지">1. 패키지</h2>
<p>패키지는 수많은 클래스를 분류하기 위해 사용됩니다. 패키지는 소스의 가장 첫 줄에 있어야 하며, 패키지 선언은 한 번만 해야 합니다. </p>
<pre><code class="language-java">package 상위패키지.하위패키지;

public class classA {
    ...
}</code></pre>
<p>패키지 이름과 위치한 폴더 이름이 같아야하는데, 그렇지 않다면 컴파일이 되지 않습니다. 또한 패키지 이름을 java로 시작하면 안됩니다. 자바 표준 API에서만 사용하기 때문입니다. 그럴 경우 <code>java.lang.SecurityException &quot;Prohibited package name:java&quot;</code> 라는 에러가 발생하게 됩니다.</p>
<h3 id="1-패키지명-규칙">1) 패키지명 규칙</h3>
<p>패키지 이름은 모두 소문자로 작성하는 것이 관례이며, 자바 예약어를 사용하면 안됩니다. 숫자로 시작하면 안되고, <code>_</code>와 <code>$</code>를 제외한 특수 문자 사용이 안됩니다. 주로 <code>com.회사명.프로젝트명</code>으로 작성합니다.</p>
<table>
  <th>패키지 시작 이름</th>
  <th>내용</th>
  <tr>
    <td>java</td>
    <td>자바 기반 패키지 (Java 벤더에서 개발)</td>
  </tr>
  <tr>
    <td>javax</td>
    <td>자바 확장 패키지 (Java 벤더에서 개발)</td>
  </tr>
  <tr>
    <td>org</td>
    <td>비영리단체(오픈소스)의 패키지</td>
  </tr>
  <tr>
    <td>com</td>
    <td>영리단체(회사)의 패키지</td>
  </tr>
</table>

<h3 id="2-패키지-선언이-포함된-클래스-컴파일">2) 패키지 선언이 포함된 클래스 컴파일</h3>
<pre><code>javac -d .           ClassName.java  &lt;- 현재 폴더 내에서 생성
javac -d ..\bin      ClassName.java  &lt;- 현재 폴더와 같은 위치의 bin 폴더에 생성
javac -d C:\Temp\bin ClassName.java  &lt;- C:\Temp\bin 폴더에 생성</code></pre><hr>
<h2 id="2-접근-제어자">2. 접근 제어자</h2>
<ul>
<li>public : 누구나 접근 가능합니다. 소스 파일의 이름은 public인 클래스 이름과 동일해야 합니다.</li>
<li>protected : 같은 패키지 혹은 상속받은 관계에서 접근 가능합니다.</li>
<li>(default) : package-private라고도 하며 같은 패키지에서만 접근 가능합니다.</li>
<li>private : 같은 클래스에서만 접근 가능합니다.</li>
</ul>
<hr>
<h2 id="3-상속">3. 상속</h2>
<pre><code class="language-java">class 자식클래스 extends 부모클래스 {...}</code></pre>
<p>자바에서의 상속은 <span style="color:red">단일 상속</span>만 가능합니다. 상속을 받으면 부모 클래스에 있는 <code>public</code> 및 <code>protected</code>로 선언된 모든 메소드나 변수를 자식 클래스에서 사용 가능합니다. 단, 접근 제어자가 없거나 private은 상속 대상에서 제외됩니다. 자식 클래스의 생성자를 호출하면, 부모 클래스의 기본 생성자가 실행됩니다.</p>
<h3 id="1-상속과-생성자">1) 상속과 생성자</h3>
<ul>
<li>부모 클래스에 기본 생성자가 없다면? 괜찮습니다.</li>
<li>그런데, 매개변수가 있는 생성자<strong>만</strong> 있다면? <img src="https://velog.velcdn.com/images/choonsik_dev/post/96b3943b-762e-4dad-8aec-f0f032384fe1/image.png" alt="부모 클래스에 기본 생성자가 없다는 컴파일 에러"> 부모 클래스에 기본 생성자가 없다는 컴파일 에러가 발생합니다.
부모 클래스의 상속을 받는 자식 클래스의 모든 생성자가 실행될 때, 개발자가 따로 지정하지 않아도 컴파일러가 자식 클래스를 컴파일할 때 자동으로 <code>super()</code>라는 문장이 들어가게 됩니다.
이 <span style="background-color:#F7e600"><code>super()</code>는 부모 클래스의 생성자를 호출한다는 의미로, <code>super()</code>는 반드시 자식 클래스의 생성자에서 가장 첫줄에 선언</span>되어야 합니다.</li>
<li>매개 변수를 갖는 생성자가 여러개거나, 혹은 null을 넘겨준다면? 부모 클래스로의 참조가 매우 모호하다는 컴파일 에러가 발생합니다. super()를 사용하여 생성자를 호출할 때에는 모호하게 null을 넘기는 것보다는 호출하고자 하는 생성자의 기본 타입을 넘겨주는 것이 좋습니다.</li>
<li>자식 클래스에서 부모 클래스와 같은 생성자를 만들면, 그 매개변수로 하는 부모 클래스의 생성자를 호출하나? 아닙니다. 자바는 부모의 기본 생성자를 찾는 것이 기본입니다. super()를 이용해서 부모 생성자를 꼭 호출해야 합니다.</li>
</ul>
<hr>
<h2 id="4-메소드-오버라이딩overriding">4. 메소드 오버라이딩(Overriding)</h2>
<p>오버라이딩은 부모 클래스에서 정의한 메소드를 자식 클래스에서 재정의해서 사용하고 싶을 경우 사용됩니다. 오버라이딩은 부모 클래스와 동일한 시그니처(메소드명, 매개 변수의 타입과 개수)와 동일한 리턴 타입을 갖는 자식 클래스의 메소드가 존재할 때 성립됩니다. 접근 제어자는 달라도 되지만, 확장되는 경우에만 허용됩니다. 축소될 경우에는 컴파일 에러가 발생합니다.
ex) private -&gt; pulic ⭕️, public -&gt; private ❌</p>
<blockquote>
<p>오버로딩: 확장 (메소드의 매개 변수들을 확장)
오버라이딩: 재정의 (부모 클래스의 메소드 시그니처를 복제해서 자식 클래스에서 재정의)</p>
</blockquote>
<hr>
<h2 id="5-참조-자료형의-형변환">5. 참조 자료형의 형변환</h2>
<pre><code class="language-java">ParentClass parentClass = new ChildClass(); //⭕️ UpCasting

ChildClass childClass = new ParentClass();  //❌
//Incompatible types. Found: &#39;com.test.inheritance.ParentClass&#39;, required: &#39;com.test.inheritance.ChildClass&#39;</code></pre>
<p>부모 클래스는 자식 클래스에 있는 모든 메소드와 변수 사용 불가능합니다. 컴파일러에서는 자식 객체를 생성할 때 부모 생성자를 사용하면 안된다고 위와 같이 컴파일 에러를 발생시킵니다. 따라서 명시적으로 형 변환(casting)을 한다고 알려줘야 합니다.</p>
<pre><code class="language-java">ChildClass childClass = (ChildClass) new ParentClass(); //DownCasting</code></pre>
<p>이 경우에는 컴파일 에러는 발생하지 않지만, <code>ClassCastException</code>이 발생합니다.</p>
<p>참고로, 기본 자료형의 경우 데이터의 범위가 넓어져도 값이 바뀌지 않기 때문에(int -&gt; long) 형변환을 굳이 명시적으로 하지 않아도 자동 형 변환이 됩니다. 하지만 long -&gt; int로 형 변환을 하면 값이 바뀔 수도 있기 때문에 명시적으로 형 변환을 해야 합니다. 이 때, 값이 동일하다는 보장은 전혀 없습니다.</p>
<hr>
<h2 id="6-instanceof">6. instanceof</h2>
<pre><code class="language-java">객체 instanceof 클래스(타입)</code></pre>
<p>객체의 타입을 확인할 때 사용됩니다. 만약 객체가 해당 클래스의 타입이라면 true값을 리턴받습니다. 주의할 점은 부모 타입도 true를 리턴하기 때문에, <strong>가장 하위에 있는 자식 타입부터 확인해야한다</strong>는 것입니다.</p>
<hr>
<h2 id="7-다형성polymorphism-多形性">7. 다형성(Polymorphism, 多形性)</h2>
<p>선언 시 모두 Parent로 선언해도 실제로 호출되는 메소드는 생성자를 사용한 클래스에 있는 것이 호출됩니다. 각 객체의 실제 타입은 다르기 때문입니다.
즉, 형변환을 하더라도 실제 호출되는 것은 원래 객체에 있는 메소드가 호출되는 것을 <code>다형성</code>이라고 합니다.</p>
<hr>
<h2 id="8-인터페이스interface">8. 인터페이스(Interface)</h2>
<pre><code class="language-java">public interface 인터페이스A {
    몸통없는메소드();
}

public class 구현클래스 implements 인터페이스A, 인터페이스B {
    //인터페이스에 정의된 모든 메소드 구현...
}</code></pre>
<p>인터페이스와 추상 클래스를 사용하는 이유는 무엇일까요?
우선 설계 시 선언해 두면 개발할 때 <span style="background-color:#F7E600">기능을 구현하는 데에만 집중이 가능</span>하고, 개발자의 역량에 따른 <span style="background-color:#F7E600">메소드명과 매개변수 선언의 격차가 완화</span>됩니다. 공통적인 인터페이스와 추상 클래스를 선언해 놓으면, <span style="background-color:#F7E600">선언과 구현 구분</span>할 수 있습니다.
인터페이스는 상속이 아니라, <code>implements</code> 키워드를 사용함으로써 해당 클레스에서 구현해야 하는 인터페이스들을 정의함으로써 클래스에 짐을 지어주고, 인터페이스에 정의된 모든 메소드를 구현해야 컴파일이 됩니다.
객체를 생성할 때는 <code>인터페이스 변수명 = new 구현클래스();</code>로 선언해주면 되며, 단일 상속을 해야 하는 클래스와는 달리 여러 인터페이스를 구현할 수 있습니다.</p>
<hr>
<h2 id="9-추상abstract-클래스">9. 추상(abstract) 클래스</h2>
<pre><code class="language-java">public abstract classA {
    public abstract void methodA(); //추상 메서드

    public String test() {
        //몸통 있는 메서드 사용 가능
    }
}</code></pre>
<p>추상 클래스는 자바에서 마음대로 초기화하고 실행 불가능합니다. 단, 추상 클래스를 구현한 클래스로는 초기화와 실행 가능합니다.
추상 클래스는 <span style="color:red"><code>abstract</code>으로 선언된 메소드가 0개 이상</span>이면 되고, 만약 1개라도 있으면 그 클래스는 반드시 <code>abstract</code>으로 선언되어야 합니다. 또한 몸통있는 메소드가 있어도 상관 없으며 <code>static</code>이나 <code>final</code> 메소드도 상관 없습니다.(<strong>단, 인터페이스는 안됩니다.</strong>)
어떤 메소드들은 미리 구현해놔도 상관없는데, 그렇다고 클래스를 만들자니 애매한 경우 사용됩니다.</p>
<hr>
<h2 id="10-final">10. final</h2>
<p>final 키워드는 어디에 선언하느냐에 따라 그 용도가 다릅니다.</p>
<ul>
<li><strong>클래스</strong>(<code>final class</code> ClassA {...}) : <span style="color:red"><strong>상속 불가</strong></span> -&gt; 더 이상 확장해서는 안되는 클래스로, 누군가 이 클래스를 상속받아서 내용 변경해서는 안될 때 사용합니다.</li>
<li><strong>메소드</strong>(<code>final</code> void methodA() {...}) : <span style="color:red"><strong>Overriding 불가</strong></span></li>
<li><strong>변수</strong>(<code>final</code> int variableA = 1;) : <span style="color:red"><strong>더 이상 바꿀 수 없음</strong></span> -&gt; 인스턴스 변수나 static으로 선언된 클래스 변수는 <span style="background-color:#F7E600">변수 생성과 동시에 초기화해야</span> 합니다.</li>
</ul>
<p>클래스가 final이라 해서 그 안에 있는 인스턴스 변수나 클래스 변수가 final일 필요는 없습니다.</p>
<pre><code class="language-java">final MemberDTO dto = new MemberDTO();
dto = new MemberDTO();     //❌
dto.name = &quot;Java&quot;;        //⭕️ MemberDTO 안에 있는 name 변수는 final이 아님.</code></pre>
<p>클래스, 인터페이스, 추상 클래스를 표로 정리하면 다음과 같습니다.
<img src="https://velog.velcdn.com/images/choonsik_dev/post/6f02bb52-8a97-48e2-af3e-ac39aae62c07/image.jpeg" alt="interface, abstract class, class"></p>
<hr>
<h2 id="11-enum-클래스열거형-클래스">11. Enum 클래스(=열거형 클래스)</h2>
<blockquote>
<p>enumeration : n. 셈, 계산, 열거, 목록, 열람표</p>
</blockquote>
<pre><code class="language-java">public enum EnumA (extends java.lang.Enum){
    MON, TUE, WED
}

public enum EnumB {
    RED(2), BLUE(350), GREEN(6) //상수 값 지정 가능하나 값 동적 할당은 불가능
}</code></pre>
<p>Enum 클래스는 JDK 1.5부터 추가된 클래스로, <span style="color:red">상수의 집합 클래스</span>를 의미합니다. 별도로 타입이나 값을 지정할 필요 없고, 상수들을 쉼표로 나열하면 됩니다. 만약 상수들만 선언할 경우 세미콜론도 필요 없습니다. 각각의 상수들은 <code>public static final</code>입니다.</p>
<p>Enum 클래스에 지정된 상수를 사용하려면 <code>EnumA.MON</code>처럼 <code>enum클래스명.상수명</code>으로 지정함으로써 객체 생성이 완료됩니다. Enum 클래스에 생성자를 만들 수는 잇지만 <span style="color:red">생성자를 통한 객체 생성은 불가능</span>하며, 각 상수를 Enum 클래스 내에서 선언할 때만 생성자 사용이 가능합니다. 생성자의 접근제어자는 <code>package-private</code>과 <code>private</code>만 사용 가능합니다.</p>
<p>Enum 클래스는 무조건 <code>java.lang.Enum</code>클래스를 상속받는데, 컴파일러가 알아서 <code>protected Enum(String name, int ordinal)</code>생성자를 호출해줍니다. 여기서 ordinal은 enum 상수의 순서이며, <span style="color:red">선언된 순서대로 0부터 증가</span>합니다. 만약 클래스 내 같은 키워드가 있으면 컴파일 에러를 발생시킵니다. </p>
<h3 id="1-enum-클래스-메소드">1) Enum 클래스 메소드</h3>
<ul>
<li><code>int compareTo(E e)</code> : 매개변수 e와의 순서(ordinal) 차이를 리턴함. e가 더 앞에 있으면 양수, 뒤면 음수, 같으면 0 리턴함.</li>
<li><code>Class&lt;E&gt; getDeclaringClass()</code> : 클래스 타입의 enum 리턴함.</li>
<li><code>String name()</code> : 상수 이름을 리턴함.</li>
<li><code>int ordinal()</code> : 상수 순서를 리턴함.</li>
<li><code>static &lt;T extends Enum&lt;T&gt;&gt; T valueOf(Class&lt;T&gt; enumType, String name)</code></li>
<li><code>values()</code> : API에는 없는 메소드로, enum 클래스에 선언되어 있는 모든 상수를 배열로 리턴함.</li>
</ul>
<hr>
<blockquote>
<p>지속해 수정해 나갈 예정입니다.
2023-07-08 v1.0
2023-07-22 v1.1 - 인터페이스, 추상클래스, enum 내용 추가함</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[Mutable과 Immutable]]></title>
            <link>https://velog.io/@choonsik_dev/Mutable%EA%B3%BC-Immutable</link>
            <guid>https://velog.io/@choonsik_dev/Mutable%EA%B3%BC-Immutable</guid>
            <pubDate>Thu, 20 Jul 2023 14:52:26 GMT</pubDate>
            <description><![CDATA[<blockquote>
<ul>
<li>Mutable: adj. 변할 수 있는, 잘 변하는</li>
</ul>
</blockquote>
<ul>
<li>Immutable: adj. 변경할 수 없는, 불변의</li>
</ul>
<p><code>Mutable</code> 변수는 값을 재할당할 수도 있고, 값을 바꿀 수도 있습니다. 그와 반대로 <code>immutable</code> 변수는 값을 바꿀 수가 없고 재할당을 할 수 없습니다. Immutable 변수는 </p>
<ul>
<li>멀티스레드일 때 thread safety를 보장해주고, </li>
<li>값을 바꿀 수 없기 때문에 보안성이 높아집니다. </li>
</ul>
<p>대표적인 예로 자바의 <code>String</code>이 있습니다.</p>
<h2 id="string-is-immutable">String is Immutable</h2>
<p><img src="https://velog.velcdn.com/images/choonsik_dev/post/4b615a6b-ecac-482c-b055-b86b90e7c84b/image.jpeg" alt="String은 immutable"></p>
<blockquote>
<p>만약 new 연산자를 사용해서 String 객체를 생성하면 String constant pool 영역 밖 Heap에 생성됩니다. 하지만 모두 Heap 영역에 생기기 때문에 GC의 영역 대상이 됩니다.(Java7~)</p>
</blockquote>
<pre><code class="language-java">String strA = &quot;a&quot;;
String strB = &quot;b&quot;;
System.out.println(System.identityHashCode(strA)); //1651191114
System.out.println(System.identityHashCode(strB)); //1579572132

strA = strA + strB;
System.out.println(System.identityHashCode(strA)); //783286238   </code></pre>
<p><code>System.identityHashCode</code> 메소드는 hashCode 메소드로 오버라이딩 하지 않은 객체의 해시코드값을 10진수로 변환해주는 메소드입니다. strA와 strB 그리고 strA와 strB를 더한 strA 변수의 메모리 주소가 다른 것을 확인할 수 있습니다.
String은 + 연산을 할 때 strA가 참조하고 있는 힙의 값이 재할당되는 것이 아니라, heap에 새로 만들어서 그 주소를 재참조합니다.</p>
<blockquote>
<p>String의 hashCode() 메소드로 호출했을 때는 ASCII 코드랑 같은 값이 나왔다..!</p>
</blockquote>
<pre><code class="language-java">String strA = &quot;a&quot;;
System.out.println(System.identityHashCode(strA)); //1651191114

strA = &quot;hello&quot;;
System.out.println(System.identityHashCode(strA)); //1579572132</code></pre>
<hr>
<h2 id="mutable---immutable">Mutable -&gt; Immutable</h2>
<h3 id="mutable을-immutable로-변경하려면-아래의-규칙들을-따라야-합니다">Mutable을 immutable로 변경하려면 아래의 규칙들을 따라야 합니다.</h3>
<blockquote>
<ul>
<li>모든 필드들을 <code>private final</code> 로 지정합니다.</li>
</ul>
</blockquote>
<ul>
<li>setter 메서드가 있으면 <strong>안됩니다</strong>. </li>
<li>mutable 필드를 리턴하는 getter 메서드가 있으면 <strong>안됩니다</strong>.<ul>
<li>객체를 리턴하는 getter 메서드가 있다면, 이 객체는 반드시 immutable 해야 합니다.</li>
<li>레퍼런스 타입의 필드에 적용됩니다.</li>
<li>또는 mutable 필드를 get해야 한다면 <code>방어적 복사</code>를 해야 합니다.</li>
</ul>
</li>
<li>자녀 클래스에서 메서드 override를 금지시켜야 합니다. 즉, 클래스 상속이 불가능하도록 <code>final 클래스</code>로 지정해줍니다.</li>
</ul>
<p>아래의 Person 클래스는 name이라는 필드를 갖고, get과 set 모두 가능한 mutable한 클래스 입니다.</p>
<pre><code class="language-java">public class Person {
    private String name;

    public Person(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
} </code></pre>
<p>Immutable하게 바꾸려면 우선 생성자를 제외하고 상태를 바꿀만한 메서드는 모두 제거해야합니다. 여기서는 <code>setName()</code>이 해당됩니다.</p>
<pre><code class="language-java">public class Person {
    private String name;

    public Person(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}</code></pre>
<p>필드가 현재 <code>private</code>으로만 되어있으므로, <code>final</code> 키워드를 붙여줌으로써, Person 클래스에서만 name 필드를 접근 가능하도록 수정해줍니다.</p>
<pre><code class="language-java">public class Person {
    private final String name;

    public Person(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}</code></pre>
<p>이렇게 변경된 클래스 Person을 상속 받음으로써 아래와 같이 mutable처럼 사용될 수 있습니다.</p>
<pre><code class="language-java">public class NewPerson extends Person {

    private String newName;

    public NewPerson(String name) {
        super(name);
        newName = name;
    }

    @Override
    public String getName() {
        return this.newName;
    }

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

public class Main {
    public static void main(String[] args) {
        Person person = new NewPerson(&quot;choonsik&quot;);
        System.out.println(&quot;person.getName() = &quot; + person.getName()); //choonsik

        NewPerson newPerson = (NewPerson) person;
        System.out.println(&quot;newPerson.getName() = &quot; + newPerson.getName()); //choonsik

        newPerson.setName(&quot;Java&quot;);
        System.out.println(&quot;person.getName() = &quot; + person.getName()); //Java
    }
}</code></pre>
<p>분명히 newPerson에서 setName을 해주었는데, person의 name이 변경되었습니다. 왜 그럴까요? 그 이유는 바로, Person 클래스를 상속받은 NewPerson 클래스에서 getName() 메소드를 NewPerson의 name을 리턴하도록 재정의했기 때문입니다. 따라서 person.getName()을 하게되면 실제로는 NewPerson 클래스의 재정의된 getName()을 호출합니다. 따라서 상태가 바뀐 것 처럼 보이게 됩니다.
이를 막기 위해, Person 클래스를 상속받지 못하도록 클래스 또한 final 클래스가 되도록 final 키워드를 붙여줍니다.</p>
<pre><code class="language-java">public final class Person {
    private final String name;

    public Person(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}</code></pre>
<h3 id="그런데-만약-객체의-필드-중에-mutable-객체를-가리키는-레퍼런스가-있다면-어떻게-될까요">그런데 만약 객체의 필드 중에 mutable 객체를 가리키는 레퍼런스가 있다면 어떻게 될까요?</h3>
<pre><code class="language-java">public class RGB {
    public int r;
    public int g;
    public int b;

    public RGB(int r, int g, int b) {
        this.r = r;
        this.g = g;
        this.b = b;
    }
} </code></pre>
<p>위와 같은 mutable한 RGB 클래스가 있다고 가정을 합니다. 그리고, Person의 클래스는 다음과 같습니다.</p>
<pre><code class="language-java">public final class Person {
    private final String name;
    private final RGB rgb;

    public Person(String name, RGB rgb) {
        this.name = name;
        this.rgb = rgb;
    }

    public String getName() {
        return name;
    }

    public RGB getRGB() {
        return rgb;
    }
}</code></pre>
<p>모든 필드가 private final로 되어있고, 상태를 변경시키는 setter 메서드도 없으며 클래스 또한 상속이 불가능한 final 클래스인데 완벽한 immutable 일까요? 아닙니다. 다음 코드를 보겠습니다.</p>
<pre><code class="language-java">public static void main(String[] args) {
    RGB green = new RGB(0, 128, 0);

    Person person = new Person(&quot;choonsik&quot;, green);
    System.out.println(&quot;person.getRGB().g = &quot; + person.getRGB().g); //128

    green.g = 0;
    System.out.println(&quot;person.getRgb().g = &quot; + person.getRGB().g); //0
}</code></pre>
<p>Person 클래스에서는 분명히 RGB값을 변경하는 메소드가 없었음에도 불구하고 green 값이 바뀐 것을 확인할 수 있습니다. Person의 생성자를 보게 되면 RGB 라는 객체(레퍼런스)를 받게 됩니다. 따라서, RGB의 값이 변경되면 Person 또한 바뀐 RGB 객체를 바라보게 됩니다.</p>
<pre><code class="language-java">public Person(String name, RGB rgb) {
    this.name = name;
    this.rgb = new RGB(rgb.r, rgb.g, rgb.b);
}</code></pre>
<p>이를 해결하기 위해서, Person 객체를 생성할 때 파라미터값으로 넘어오는 RGB 객체를 사용해 다시 새로운 RGB 객체를 생성하고 person의 rgb 멤버 변수는 이 새로운 객체를 가리키게 됩니다. 즉, <code>green.g = 0;</code>을 했을 때에도 변경되지 않은 값(128)을 리턴해주게 됩니다. 그래도! 문제가 있습니다.</p>
<pre><code class="language-java">RGB myRGB = person.getRGB();
myRGB.g = 0;
System.out.println(&quot;person.getRgb().g = &quot; + person.getRGB().g); //0</code></pre>
<p>여기서는 Person 클래스의 <code>getRGB()</code> 메소드에서 문제가 되는데, Person이 가지고 있는 RGB 객체를 그대로 리턴해주었기 때문에 문제가 발생한겁니다. 다시 말해, 리턴할 때의 RGB 객체는 값을 변경할 수 있는 mutable 객체로 값을 person에서 <code>getRGB()</code>를 하더라도 실질적으로는 Mutable한 객체 RGB를 전달해주기 때문에 값이 변경된 것입니다. 이를 해결하기 위해서, <code>getRGB()</code>를 할 때 다시 한 번 새로운 RGB를 리턴하도록 변경해주어야 합니다. -&gt; <code>방어적 복사</code></p>
<pre><code class="language-java">public RGB getRGB() {
    return new RGB(rgb.r, rgb.g, rgb.b);
}</code></pre>
<blockquote>
<p><strong>방어적 복사</strong> : 객체를 복사해서 새로운 독립적인 객체를 생성하는 복사 기법. 기존의 객체와는 별개로 관리되어 기존 객체의 값이 변경되지 않는다.</p>
</blockquote>
<h3 id="collection은-어떻게-immutable-하도록-변경할-수-있을까">Collection은 어떻게 Immutable 하도록 변경할 수 있을까?</h3>
<p>RGB 클래스에서 <code>toString()</code>을 재정의해 주었습니다.</p>
<pre><code class="language-java">public class RGB {
    public int r;
    public int g;
    public int b;

    public RGB(int r, int g, int b) {
        this.r = r;
        this.g = g;
        this.b = b;
    }

    @Override
    public String toString() {
        return &quot;RGB{&quot; +
                &quot;r=&quot; + r +
                &quot;, g=&quot; + g +
                &quot;, b=&quot; + b +
                &#39;}&#39;;
    }
}</code></pre>
<p>Person 클래스의 RGB 필드 대신 <code>List&lt;RGB&gt; rgbs</code>를 새로 만들고, <code>copy()</code> 메소드가 추가되었습니다.</p>
<pre><code class="language-java">public final class Person {
    private final String name;
    private final List&lt;RGB&gt; rgbs;

    public Person(String name, List&lt;RGB&gt; rgbs) {
        this.name = name;
        this.rgbs = copy(rgbs);
    }

    public String getName() {
        return name;
    }

    public List&lt;RGB&gt; getRGBs() {
        return copy(rgbs);
    }

    private List&lt;RGB&gt; copy(List&lt;RGB&gt; rgbs) {
        List&lt;RGB&gt; cps = new ArrayList&lt;&gt;();
        rgbs.forEach(o -&gt; cps.add(new RGB(o.r, o.g, o.b)));
        return cps;
    }
}</code></pre>
<p>이 copy 메소드에서 List를 새로 생성하고, 이 새로 생성된 cps에 파라미터로 들어온 List rgbs의 각 객체를 꺼내 cps에 추가해주고, cps를 리턴해줍니다. 이렇게 함으로써 방어적 복사를 할 수가 있습니다.</p>
<pre><code class="language-java">public static void main(String[] args) {
    RGB red = new RGB(128, 0, 0);
    RGB green = new RGB(0, 128, 0);
    RGB blue = new RGB(0, 0, 128);

    List&lt;RGB&gt; rgbs = new ArrayList&lt;&gt;();
    rgbs.add(red);
    rgbs.add(green);
    rgbs.add(blue);

    Person person = new Person(&quot;choonsik&quot;, rgbs);
    List&lt;RGB&gt; rgbList = person.getRGBs();
    System.out.println(&quot;rgbList.toString() = &quot; + rgbList.toString()); 
    //[RGB{r=128, g=0, b=0}, RGB{r=0, g=128, b=0}, RGB{r=0, g=0, b=128}]
    }</code></pre>
<p>만약 <code>List&lt;RGB&gt; rgbs</code> 가 immutable이라면 copy() 메소드를 다음과 같이 변경하면 됩니다. 얕은 복사를 통해 List는 새로 만들지만 안에 있는 RGB 객체는 같은 레퍼런스를 복사하게 됩니다.</p>
<pre><code class="language-java">private List&lt;RGB&gt; copy(List&lt;RGB&gt; rgbs) {
    return new ArrayList&lt;&gt;(rgbs); //얕은 복사: 주소값 복사
}</code></pre>
<p>또는 생성자를 변경해줄 수도 있습니다.</p>
<pre><code class="language-java">public Person(String name, List&lt;RGB&gt; rgbs) {
    this.name = name;
    this.rgbs = Collections.unmodifiableList(rgbs);
}

public List&lt;RGB&gt; getRGBs() {
    return this.rgbs;
}</code></pre>
<hr>
<p><strong>참조</strong>
<a href="https://youtu.be/Bj9Mx_Lx3q4">Java Strings are Immutable - Here&#39;s What That Actually Means</a>
<a href="https://youtu.be/EOGOJdBy2Rg">불변 객체(immutable object)는 안정적인 개발에 아주 도움이 됩니다! 불변 객체의 개념과 장점, 구현 방법을 자바 예제를 통해 배워보아요~!</a> &lt;- 강추👍🏻
<a href="https://youtu.be/qbNMAJvv7qI">Immutable Classes and Objects in Java</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[가고싶어요, 인프콘 2023.]]></title>
            <link>https://velog.io/@choonsik_dev/%EA%B0%80%EA%B3%A0%EC%8B%B6%EC%96%B4%EC%9A%94-%EC%9D%B8%ED%94%84%EC%BD%98-2023</link>
            <guid>https://velog.io/@choonsik_dev/%EA%B0%80%EA%B3%A0%EC%8B%B6%EC%96%B4%EC%9A%94-%EC%9D%B8%ED%94%84%EC%BD%98-2023</guid>
            <pubDate>Thu, 20 Jul 2023 12:22:53 GMT</pubDate>
            <description><![CDATA[<p>[<a href="https://www.inflearn.com/infcon-2023/schedule/share?id=395405&amp;hash=haerbinhej%40c9cf66b2&amp;name=ggggg">인프콘 시간표 URL 공유</a>]
<img src="https://velog.velcdn.com/images/choonsik_dev/post/926b3b31-520f-45bc-9647-31d6b985d264/image.png" alt="개발자 춘식이의 인프콘 시간표"></p>
<h3 id="직장이-코엑스-근처인데-어찌된게-인프콘-2022도-춘식이-팬미팅도-다-코엑스에서-열렸는데-한-번도-당첨이-되지-않을까"><del>직장이 코엑스 근처</del>인데, 어찌된게 인프콘 2022도 춘식이 팬미팅도 다 코엑스에서 열렸는데 한 번도 당첨이 되지 않을까.</h3>
<p><img src="https://velog.velcdn.com/images/choonsik_dev/post/5b105547-4702-4bba-b9ab-3bc5162b4e36/image.png" alt="인프콘 탈락 메시지"></p>
<p>처음에는 인프콘 떨어졌다는 메일을 받았는데, 카카오톡으로 연속 두 번이나 확인 사살 받았습니다. 그러니까 전 <span style="color:red">총 <strong>3</strong>번의 탈락</span> 알람을 받은거죠!!! 🥲
그런데, 다행히도 <a href="https://inf.run/5EWo">인프콘 시간표를 공유</a>하면 총 25명에게 입장권을 준다고 합니다. 그 중 한명이 되고 싶네요. 인프콘 관계자 분은 소중한 입장권 하나를 저에게 주시면 인프콘에 선정된 동기와 함께 손잡고 알차게 세션 듣다가 가겠습니다!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[IEEE 754 - Standard for Binary Floating-Point Arithmetic]]></title>
            <link>https://velog.io/@choonsik_dev/IEEE-754-Standard-for-Binary-Floating-Point-Arithmetic</link>
            <guid>https://velog.io/@choonsik_dev/IEEE-754-Standard-for-Binary-Floating-Point-Arithmetic</guid>
            <pubDate>Sun, 16 Jul 2023 07:12:05 GMT</pubDate>
            <description><![CDATA[<p>컴퓨터는 이진법을 사용해서 숫자를 비롯한 모든 데이터를 이진 데이터로 표현하고 처리합니다. 하지만 이 방식으로는 모든 숫자를 정확하게 표현하지 못합니다. 따라서 거의 모든 프로그래밍 언어에서 <code>IEEE 754</code>라는 표준을 따르며, 자바의 float과 double도 이 표준을 따릅니다.
Float은 single-precision 32-bit(32비트 단일 정확도)이며, Double은 double-precision 64-bit(64비트 이중 정확도) 숫자 입니다.
IEEE 754는 <code>부호(sign)</code>부, <code>지수(exponent)</code>부, <code>가수(mantissa)</code>부로 나뉩니다.</p>
<ul>
<li><strong>부호부</strong>: 0은 양수를, 1은 음수를 나타냅니다.</li>
<li><strong>지수부</strong>: 소수점 이전 숫자열(정수)를 나타냅니다.</li>
<li><strong>가수부</strong>: 소수점 이후 숫자열(소수) 전체를 나타냅니다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/choonsik_dev/post/1bf37aa5-be1f-4b25-8e6c-f8022e913c0f/image.jpg" alt="single precision">
Float은 부호 1자리 + 지수 8자리 + 가수 23자리 총 32비트를 나타냅니다.
<img src="https://velog.velcdn.com/images/choonsik_dev/post/f2aa2ca6-e2ec-42a5-aa83-545b250942d7/image.jpg" alt="double precision">
Double은 부호 1자리 + 지수 11자리 + 가수 52자리 총 64비트는 나타냅니다.</p>
<p>예를 들어 263.3 이라는 숫자가 있다고 가정해봅시다.
<img src="https://velog.velcdn.com/images/choonsik_dev/post/0b59e787-28c0-4d2e-acf7-811eccb3b8b4/image.jpeg" alt="263.3">
정수 263을 이진수로 변환하려면 2로 계속 나누어 나머지를 위에서부터 위로 적어주면 되는데, 결과값은 100000111이 나옵니다. 
소수 0.7을 이진수로 변환하려면 2를 반복해서 곱해주면서 그 결과가 1이 나오거나 반복되는 숫자가 나올 때 까지 진행해주면 됩니다. 0.7은 0011이 반복해서 나왔으며 결과값은 010011001100110011... 이 나옵니다.</p>
<blockquote>
<p>263.3 = 100000111.010011001100110011....</p>
</blockquote>
<p>single precision 에서는 부호 1자리 + 지수 8자리 + 가수 23자리 총 32비트를 나타냅니다. </p>
<blockquote>
<p>x xxxxxxxx xxxxxxxxxxxxxxxxxxxxxxx</p>
</blockquote>
<p>263.3은 양수이므로 부호는 0이 됩니다.</p>
<blockquote>
<p>0 xxxxxxxx xxxxxxxxxxxxxxxxxxxxxxx</p>
</blockquote>
<p>정규화를 하기 위해 소수점을 왼쪽으로 이동시켜 정수 부분이 1이 되도록 만들어주어야 합니다. </p>
<blockquote>
<p>1.00000111010011001100110011.... X 2⁸</p>
</blockquote>
<p>소수점을 왼쪽으로 8번 이동시켰으므로 지수부는 8이 됩니다. 지수부 8을 표현하기 위해 bias 127(double에서는 1023)을 더해줍니다. 지수부는 8bit로 256개의 수를 표현할 수 있는데, 음수와 양수 모두를 나타내기 위해 일정한 상수(127)을 더해주어 표현합니다.
8 + 127 = 135로, 135를 다시 이진수로 변환하면 10000111이 나옵니다.</p>
<blockquote>
<p>0 10000111 xxxxxxxxxxxxxxxxxxxxxxx</p>
</blockquote>
<p>나머지 가수부는 위에서 소수점 뒤 00000111010011001100110011.... 을 23비트까지 잘라줍니다.</p>
<blockquote>
<p>0 10000111 00000111010011001100110
0100 0011 1000 0011 1010 0110 0110 0110 -&gt; 32bit</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/choonsik_dev/post/07180122-cbdc-410a-a469-7ab6f0e785c7/image.png" alt="IEEE 754 Converter를 이용한 263.3"></p>
<p>위 사진은 <a href="https://www.h-schmidt.net/FloatConverter/IEEE754.html">IEEE 754 Converter</a> 사이트를 통해 결과값을 얻은 것으로 동일한 결과가 나왔음을 알 수 있습니다. 실제로 입력한 값은 263.3이지만 실제로 float에 저장된 값은 263.29998779296875 이라고 합니다. 따라서 왜 중요한 계산에서는 float이나 double을 지양해야 하는지 알 수 있었습니다.</p>
<hr>
<p><strong>참고</strong>
<a href="https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-2.html#jvms-2.8.1">Chapter 2. The Structure of the Java Virtual Machine</a>
<a href="https://youtu.be/ZQDsWySjY6g">부동소수점 (+ 실수계산 오차가 생기는 이유)</a>
<a href="https://www.geeksforgeeks.org/ieee-standard-754-floating-point-numbers/">IEEE Standard 754 Floating Point Numbers - GeeksforGeeks</a>
<a href="https://youtu.be/8afbTaA-gOQ">Decimal to IEEE 754 Floating Point Representation</a>
<a href="https://www.h-schmidt.net/FloatConverter/IEEE754.html">IEEE-754 Floating Point Converter</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[JVM Memory Model]]></title>
            <link>https://velog.io/@choonsik_dev/JVM-Memory-Model</link>
            <guid>https://velog.io/@choonsik_dev/JVM-Memory-Model</guid>
            <pubDate>Wed, 12 Jul 2023 16:19:42 GMT</pubDate>
            <description><![CDATA[<p>자바 메모리 모델을 알기 위해서는 자바의 JVM 아키텍처에 대해서 먼저 알 필요가 있습니다. 모든 프로그래밍 언어는 그 언어를 실행하기 위한 컴포넌트, 라이브러리, API 등이 있어야 합니다. 자바는 <code>JRE</code>와 <code>JDK</code>가 이에 해당합니다.</p>
<ul>
<li><strong>JRE(Java Runtime Environment)</strong>: JRE는 자바 애플리케이션이 구동되기 위한 최소한의 환경입니다. JVM과 개발 도구들이 포함되어 있습니다.<ul>
<li><strong>JVM(Java Virtual Machine)</strong>: OS에 독립적으로 구동될 수 있도록 JVM 위에 애플리케이션이 돌아갑니다.</li>
</ul>
</li>
<li><strong>JDK(Java Development Kit)</strong>: 개발자는 개발하기 위해 JDK를 설치해야 합니다. JDK는 완전한 개발 환경을 만들어 주며, JRE와 개발 도구를 포함합니다.</li>
</ul>
<hr>
<h1 id="jvm-아키텍처">JVM 아키텍처</h1>
<p><img src="https://velog.velcdn.com/images/choonsik_dev/post/062926eb-9c74-4777-a2db-b5a3ce6e7676/image.png" alt="JVM Architecture"></p>
<h2 id="1-class-loader-subsystem">1. Class Loader Subsystem</h2>
<p>JVM은 RAM 안에서 상주합니다. 자바를 실행하게 되면, <code>Class Loader Subsystem</code>이 실행되면서 클래스 파일(.class)들을 RAM으로 가져오는데, 이를 <code>동적 클래스 로딩</code>이라고 합니다. Class Loader Subsystem을 통해 런타임 시 처음으로 로드될 때 동적으로 클래스 파일을 Loading, Linking 및 Initialization 합니다.</p>
<ul>
<li><strong>Loading</strong>: 특정 이름을 가진 클래스 또는 인터페이스 바이너리 데이터를 찾고 JVM method area에 저장합니다.</li>
<li><strong>Linking</strong>: 클래스 또는 인터페이스를 가져와 실행될 수 있도록 런타입 상태의 JVM에 결합해 주는 프로세스입니다.</li>
<li><strong>Initialization</strong>: 컴파일러에 의해 제공되는 클래스 및 인터페이스 초기화 메서드 <code>&lt;clinit&gt;</code> 실행을 해 초기화합니다.</li>
</ul>
<hr>
<h2 id="2-runtime-data-area">2. Runtime Data Area</h2>
<p><img src="https://velog.velcdn.com/images/choonsik_dev/post/f4a4a174-738b-4f59-aa7d-ea95a5ba082e/image.png" alt="Runtime Data Area"></p>
<p><code>Runtime Data Area</code>는 <span style="color:red">JVM이 OS에서 실행될 때 할당받는 메모리 영역</span>입니다. </p>
<p><code>Class Loader Subsystem</code>은 클래스 파일을 읽는 것 외에도 바이너리 데이터를 생성하고 <span style="background-color:#F7E600">각 클래스에 대한 정보를 <code>Method Area</code>에 적재합니다. 모든 클래스 파일이 로드가 완료되면, <code>heap</code> 메모리에 클래스 파일을 나타내는 하나의 클래스 객체를 만듭니다.</span> 이 클래스 객체는 나중에 코드에서 클래스 정보(클래스명, 부모명, 메서드, 변수정보, 정적 변수 등)을 읽는데 사용됩니다.</p>
<h3 id="1-method-areastatic-area-class-area">1) Method Area(=Static Area, Class Area)</h3>
<p>Method Area는 JVM이 시작될 때 생성되며, JVM 한 개에 하나의 Method Area만 생깁니다. <span style="color:red">모든 JVM thread들은 같은 Method Area를 공유</span>합니다. Method Area는 논리적으로 <code>Heap</code>에 포함되며, <span style="background-color:#F7E600">Java 7 이전에는 Heap의 <code>PermGen</code>이라는 영역에 속했지만, Java 8 이후로는 <code>Metaspace</code>라는 OS가 관리하는 영역</span>으로 옮겨졌습니다. Method Area는 static 변수, 런타임 상수 풀, 필드 데이터, 메소드 데이터, 클래스로더 레퍼런스 등 <span style="color:red">클래스 레벨의 데이터를 저장</span>합니다. </p>
<h3 id="2-heap">2) Heap</h3>
<p>Heap 또한 JVM 당 하나의 Heap Area가 생기며, <span style="color:red">모든 JVM thread들은 같은 Heap을 공유</span>합니다. <code>new</code> 연산자로 생성된 <span style="color:red">모든 객체</span>와 관련된 인스턴스 변수와 배열, 문자열에 대한 정보를 가진 String Pool 등이 Heap 영역에 저장되며, <span style="color:red">GC의 대상</span>이 됩니다. Method Area와 Heap은 멀티 스레드에서 공유되는 메모리로, Method Area와 Heap Area에 저장되는 데이터들은 thread safe 하지 않습니다. </p>
<p><img src="https://velog.velcdn.com/images/choonsik_dev/post/0809b2b8-1f5f-44f6-85a1-531fda836a7e/image.png" alt="Java7HotSpotJVM Java8HotSpotJVM"></p>
<p>Java 7의 <code>Permanent</code> 영역은 보통 Class의 메타 정보나 method의 메타 정보, Static 변수와 상수 정보들이 저장되는 공간으로 흔히 <code>메타데이터 저장 영역</code>이라고도 합니다. Java 8부터는 Native 영역으로 이동하여 <code>Metaspace</code> 영역으로 변경되었습니다. 기존 permanent 영역에 존재하던 <span style="background-color:#F7E600">Static Object 변수, 상수는 Heap 영역으로 이동</span>되었습니다.
Heap 영역은 JVM에 의해 관리되는 영역이며, Native 영역은 OS 레벨에서 관리하는 영역으로 구분됩니다. Metaspace가 Native 메모리를 이용함으로써 개발자는 영역 확보의 상한을 크게 신경 쓸 필요가 없어져서 제거되었습니다.</p>
<h3 id="3-stack">3) Stack</h3>
<p><img src="https://velog.velcdn.com/images/choonsik_dev/post/da782328-3dd4-4072-9b70-7f495bead265/image.png" alt="StackFrame"></p>
<p><code>Stack</code>은 공유 자원이 아니므로 thread safe하고 각 thread가 시작되면, 메서드 호출을 저장하기 위한 별도의 런타임 스택이 각각 생성됩니다. <span style="background-color:#F7E600">모든 메소드를 호출할 때마다 하나의 <code>Frame</code>이 생성되어 런타임 스택의 맨 위에 추가(push)되며 이러한 항목을 <code>Stack Frame</code></span>이라고 합니다. </p>
<p>각 stack frame은 실행 중인 메서드가 속한 클래스의 로컬 변수 배열, 피연산자 스택(메소드 내 연산을 위해 바이트 코드 명령문들이 있는 공간), 런타임 상수 풀에 대한 참조가 있습니다. 로컬 변수 배열과 피연산자 스택의 크기는 컴파일하는 동안 결정됩니다. 따라서 스택 프레임의 사이즈는 메소드에 의해 크기가 고정됩니다.</p>
<p>메소드가 정상적으로 실행 후 종료되거나 에러가 발생했을 때 해당 frame은 stack으로부터 제거(pop)됩니다. 만약 에러가 발생했다면, 각 줄의 stack trace(pringStackTrace() 메소드명의 유래가 여기서 나오네요.)를 한 스택 프레임에 나타냅니다. </p>
<p>stack의 사이즈는 동적이거나 고정될 수 있습니다. 만약 스레드가 큰 스택을 요구한다면 <code>StackOverflowError</code>가 발생하고, 만약 새로운 frame을 생성해야 하는데 메모리가 충분하지 않다면 <code>OutOfMemoryError</code>가 발생합니다.</p>
<h3 id="4-pc-registers">4) PC Registers</h3>
<p>각 JVM 스레드가 시작되면 <code>PC Register</code>는 현재 실행 중인 명령의 주소(Method area의 메모리 주소)를 기억하기 위해 PC(Program Counter) Register가 생성됩니다. 실행이 끝나면, PC register는 다음 명령의 주소로 업데이트됩니다.</p>
<h3 id="5-native-method-stack">5) Native Method Stack</h3>
<p>Java thread에 대한 모든 상태를 준비한 후 JNI를 통해 호출되는 네이티브 메소드 정보(C/C++)를 저장하기 위해 별도의 기본 스택이 생성됩니다.</p>
<p>네이티브 스레드가 한 번 생성되고 초기화되면, 자바 스레드의 <code>run()</code> 메소드를 호출합니다. run() 메소드가 리턴될 때 uncaught exeption을 포착하면 네이티브 스레드는 JVM을 종료해야 하는지 여부를 확인합니다. 스레드가 종료되면 모든 네이티브 및 자바 스레드의 리소스들은 해제됩니다. 네이티브 스레드는 자바 스레드가 종료되면 회수됩니다. 따라서 운영 체제는 모든 스레드를 스케줄링할 수 있고 사용할 수 있는 CPU로 참조합니다.</p>
<hr>
<h2 id="3-execution-engine">3. Execution Engine</h2>
<p>bytecode의 실제 실행은 <code>Execution Engine</code>에서 이루어집니다. Execution Engine은 Runtime data areas에 할당된 데이터를 읽어 bytecode의 명령을 한 줄씩 실행합니다.</p>
<h3 id="1-interpreter">1) Interpreter</h3>
<p>Interpreter는 바이트코드를 해석하고 명령을 하나씩 실행합니다. 하나의 바이트코드 라인을 빠르게 해석할 수는 있지만, 그 결과를 실행하는 작업은 느립니다. </p>
<h3 id="2-just-in-timejit-compiler">2) Just-In-Time(JIT) Compiler</h3>
<p>JIT는 모든 바이트코드를 네이티브코드(machine code)로 컴파일하고, 반복되는 메소드 호출에 대해 네이티브 코드를 직접 제공하여 향상된 속도를 제공합니다. 네이티브 코드는 캐시에 저장되므로 컴파일된 코드를 더 빨리 실행할 수 있습니다.</p>
<h3 id="3-garbage-collector-gc">3) Garbage Collector (GC)</h3>
<p>객체가 더이상 참조되지 않으면 GC는 해당 객체를 제거하고 사용되지 않은 메모리를 회수합니다.</p>
<hr>
<p>  <strong>참고</strong>
<a href="https://medium.com/platform-engineer/understanding-jvm-architecture-22c0ddf09722">Understanding JVM Architecture</a>
<a href="https://tecoble.techcourse.co.kr/post/2021-08-09-jvm-memory/">JVM에 관하여 - Part 3, Run-Time Data Area</a>
<a href="https://jithub.tistory.com/40">자바 메모리 구조(Runtime Data Area)</a>
<a href="https://jithub.tistory.com/296">Java Heap (with GC)</a>
<a href="https://johngrib.github.io/wiki/java8-why-permgen-removed/">JDK 8에서 Perm 영역은 왜 삭제됐을까</a></p>
<blockquote>
<p>지속적으로 수정해나갈 예정입니다.
2023-07-13 v1.0</p>
</blockquote>
]]></description>
        </item>
    </channel>
</rss>