<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>잊혀질 생각, 기록의 가치</title>
    <link>https://goldfishhead.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Thu, 21 May 2026 05:57:29 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>머래그로프</managingEditor>
    <image>
      <title>잊혀질 생각, 기록의 가치</title>
      <url>https://tistory1.daumcdn.net/tistory/3057791/attach/37248324ffa44bb99acd1a2681544eac</url>
      <link>https://goldfishhead.tistory.com</link>
    </image>
    <item>
      <title>[Java] 클래스 (live-study 5주차)</title>
      <link>https://goldfishhead.tistory.com/113</link>
      <description>&lt;h5&gt;목표&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;자바의 Class에 대해 학습하세요.&lt;/li&gt;
&lt;/ul&gt;
&lt;h5&gt;학습할 것 (필수)&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;클래스 정의하는 방법&lt;/li&gt;
&lt;li&gt;객체 만드는 방법 (new 키워드 이해하기)&lt;/li&gt;
&lt;li&gt;메소드 정의하는 방법&lt;/li&gt;
&lt;li&gt;생성자 정의하는 방법&lt;/li&gt;
&lt;li&gt;this 키워드 이해하기&lt;/li&gt;
&lt;/ul&gt;
&lt;h5&gt;과제 (Optional)&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;int 값을 가지고 있는 이진 트리를 나타내는 Node 라는 클래스를 정의하세요.&lt;/li&gt;
&lt;li&gt;int value, Node left, right를 가지고 있어야 합니다.&lt;/li&gt;
&lt;li&gt;BinrayTree라는 클래스를 정의하고 주어진 노드를 기준으로 출력하는 bfs(Node node)와 dfs(Node node) 메소드를 구현하세요.&lt;/li&gt;
&lt;li&gt;DFS는 왼쪽, 루트, 오른쪽 순으로 순회하세요.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;클래스 정의하는 방법&lt;/h3&gt;
&lt;p&gt;자바에서 클래스(class)란 객체를 정의하는 틀 또는 설계도라고 합니다. 자바에서는 이러한 설계도인 클래스(class)를 가지고 여러 객체를 생성하여 사용하게 됩니다. 클래스는 객체의 상태를 나타내느 필드(field)와 객체의 행위를 나태는 메서드(method)로 구성됩니다.&lt;/p&gt;
&lt;h4&gt;필드&lt;/h4&gt;
&lt;p&gt;객체지향 개념에서 속성에 해당하는 것으로 멤버 변수라고도 합니다. 구조는 다음과 같습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;private String name; 한정자 / 자료형 / 변수명&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;메서드&lt;/h4&gt;
&lt;p&gt;객체지향 개념에서 행위에 해당하는 것으로, 클래스를 사용하여 실행하는 동작입니다. 구조는 다음과 같습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;// 한정자 / 리턴값 / 함수명(매개변수 ,,,)
public int add(int a, int b) { 
  return a + b;
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;객체 만드는 방법 (new 키워드 이해하기)&lt;/h3&gt;
&lt;p&gt;클래스를 이용하여 객체를 생성 하게 되는데, 객체를 new 키워드로 할당하게 되면 인스턴스가 생성되어 집니다. 인스턴스가 생성될 때에는 메모리에 올라간 상태입니다.&lt;/p&gt;
&lt;h5&gt;객체(인스턴스) 생성 방법&lt;/h5&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;// 클래스 / 참조변수 / 생성자
Student student = new Student(); &lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;생성자는 클래스나 인스턴스를 생성할 때 호출하는 특수한 목적의 메서드입니다. 일반적으로 변수를 초기화하거나 필요한 다른 개체를 생성하는 작업을 처리합니다. new 키워드를 사용하여 객체를 생성하는 시점에 호출되며, 클래스는 하나 이상의 생성자를 가질 수 있습니다.&lt;/p&gt;
&lt;h5&gt;생성자 예제&lt;/h5&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;public class People {
  private String name;
  private int age;

  public Student() { // 기본생성자
    name = &amp;quot;이름없음&amp;quot;;
    age = 0;
  }

  public People(String name, int age) { // 파라미터가 있는 생성자
    this.name = name;
    this.age = age;
    }
  }

public class Main {
  public static void main(String[] args) {
    Student student = new Student();
    System.out.println(&amp;quot;이름 : &amp;quot; + student.getName()); // 이름없음
    System.out.println(&amp;quot;나이 : &amp;quot; + student.getAge());    // 0

    Student a = new Student(&amp;quot;둘리&amp;quot;, 18);
    System.out.println(&amp;quot;이름 : &amp;quot; + a.getName());          // 둘리 
    System.out.println(&amp;quot;나이 : &amp;quot; + a.getAge());             // 18
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;메소드 정의하는 방법 (메서드 오버로딩, 오버라이딩)&lt;/h3&gt;
&lt;h4&gt;메서드 오버로딩&lt;/h4&gt;
&lt;p&gt;메서드 이름을 동일하지만 파라미터가 다른 여러 메서드를 만드는 것을 말합니다.&lt;/p&gt;
&lt;h5&gt;메서드 오버로딩 예제&lt;/h5&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;public class Calculator {
  public int sum(int num1, int num2) {
    return num1 + num2;
  }

  public int sum(int num1, int num2, num3) {
    return num1 + num2 + num3;
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;메서드 오버라이딩&lt;/h4&gt;
&lt;p&gt;메서드 오버라이딩은 상위 클래스에서 정의한 메서드를 서브 클래스에서 재정의하는 것을 말합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;class Gun {
  public void shot() {
    System.out.println(&amp;quot;탕&amp;quot;);
  }
}

class Ak47 extends Gun {
  @Override
  public void shot() {
    System.out.println(&amp;quot;두두두두두두두두둗두두두두둗ㄷ&amp;quot;);
  }
}

public class Test {
  public static void main(String[] args) {
    Gun gun = new Gun();
    Ak47 ak47 = new Ak47();

    gun.shot(); // 탕
    ak47.shot(); // 두두두두두두두두둗두두두두둗ㄷ
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;this 키워드&lt;/h3&gt;
&lt;p&gt;자바에서(Java) this는 &amp;#39;객체, 자기 자신&amp;#39;을 나타냅니다.&lt;/p&gt;
&lt;h4&gt;특징&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;this는 생성자와도 같이 사용할 수 있습니다. 자기 자신의 생성자를 호출할 때 사용됩니다.&lt;/li&gt;
&lt;li&gt;this는 객체 자신을 가리키는 키워드입니다. 이를 이용하여 자기 자신의 레퍼런스를 반환할 수 있습니다.&lt;/li&gt;
&lt;li&gt;this 키워드는 실행 중에 즉, 동적으로 자기 자신 객체에 대한 레퍼런스라고 정의됩니다. 따라서 정적(static) 메서드에서는 사용할 수 없습니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;과제 (Optional)&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;int 값을 가지고 있는 이진 트리를 나타내는 Node 라는 클래스를 정의하세요.&lt;/li&gt;
&lt;li&gt;int value, Node left, right를 가지고 있어야 합니다.&lt;/li&gt;
&lt;li&gt;BinrayTree라는 클래스를 정의하고 주어진 노드를 기준으로 출력하는 bfs(Node node)와 dfs(Node node) 메소드를 구현하세요.&lt;/li&gt;
&lt;li&gt;DFS는 왼쪽, 루트, 오른쪽 순으로 순회하세요.&lt;/li&gt;
&lt;/ul&gt;
&lt;h5&gt;Node.java&lt;/h5&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;public class Node {

    private int value;
    private Node left;
    private Node right;

    public Node(int value) {
        this.value = value;
        this.left = null;
        this.right = null;
    }

    public Node(int value, Node left, Node right) {
        this.value = value;
        this.left = left;
        this.right = right;
    }

    public int getValue() {
        return value;
    }

    public Node getLeft() {
        return left;
    }

    public Node getRight() {
        return right;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;BinrayTree.java&lt;/h5&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;public class BinrayTree {

    private Node root;

    public BinrayTree(Node root) {
        this.root = root;
    }

    public void bfs() {
        Queue&amp;lt;Node&amp;gt; q = new LinkedList&amp;lt;&amp;gt;();
        q.add(root);

        while(!q.isEmpty()) {
            int size = q.size();
            for (int i = 0; i &amp;lt; size; i++) {
                Node cur = q.poll();
                System.out.print(cur.getValue() + &amp;quot; &amp;quot;);
                if (cur.getLeft() != null) q.offer(cur.getLeft());
                if (cur.getRight() != null) q.offer(cur.getRight());
            }
        }
    }

    // 왼쪽, 루트, 오른쪽 순
    public void dfs() {
        dfs(root);
    }

    private void dfs(Node node) {
        if (node == null) {
            return;
        }

        dfs(node.getLeft());
        System.out.print(node.getValue() + &amp;quot; &amp;quot;);
        dfs(node.getRight());
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;BinrayTreeTest.java&lt;/h5&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;class BinrayTreeTest {

    BinrayTree binrayTree;

    @BeforeEach
    void before() {
        Node node4 = new Node(4);
        Node node5 = new Node(5);
        Node node2 = new Node(2, node4, node5);
        Node node3 = new Node(3);
        Node root = new Node(1, node2, node3);
        binrayTree = new BinrayTree(root);
        /*
             1
            / \
           2   3
          / \
         4   5
         */
    }

    @Test
    void test() {
        System.out.print(&amp;quot;BFS : &amp;quot;);
        binrayTree.bfs();
        System.out.println();
        System.out.print(&amp;quot;DFS : &amp;quot;);
        binrayTree.dfs();

        // BFS : 1 2 3 4 5
        // DFS : 4 2 5 1 3
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;참조&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.javatpoint.com/object-and-class-in-java&quot;&gt;https://www.javatpoint.com/object-and-class-in-java&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://library1008.tistory.com/4&quot;&gt;https://library1008.tistory.com/4&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://madplay.github.io/post/what-is-the-meaning-of-this-in-java&quot;&gt;https://madplay.github.io/post/what-is-the-meaning-of-this-in-java&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://yhmane.tistory.com/119&quot;&gt;https://yhmane.tistory.com/119&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Java</category>
      <category>Java</category>
      <category>live-study</category>
      <author>머래그로프</author>
      <guid isPermaLink="true">https://goldfishhead.tistory.com/113</guid>
      <comments>https://goldfishhead.tistory.com/113#entry113comment</comments>
      <pubDate>Fri, 18 Dec 2020 23:29:52 +0900</pubDate>
    </item>
    <item>
      <title>나의 이야기, 그리고 취준 기록...  </title>
      <link>https://goldfishhead.tistory.com/112</link>
      <description>&lt;h2&gt;학부 시절 (2014~)&lt;/h2&gt;
&lt;p&gt;내 전공은 전자공학과이다. 하지만 나는 내 전공에 전혀 흥미가 없었다. 전공 시험공부를 하면서 이런 걸 배워서 대체 어디에 쓰나.. 하는 생각이 내 머릿속을 지배했기 때문에 공부가 너무 재미없었고 하기도 싫었다. (네 다음 핑계~) 그렇게 3학년 1학기를 마치고 맞이한 여름 방학에 앞으로 나의 미래에 대해 진지하게 걱정이 되었다. 만약 운이 좋게 내 전공 쪽으로 취업한다고 하더라도 전혀 행복하지 않을 것 같았다. 그렇다면 내가 좋아하는 건 뭘까.. 내가 잘하는 게 있긴 한가.. 없네.. ㅠㅠ.. 그럼 내가 앞으로 뭘 하면 잘할 수 있을까.. 고민이 시작되었다.&lt;/p&gt;
&lt;p&gt;나는 어렸을 때부터 무언가 만드는 걸 좋아했다. 예를 들면 종이접기, 레고, 그림 그리기 같은 것들을 좋아했다. 특히 그림 그리기를 굉장히 좋아했는데, 당시에 그렸던 만화들과 캐릭터 그림이 담긴 공책들이 지금도 책장에 꽂혀있다. 그림을 통해 내 상상 속 무언가를 표현하는 것이 재미있었고 근처 지인들이 내가 그린 만화를 보며 즐거워하는 모습을 보는 게 정말 행복했었다.&lt;/p&gt;
&lt;p&gt;피아노와 기타를 치는 것도 굉장히 좋아했다. 특히 피아노는 재미있어서 학창 시절 내내 시간이 날 때마다 꾸준히 쳤었다.&lt;/p&gt;
&lt;p&gt;대학 시절에 우연히 작곡 프로그램을 노트북에 설치하게 되었는데... 프로그램을 통해 다양한 소리를 조합해서 멜로디나 비트를 만들어 나가는 과정이 너무 재미있었다. 마치 예전에 만화를 그릴 때와 비슷한 느낌을 받았다. 그렇게 뚱땅뚱땅 음악을 만들며 작곡에 크게 관심이 생겼고 막연하게 작곡가가 되고 싶은 마음이 조금씩 커져갔다. 무언가 하고 싶은 것이 생긴 건 이때가 처음이었다.&lt;/p&gt;
&lt;p&gt;그렇게 심심할 때마다 멜로디나 비트를 하나씩 만들던 중, 유튜브에서 하나의 영상을 보게 되었다. 유튜브 알고리즘의 뜬금없는 추천으로 나타난 생활코딩의 WEB 수업 영상이었다. 영상을 하나 보고 나니 이어지는 다음 영상이 있었고 그 이어지는 영상 다음에는 또 다른 이어지는 영상이 있었다. 정말 시간 가는 줄 모르고 봤다. 그동안 브라우저를 통해 별 생각 없이 사용하던 인터넷 관련 서비스들의 원리와 흐름을 알고 나니 뒤통수를 정말 세게 맞은 기분이었다. 왜 여태까지 이런 궁금증을 가져보지 않았던 걸까? 영상을 보면 볼수록 궁금증들이 꼬리에 꼬리를 물며 생겼다. 영상 속 코드 내용을 따라치는 내 모습이 뭔가 멋있어 보였다. 간단한 html과 css 문법으로 웹 화면을 만들면서 나는 그림 그리기나 작곡을 할 때 느꼈던 기분 좋은 희열을 느낄 수 있었다.&lt;/p&gt;
&lt;p&gt;나는 이렇게 개발에 관심을 가지게 되었다. 여름방학을 생활코딩과 보내다 보니 컴퓨터 공학 수업을 듣고 싶어졌다. 전자과 수업이 너무 재미없었고... 딱히 듣고 싶은 교양도 없었기 때문에... 앞으로 전자과 수업은 필수 전공 수업만 듣고 나머지는 학점은 모두 컴퓨터 공학 수업을 듣기로 했다.&lt;/p&gt;
&lt;h3&gt;3학년 2학기(2018) ~ 4학년 1학기(2019)&lt;/h3&gt;
&lt;p&gt;컴공 수업들은 역시나 재미있었다. 특히 과제가 주어지는 실습 위주의 수업이 굉장히 재미있었다. 다른 분야와는 다르게 컴퓨터 공학 분야는 새로운 것을 배우는 것에 재미있었고, 더 많은 것들을 배워가면서 내 자신을 발전시키는 과정이 즐겁게 느껴졌다.&lt;/p&gt;
&lt;p&gt;4학년 1학기 때는 computer science 과목들을 몰아서 들었고 공부하는 김에 정보처리기사도 1+1 느낌으로 땄다. 운영체제 과목이 굉장히 어려웠던 기억이 난다.&lt;/p&gt;
&lt;p&gt;학교 내 개발 동아리에 들어가서 좋은 사람들을 만났다. 처음 사귄 컴퓨터 공학과 사람들이었다. 그중 한 명은 동아리 회장 형이었다. 항상 열심히 공부했던 회장 형은 내 개인적인 학습에 동기 부여가 많이 되어주었다.&lt;/p&gt;
&lt;h3&gt;2019.08 ~ 09&lt;/h3&gt;
&lt;p&gt;부스트코스 웹 과정을 진행했다. 자바 기반의 스프링 프레임워크를 배울 수 있었다. 매주 주어지는 미션을 진행하는 방식인데 코드 리뷰를 통과해야 다음 미션을 진행할 수가 있다. 부스트코스는 당시에 수료 후 스타트업이랑 연계해주는 프로그램을 진행했었다. 나는 사실 이 연계 프로그램을 하기 위해서 부스트코스를 시작했다. 매주 진행했던 내용에 대한 회고는 블로그에 정리해 놓았다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://goldfishhead.tistory.com/category/%EB%B6%80%EC%8A%A4%ED%8A%B8%20%EC%BD%94%EC%8A%A4&quot;&gt;부스트 코스 주차별 회고 목록&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;2019.09&lt;/h3&gt;
&lt;p&gt;동아리원들과 공모전에 참가하여 교내 대회에서 상을 탔다. 그 때 당시의 짜릿한 기분은 아직도 생생하다. 개발도 개발이지만 그냥 같이 뭉쳐다니던게 참 재미있었다.&lt;/p&gt;
&lt;h3&gt;2019.10&lt;/h3&gt;
&lt;p&gt;운이 좋게도 부스트코스에서 한 스타트업 기업과 연락이 닿아 10월부터 인턴으로 웹 개발 업무를 시작하게 되었다.&lt;/p&gt;
&lt;p&gt;실제 사용자들이 사용하는 서비스의 코드를 보는 것은 처음이라 굉장히 설렜다. 코드를 보고 나서 나의 설렘은 걱정으로 바뀌었다. 여태까지 보지 못한 큰 규모의 코드였다. 나는 보잘것없는 나에게 기회를 주신 대표님을 실망시켜드리고 싶지 않았다. 하루종일 코드를 보며 모르는 것들을 공부하며 이해하기 위해 노력했다.&lt;/p&gt;
&lt;p&gt;그렇게 처음으로 실제 서비스에 내가 구현한 기능이 올라가게 되었고 내가 구현한 기능을 사람들이 사용하는 걸 보면서 말로 표현할 수 없는 희열이 느껴졌다. 개발을 업으로 삼아도 되겠다는 확신이 든 순간이었다.&lt;/p&gt;
&lt;h3&gt;2019.11&lt;/h3&gt;
&lt;p&gt;11월에는 네이버 캠퍼스 핵데이에 합격하여 대규모 sns 구독 서비스라는 주제로 참여했다.&lt;/p&gt;
&lt;p&gt;당시의 나는 제대로 아는 것이 거의 없었고 근무 중이었던 스타트업에서 주어진 업무를 하는 것만으로도 벅찼기 때문에 제대로 된 준비를 하지 못했다. 역시나 아쉽게도 네이버 인턴 연계는 되지 않았다. 나는 스타트업에서 사용하는 코드가 정답이라는 안일한 생각을 하고 있었다. 멘토님으로부터 팩폭에 가까운 피드백을 받았다. 개발은 기능 구현이 다인 줄 알았는데 대규모 서비스에 대해 알게 되었던 건 큰 충격이었다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://goldfishhead.tistory.com/21&quot;&gt;2019 네이버 핵데이 윈터 후기 글&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;2020.02&lt;/h3&gt;
&lt;p&gt;코로나 때문에 회사 상황이 많이 나빠지면서 인턴에서 정규직으로 전환되지 않고 안타깝게도 작별 인사를 해야 했다.&lt;/p&gt;
&lt;p&gt;구직 활동을 시작했으나 서류에서 줄줄이 탈락했다. 서류에 합격한다 해도 코딩테스트에서 항상 멘붕이 왔다. 나름 현업이라고 생각했던 곳에서 요구사항들을 구현하며 많이 성장했다고 생각했으나 기본도 안되어있는 내 모습을 보고 자존감이 굉장히 낮아졌다. 당시의 나는 기능 구현을 꾸역꾸역 하긴 했으나 성능에 대해서는 고민하지 않았었고 좋은 코드에 대한 고민 또한 하지 않았었다. 사실은 몰랐다고 하는 게 맞는 거 같다. 부족한 부분을 빠르게 채워야겠다는 생각이 들었다.&lt;/p&gt;
&lt;p&gt;당시의 나는 그때그때 궁금한 거나 필요한 정보를 인터넷 검색을 통해서만 찾으며 공부를 했었다. 블로그 글보다는 책을 통해서 깊은 지식을 쌓으라는 핵데이 멘토님의 조언이 생각나서 이때부터 책을 하나씩 구매하기 시작했다. 처음부터 너무 어려운 책들을 구매하는 바람에 책 읽기에 흥미를 잃을 뻔했다 ㅎㅎ.. 내가 이해할 수 있는 수준의 적당한 책을 고르는 것도 중요한 것 같다.&lt;/p&gt;
&lt;h2&gt;취준 기록&lt;/h2&gt;
&lt;h3&gt;2020 상반기&lt;/h3&gt;
&lt;p&gt;이름 있는 기업의 IT 직군이라면 막무가내로 무조건 지원했다. 서류에서 정말 많이 떨어졌다. 여태까지 해왔던 경험들을 잘 풀어서 나를 잘 어필했다고 생각했는데 서류에서부터 주루룩 떨어지니 너무 속상했다. 나는 그 이유를 나의 실력을 어필할 수 있는 개인 프로젝트가 없었기 때문이라고 생각했다. 그래서 개인 프로젝트를 하나 만들기 시작했다.&lt;/p&gt;
&lt;p&gt;당시에 만들고 싶었던 아이디어를 바탕으로 개인 프로젝트를 진행했다. 나는 이 프로젝트를 진행하면서 백엔드 부분보다 프론트엔드 부분이 더 재미있게 느껴졌다. 사실 취업은 서버 개발자로 하려고 마음을 먹었었으나 개인 프로젝트를 하면서 백엔드와 프론트엔드 중 무엇을 선택하여 집중할지에 대한 고민이 시작되었다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/sky7th/dev-timeline&quot;&gt;개인 프로젝트 github&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;카카오 인턴십&lt;/h4&gt;
&lt;p&gt;코딩 테스트(탈)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;알고리즘 문제 풀이에 크게 흥미를 느끼지 못해서 코딩 테스트에 대한 준비를 열심히 하진 않았던 것 같다. 나는 어느 정도만 하면 된다고 생각했고 그 어느 정도라는 아슬아슬한 선을 넘지 않았다. 사실싱 코딩 테스트를 통과해야 본인을 어필할 수 있는 면접의 기회가 주어지는 건데... 이 아슬아슬한 선을 확실하게 넘도록 준비를 했어야 했다. 그렇지 못했던게 정말 후회된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;우아한 테크 캠프(프론트엔드)&lt;/h4&gt;
&lt;p&gt;서류(합) - 1차 코딩 테스트(합) - 2차 코딩 테스트(합) - 면접(탈)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;2차 코딩 테스트가 알고리즘이 아니라 바닐라 자바스크립트로 요구 사항을 구현하는 문제 형식으로 나왔다. 생각보다 굉장히 어려웠고 내가 할 수 있는 부분까지만 구현했다. 합격했던 건 운이 좋았던 것 같다.&lt;/li&gt;
&lt;li&gt;이전에 근무했던 스타트업에서의 면접은 대표님과의 간단한 대화정도였기 때문에 사실상 첫 면접이였다. 너무 긴장되어서 인데놀이라는 긴장을 완화해주는 알약까지 먹었으나 효과는... 미미했다. 사전에 기술 면접이라고 안내해서 프론트 관련 공부를 많이 했다. 하지만 실제 면접에서 받았던 질문은 기술적인 질문이 아닌 내 경험과 생각을 묻는 질문이였다. 간단한 질문이였는데 너무 긴장한 나머지 나를 잘 어필하지 못한 것 같다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;2020 하반기&lt;/h3&gt;
&lt;p&gt;그동안 학습한 내용을 바탕으로 개인 프로젝트를 확 갈아엎고 리팩토링했다. 이 과정에서 좋은 코드에 대해 많이 고민할 수 있었고 실제로 디렉토리 구조, 네이밍, 코드 구조를 하나 하나 리팩토링하면서 깨끗한 코드의 중요성을 확실히 느낄 수 있었다. (과거의 나를 찾아가고 싶었다..후...) 이러한 내용을 바탕으로 서류 내용을 보충했다.&lt;/p&gt;
&lt;h4&gt;네이버&lt;/h4&gt;
&lt;p&gt;서류 + 코딩 테스트(탈)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;정말 가고 싶은 기업이였으나 코딩 테스트에서 뜨거운 눈물을 흘렸다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;라인&lt;/h4&gt;
&lt;p&gt;서류 - 1차 코딩 테스트(탈)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;역시나 코딩 테스트가 문제... 그동안 코딩 테스트에 대한 준비를 철저하게 하지 않았던 내 잘못이였다. (문제 풀이에 흥미가 없었음..)&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;쿠팡&lt;/h4&gt;
&lt;p&gt;코딩 테스트(탈)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;정말 후회했다. 책 읽고 개인 프로젝트 할 시간을 줄이고 알고리즘 문제를 하나 더 풀걸...&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;카카오&lt;/h4&gt;
&lt;p&gt;1차 코딩 테스트(합) - 2차 코딩 테스트(합) - 1차 면접(탈)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;2차까지 코딩 테스트를 합격한 건 운이 정말 좋았다고 생각한다.&lt;/li&gt;
&lt;li&gt;우아한 테크 캠프에 이은 2번째 면접이였다. 역시나 긴장의 정도가 한계치를 뚫어버리면서 머리가 하얘지고 혀는 말을 안들었다. cs 위주로 준비를 열심히 했다고 생각했는데 머리 속에 넣는 것과 머리 속에 있는 것을 말하는 건 정말 다르다는 것을 느꼈다. 질문에 대한 답을 상대방이 이해하기 쉽게 설명하지 못하는 것은 나 자신이 완벽하게 이해하지 못했기 때문인 것을 깨달았다. 면접보는 내내 쥐구멍에 숨고 싶었다...&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;당근 마켓 (프로그래머스 윈터 코딩)&lt;/h4&gt;
&lt;p&gt;코딩 테스트(합) - 서류(합) - 1차 면접(탈)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;댱근 마켓도 정말 가고 싶었던 기업 중 하나이다.&lt;/li&gt;
&lt;li&gt;서류에 spring으로 진행한 프로젝트를 적어 놓아서 그런지 질문의 대부분이 spring과 관련된 질문들이였다. 개인 프로젝트를 하면서 공부했던 내용들이였는데... 너무 긴장한 나머지 머리가 하얘지는 바람에 잘 대답하지 못하였다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;NHN&lt;/h4&gt;
&lt;p&gt;코딩 테스트(합) - 필기 테스트(합) - 1차 면접(탈)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;카카오 면접 탈락 이후 cs 공부를 정말 많이 했다. (소 잃고 외양간 고친 꼴...) cs는 자신 있었으나 NHN 1차 면접 유형은 문제 풀이었다. 면접에 나올 법한 문제들을 다양하게 풀어보면서 준비했다.&lt;/li&gt;
&lt;li&gt;면접을 보는 내내 지금 내 상태로는 면접을 보면 안 되겠다는 생각이 강하게 들었다. 컴공 자료구조 수업시간에 배운 문제가 나왔는데도 불구하고 문제를 제대로 풀지 못했다. 내 자신이 너무 한심했다. 마음이 간절할수록 긴장감이 배가 되는 걸까... 면접 시간 내낸 머릿속이 오만가지 걱정, 잡생각들로 가득 차면서 아무것도 할 수 없는 상태가 되었다. 면접이 끝나고 마음을 추스린 후에 문제를 복기하며 다시 풀어보았는데 쉽게 풀리는 문제였다. 정말 가고 싶었던 기업 중에 하나였기 때문에 아쉬움이 컸다. 내 마음이 간절할수록 더 크게 긴장하는 것 같다. 한편으로는 내 실력에 확신이 없어서 자꾸 긴장하는 게 아닌가 하는 생각도 든다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;우아한 테크 코스&lt;/h4&gt;
&lt;p&gt;서류 + 1차 코딩 테스트(합) - 3주 프리코스 - 2차 코딩 테스트&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;서류에 내가 개발자가 되고 싶은 이유와 개발에 대한 흥미와 열정을 아주 넘치게 담아 앞으로의 포부까지 나의 생각을 정말 솔직하게 써내려갔다.&lt;/li&gt;
&lt;li&gt;평소에 객체지향에 관심이 많았던터라 3주 프리코스 과제가 굉장히 재미있었다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;NHN godo&lt;/h4&gt;
&lt;p&gt;서류(합) - 1차 코딩 테스트(합) - 1차 면접(?)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;긴장하면 말을 왜 이렇게 못하는지 모르겠다. 물론 평소에 말을 잘하는 편은 절대 아니다 ㅎㅎ... 확실하게 아는 것도 설명을 듣는 입장에서 이해하기 힘들게 설명했던 것 같다. 앞으로는 무언가 중요한 개념을 배울 때는 화이트보드 같은 걸 갖다 놓고 누군가에게 설명하듯이 공부를 해야겠다...&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;티몬&lt;/h4&gt;
&lt;p&gt;서류(합) - 1차 코딩 테스트(합) - 1차 면접(불참)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;1차 코딩 테스트가 굉장히 특별해서 기억에 남는다. 자세하게는 언급할 수 없지만 지원자가 주어진 요구사항에 맞는 시스템을 객체지향적으로 잘 설계하면서 만들 수 있는지를 판단하기 위해 낸 문제라는 느낌을 많이 받았다. 원래 코딩 테스트는 파이썬으로 풀지만 티몬의 기술 스택이 자바 기반 스프링인지라 자바로 풀면 채점자 분들이 좋아하실 거 같아서(개인적인 생각ㅋ..) 자바 언어를 사용해서 객체지향적으로 문제를 풀었다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;가비아 동계 인턴십&lt;/h4&gt;
&lt;p&gt;서류(합) - 1차 면접(불참)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;이전 상반기 채용 공고에서는 서류에서 탈락했었다. 이번에는 나를 어필할 수 있는 내용들을 잘 정리해서 포트폴리오로 첨부를 했는데 이걸 좋게 봐주신 것 같다. 개인적인 생각으로는 자소서 외의 개인 포트폴리오를 꼭 준비해서 지원 시 넣는게 무조건 좋다고 생각한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;몬드리안 AI&lt;/h4&gt;
&lt;p&gt;서류(합) - 면접(합??인가?)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;인천 송도(집 근처^o^)에 있는 인공지능/머신러닝, 데이터분석, 데이터 시각화 전문 기업이다. AI 개발 플랫폼, 컨테이너 통합 관리 솔루션 등 다양한 개발 솔루션을 제공한다.&lt;/li&gt;
&lt;li&gt;지인이 인턴을 했던 곳이라서 이름을 알고 있었는데 사람인에 공고가 뜬 걸 보고 지원했다. 회사에 대해서 찾아보면 찾아볼수록 다양한 개발 경험을 통해서 많이 배울 수 있을 것 같다는 생각이 강하게 들었다. 서류에서 합격하고 면접을 보러 갔다. 대면 면접은 처음이었는데 비대면보단 대면이 조금 더 긴장이 덜 되어서 다행이었다. 하지만 긴장이 아예 안 될 순 없기에... 아무 말 대잔치를 좀 했던 것 같다.&lt;/li&gt;
&lt;li&gt;결과적으로는 해당 공고에서는 탈락했지만(경력 공고였기에..) 우선 인턴으로 일해보고 평가 후에 전환하는 게 어떻겠냐는 대표님의 연락을 받았다. 인턴으로 입사하게 되면 당시에 남아있던 면접들은 포기해야 하는 상황이라서 현재 나의 상황을 대표님께 솔직하게 말씀드렸더니 우선은 면접을 보면서 마음이 결정되면 연락을 다시 해달라고 말씀해주셨다. 이렇게 말씀해주신 대표님께 너무너무 감사했다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;최종 고민&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;몬드리안 AI에 인턴으로 입사&lt;/li&gt;
&lt;li&gt;나머지 남은 면접들을 보고 최종 합격하면 입사 or 모두 떨어지면 다시 취준생&lt;/li&gt;
&lt;li&gt;우아한 테크코스에 최종 테스트에 합격을 하면 10개월 동안 교육 코스에 집중&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;면접들을 경험하면서 당장은 거의 트라우마 급으로 생겨버린 면접에 대한 긴장감을 잘 극복해나가지 못했기 때문에 남은 면접들에서 나를 제대로 어필할 수 있을까 하는 생각이 들었다. 그리고 여기에 겹쳐서 몬드리안 AI에서 일하면 재밌을 것 같다는 생각이 강하게 들었다. 그 이유는 몬드리안 AI의 다양한 솔루션들이 굉장히 흥미로웠고 그 내부의 기술들이 궁금했기 때문이다. 그리고 개발 블로그의 글들을 보고 사내 분위기가 굉장히 좋을 것 같다는 느낌을 받았다.&lt;/p&gt;
&lt;p&gt;물론 처음부터 이름있는 IT 기업에 입사하면 굉장히 좋겠지만, 작지만 좋은 기업에서 다양한 경험을 하면서 기술적으로 빠르게 성장하는 것도 좋은 방향일 것 같다는 생각이 들었다. 지금의 나에게 돈은 크게 중요하지 않다. 좋은 사람들을 만나서 다양한 개발 경험을 해보는 것이 가장 중요하다고 생각한다. 그래서 나는 몬드리안 AI에서 인턴부터 시작하기로 마음의 결정을 내렸다. 많이 부족한 나에게 기회를 주신 대표님께 다시 한 번 감사하다고 말씀을 드리고 싶다.&lt;/p&gt;
&lt;p&gt;아 참, 그리고 우아한 테크코스는 최종 테스트를 아직 보지 않았지만 만약에 합격을 한다고 했을 때의 행복한 고민도 잠시 해보았다. 해당 교육 코스를 통해 배우는 것도 정말 많을 것이다. 하지만 나는 10개월이라는 시간 동안 현업에서의 다양한 경험과 개인적인 학습을 통해서 더 빠르게 성장할 수 있을 것이라는 생각이 들었다. 나랑 회사가 같이 성장해나가는 과정이 굉장히 재미있을 것 같다는 생각도 든다. 내 선택에 후회가 없었으면 좋겠다. 아니, 후회가 없도록 만들 것이다.&lt;/p&gt;
&lt;h2&gt;마치며&lt;/h2&gt;
&lt;p&gt;그동안 남들이 하니깐.. 하고 싶은 게 없으니깐 이거라도 해야지.. 하면서 수동적인 삶을 살아왔었던 나에게 2019년은, 내가 하고 싶은 것들을 찾아서 하며 능동적인 삶을 살았던 정말 뜻깊은 한 해였다. 내 반오십 인생에서 가장 버라이어티하고 재미있었던 한 해였다.&lt;/p&gt;
&lt;p&gt;그렇다면 2020년은 어땠을까? 사실 아쉬움이 많이 남는다. 당시에는 정말 열심히 했었지만 지금 생각해보면 효율적이지 못한 공부를 많이 했던 것 같다. 다양한 분야에 관심이 많았던지라 얇고 넓게 공부했던 것이 내 실패 요인 중 하나이다. 물론 넓게 공부하는 것도 중요하지만 그 전에 알고리즘(문제 해결 능력), cs 이 두 가지는 자신 있을 정도로 무조건 깊이 공부해야 한다고 생각한다.&lt;/p&gt;
&lt;p&gt;나는 그동안 내 자신을 스스로 남들과 비교하면서 스트레스를 많이 받아왔다. 하지만 앞으로는 개발에 대한 흥미를 바탕으로 즐겁게 공부하면서 오로지 나의 성장에만 집중할 것이다.&lt;/p&gt;</description>
      <category>경험</category>
      <category>회고</category>
      <author>머래그로프</author>
      <guid isPermaLink="true">https://goldfishhead.tistory.com/112</guid>
      <comments>https://goldfishhead.tistory.com/112#entry112comment</comments>
      <pubDate>Mon, 14 Dec 2020 00:56:36 +0900</pubDate>
    </item>
    <item>
      <title>[Java] 제어문 (live-study 4주차)</title>
      <link>https://goldfishhead.tistory.com/111</link>
      <description>&lt;h5&gt;목표&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;자바가 제공하는 제어문을 학습하세요.&lt;/li&gt;
&lt;/ul&gt;
&lt;h5&gt;학습할 것&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;선택문&lt;/li&gt;
&lt;li&gt;반복문&lt;/li&gt;
&lt;/ul&gt;
&lt;h5&gt;과제&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;과제 0. JUnit 5 학습하세요.&lt;/li&gt;
&lt;li&gt;과제 1. live-study 대시 보드를 만드는 코드를 작성하세요.&lt;/li&gt;
&lt;li&gt;과제 2. LinkedList를 구현하세요.&lt;/li&gt;
&lt;li&gt;과제 3. Stack을 구현하세요.&lt;/li&gt;
&lt;li&gt;과제 4. 앞서 만든 ListNode를 사용해서 Stack을 구현하세요.&lt;/li&gt;
&lt;li&gt;과제 5. Queue를 구현하세요.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;선택문&lt;/h2&gt;
&lt;p&gt;자바는 순차적으로 위에서 아래로 차례대로 실행합니다. 실행 순서는 조건에 의해 변경할 수 있습니다. 실행 순서를 변경하기 위해 제어문(Control Statement)을 사용합니다.&lt;/p&gt;
&lt;p&gt;제어문은 다음과 같이 구분합니다.&lt;/p&gt;
&lt;p&gt;1) 선택문: if 문, if ~ else 문, switch 문&lt;br&gt;2) 반복문: while 문, do ~ while 문, for 문&lt;br&gt;3) 점프문: goto 문, continue 문, break 문&lt;/p&gt;
&lt;p&gt;특정한 기준(조건)에 해당할 때 지정한 문장을 실행할 것인지 안할 것인지 결정할 때 선택문(조건문)을 사용합니다.&lt;/p&gt;
&lt;h3&gt;if 문&lt;/h3&gt;
&lt;p&gt;if 문은 &amp;quot;만약 ~ 이라면(if)을 하고 그렇지 않으면(else) ~를 한다&amp;quot; 라는 논리 구조로 만약 ~ 부분의 조건을 조건식으로 표기합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;if(조건식) {
    // 조건식이 맞으면 수행할 문장
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;if ~ else 문&lt;/h3&gt;
&lt;p&gt;if 문은 else와 함께 사용하여 하나의 세트 문장으로 인식합니다. else는 false 선택일 때 처리하기 위해서 사용합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;if (조건식) {
    // 조건식이 맞으면 수행할 문장
} else {
    // 조건식이 안 맞으면 수행할 문장
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;중첩 if 문&lt;/h3&gt;
&lt;p&gt;else 문 안에 또 다른 if 문을 포함하는 것을 중첩 if 문이라고 합니다. 여러 조건에 의해 선택하는 경우에 중첩합니다.&lt;/p&gt;
&lt;p&gt;if 문은 또 다른 if 문이나 if ~ else 문을 포함한 어떤 문장도 얼마든지 중첩할 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;if (조건식1) {
    // 위의 조건식과 맞으면 수행할 문장
} else if (조건식2) {
    // 위의 조건식과 맞으면 수행할 문장
} else {
    // 나머지의 경우 수행할 문장
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;switch 문&lt;/h3&gt;
&lt;p&gt;조건문의 경우의 수가 많은 경우에는 if 문 대신 switch 문을 사용하면 간결하고 가독성이 높습니다. switch 문은 다중 선택문 입니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;switch (식) {
    case 정수1:  
        // 식의 결과 값이 정수1이면 실행할 문장1;
        break;

    case 정수2:  
        // 식의 결과 값이 정수2이면 실행할 문장2;
        break;

    case 정수n:  
        // 식의 결과 값이 정수n이면 실행할 문장n;
        break;

    default:       
        // 옵션, 식의 결과 값과 일치하는 case 문이 없을 때 실행할 문장

}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;반복문&lt;/h2&gt;
&lt;p&gt;사람이 하기 힘든, 귀찮은 반복적인 일을 반복문을 통해 기계가 대신 수행할 수 있습니다.&lt;/p&gt;
&lt;h3&gt;while&lt;/h3&gt;
&lt;p&gt;while 문은 반복 조건이 참(true)이면 중괄호 구간을 반복적으로 실행하고 조건이 거짓(false)이면 반복문을 실행하지 않고 빠져나옵니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;while(조건){
    // 반복 실행 영역
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;int i = 0;
while(i &amp;lt; 5){         
    System.out.println(i);
    i++;
}
// 0
// 1
// 2
// 3
// 4&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;i의 값이 10보다 작다면 true, 크다면 false가 됩니다. 구간 내 작업을 수행하고 크기가 증가하는 i의 값을 while문의 조건절에서 매번 확인하고 계속 반복을 해야하는지를 판단합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;for&lt;/h3&gt;
&lt;p&gt;for문은 특정한 횟수만큼 반복 실행을 하는 경우에 자주 사용됩니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;for(초기화; 종료조건; 반복실행){
    반복적으로 실행될 구문
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;for (int i = 0; i &amp;lt; 5; i++) {
    System.out.println(i);
}
// 0
// 1
// 2
// 3
// 4&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;초기화 : 반복문이 실행될 때 1회 실행됩니다.&lt;/li&gt;
&lt;li&gt;종료조건 : 초기화가 실행된 후에 종료조건이 실행됩니다. 종료조건의 값이 false일 때까지 반복문의 중괄호 구간의 코드가 반복 실행됩니다.&lt;/li&gt;
&lt;li&gt;중괄호 구간의 실행이 끝나면 반복 실행이 실행됩니다. 일반적으로 이 곳에 i++와 같이 변수를 증가시키는 로직이 위치하고, 이것이 실행된 후에 종료조건이 실행됩니다. 종료조건이 false가 될 때까지 이 과정이 반복됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;break&lt;/h3&gt;
&lt;p&gt;반복 작업을 중간에 중단시키고 싶다면 break를 사용하면 됩니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;for (int i = 0; i &amp;lt; 5; i++) {
    if (i == 3)
        break;
    System.out.println(&amp;quot;i&amp;quot;);
}
// 0
// 1
// 2&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;continue&lt;/h3&gt;
&lt;p&gt;현재 반복 작업을 건너뛰고 다음 차례 반복 작업을 하고 싶다면 continue를 사용하면 됩니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;for (int i = 0; i &amp;lt; 5; i++) {
    if (i == 2)
        continue;
    System.out.println(&amp;quot;Coding Everybody &amp;quot; + i);
}

// 0
// 1
// 3
// 4&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;과제 0. JUnit 5 학습하세요.&lt;/h2&gt;
&lt;h5&gt;목표&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;인텔리J, 이클립스, VS Code에서 JUnit 5로 테스트 코드 작성하는 방법에 익숙해 질 것.&lt;/li&gt;
&lt;li&gt;이미 JUnit 알고 계신분들은 다른 것 아무거나!&lt;/li&gt;
&lt;li&gt;더 자바, 테스트 강의도 있으니 참고하세요~&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;기본 Annotation&lt;/h3&gt;
&lt;h4&gt;@DisplayName&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;테스트 클래스 또는 테스트 메서드의 이름을 정의할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;@Disable&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;테스트 클래스 또는 메서드를 비활성화할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;@BeforeAll&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;해당 annotation 이 달린 메서드가 현재 클래스의 모든 테스트 메서드보다 먼저 실행됩니다.&lt;/li&gt;
&lt;li&gt;해당 메서드는 static 이어야 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;@BeforeEach&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;해당 annotation 이 달린 메서드가 각 테스트 메서드 전에 실행됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;@AfterAll&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;해당 annotation 이 달린 메서드가 현재 클래스의 모든 테스트 메소드보다 이후에 실행됩니다.&lt;/li&gt;
&lt;li&gt;해당 메서드는 static 이어야 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;@AfterEach&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;해당 annotation 이 달린 메서드가 각 테스트 메서드 이후에 실행됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Assertions and Assumptions&lt;/h3&gt;
&lt;h4&gt;Assertions&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Assert 구문은 어떤 조건이 참인지 검증하는 방법이다. 단언한 조건이 참이 아니면 테스트는 그 자리에서 멈추고 실패합니다.&lt;/li&gt;
&lt;li&gt;assertTrue, assertThat 등이 있습니다.&lt;/li&gt;
&lt;li&gt;assertAll()을 사용하여 assertions 을 그룹화하여 그룹 내에서 실패한 assertions 을 MultipleFailuresError 와 함께 기록할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Assumptions&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;특정 조건이 충족되는 경우에만 테스트를 실행하는 데 사용됩니다.&lt;/li&gt;
&lt;li&gt;일반적으로 테스트가 제대로 실행되기 위해 필요한 외부 조건에 사용됩니다.&lt;/li&gt;
&lt;li&gt;테스트와 직접적인 관련은 없습니다.&lt;/li&gt;
&lt;li&gt;assumptions 이 실패하면 TestAbortedException이 발생하고 테스트는 수행되지 않습니다.&lt;/li&gt;
&lt;li&gt;assumeTrue(), assumeFalse(), assumingThat() 등이 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Exception Testing&lt;/h3&gt;
&lt;h4&gt;assertThrows&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;h5&gt;발생한 예외의 세부 사항을 확인&lt;/h5&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;@Test
void shouldThrowException() {
    Throwable exception = assertThrows(UnsupportedOperationException.class, () -&amp;gt; {
        throw new UnsupportedOperationException(&amp;quot;Not supported&amp;quot;);
    });
    assertEquals(exception.getMessage(), &amp;quot;Not supported&amp;quot;);
}&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;h5&gt;예외 유형의 유효성을 검사&lt;/h5&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;@Test
void assertThrowsException() {
    String str = null;
    assertThrows(IllegalArgumentException.class, () -&amp;gt; {
        Integer.valueOf(str);
    });
}&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Test Suites&lt;/h3&gt;
&lt;h4&gt;@SelectPackages&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Test Suites 를 실행할 때 선택할 패키지 이름 지정합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;SelectClasses&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Test Suites 를 실행할 때 선택할 클래스 지정합니다.&lt;/li&gt;
&lt;li&gt;각 테스트는 하나의 패키지에 있지 않아도 됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Dynamic Tests&lt;/h3&gt;
&lt;h4&gt;@TestFactory&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;해당 annotation 이 달린 메서드는 동적 테스트를 위한 test factory 메서드입니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;런타임에 생성된 테스트 케이스를 선언하고 실행할 수 있습니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;각각 in, out 이라는 두 개의 ArrayList 를 사용하여 단어를 번역합니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;@TestFactory
public Stream&amp;lt;DynamicTest&amp;gt; translateDynamicTestsFromStream() {
    return in.stream()
      .map(word -&amp;gt;
          DynamicTest.dynamicTest(&amp;quot;Test translate &amp;quot; + word, () -&amp;gt; {
            int id = in.indexOf(word);
            assertEquals(out.get(id), translate(word));
          })
    );
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;@TestFactory 메서드는 private 또는 static 이면 안됩니다.&lt;/li&gt;
&lt;li&gt;테스트 수는 동적이며, ArrayList 크기에 따라 달라집니다.&lt;/li&gt;
&lt;li&gt;@TestFactory 메서드는 Stream, Collection, Iterable 또는 Iterator 를 return 해야 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;과제 1. live-study 대시 보드를 만드는 코드를 작성하세요.&lt;/h3&gt;
&lt;h5&gt;목표&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;깃헙 이슈 1번부터 18번까지 댓글을 순회하며 댓글을 남긴 사용자를 체크 할 것.&lt;/li&gt;
&lt;li&gt;참여율을 계산하세요. 총 18회에 중에 몇 %를 참여했는지 소숫점 두자리가지 보여줄 것.&lt;/li&gt;
&lt;li&gt;Github 자바 라이브러리를 사용하면 편리합니다.&lt;/li&gt;
&lt;li&gt;깃헙 API를 익명으로 호출하는데 제한이 있기 때문에 본인의 깃헙 프로젝트에 이슈를 만들고 테스트를 하시면 더 자주 테스트할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h5&gt;Github.java&lt;/h5&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;public class Github {

    private static final String LIVE_STUDY_PATH = &amp;quot;whiteship/live-study&amp;quot;;
    private static final int TOTAL_NUMBER_OF_ISSUE = 18;

    private GitHub github;

    public Github(String token) throws IOException {
        github = new GitHubBuilder().withOAuthToken(token).build();
    }

    public List&amp;lt;GithubUserReport&amp;gt; getGithubUserReportOfParticipation() throws IOException {
        Map&amp;lt;String, Integer&amp;gt; numberOfParticipationCounter = new HashMap&amp;lt;&amp;gt;();

        List&amp;lt;GHIssue&amp;gt; issues = github.getRepository(LIVE_STUDY_PATH).getIssues(GHIssueState.ALL);
        for (GHIssue issue : issues) {
            Set&amp;lt;String&amp;gt; notDuplicatedUser = getNotDuplicatedUserCommentedInIssue(issue);
            updateNumberOfParticipationCounter(numberOfParticipationCounter, notDuplicatedUser);
        }

        return getGithubUserReportOfParticipation(numberOfParticipationCounter);
    }

    private Set&amp;lt;String&amp;gt; getNotDuplicatedUserCommentedInIssue(GHIssue issue) throws IOException {
        Set&amp;lt;String&amp;gt; notDuplicatedUser = new HashSet&amp;lt;&amp;gt;();
        for (GHIssueComment ghIssueComment : issue.getComments()) {
            String user = ghIssueComment.getUser().getLogin();
            notDuplicatedUser.add(user);
        }

        return notDuplicatedUser;
    }

    private void updateNumberOfParticipationCounter(Map&amp;lt;String, Integer&amp;gt; counter, Set&amp;lt;String&amp;gt; notDuplicatedUser) {
        for (String user : notDuplicatedUser) {
            if (counter.containsKey(user)){
                counter.put(user, counter.get(user) + 1);
            } else {
                counter.put(user, 1);
            }
        }
    }

    private List&amp;lt;GithubUserReport&amp;gt; getGithubUserReportOfParticipation(Map&amp;lt;String, Integer&amp;gt; counter) {
        return counter.entrySet().stream()
            .map(e -&amp;gt; {
                String username = e.getKey();
                int numberOfParticipation = e.getValue();
                double rate = calculateRateOfParticipant(e.getValue());

                return new GithubUserReport(username, numberOfParticipation, rate);
            })
            .sorted(Comparator.comparing(GithubUserReport::getUsername))
            .collect(Collectors.toList());
    }

    private double calculateRateOfParticipant(int value) {
        return (double) (value * 100) / TOTAL_NUMBER_OF_ISSUE;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;GithubUserReport.java&lt;/h5&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;public class GithubUserReport {

    private final String username;
    private final int numberOfParticipation;
    private final double rateOfParticipation;

    public GithubUserReport(String username, int numberOfParticipation, double rateOfParticipation) {
        this.username = username;
        this.numberOfParticipation = numberOfParticipation;
        this.rateOfParticipation = rateOfParticipation;
    }

    public String getUsername() {
        return username;
    }

    public int getNumberOfParticipation() {
        return numberOfParticipation;
    }

    public double getRateOfParticipation() {
        return rateOfParticipation;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;GithubTest.java&lt;/h5&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;class GithubTest {

    @Test
    void printGithubUserReportOfParticipation() throws IOException {
        final String TITLE_ALIGNMENT = &amp;quot;| %20s | %5s | %5s |&amp;quot;;
        final int WIDTH = 45;
        final String ROW_ALIGNMENT = &amp;quot;| %20s | %8d | %8.2f |&amp;quot;;
        final String HYPHEN = &amp;quot;-&amp;quot;;
        final String TOKEN = &amp;quot;github 토큰을 넣으면 됩니당&amp;quot;;

        Github github = new Github(TOKEN);

        System.out.println(HYPHEN.repeat(WIDTH));
        System.out.println(String.format(TITLE_ALIGNMENT, &amp;quot;Github ID&amp;quot;, &amp;quot;참여 횟수&amp;quot;, &amp;quot;참여율(%)&amp;quot;));
        System.out.println(HYPHEN.repeat(WIDTH));

        List&amp;lt;GithubUserReport&amp;gt; userReports = github.getGithubUserReportOfParticipation();
        userReports.forEach(userReport -&amp;gt; {
            System.out.println(
                String.format(ROW_ALIGNMENT,
                    userReport.getUsername(),
                    userReport.getNumberOfParticipation(),
                    userReport.getRateOfParticipation()));
        });

        System.out.println(HYPHEN.repeat(WIDTH));
    }
}

---------------------------------------------
|            Github ID | 참여 횟수 | 참여율(%) |
---------------------------------------------
|          0417taehyun |        3 |    16.67 |
|             1031nice |        3 |    16.67 |
|              2yeseul |        2 |    11.11 |
|               372dev |        3 |    16.67 |
|              9m1i9n1 |        3 |    16.67 |
|              Ahnyezi |        1 |     5.56 |
|              BaeJi77 |        2 |    11.11 |
|           ByungJun25 |        3 |    16.67 |
|              CODEMCD |        3 |    16.67 |
...&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;과제 2. LinkedList를 구현하세요.&lt;/h3&gt;
&lt;h5&gt;목표&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;LinkedList에 대해 공부하세요.&lt;/li&gt;
&lt;li&gt;정수를 저장하는 ListNode 클래스를 구현하세요.&lt;/li&gt;
&lt;li&gt;ListNode add(ListNode head, ListNode nodeToAdd, int position)를 구현하세요.&lt;/li&gt;
&lt;li&gt;ListNode remove(ListNode head, int positionToRemove)를 구현하세요.&lt;/li&gt;
&lt;li&gt;boolean contains(ListNode head, ListNode nodeTocheck)를 구현하세요.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;과제 3. Stack을 구현하세요.&lt;/h3&gt;
&lt;h5&gt;목표&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;int 배열을 사용해서 정수를 저장하는 Stack을 구현하세요.&lt;/li&gt;
&lt;li&gt;void push(int data)를 구현하세요.&lt;/li&gt;
&lt;li&gt;int pop()을 구현하세요.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;과제 4. 앞서 만든 ListNode를 사용해서 Stack을 구현하세요.&lt;/h3&gt;
&lt;h5&gt;목표&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;ListNode head를 가지고 있는 ListNodeStack 클래스를 구현하세요.&lt;/li&gt;
&lt;li&gt;void push(int data)를 구현하세요.&lt;/li&gt;
&lt;li&gt;int pop()을 구현하세요.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;과제 5. Queue를 구현하세요.&lt;/h3&gt;
&lt;h5&gt;목표&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;배열을 사용해서 한번&lt;/li&gt;
&lt;li&gt;ListNode를 사용해서 한번.&lt;/li&gt;
&lt;/ul&gt;
&lt;h5&gt;출처&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://opentutorials.org/course/1223/5366&quot;&gt;https://opentutorials.org/course/1223/5366&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://palpit.tistory.com/607&quot;&gt;https://palpit.tistory.com/607&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://gmlwjd9405.github.io/2019/11/26/junit5-guide-basic.html&quot;&gt;https://gmlwjd9405.github.io/2019/11/26/junit5-guide-basic.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Java</category>
      <category>Java</category>
      <category>live-study</category>
      <author>머래그로프</author>
      <guid isPermaLink="true">https://goldfishhead.tistory.com/111</guid>
      <comments>https://goldfishhead.tistory.com/111#entry111comment</comments>
      <pubDate>Fri, 11 Dec 2020 16:50:07 +0900</pubDate>
    </item>
    <item>
      <title>[Java] 연산자 (live-study 3주차)</title>
      <link>https://goldfishhead.tistory.com/110</link>
      <description>&lt;h5&gt;목표&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;자바가 제공하는 다양한 연산자를 학습합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h5&gt;학습할 것&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;산술 연산자&lt;/li&gt;
&lt;li&gt;비트 연산자&lt;/li&gt;
&lt;li&gt;관계 연산자&lt;/li&gt;
&lt;li&gt;논리 연산자&lt;/li&gt;
&lt;li&gt;대입 연산자&lt;/li&gt;
&lt;li&gt;instanceof&lt;/li&gt;
&lt;li&gt;화살표(-&amp;gt;) 연산자&lt;/li&gt;
&lt;li&gt;3항 연산자&lt;/li&gt;
&lt;li&gt;연산자 우선 순위&lt;/li&gt;
&lt;li&gt;(optional) Java 13. switch 연산자&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;연산자&lt;/h3&gt;
&lt;p&gt;우선 연산자에 대해서 알아봅시다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;연산자는 변수의 값을 변경하거나 대입하는데 사용됩니다.&lt;/li&gt;
&lt;li&gt;자바에서는 연산자에 따라 연산 대상이 될 수 있는 데이터 타입이 정해져 있습니다. (정수나 실수만 가능)&lt;/li&gt;
&lt;li&gt;오버플로(overflow) 또는 언더플로(underflow) 등 범위를 벗어나는 연산들에 대해서는&lt;/li&gt;
&lt;li&gt;연산 결과에 불필요한 값이 저장됩니다.&lt;/li&gt;
&lt;li&gt;0으로 나눌 경우는 오류가 발생합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;산술 연산자 ( +, -, *, /, % )&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;산술 연산자는 사칙연산을 다루는 기본적이면서도 가장 많이 사용되는 연산자입니다.&lt;/li&gt;
&lt;li&gt;산술 연산자는 모두 두 개의 피연산자를 가지는 이항 연산자이며, 피연산자들의 결합 방향은 왼쪽에서 오른쪽입니다.&lt;/li&gt;
&lt;li&gt;나머지 연산은 결과가 항상 정수입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;관계 연산자 ( &amp;lt;, &amp;lt;=, &amp;gt;, &amp;gt;=, ==, != )&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;비교 연산자의 결과 값은 크기 값을 비교하여 조건을 만족하면 true 그렇지 않으면 false를 반환합니다.&lt;/li&gt;
&lt;li&gt;만약 비교되는 숫자의 데이터 타입이 다를 경우 기본적으로 크기가 큰 데이터 타입에 맞추어 비교 연산을 실행합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;비트 연산자&lt;/h3&gt;
&lt;h4&gt;비트 연산자 ( &amp;amp;, |, ^ )&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;비트 연산자는 두 수를 각각 2진수로 변환하여 두 수의 각 비트 연산을 수행합니다.
&lt;ul&gt;
&lt;li&gt;&lt;b&gt;&amp;amp;&lt;/b&gt; (비트곱): 두 비트가 1일 때 1, 나머지는 0.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;|&lt;/b&gt; (비트합): 두 비트 중 하나 이상이 1이면 1, 두 비트 모두 0이면 0.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;^&lt;/b&gt; (xor 배타적 논리합): 두 비트가 다르면 1, 같으면 0.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;비트 이동 연산자 ( &amp;lt;&amp;lt;, &amp;gt;&amp;gt; )&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;왼쪽 항의 값을 2진수로 변환하여 오른쪽 항의 값만큼 비트를 왼쪽(&amp;lt;&amp;lt;), 오른쪽(&amp;gt;&amp;gt;)으로 이동시키는 연산을 수행합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;논리 연산자 ( &amp;amp;, |, &amp;amp;&amp;amp;, || )&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;b&gt;&amp;amp;&lt;/b&gt; : 연산을 수행하여 양쪽 항이 모두 true일 때만 true를 반환합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;|&lt;/b&gt; : 연산을 수행하여 양쪽 항 중 한쪽만 true를 만족해도 true를 반환합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&amp;amp;&amp;amp;&lt;/b&gt; : 만일 왼쪽 항이 false일 경우에는 오른쪽 항을 수행하지 않고 무조건 false를 반환합니다. (양쪽 항을 모두 검사하는 &amp;amp; 보다 속도가 빠름)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;||&lt;/b&gt; : 만일 왼쪽 항이 true일 경우에는 오른쪽 항을 수행하지 않고 무조건 true를 반환합니다. (양쪽 항을 모두 검사하는 | 보다 속도가 빠름)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;대입 연산자 (=, +=, -=, *=, /=, %=)&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;대입 연산자는 변수에 값을 대입할 때 사용하는 이항 연산자이며, 피연산자들의 결합 방향은 오른쪽에서 왼쪽입니다.&lt;/li&gt;
&lt;li&gt;또한, 앞서 살펴본 산술 연산자와 결합한 다양한 복합 대입 연산자가 존재합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;3항 연산자&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;단순한 IF문일 경우 라인수를 획기적으로 줄여주는 방식이 바로 3항 연산자입니다.&lt;/li&gt;
&lt;li&gt;삼항연산자는 (조건문) : ? 참 : 거짓 이라는 문법을 가지게 됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;//IF ELSE
int a;
if(5&amp;lt;4) {
    a = 50;
}else {
    a = 40;
}
System.out.println(a); //결과 = 40 

//삼항연산자
int b = (5 &amp;lt; 4) ? 50 : 40; 
System.out.println(b); //결과 = 40&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;위와같이 삼항연산자가 할수있는 것은 IF ELSE문을 통해서도 처리가 가능합니다.&lt;/li&gt;
&lt;li&gt;삼항연산자를 사용하여 코드의 라인이 줄어들었다고 컴파일 속도가 빨라지는 것은 아닙니다.&lt;/li&gt;
&lt;li&gt;삼항연산자를 중복해서 처리할 경우. 가독성이 떨어질 수 있으므로 중복처리는 피하는것이 좋습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;산술, 관계, 비트, 대입, 3항 연산자 정리&lt;/h3&gt;
&lt;table class=&quot;mark-table&quot; cellspacing=&quot;0&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;th&gt;구분&lt;/th&gt;
&lt;th&gt;연산자&lt;/th&gt;
&lt;th&gt;사용예&lt;/th&gt;
&lt;th&gt;설명&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td rowspan=&quot;9&quot;&gt;산술 연산자&lt;/td&gt;
&lt;td&gt;+&lt;/td&gt;
&lt;td&gt;a + b&lt;/td&gt;
&lt;td&gt;덧셈&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;a - b&lt;/td&gt;
&lt;td&gt;뺄셈&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;*&lt;/td&gt;
&lt;td&gt;a * b&lt;/td&gt;
&lt;td&gt;곱셈&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;/&lt;/td&gt;
&lt;td&gt;a / b&lt;/td&gt;
&lt;td&gt;나눗셈&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;%&lt;/td&gt;
&lt;td&gt;a % b&lt;/td&gt;
&lt;td&gt;나머지&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td rowspan=&quot;2&quot;&gt;++&lt;/td&gt;
&lt;td&gt;++a&lt;/td&gt;
&lt;td&gt;연산전에 변수값 1증가&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;a++&lt;/td&gt;
&lt;td&gt;연산후에 변수값 1증가&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td rowspan=&quot;2&quot;&gt;--&lt;/td&gt;
&lt;td&gt;--a&lt;/td&gt;
&lt;td&gt;연산전에 변수값 1감소&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;a--&lt;/td&gt;
&lt;td&gt;연산후에 변수값 1감소&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td rowspan=&quot;6&quot;&gt;관계 연산자&lt;/td&gt;
&lt;td&gt;&amp;gt;&lt;/td&gt;
&lt;td&gt;a &amp;gt; b&lt;/td&gt;
&lt;td&gt;a 가 b 보다 크면 true&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;gt;=&lt;/td&gt;
&lt;td&gt;a &amp;gt;= b&lt;/td&gt;
&lt;td&gt;a 가 b 보다 크거나 같으면 true&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;&lt;/td&gt;
&lt;td&gt;a &amp;lt; b&lt;/td&gt;
&lt;td&gt;a 가 b 보다 작으면 true&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;=&lt;/td&gt;
&lt;td&gt;a &amp;lt;= b&lt;/td&gt;
&lt;td&gt;a 가 b 보다 작거나 같으면 true&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;==&lt;/td&gt;
&lt;td&gt;a == b&lt;/td&gt;
&lt;td&gt;a 가 b 와 같으면 true&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;!=&lt;/td&gt;
&lt;td&gt;a != b&lt;/td&gt;
&lt;td&gt;a 가 b 와 같지 않으면 true&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td rowspan=&quot;3&quot;&gt;비트 연산자&lt;/td&gt;
&lt;td&gt;&amp;amp;&amp;amp;&lt;/td&gt;
&lt;td&gt;a &amp;amp;&amp;amp; b&lt;/td&gt;
&lt;td&gt;a, b 모두 true 이면 true&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;||&lt;/td&gt;
&lt;td&gt;a || b&lt;/td&gt;
&lt;td&gt;a,b 둘 중 하나라도 true 이면 true&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;!&lt;/td&gt;
&lt;td&gt;!a&lt;/td&gt;
&lt;td&gt;a 가 true 이면 false, false 이면 true&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td rowspan=&quot;6&quot;&gt;대입 연산자&lt;/td&gt;
&lt;td&gt;=&lt;/td&gt;
&lt;td&gt;a = 9;&lt;/td&gt;
&lt;td&gt;변수 a 에 값 9 를 할당&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;+=&lt;/td&gt;
&lt;td&gt;a += b;&lt;/td&gt;
&lt;td&gt;a = a + b;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;-=&lt;/td&gt;
&lt;td&gt;a -= b;&lt;/td&gt;
&lt;td&gt;a = a - b;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;*=&lt;/td&gt;
&lt;td&gt;a *= b;&lt;/td&gt;
&lt;td&gt;a = a * b;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;/=&lt;/td&gt;
&lt;td&gt;a /= b;&lt;/td&gt;
&lt;td&gt;a = a / b;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;%=&lt;/td&gt;
&lt;td&gt;a %= b;&lt;/td&gt;
&lt;td&gt;a = a % b;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3항 연산자&lt;/td&gt;
&lt;td colspan=&quot;2&quot;&gt;max = a &amp;gt; b ? a : b;&lt;/td&gt;
&lt;td&gt;a 가 b 보다 크면 a의 값을 max 에 할당&lt;br /&gt;a 가 b 보다 크지 않다면 b의 값을 max 에 할당&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;instanceof&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;참조변수가 참조하고 있는 인스턴스의 실제 타입을 알아보기 위해 instanceof 연산자를 사용합니다.&lt;/li&gt;
&lt;li&gt;주로 조건문에 사용되며, instanceof의 왼쪽에는 참조변수를 오른쪽에는 타입(클래스명)이 피연산자로 위치합니다.&lt;/li&gt;
&lt;li&gt;연산의 결과로 boolean값인 true, false 중의 하나를 반환 합니다.&lt;/li&gt;
&lt;li&gt;instanceof를 이용한 연산결과로 true를 얻었다는 것은 참조변수가 검사한 타입으로 형변환이 가능하다는 것을 뜻합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;화살표(-&amp;gt;) 연산자&lt;/h3&gt;
&lt;p&gt;람다식에 사용되는 연산자입니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;람다식이란 식별자없이 실행가능한 함수를 의미합니다.&lt;/li&gt;
&lt;li&gt;함수인데 함수를 따로 만들지 않고 코드한줄에 함수를 써서 그것을 호출하는 방식입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;livescript&quot;&gt;&lt;code&gt;(매개변수, ...) -&amp;gt; { 실행문 ... }&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;(매개변수, ...)&lt;/code&gt;는 오른쪽 중괄호 &lt;code&gt;{ }&lt;/code&gt; 블록을 실행하기 위해 필요한 값을 제공하는 역할을 합니다. 매개 변수의 이름은 개발자가 자유롭게 지정할 수 있으며 인자타입도 명시하지 않아도 됩니다. &lt;code&gt;-&amp;gt;&lt;/code&gt; 기호는 매개 변수를 이용해서 중괄호 &lt;code&gt;{ }&lt;/code&gt; 바디를 실행한다는 뜻으로 해석하면 됩니다.&lt;/p&gt;
&lt;h4&gt;람다식 장점&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;코드를 간결하게 만들 수 있습니다.&lt;/li&gt;
&lt;li&gt;코드가 간결하고 식에 개발자의 의도가 명확히 드러나므로 가독성이 향상됩니다.&lt;/li&gt;
&lt;li&gt;함수를 만드는 과정없이 한번에 처리할 수 있기에 코딩하는 시간이 줄어듭니다.&lt;/li&gt;
&lt;li&gt;병렬 프로그래밍이 용이합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;람다식 단점&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;람다를 사용하면서 만드는 무명함수는 재사용이 불가능합니다.&lt;/li&gt;
&lt;li&gt;디버깅이 다소 까다롭습니다.&lt;/li&gt;
&lt;li&gt;람다를 남발하면 코드가 지저분해질 수 있습니다. (비슷한 함수를 계속 중복생성할 가능성이 높음)&lt;/li&gt;
&lt;li&gt;재귀로 만들경우에는 다소 부적합한면이 있습니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;연산자 우선 순위&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;최우선연산자 ( ., [], () )&lt;/li&gt;
&lt;li&gt;단항연산자 ( ++,--,!,~,+/- : 부정, bit변환&amp;gt;부호&amp;gt;증감)&lt;/li&gt;
&lt;li&gt;산술연산자 ( *,/,%,+,-,shift) &amp;lt; 시프트연산자 ( &amp;gt;&amp;gt;,&amp;lt;&amp;lt;,&amp;gt;&amp;gt;&amp;gt; ) &amp;gt;&lt;/li&gt;
&lt;li&gt;비교연산자 ( &amp;gt;,&amp;lt;,&amp;gt;=,&amp;lt;=,==,!= )&lt;/li&gt;
&lt;li&gt;비트연산자 ( &amp;amp;,|,,~ )&lt;/li&gt;
&lt;li&gt;논리연산자 (&amp;amp;&amp;amp; , || , !)&lt;/li&gt;
&lt;li&gt;삼항연산자 (조건식) ? :&lt;/li&gt;
&lt;li&gt;대입연산자 ( =,*=,/=,%=,+=,-= )&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;(optional) Java 13. switch 연산자&lt;/h3&gt;
&lt;p&gt;Java 12부터 프리뷰로 추가되었습니다.&lt;/p&gt;
&lt;h5&gt;: 대신 -&amp;gt; 를 사용할 수 있습니다.&lt;/h5&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;int a = 1;

switch(a){
    case 1 -&amp;gt; System.out.println(&quot;case1&quot;);
    case 2 -&amp;gt; System.out.println(&quot;case2&quot;);
}&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;yield 기능을 이용해 값을 리턴 할 수 있습니다.&lt;/h5&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;int a = 1;
int b = switch (a) {
  case 1 -&amp;gt;{
    System.out.println(&quot;case1&quot;);
    yield 10;
  }
  case 2 -&amp;gt;{
    System.out.println(&quot;case2&quot;);
    yield 12;
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;출처&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://www.tcpschool.com/c/c_operator_arithmetic&quot;&gt;http://www.tcpschool.com/c/c_operator_arithmetic&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://neoroid.tistory.com/8&quot;&gt;https://neoroid.tistory.com/8&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ejfrmjava.tistory.com/59&quot;&gt;https://ejfrmjava.tistory.com/59&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://arabiannight.tistory.com/313&quot;&gt;https://arabiannight.tistory.com/313&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://coding-factory.tistory.com/266&quot;&gt;https://coding-factory.tistory.com/266&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://programmers.co.kr/learn/courses/5/lessons/116&quot;&gt;https://programmers.co.kr/learn/courses/5/lessons/116&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://velog.io/@pond1029/operator&quot;&gt;https://velog.io/@pond1029/operator&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Java</category>
      <category>Java</category>
      <author>머래그로프</author>
      <guid isPermaLink="true">https://goldfishhead.tistory.com/110</guid>
      <comments>https://goldfishhead.tistory.com/110#entry110comment</comments>
      <pubDate>Fri, 27 Nov 2020 20:54:44 +0900</pubDate>
    </item>
    <item>
      <title>[Java] 자바 데이터 타입, 변수 그리고 배열 (live-study 2주차)</title>
      <link>https://goldfishhead.tistory.com/109</link>
      <description>&lt;h5&gt;목표&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;자바의 프리미티브 타입, 변수 그리고 배열을 사용하는 방법을 익힙니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h5&gt;학습할 것&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;프리미티브 타입 종류와 값의 범위 그리고 기본 값&lt;/li&gt;
&lt;li&gt;프리미티브 타입과 레퍼런스 타입&lt;/li&gt;
&lt;li&gt;리터럴&lt;/li&gt;
&lt;li&gt;변수 선언 및 초기화하는 방법&lt;/li&gt;
&lt;li&gt;변수의 스코프와 라이프타임&lt;/li&gt;
&lt;li&gt;타입 변환, 캐스팅 그리고 타입 프로모션&lt;/li&gt;
&lt;li&gt;1차 및 2차 배열 선언하기&lt;/li&gt;
&lt;li&gt;타입 추론, var&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;프리미티브 타입 종류와 값의 범위 그리고 기본 값&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;java 자료형.png&quot; data-origin-width=&quot;478&quot; data-origin-height=&quot;197&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bc4P8y/btqNU2X7OBC/gUDXWXnggNnrqr8FBBdN80/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bc4P8y/btqNU2X7OBC/gUDXWXnggNnrqr8FBBdN80/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bc4P8y/btqNU2X7OBC/gUDXWXnggNnrqr8FBBdN80/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbc4P8y%2FbtqNU2X7OBC%2FgUDXWXnggNnrqr8FBBdN80%2Fimg.png&quot; data-filename=&quot;java 자료형.png&quot; data-origin-width=&quot;478&quot; data-origin-height=&quot;197&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2&gt;프리미티브 타입과 레퍼런스 타입&lt;/h2&gt;
&lt;p&gt;타입(Data type)이란 해당 데이터가 메모리에 어떻게 저장되고, 프로그램에서 어떻게 처리되어야 하는지를 명시적으로 알려주는 것입니다. 자바에서 타입은 크게 기본형 타입과 참조형 타입이 있습니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;기본형(primitive type): 논리형, 문자형, 정수형, 실수형이 있다. 계산을 위해 실제 값을 저장합니다.&lt;/li&gt;
&lt;li&gt;참조형(reference type): 객체의 주소를 저장한다. null 또는 객체의 주소(4byte, 0x0 ~ 0xffffffff)를 갖는다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;프리미티브 타입&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;총 8가지의 기본형 타입(Primitive type)을 미리 정의하여 제공합니다.&lt;/li&gt;
&lt;li&gt;기본값이 있기 때문에 Null이 존재하지 않는다. 만약 기본형 타입에 Null을 넣고 싶다면 래퍼 클래스를 활용합니다.&lt;/li&gt;
&lt;li&gt;실제 값을 저장하는 공간으로 스택(Stack) 메모리에 저장됩니다.&lt;/li&gt;
&lt;li&gt;만약 컴파일 시점에 담을 수 있는 크기를 벗어나면 에러를 발생시키는 컴파일 에러가 발생합니다. 주로 문법상의 에러가 많습니다. (예를 들어 ;을 안붙였다는 이유로 빨간 줄이 쳐지는 경우)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://gbsb.tistory.com/144?category=735872&quot;&gt;자바의 데이터 타입(Primitive type)의 유의사항과 응용&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;레퍼런스 타입&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;기본형 타입을 제외한 타입들이 모두 참조형 타입(Reference type)입니다.&lt;/li&gt;
&lt;li&gt;빈 객체를 의미하는 Null이 존재합니다.&lt;/li&gt;
&lt;li&gt;값이 저장되어 있는 곳의 주소값을 저장하는 공간으로 힙(Heap) 메모리에 저장됩니다.&lt;/li&gt;
&lt;li&gt;문법상으로는 에러가 없지만 실행시켰을 때 에러가 나는 런타임 에러가 발생합니다. 예를 들어 객체나 배열을 Null 값으로 받으면 NullPointException이 발생하므로 변수값을 넣어야 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;리터럴&lt;/h2&gt;
&lt;p&gt;리터럴은 데이터 그 자체를 뜻 한다. 변수에 넣는 변하지 않는 데이터를 의미하는 것입니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;int a = 1;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;int 앞에 a는 변수이고, 여기서의 1은 리터럴입니다. 즉, 1과 같이 변하지 않는 데이터(boolean, char, double, long, int, etc...)를 리터럴(literal)이라고 부릅니다.&lt;/p&gt;
&lt;h3&gt;정수 - integer&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;가장 일반적으로 사용되는 데이터 자료형입니다. 모든 임의의 정수 값은 정수 리터럴입니다. 예를 들어 1, 2, 3, 42는 정수 리터널입니다. 이런한 값들은 10진수이나, 8진수나 16진수를 사용할 수도 있습니다.&lt;/li&gt;
&lt;li&gt;8진수는 숫자 0을 앞에 표시하고 숫자를 표시한다. 일반 10진수에는 0을 표시하지 않기 때문에 07과 7은 다른게 인식합니다.&lt;/li&gt;
&lt;li&gt;16진수는 0x(또는 0X)를 먼저 적고 16진수 상수를 지정합니다. 범위는 0&lt;del&gt;16까지 이며 a&lt;/del&gt;f(A&lt;del&gt;F)가 10&lt;/del&gt;15까지를 대신한다.&lt;/li&gt;
&lt;li&gt;모든 정수형 데이터가 기본적으로 int형이기 때문에 long 데이터 자료형에 정확한 long 리터럴을 지정하기 위해서는 숫자 뒤에 알파벨 l(또는 L)를 추가 해줘야 합니다.&lt;/li&gt;
&lt;li&gt;byte와 short 변수에 숫자를 저장할 때 저장되는 숫자의 범위가 해당 데이터 형의 자료형에 포함된다면 에러가 발생하지 않습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;부동 소수점 - floating point&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;부동 소수점 리터널은 소수점 이하(분수)를 가진 10진 값들입니다. 예를 들어 2.0, 3.1415, -0.6667은 모두 부동 소수점 리터널입니다.&lt;/li&gt;
&lt;li&gt;double형 자료형은 부동 소수점의 기본형입니다. 숫자 뒤에 알파벨 d(또는 D)를 추가할 수도 있습니다.&lt;/li&gt;
&lt;li&gt;float형 자료형은 부동 소수점의 기본형입니다. 숫자 뒤에 알파벨 d(또는 D)를 추가할 수도 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;부울 - boolean&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;부울 상수는 두개의 논리적인 값, true(참), false(거짓)만 있습니다. C/C++에서와 같이 참, 거짓을 숫자로 표시 할 수는 없습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;문자 - character&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;자바의 모든 문자들은 Unicode를 사용합니다. 정수로 변환될 수 있으며, 더하고 빼는 거와 같은 연산도 가능합니다.&lt;/li&gt;
&lt;li&gt;정부 형태가 아닌 문자 리터럴을 표시하고자 할 때는 단일 인용 부호(&lt;code&gt;,&lt;/code&gt;)를 사용합니다.&lt;/li&gt;
&lt;li&gt;유니코드나 직접 입력이 불가능한 문자들에 대해서는 역슬래쉬( \ )를 이용하여 표시할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;escape 시퀀스&lt;/th&gt;
&lt;th&gt;설명&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;\ddd&lt;/td&gt;
&lt;td&gt;8진 문자 (ddd)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;\uxxxx&lt;/td&gt;
&lt;td&gt;16진수 Unicode 문자 (xxxx)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;\`&lt;/td&gt;
&lt;td&gt;단일 인용 부호&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;\&quot;&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;/tr&gt;
&lt;tr&gt;
&lt;td&gt;\r&lt;/td&gt;
&lt;td&gt;캐리지 리턴&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;\n&lt;/td&gt;
&lt;td&gt;뉴 라인(또는 라인 피드)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;\f&lt;/td&gt;
&lt;td&gt;폼 피드(form feed)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;\t&lt;/td&gt;
&lt;td&gt;탭(tab)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;\b&lt;/td&gt;
&lt;td&gt;백스페이스(backspace)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;문자열 - string&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;문자열 리터널은 이중 인용 부호(&quot;, &quot;)로 지정하여 사용합니다.&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;String str = &quot;안녕하세요.&quot;;&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;변수 선언 및 초기화하는 방법&lt;/h2&gt;
&lt;h3&gt;변수(variable)&lt;/h3&gt;
&lt;p&gt;값을 저장할 수 있는 메모리상의 공간.&lt;/p&gt;
&lt;h3&gt;변수 선언&lt;/h3&gt;
&lt;pre class=&quot;dart&quot;&gt;&lt;code&gt;int num; // int: 변수 타입, num: 변수 이름&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;변수 타입은 변수에 저장될 값이 어떤 타입인지를 지정하는 것입니다.&lt;/li&gt;
&lt;li&gt;변수 이름은 변수에 붙인 이름입니다.&lt;/li&gt;
&lt;li&gt;변수는 '값을 저장할 수 있는 메모리 공간'이므로 변수의 이름은 메모리 공간에 이름을 붙여주는 것입니다.&lt;/li&gt;
&lt;li&gt;이름을 이용해 저장공간(변수)에 값을 저장하고, 저장된 값을 읽어올 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;변수를 선언하면 메모리의 빈 공간에 변수타입에 맞는 크기의 저장공간이 확보되고, 앞으로 이 공간은 변수이름을 통해 사용할 수 있게 된다.&lt;/p&gt;
&lt;h3&gt;변수의 초기화&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;변수를 사용하기 전에 처음으로 값을 저장하는 것입니다.&lt;/li&gt;
&lt;li&gt;알 수 없는 값(쓰레기값, garbage value)이 남아 있을 수 있기 때문에 변수를 사용하기 전에 반드시 초기화(initialization)를 해야합니다.&lt;/li&gt;
&lt;li&gt;변수에 값을 저장할 때는 대입연산자 '='를 사용합니다.&lt;/li&gt;
&lt;li&gt;오른쪽의 값을 왼쪽(변수)에 저장하라는 뜻이빈다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;int x;   // 변수의 선언
x = 0;   // 변수의 초기화

int x = 0;  // 변수 x를 선언하고 0으로 초기화 한다.&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;변수의 스코프와 라이프타임&lt;/h2&gt;
&lt;p&gt;변수의 스코프는 변수에 접근할 수 있는 프로그램 영역을 가리킨다. 라이프타임은 변수 혹은 레퍼런스의 '유효범위'이다. 변수가 메모리에 얼마나 유지되어있는가의 정도라고도 볼 수 있다.&lt;/p&gt;
&lt;h5&gt;&lt;a href=&quot;https://www.learningjournal.guru/article/programming-in-java/scope-and-lifetime-of-a-variable/&quot;&gt;https://www.learningjournal.guru/article/programming-in-java/scope-and-lifetime-of-a-variable/&lt;/a&gt;&lt;/h5&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;public class scope_and_lifetime {
    int num1, num2;   //Instance Variables
    static int result;  //Class Variable
    int add(int a, int b){  //Local Variables
        num1 = a;
        num2 = b;
        return a+b;
    }
    public static void main(String args[]){
        scope_and_lifetime ob = new scope_and_lifetime();
        result = ob.add(10, 20);
        System.out.println(&quot;Sum = &quot; + result);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;인스턴스 변수&lt;/h3&gt;
&lt;p&gt;클래스안에 있지만 다른 메서드 밖에 선언된 변수를 인스턴스 변수라고 한다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;scope : static method를 제외한 클래스어디서나 사용가능&lt;/li&gt;
&lt;li&gt;lifetime : 클래스 객체가 메모리에 남아있을때 까지&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;클래스 변수&lt;/h3&gt;
&lt;p&gt;클래스안에 선언되고 static으로 선언된 변수를 클래스변수라한다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;scope : 클래스안에 어디서나 사용가능&lt;/li&gt;
&lt;li&gt;lifetime : 프로그램이 끝날때까지&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;지역변수&lt;/h3&gt;
&lt;p&gt;클래스변수와 인스턴스 변수가 아닌 모든 변수들을 지역변수라 한다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;scope : 선언된 블록안에서만 가용가능&lt;/li&gt;
&lt;li&gt;lifetime : 블록이 컨트롤될때까지&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;타입 변환, 캐스팅 그리고 타입 프로모션&lt;/h2&gt;
&lt;p&gt;타입변환 : 변수 또는상수의 타입을 다른 타입으로 변환하는 것입니다.&lt;/p&gt;
&lt;p&gt;서로 다른 타입간의 대입이나 연산을 할 때 형변환으로 타입을 일치시켜서 하는것이 원칙입니다.&lt;/p&gt;
&lt;p&gt;타입은 아래의 두 가지 방법으로 변환될 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;형변환.png&quot; data-origin-width=&quot;915&quot; data-origin-height=&quot;229&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cdbQ8P/btqNXGMPAV3/SAAQrK8uhmvguk5b64eWVK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cdbQ8P/btqNXGMPAV3/SAAQrK8uhmvguk5b64eWVK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cdbQ8P/btqNXGMPAV3/SAAQrK8uhmvguk5b64eWVK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcdbQ8P%2FbtqNXGMPAV3%2FSAAQrK8uhmvguk5b64eWVK%2Fimg.png&quot; data-filename=&quot;형변환.png&quot; data-origin-width=&quot;915&quot; data-origin-height=&quot;229&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3&gt;타입 프로모션 (자동형변환)&lt;/h3&gt;
&lt;p&gt;표현범위가 좁은 타입에서 넓은 타입으로 형변환하는 경우, 값손실이 없으므로 두 타입중에서 표현범위가 더 넓은 쪽으로 자동으로 형변환됩니다.&lt;/p&gt;
&lt;p&gt;왼쪽에서 오른쪽으로의 변환은 형변환 연산자를 사용하지 않아도 자동 형변환이 되며, 그 반대 방향으로의 변환은 반드시 형변환 연산자를 써줘야 합니다.&lt;/p&gt;
&lt;h3&gt;캐스팅 (명시적 형변환)&lt;/h3&gt;
&lt;p&gt;캐스팅은 더 큰 범위의 타입 값을 더 작은 범위의 타입값에 할당하기 위해서 반드시 축소(캐스팅)를 해줘야 합니다.&lt;/p&gt;
&lt;p&gt;이것은 자동형변환할 수 없는 호환되지 않는 데이터 유형에 사용할 수 있습니다. 타입을 명시해줘서 강제적으로 형변환(캐스팅) 해 줄 수 있습니다.&lt;/p&gt;
&lt;h2&gt;1차 및 2차 배열 선언하기&lt;/h2&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;public class Test {

    public static void main(String[] args) {

        // 1차원 배열 생성
        int[] arr1 = {1,2,3,4,5};

        // 또다른 1차원 배열 생성 (값 입력 안됨)
        int[] arr1 = new int[5];

        // 2차원 배열 생성
        int[][] arr2 = {{1,1,1,1,1},{2,2,2,2,2}};

        // 2차원 배열 생성 (값 입력 안됨)
        int[][] arr2 = new int[2][5];
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;타입 추론, var&lt;/h2&gt;
&lt;p&gt;타입추론이란 코드 작성 당시 타입이 정해지지 않았지만, 컴파일러가 그 타입을 유추하는 것입니다.&lt;/p&gt;
&lt;p&gt;자바 10부터 타입 추론을 지원하는 var키워드가 추가되었습니다. 이 키워드는 local variable이면서 선언과 동시에 초기화가 필수적으로 요구됩니다.&lt;/p&gt;
&lt;h4&gt;장점&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;생산성 향상, 가독성 좋음, 인스턴스 객체의 타입이 바뀌어도 생성되는 클래스부분은 수정하지 않아도 됩니다.&lt;/li&gt;
&lt;li&gt;Object대안 가능, 람다식, 익명클래스 등에서 활용 가능.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;단점&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;IDE에따라 바로 타입 확인이 어렵습니다.&lt;/li&gt;
&lt;li&gt;해당 위치만 보고 어떤 타입의 데이터가 할당될지 알 수 없을 수도 있습니다.(변수명을 var checkList 등 의미있는 네이밍으로 어느정도 보완 가능)&lt;/li&gt;
&lt;/ul&gt;
&lt;h5&gt;참조&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://gbsb.tistory.com/6&quot;&gt;https://gbsb.tistory.com/6&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.devkuma.com/books/pages/29&quot;&gt;http://www.devkuma.com/books/pages/29&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://sujin-k.tistory.com/60&quot;&gt;https://sujin-k.tistory.com/60&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.notion.so/Java-Day-2-dec4ca0ee04a436f8a785a3da2fed2cd&quot;&gt;https://www.notion.so/Java-Day-2-dec4ca0ee04a436f8a785a3da2fed2cd&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://gblee1987.tistory.com/176&quot;&gt;https://gblee1987.tistory.com/176&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Java</category>
      <category>Java</category>
      <author>머래그로프</author>
      <guid isPermaLink="true">https://goldfishhead.tistory.com/109</guid>
      <comments>https://goldfishhead.tistory.com/109#entry109comment</comments>
      <pubDate>Sat, 21 Nov 2020 10:59:04 +0900</pubDate>
    </item>
    <item>
      <title>[Java] JVM은 무엇이며 자바 코드는 어떻게 실행하는 것인가 (live-study 1주차)</title>
      <link>https://goldfishhead.tistory.com/108</link>
      <description>&lt;h5&gt;목표&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;자바 소스 파일(.java)을 JVM으로 실행하는 과정 이해하기.&lt;/li&gt;
&lt;/ul&gt;
&lt;h5&gt;학습할 것&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;JVM이란 무엇인가&lt;/li&gt;
&lt;li&gt;컴파일 하는 방법&lt;/li&gt;
&lt;li&gt;실행하는 방법&lt;/li&gt;
&lt;li&gt;바이트코드란 무엇인가&lt;/li&gt;
&lt;li&gt;JIT 컴파일러란 무엇이며 어떻게 동작하는지&lt;/li&gt;
&lt;li&gt;JVM 구성 요소&lt;/li&gt;
&lt;li&gt;JDK와 JRE의 차이&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;JVM이란 무엇인가&lt;/h2&gt;
&lt;h3&gt;JVM의 용도와 정의&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Java로 개발한 프로그램을 컴파일하여 만들어지는 바이트코드를 실행시키기 위한 가상머신입니다.&lt;/li&gt;
&lt;li&gt;플랫폼 독립적으로, JVM이 실행 가능한 환경이라면 어디서든 Java 프로그램이 실행될 수 있도록 합니다. 즉, Java의 모토인 Write once, Run anywhere는 JVM을 통해 가능한 것입니다.&lt;/li&gt;
&lt;li&gt;프로그램 메모리를 관리하고 최적화합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;바이트코드는 실제의 기계에서 직접 실행되는 것이 아니라 JVM의 해석 단계를 거쳐 실행되므로 Java로 개발된 프로그램은 같은 기능의 네이티브 언어보다 실행 속도가 느립니다. 과거에는 바이트코드를 순수하게 인터프리트하여 매우 느렸으나 현재는 JIT 컴파일의 도입과 하드웨어의 발전으로 성능이 개선되었습니다.&lt;/p&gt;
&lt;p&gt;JVM은 추상적인 머신이며, 메모리의 접근을 가상 머신 차원에서 관리하고 있으므로 런타임에 최적화가 가능합니다. 매우 극단적이고 특수한 상황을 가정하면 극히 일부 기능에 대해서는 네이티브 언어보다 더 우월한 성능을 보여 주기도 합니다. 하지만 JIT 컴파일 시간, 가비지 컬렉션을 위한 시간 등이 필요하므로 근본적인 한계가 있습니다.&lt;/p&gt;
&lt;h2&gt;컴파일 &amp;amp; 실행하는 방법&lt;/h2&gt;
&lt;p&gt;우리가 Java Source Code를 작성하면 java라는 확장자를 가진 파일로 만들어지게 됩니다. Java Source파일은 단순히 코드만을 담고 있는 파일일 뿐, 이 자체로는 수행 불가능하므로 수행하도록 하기 위해서는 Class 파일의 형태로 변경해 주어야 합니다. 이 작업을 Compile이라고 합니다.&lt;/p&gt;
&lt;p&gt;Java에서 Compile은 보통 JDK에 내장되어 있는 'javac'라고 하는 Compiler를 이용하여 수행합니다. 이 작업을 수행하면 Source File과 같은 이름이지만 class라는 확장자를 가지는 Binary파일이 생성됩니다.&lt;/p&gt;
&lt;p&gt;bytecode라고도 부르는 이 class라는 확장자를 가진 파일은 수행이 가능한 형태의 파일입니다. exe 파일처럼 수행할수 있는 형태가 아니라 단순히 수행 가능한 형태라는 말입니다. 이것을 수행하기위해서는 적어도 JRE(Java Runtime Environment)라는 것이 필요합니다. 물론 JDK도 가능합니다.&lt;/p&gt;
&lt;p&gt;이 프로그램을 수행하기 위해서는 'java'라는 프로그램을 호출하여 우리가 생성한 class파일을 인수로 제공하면 됩니다. 이때 'java'라고 하는 프로그램은 Java실행 환경에 class파일을 가지고 들어가는 역할을 하게 됩니다. 'java'라고 하는 프로그램은 JVM을 하나의 프로세스로 올리는 작업과 함께 class파일의 로딩도 수행합니다.&lt;/p&gt;
&lt;p&gt;그 이후 class파일을 분석하여 JRE내에 있는 Java Application Program Interface(Java API)와 더불어 프로그램을 수행하도록 합니다. 그 과정은 아래와 같습니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;bytecode는 JVM 내부에 있는 Class Loader을 통해 Execution Engine 으로 배치됩니다.&lt;/li&gt;
&lt;li&gt;Execution Engine 안은 Interpreter와 JIT로 구성되어 있는데, Interpreter은 코드를 한줄씩 읽어들이다가 일정 시점이 되면 JIT Compiler에 캐싱하여 보관합니다.&lt;/li&gt;
&lt;li&gt;한번 컴파일된(JIT에 캐싱된)코드는 빠르게 실행되는 장점이 있지만, 한번만 실행될 코드는 Interpreter 하는게 더 유리할 수도 있습니다. (컴파일 비용이 들기 때문)&lt;/li&gt;
&lt;li&gt;Execution Engine에서 실행한 필요한 녀석들은 Runtime Area로 이동하며, JVM의 메모리에 올려 실행합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;바이트코드란 무엇인가&lt;/h2&gt;
&lt;p&gt;자바에서 코드를 컴파일하면 바이트코드 즉 (.class)형태로 출력이 되는데 이 Class형태는 JVM에 의해 런타임시완벽한 기계코드로 변경되어 실행됩니다. 자바 바이트 코드는 자바 가상 머신만 설치되어 있으면, 어떤 운영체제에서라도 실행될 수 있습니다.&lt;/p&gt;
&lt;p&gt;하지만 바이트 코드를 완전한 기계코드로 변환하는 과정에서 일반적인 컴파일 언어보다 속도가 많이 느리다는 단점이 있습니다.&lt;/p&gt;
&lt;h2&gt;JIT 컴파일러란 무엇이며 어떻게 동작하는지&lt;/h2&gt;
&lt;p&gt;명령어를 하나 하나 실행하는 Interpreter 방식이 있고 JIT(Just-In-Time) 컴파일러를 이용하는 방식이 있습니다.&lt;/p&gt;
&lt;p&gt;Interpreter는 바이트 코드를 한줄씩 읽기 때문에 실행이 느린 단점이 있었습니다. 이러한 단점을 보완하기 위해 나온 것이 JIT Compiler 입니다.&lt;/p&gt;
&lt;p&gt;처음에는 인터프리터 방식으로 실행을 하다가 적절한 시점에 바이트 코드 전체를 컴파일해서 더이상 인터프리팅 하지 않고 해당 코드를 직접 실행 하는 것 입니다.&lt;/p&gt;
&lt;p&gt;JIT Compiler에 의해 해석된 코드는 캐시에 보관하기 때문에 한 번 컴파일 된 후에는 빠르게 수행하는 장점이 있습니다. 하지만 인터프리팅 방식보다는 훨씬 오래 걸리므로 한번만 실행하면 되는 코드는 인터프리팅 하는 것이 유리합니다.&lt;/p&gt;
&lt;h2&gt;JVM 구성 요소&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;jvm 메모리.png&quot; data-origin-width=&quot;1202&quot; data-origin-height=&quot;1109&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cy0728/btqNVCdGwF3/BKQM01CRDxNvUwGb7O1hB1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cy0728/btqNVCdGwF3/BKQM01CRDxNvUwGb7O1hB1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cy0728/btqNVCdGwF3/BKQM01CRDxNvUwGb7O1hB1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcy0728%2FbtqNVCdGwF3%2FBKQM01CRDxNvUwGb7O1hB1%2Fimg.png&quot; data-filename=&quot;jvm 메모리.png&quot; data-origin-width=&quot;1202&quot; data-origin-height=&quot;1109&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Class Loader: JVM내로 클래스를 로드하고 링크를 통해 배치하는 작업을 수행하는 모듈로써 런타임시 동적으로 클래스를 로드합니다.&lt;/li&gt;
&lt;li&gt;Execution Engine: Class Loader를 통해 JVM 내의 런타임 데이터 영역에 배치된 바이트 코드를 실행합니다. 이 때, Execution Engine은 자바 바이트 코드를 명령어 단위로 읽어서 실행합니다.&lt;/li&gt;
&lt;li&gt;Garbage Collector: JVM은 Garbage Collector를 통해 메모리 관리 기능을 자동으로 수행합니다. 애플리케이션이 생성한 객체의 생존 여부를 판단하여 더 이상 사용되지 않는 객체를 해제하는 방식으로 메모리를 자동 관리합니다.&lt;/li&gt;
&lt;li&gt;Runtime Data Areas: JVM이 운영체제 위에서 실행되면서 할당받는 메모리 영역입니다. Class Loader에서 준비한 데이터들을 보관하는 저장소입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;jvm 메모리2.png&quot; data-origin-width=&quot;1777&quot; data-origin-height=&quot;1034&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cpZXOy/btqNSy3UiFx/4CPZGg7pNAGaRLMunCDTi0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cpZXOy/btqNSy3UiFx/4CPZGg7pNAGaRLMunCDTi0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cpZXOy/btqNSy3UiFx/4CPZGg7pNAGaRLMunCDTi0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcpZXOy%2FbtqNSy3UiFx%2F4CPZGg7pNAGaRLMunCDTi0%2Fimg.png&quot; data-filename=&quot;jvm 메모리2.png&quot; data-origin-width=&quot;1777&quot; data-origin-height=&quot;1034&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Method (Static) Area
&lt;ul&gt;
&lt;li&gt;JVM이 읽어들인 클래스와 인터페이스 대한 런타임 상수 풀, 멤버 변수(필드), 클래스 변수(Static 변수), 생성자와 메소드를 저장하는 공간입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Runtime Constant Pool
&lt;ul&gt;
&lt;li&gt;메소드 영역에 포함되지만 독자적 중요성이 있습니다.&lt;/li&gt;
&lt;li&gt;클래스 파일 constant_pool 테이블에 해당하는 영역입니다.&lt;/li&gt;
&lt;li&gt;클래스와 인터페이스 상수, 메소드와 필드에 대한 모든 레퍼런스를 저장합니다.&lt;/li&gt;
&lt;li&gt;VM은 런타임 상수 풀을 통해 해당 메소드나 필드의 실제 메모리 상 주소를 찾아 참조합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Heap Area
&lt;ul&gt;
&lt;li&gt;JVM이 관리하는 프로그램 상에서 데이터를 저장하기 위해 런타임 시 동적으로 할당하여 사용하는 영역입니다.&lt;/li&gt;
&lt;li&gt;New 연산자로 생성된 객체 또는 객체(인스턴스)와 배열을 저장합니다.&lt;/li&gt;
&lt;li&gt;힙 영역에 생성된 객체와 배열은 스택 영역의 변수나 다른 객체의 필드에서 참조합니다.&lt;/li&gt;
&lt;li&gt;참조하는 변수나 필드가 없다면 의미 없는 객체가 되어 GC의 대상이 됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Stack Area
&lt;ul&gt;
&lt;li&gt;각 스레드마다 하나씩 존재하며, 스레드가 시작될 때 할당됩니다.&lt;/li&gt;
&lt;li&gt;메소드를 호출할 때마다 프레임(Frame)을 추가(push)하고 메소드가 종료되면 해당 프레임을 제거(pop)하는 동작을 수행합니다.&lt;/li&gt;
&lt;li&gt;선입후출(FILO, First In Last Out) 구조로 push와 pop 기능을 사용합니다.&lt;/li&gt;
&lt;li&gt;메소드 호출 시 생성되는 스레드 수행정보를 기록하는 Frame을 저장합니다.&lt;/li&gt;
&lt;li&gt;메소드 정보, 지역변수, 매개변수, 연산 중 발생하는 임시 데이터 저장합니다.&lt;/li&gt;
&lt;li&gt;기본(원시)타입 변수는 스택 영역에 직접 값을 가집니다.&lt;/li&gt;
&lt;li&gt;참조타임 변수는 힙 영역이나 메소드 영역의 객체 주소를 가집니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;PC Register
&lt;ul&gt;
&lt;li&gt;현재 수행 중인 JVM 명령 주소를 갖습니다.&lt;/li&gt;
&lt;li&gt;프로그램 실행은 CPU에서 인스트럭션(Instruction)을 수행합니다.&lt;/li&gt;
&lt;li&gt;CPU는 인스트럭션을 수행하는 동안 필요한 정보를 CPU 내 기억장치인 레지스터에 저장합니다.&lt;/li&gt;
&lt;li&gt;연산 결과값을 메모리에 전달하기 전 저장하는 CPU 내의 기억장치입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Native Method Stack Area
&lt;ul&gt;
&lt;li&gt;자바 외 언어로 작성된 네이티브 코드를 위한 Stack입니다.&lt;/li&gt;
&lt;li&gt;즉, JNI(Java Native Interface)를 통해 호출되는 C/C++ 등의 코드를 수행하기 위한 스택입니다.&lt;/li&gt;
&lt;li&gt;네이티브 메소드의 매개변수, 지역변수 등을 바이트 코드로 저장합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;JDK와 JRE의 차이&lt;/h2&gt;
&lt;h3&gt;JDK ( Java Development Kit )&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Java 용 SDK ( Software Development Kit ) 입니다.&lt;/li&gt;
&lt;li&gt;Java 개발자가 Java 기반 프로그램을 개발할 수 있도록 컴파일러, 툴 등을 제공합니다.&lt;/li&gt;
&lt;li&gt;다만 자바로 개발된 Application을 사용하기 위한 실행 환경은 있어야 합니다. 그 역할을 JRE 가 합니다.
&lt;ul&gt;
&lt;li&gt;JDK 는 다시 J2EE ( Enterprise Edition ) , J2SE ( Standard Edition ) , J2ME ( Micro Edition ) 등으로 분류합니다.&lt;/li&gt;
&lt;li&gt;EE는 기업용 대규모 개발 환경에서 적합한 버전&lt;/li&gt;
&lt;li&gt;SE는 일반적인 개발 환경&lt;/li&gt;
&lt;li&gt;ME는 PDA나 Embeded 환경에서의 개발환경을 제공&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;JRE ( Java Runtime Environment )&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Java 프로그램을 실행시키기 위한 환경을 제공합니다. 즉, Java 언어로 만들어져서 컴파일된 프로그램을 실행하려면 JRE 는 설치되어 있어야 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;정리하자면,&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;JDK = JRE + 개발툴 + Java 컴파일러&lt;/li&gt;
&lt;li&gt;JRE = JVM, 라이브러리, 기타 애플릿이나 어플리케이션 구동 요소&lt;/li&gt;
&lt;/ul&gt;
&lt;h5&gt;출처&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.embian.com/56&quot;&gt;https://blog.embian.com/56&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://namu.wiki/w/%EC%9E%90%EB%B0%94%20%EA%B0%80%EC%83%81%20%EB%A8%B8%EC%8B%A0?from=Java%20Virtual%20Machine&quot;&gt;https://namu.wiki/w/%EC%9E%90%EB%B0%94%20%EA%B0%80%EC%83%81%20%EB%A8%B8%EC%8B%A0?from=Java%20Virtual%20Machine&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://hoonmaro.tistory.com/19&quot;&gt;https://hoonmaro.tistory.com/19&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://won-percent.tistory.com/57?category=1156013&quot;&gt;https://won-percent.tistory.com/57?category=1156013&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Java</category>
      <category>Java</category>
      <author>머래그로프</author>
      <guid isPermaLink="true">https://goldfishhead.tistory.com/108</guid>
      <comments>https://goldfishhead.tistory.com/108#entry108comment</comments>
      <pubDate>Sat, 21 Nov 2020 01:15:18 +0900</pubDate>
    </item>
    <item>
      <title>[이펙티브 자바] 일반적인 프로그래밍 원칙</title>
      <link>https://goldfishhead.tistory.com/107</link>
      <description>&lt;h2&gt;item 57. 지역변수의 범위를 최소화하라&lt;/h2&gt;
&lt;h3&gt;지역변수의 범위를 줄이는 방법&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;지역변수의 범위를 줄이는 가장 강력한 기법은 역시 &amp;#39;가장 처음 쓰일 때 선언하기&amp;#39;다.&lt;/li&gt;
&lt;li&gt;거의 모든 지역변수는 선언과 동시에 초기화해야 한다.&lt;ul&gt;
&lt;li&gt;try-catch 문은 이 규칙에서 예외다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;while 문 보다는 for 문을 사용하자.&lt;/li&gt;
&lt;li&gt;메서드를 작게 유지하고 한 가지 기능에 집중하자.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;item 58. 전통적인 for 문보다는 for-each 문을 사용하라&lt;/h2&gt;
&lt;p&gt;item 45에서 이야기했듯, 스트림이 제격인 작업이 있고 반복이 제격인 작업이 있다.&lt;/p&gt;
&lt;p&gt;전통적인 for 문과 비교했을 때 for-each 문은 명료하고, 유연하고, 버그를 예방해준다. 성능 저하도 없다. &lt;/p&gt;
&lt;p&gt;가능한 모든 곳에서 for 문이 아닌 for-each 문을 사용하자.&lt;/p&gt;
&lt;h3&gt;for-each 문을 사용할 수 없는 상황&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;파괴적인 필터링(원소 제거)&lt;/li&gt;
&lt;li&gt;변형(원소 수정, 전체 수정)&lt;/li&gt;
&lt;li&gt;병렬 반복(각각의 반복자와 인덱스 변수가 필요할 경우)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;item 59. 라이브러리를 익히고 사용하라&lt;/h2&gt;
&lt;p&gt;메이저 릴리스마다 주목할 만한 수많은 기능이 라이브러리에 추가된다.&lt;/p&gt;
&lt;p&gt;자바 프로그래머라면 적어도 jaav.lang, java.util, java.id와 그 하위 패키지들에는 익숙해져야 한다.&lt;/p&gt;
&lt;p&gt;다른 라이브러리들은 필요할 때마다 익히자.&lt;/p&gt;
&lt;h4&gt;알아두면 좋은 라이브러리&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;컬렉션 프레임워크&lt;/li&gt;
&lt;li&gt;스트림 라이브러리 (item 45~48)&lt;br&gt;java.util.concurrent의 동시성 기능&lt;/li&gt;
&lt;li&gt;멀티스레드 프로그래밍 작업을 단순화해주는 고수준의 편의 기능 제공 (item 80~81)&lt;ul&gt;
&lt;li&gt;고수준 개념을 직접 구현할 수 있도록 도와주는 저수준 요소들을 제공&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;자바 표준 라이브러리 다음 선택지는 고품질의 서드파티 라이브러리이다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;구글의 구아바 라이브러리가 대표적이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;item 60. 정확한 답이 필요하다면 float와 double은 피하라&lt;/h2&gt;
&lt;p&gt;float와 double 타입은 특히 금융 관련 계산과는 맞지 않는다.&lt;/p&gt;
&lt;h4&gt;정확한 답이 필요한 계산에는 float이나 double을 피하라.&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;0.1 혹은 10의 음의 거듭 제곱 수(10^-1, 10^-2 등)를 표현할 수 없기 때문이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;BigDecimal 사용&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;BigDecimal이 제공하는 여덟가지 반올림 모드를 이용하여 반올림을 완벽히 제어할 수 있다.&lt;/li&gt;
&lt;li&gt;소수점 추적은 시스템에 맞긴다.&lt;/li&gt;
&lt;li&gt;코딩 시에 불편함하다.&lt;/li&gt;
&lt;li&gt;기본 타입보다 훨씬 느리다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;성능이 중요하고 소수점을 직접 추적할 수 있고 숫자가 너무 크지 않다면 int나 double을 사용하라.&lt;/p&gt;
&lt;h4&gt;int, long 사용&lt;/h4&gt;
&lt;p&gt;다룰 수 있는 값의 크기가 제한되고, 소수점을 직접 관리해야 함&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;숫자를 아홉 자리 십진수로 표현할 수 있다면 int를 사용&lt;/li&gt;
&lt;li&gt;열여덟 자리 십진수로 표현할 수 있다면 long을 사용&lt;/li&gt;
&lt;li&gt;이를 넘어가면 BigDecimal을 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;item 61. 박싱된 기본 타입보다는 기본 타입을 사용하라&lt;/h2&gt;
&lt;p&gt;기본 타입과 박싱된 기본 타입 중 하나를 선택해야 한다면 가능하면 기본 타입을 사용하자.&lt;/p&gt;
&lt;p&gt;기본 타입은 간단하고 빠르다.&lt;/p&gt;
&lt;h4&gt;기본타입&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;값만 가지고 있다.&lt;/li&gt;
&lt;li&gt;값은 유효 하다.&lt;/li&gt;
&lt;li&gt;박싱된 기본타입보다 시간과 메모리 사용율에서 더 효율적이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;박싱된 기본타입&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;값에 더해서 식별성(identity)이란 속성을 가진다.&lt;ul&gt;
&lt;li&gt;값은 같아도 서로 다르게 인식할수있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;유효하지 않은 값을 가질수 있다. &lt;ul&gt;
&lt;li&gt;null을 가질수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;박싱된 기본 타입을 써야 한다면 주의를 기울이자.&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;두 박싱된 기본 타입을 == 연산자로 비교한다면 동일성 비교를 하게 된다.&lt;/li&gt;
&lt;li&gt;같은 연산에서 기본 타입과 박싱된 기본 타입을 혼용하면 언박싱이 이뤄지며, 언박싱 과정에서 NullPointerException을 던질 수 있다.&lt;/li&gt;
&lt;li&gt;기본 타입을 박싱하는 작업은 필요 없는 객체를 생성하는 부작용을 나을 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;박싱된 기본 타입을 언제 써야 되나?&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;컬렉션의 원소, 키값으로 쓴다. &lt;/li&gt;
&lt;li&gt;컬렉션은 기본 타입을 가질수 없으므로 어쩔수 없이 박싱된 기본 타입을 써야 된다.&lt;/li&gt;
&lt;li&gt;매개변수화 타입이나 매개변수화 매서드의 타입 매개변수로는 박싱된 기본타입을 써야 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;item 62. 다른 타입이 적절하다면 문자열 사용을 피하라&lt;/h2&gt;
&lt;p&gt;더 적합한 데이터 타입이 있거나 새로 작성할 수 있다면, 문자열을 쓰고 싶은 유혹을 뿌리쳐라.&lt;/p&gt;
&lt;p&gt;문자열은 잘못 사용하면 번거롭고, 덜 유연하고, 느리고, 오류 가능성도 크다.&lt;/p&gt;
&lt;p&gt;문자열을 잘못 사용하는 흔한 예로는 기본 타입, 열거 타입, 혼합 타입이 있다.&lt;/p&gt;
&lt;h4&gt;문자열은 다른 값을 대신하기 적절하지 않다.&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;숫자를 표현할때는 int, float 등등의 타입이 좋고 예/아니오는 boolean 타입이 좋다&lt;h4&gt;문자열은 열거타입을 대신하기에 적합하지 않다.&lt;/h4&gt;
&lt;/li&gt;
&lt;li&gt;상수를 열거할 때는 문자열보다는 열거 타입이 월등히 낫다. (item 34)&lt;h4&gt;문자열은 혼합타입을 대신하기 적합하지 않다.&lt;/h4&gt;
&lt;/li&gt;
&lt;li&gt;&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;// 문자열로 혼합 타입을 작성
String compoundKey = className + &amp;quot;#&amp;quot; + i.next();&lt;/code&gt;&lt;/pre&gt;
구분을 #으로만 하고 두 타입 중에 하나라도 #이 쓰이면 문제가 된다. 그리고 타입을 나눌 때는 파싱을 해야 되기 때문에 느리다.&lt;h4&gt;문자열은 권한을 표현하기에 적합하지 않다.&lt;/h4&gt;
에를 들어 스레드 지역변수 기능을 설계한다고 해보자. 그 이름처럼 각 스레드가 자신만의 변수를 갖게 해주는 기능이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;// 잘못된 예 - 문자열을 사용해 권한을 구분하였다.
public class ThreadLocal {
    private ThreadLocal() { } // 객체 생성 불가

    // 현 스레드의 값을 키로 구분해 저장한다.
    public static void set(String key, Object value);

    // (키가 가르키는) 현 스레드의 값을 반환한다.
    public static Object get(String key);
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;각 클라이언트가 고유한 키를 제공해야 하지만 문제점이 존재한다.&lt;/li&gt;
&lt;li&gt;이 방식의 문제는 스레드 구분용 문자열 키가 전역 이름공간에서 공유된다는 점이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;이 API는 문자열 대신 위조할 수 없는 키를 사용하면 해결된다. 이 키를 권한(capacity)이라고도 한다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;// Key 클래스로 권한을 구분했다.
public class ThreadLocal {
    private ThreadLocal() { } // 객체 생성 불가

    public static class Key { // (권한)
        Key() { }
    }

    // 위조 불가능한 고유 키를 생성한다.
    public static Key getKey() {
        return new Key();
    }

    public static void set(Key key, Object value);
    public static Object get(Key key);
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;이 방법은 위의 문제를 해결해주지만 개선할 여지가 있다.&lt;/li&gt;
&lt;li&gt;set과 get은 이제 정적 메서드일 이유가 없으니 Key클래스의 인스턴스 메서드로 바꾸자.&lt;/li&gt;
&lt;li&gt;이렇게 하면 Key는 더 이상 스레드 지역변수를 구분하기 위한 키가 아니라, 그 자체가 스레드 지역변수가 된다.&lt;/li&gt;
&lt;li&gt;중첩 클래스 Key의 이름을 ThreadLocal로 바꿔버리자.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;// 리팩터링하여 Key를 ThreadLocal로 변경
public final class ThreadLocal {
    public ThreadLocal();
    public static void set(Object value);
    public static Object get();
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;이 API에서는 get으로 얻은 Object를 실제 타입으로 형변환해 써야 해서 타입 안전하지 않다.&lt;/li&gt;
&lt;li&gt;ThreadLocal을 매개변수화 타입(item 29)으로 선언하면 간단하게 문제가 해결된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;// 매개변수화하여 타입안전성 확보
public final class ThreadLocal&amp;lt;T&amp;gt; {
    public ThreadLocal();
    public static void set(T value);
    public static T get();
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;이제 자바의 java.lang.ThreadLocal과 흡사해졌다.&lt;/li&gt;
&lt;li&gt;문자열 기반 API의 문제를 해결해주며, 키 기반 API보다 빠르고 우아하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;item 63. 문자열 연결은 느리니 주의하라&lt;/h2&gt;
&lt;p&gt;성능에 신경 써야 한다면 많은 문자열을 연결할 때는 문자열 연결 연산자(+)를 피하자.&lt;/p&gt;
&lt;p&gt;문자열 연결 연산자로 문자열 n개를 연결하는 시간은 n^2에 비례한다.&lt;/p&gt;
&lt;p&gt;문자열은 불변(item 17)이라서 두 문자열을 연결할 경우 양쪽의 내용을 모두 복사해야 하므로 성능 저하가 생긴다.&lt;/p&gt;
&lt;h5&gt;문자열 연결을 잘못 사용한 예 - 느리다!&lt;/h5&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;public String statement() {
    String result = &amp;quot;&amp;quot;;
    for (int i = 0; i &amp;lt; numItems(); i++) {
        result += lineForItem(i); //문자열 연결
    }
    return result;
}&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;StringBuilder를 사용하면 문자열 연결 성능이 크게 개선된다.&lt;/h5&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;public String statement2() {
    StringBuilder sb = new StringBuilder(numItems() * LINE_WIDTH);
    for (int i = 0; i &amp;lt; numItems(); i++) {
       sb.append(lineForItem(i));
    }
    return sb.toString();
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;StringBuilder를 전체 결과를 담기에 충분한 크기로 초기화한 점을 잊지 말자.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;item 64. 객체는 인터페이스를 사용해 참조하라&lt;/h2&gt;
&lt;p&gt;적합한 인터페이스만 있다면 매개변수뿐 아니라 반환값, 변수, 필드를 전부 인터페이스 타입으로 선언하라.&lt;/p&gt;
&lt;p&gt;객체의 실제 클래스를 사용해야 할 상황은 &amp;#39;오직&amp;#39; 생성자로 생성할 때뿐이다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;// 좋은 예
Set&amp;lt;Son&amp;gt; sonSet = new LinkedHashSet&amp;lt;&amp;gt;();

// 나쁜 예
LinkedHashSet&amp;lt;Son&amp;gt; sonSet = new LinkedHashSet&amp;lt;&amp;gt;();&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;인터페이스 타입의 장점&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;인터페이스 타입을 사용하면 클라이언트 코드를 수정하지 않고도 참조 객체를 변경할 수 있다.&lt;/li&gt;
&lt;li&gt;다른 타입의 객체를 사용하더라도 컴파일에러/런타임에러에 대한 걱정을 하지 않아도 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;인터페이스 타입의 단점&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;인터페이스 타입에 선언된 메서드를 구현한 메서드만 사용이 가능하다.&lt;/li&gt;
&lt;li&gt;특정 구현체의 내부 메서드를 사용할 수 없다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;클래스를 참조해야 하는 경우&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;String, BigInteger 같은 값 클래스처럼 적합한 인터페이스가 없을 경우&lt;ul&gt;
&lt;li&gt;Integer, Long과 같은 타입을 사용할 때는 Number와 같은 상위 타입을 사용하지 말아야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;인터페이스에는 없는 메서드를 사용할 경우&lt;ul&gt;
&lt;li&gt;PriorityQueue 클래스에는 Queue 인터페이스에는 없는 comparator 메서드를 제공한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;적합한 인터페이스가 없다면 클래스의 계층구조 중 필요한 기능을 만족하는 가장 덜 구체적인(상위의)클래스를 타입으로 사용하자.&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Java</category>
      <category>이펙티브 자바</category>
      <author>머래그로프</author>
      <guid isPermaLink="true">https://goldfishhead.tistory.com/107</guid>
      <comments>https://goldfishhead.tistory.com/107#entry107comment</comments>
      <pubDate>Mon, 21 Sep 2020 11:21:24 +0900</pubDate>
    </item>
    <item>
      <title>[이펙티브 자바] 메서드</title>
      <link>https://goldfishhead.tistory.com/106</link>
      <description>&lt;h2&gt;item 49. 매개변수가 유효한지 검사하라&lt;/h2&gt;
&lt;p&gt;메서드 몸체가 실행되기 전에 매개변수를 확인한다면 잘못된 값이 넘어왔을때 즉각적이고 깔끔한 방식으로 예외를 던질 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;매개변수 검사를 제대로 하지 못하면 생길 수 있는 문제&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;메서드가 수행되는 중간에 모호한 예외를 던지며 실패할 수 있다.&lt;/li&gt;
&lt;li&gt;메서드가 잘 수행되지만 잘못된 결과를 반환할 수 있다.&lt;/li&gt;
&lt;li&gt;메서드는 문제없이 수행됐지만, 어떤 객체를 이상한 상태로 만들어놓아서 미래의 알 수 없는 시점에 이 메서드와는  관련 없는 오류를 낼 수도 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;public과 protected 메서드는 매개변수 값이 잘못됐을 때 던지는 예외를 문서화해야 한다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;클래스 수준 주석은 그 클래스의 모든 public 메서드에 적용되므로 훨씬 깔끔하다. &lt;/p&gt;
&lt;p&gt;@Nullable과 같은 어노테이션을 사용할 수도 있지만 표준은 아니다. &lt;/p&gt;
&lt;p&gt;&lt;strong&gt;더불어 생성자 매개변수 검사도 클래스 불변식을 어기는 객체가 생성되지 않게 하기 위하여 꼭 필요하다.&lt;/strong&gt;&lt;/p&gt;
&lt;h3&gt;assert&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;private으로 공개되지 않은 메서드라면 개발자가 직접 호출되는 상황을 통제할 수 있다. &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;이럴 때는 assert를 사용하여 매개변수 유효성을 검사할 수 있다. &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;실행시에 assert를 수행하려면 인텔리제이 기준으로 VM Options에 -ea 또는 --enableassertions 를 넘겨주어야 한다. &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;값을 넘겨주지 않으면 무시된다. &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;넘어온 매개변수가 조건식을 참으로 만들지 않으면 AssertionError를 던진다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;  // 재귀 정렬용 private 도우미 함수
  private void sort(long a[], int offset, int length) {
      assert a != null;
      assert offset &amp;gt;= 0 &amp;amp;&amp;amp; offset &amp;lt;= a.length;
      assert length &amp;gt;= 0 &amp;amp;&amp;amp; length &amp;lt;= a.length - offset;
      ... // 계산 수행
  }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;유효성 검사가 필요 없는 경우&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;유효성을 검사하는 비용이 지나치게 큰 경우 또는 계산 과정에서 암묵적으로 유효성 검사가 진행될 때&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;item 50. 적시에 방어적 복사본을 만들라&lt;/h2&gt;
&lt;p&gt;클라이언트가 우리의 불변식을 깨뜨리려 혈안이 되어 있다고 가정하고 &lt;strong&gt;방어적으로 프로그래밍해야 한다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;주의를 기울이지 않으면 자기도 모르게 내부를 수정하도록 허락하는 경우가 생긴다.&lt;/p&gt;
&lt;p&gt;Date는 낡은 API이니 새로운 코드를 작성할 때는 더 이상 사용하면 안 된다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Date가 가변이라는 사실을 이용해서 불변식을 깬다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;생성자&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;생성자에서 받은 가변 매개변수 각각을 방어적으로 복사해야 한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;매개변수의 유효성을 검사(item 49)하기 전에 방어적 복사본을 만들고, 이 복사본으로 유효성을 검사하자.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;멀티스레딩 환경이라면 원본 객체의 유효성을 검사한 후 복사본을 만드는 그 찰나의 취약한 순간에 다른 스레드가 원본 객체를 수정할 위험이 있기 때문이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;매개변수가 제3자에 의해 확장될 수 있는 타입이라면 방어적 복사본을 만들 때 clone을 사용해서는 안 된다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;clone이 다른 곳에서 정의한 것일 수도 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;접근자&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;접근자 메서드는 가변 필드의 방어적 복사본을 반환하자.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;  public Date start() {
      return new Date(start.getTime());
  }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;생성자와 달리 접근자 메서드에서는 방어적 복사에 clone을 사용해도 된다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Period가 가지고 있는 Date 객체는 java.util.Date임이 확실하기 때문이다.&lt;/li&gt;
&lt;li&gt;그렇더라도 item 13에서 설명한 이유 때문에 인스턴스를 복사하는 데는 일반적으로 생성자나 정적 팩터리를 쓰는게 좋다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;매개변수를 방어적으로 복사하는 목적이 불변 객체를 만들기 위해서만은 아니다.&lt;/p&gt;
&lt;h3&gt;클라이언트로부터 받을 때&lt;/h3&gt;
&lt;p&gt;메서드든 생성자든 &lt;strong&gt;클라이언트가 제공한 객체의 참조를 내부의 자료구조에 보관해야 할 때면 항시 그 객체가 잠재적으로 변경될 수 있는지를 생각해야 한다.&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;변경될 수 있는 객체라면 그 객체가 클래스에 넘겨진 뒤 임의로 변경되어도 그 클래스가 문제없이 동작할지를 따져보라.&lt;/li&gt;
&lt;li&gt;확신할 수 없다면 복사본을 만들어 저장해야 한다.&lt;/li&gt;
&lt;li&gt;클라이언트가 건네준 객체를 내부의 Set 인스턴스에 저장하거나 Map 인스턴스의 키로 사용한다면, 추후 그 객체가 변경될 경우 객체를 담고 있는 Set 혹은 Map의 불변식이 깨질 것이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;클래스가 불변이든 가변이든, 가변인 내부 객체를 클라이언트에 반환할 때는 반드시 심사숙고해야 한다.&lt;/p&gt;
&lt;h3&gt;클라이언트로 반환할 때&lt;/h3&gt;
&lt;p&gt;내부에서 사용하는 배열을 클라이언트에 반환할 때는 항상 방어적 복사를 수행해야 한다. 혹은 배열의 불변 뷰를 반환하라.(item 15)&lt;/p&gt;
&lt;p&gt;되도록 불변 객체들을 조합해 객체를 구성해야 방어적 복사를 할 일이 줄어든다.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;방어적 복사에는 성능 저하가 따르고, 항상 쓸 수 있는 것도 아니다(같은 패키지에 속하는 등의 이유로).&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;다른 패키지에서 사용한다고 해서 넘겨받은 가변 매개변수를 항상 방어적으로 복사해 저장해야 하는 것은 아니다.&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;때로는 메서드나 생성자의 매개변수로 넘기는 행위가 그 객체의 통제권을 명백히 이전함을 뜻하기도 한다.&lt;/li&gt;
&lt;li&gt;이처럼 통제권을 이전하는 메서드를 호출하는 클라이언트는 해당 객체를 더 이상 직접 수정하는 일이 없다고 약속해야 한다.&lt;/li&gt;
&lt;li&gt;관련 메서드나 생성자에 그 사실을 확실히 문서에 기재해야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;방어적 복사를 생략해도 되는 상황&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;해당 클래스와 그 클라이언트가 상호 신뢰할 수 있을 때&lt;/li&gt;
&lt;li&gt;불변식이 깨지더라도 그 영향이 오직 호출한 클라이언트로 국한될 때&lt;ul&gt;
&lt;li&gt;래퍼 클래스 패턴(itme 18)의 특성상 클라이언트는 래퍼에 넘긴 객체에 여전히 직접 접근할 수 있다.&lt;/li&gt;
&lt;li&gt;따라서 래퍼의 불변식을 쉽게 파괴할 수 있지만 그 영향을 오직 클라이언트 자신만 받게 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;item 51. 메서드 시그니처를 신중하게 설계하라&lt;/h2&gt;
&lt;h3&gt;메서드 이름을 신중하게 짓자&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;표쥰 명명 규칙에 따라 지으며 긴 이름은 지양해야 한다. &lt;/li&gt;
&lt;li&gt;애매하다면 자바 라이브러리 가이드를 참조해도 좋다.&lt;/li&gt;
&lt;li&gt;같은 패키지에 속한 다른 이름들과 일관되게 짓는 것이 좋다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;편의 메서드를 너무 많이 만들지 말자.&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;너무 많은 메서드는 그에 따른 문서화, 유지보수, 테스트를 요구한다. &lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;매개변수 목록은 짧게 유지하자.&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;메서드의 매개변수 목록도 4개 이하가 적절하다.&lt;/li&gt;
&lt;li&gt;특히 같은 타입의 매개변수 여러 개가 연달아 나오는 것은 좋지 않다. &lt;/li&gt;
&lt;li&gt;매개변수를 줄일 수 있는 방법들을 살펴보자.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;매개변수 타입으로는 클래스 보다 인터페이스가 낫다.&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;hashmap 보다는 map이 arraylist 보다는 list가 낫다.&lt;/li&gt;
&lt;li&gt;boolean보다는 원소 2개 짜리 enum 이 낫다. &lt;ul&gt;
&lt;li&gt;이름을 가질수 있어서 코드를 보는데 조금더 도움이 된다.&lt;/li&gt;
&lt;li&gt;물론 이름상 boolean 형태가 명확한 경우는 예외다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;item 52. 다중정의는 신중히 사용하라&lt;/h2&gt;
&lt;h3&gt;다중정의(overloading)&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;어느 메서드를 호출할지가 컴파일타임에 정해진다. &lt;/li&gt;
&lt;li&gt;다중정의한 메서드는 정적으로 선택된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;재정의(override)&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;재정의한 메서드는 동적으로 선택된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;API 사용자가 매개변수를 넘기면서 어떤 다중정의 메서드가 호출될지를 모른다면 프로그램이 오동작하기 쉽다. &lt;/p&gt;
&lt;p&gt;다중정의가 혼동을 일으키는 상황을 피해야 한다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;안전하고 보수적으로 가려면 매개변수 수가 같은 다중정의는 만들지 말자.&lt;/strong&gt;&lt;/p&gt;
&lt;h4&gt;이름을 다르게&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;다중정의하는 대신 메서드 이름을 다르게 지어주는 방법이 있다.&lt;/li&gt;
&lt;li&gt;한편, 생성자의 경우는 이름을 다르게 지을 수 없다. 그렇기 때문에 두 번째 생성자부터는 무조건 다중정의를 하게 되는 셈이다. 생성자는 정적 팩터리를 사용하는 대안을 활용할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;함수형 인터페이스는?&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;다중정의된 메서드들이 함수형 인터페이스를 인수로 받을 때 비록 서로 다른 함수형 인터페이스라도 인수 위치가 같으면 혼란이 생긴다. &lt;/li&gt;
&lt;li&gt;따라서 메서드를 다중정의할 때 서로 다른 함수형 인터페이스라도 같은 위치의 인수로 받아서는 안된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;프로그래밍 언어가 다중정의를 허용한다고 해서 다중정의를 꼭 활용하란 뜻은 아니다.&lt;/p&gt;
&lt;h2&gt;item 53. 가변인수는 신중히 사용하라&lt;/h2&gt;
&lt;p&gt;가변인수 메서드를 호출하면 인수의 개수와 길이가 같은 배열을 만들고 인수들을 만들어진 배열에 저장한 후에 가변인수 메서드에 전달해준다. &lt;/p&gt;
&lt;p&gt;&lt;strong&gt;인수가 1개 이상이어야 할 때&lt;/strong&gt;는 아래와 같이 가변인수 앞에 필수 매개변수를 받도록 하자.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;static int min(int firstArg, int... remainingArgs) {
    int min = firstArg;
    for (int arg : remainingArgs) {
        if (arg &amp;lt; min) {
            min = arg;
        }
    }
    return min;
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;가변인수는 성능에 해가 될 수 있다.&lt;/h3&gt;
&lt;p&gt;가변인수 메서드가 호출될 때마다 배열을 새로 할당하고 초기화하기 때문이다. &lt;/p&gt;
&lt;p&gt;따라서 아래와 같은 패턴으로 변경할 수도 있다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;```java
public void foo() {}
public void foo(int arg1) {}
public void foo(int arg1, arg2) {}
public void foo(int arg1, arg2, arg3) {}
public void foo(int arg1, arg2, arg3, int... restArg) {}
```&lt;/code&gt;&lt;/pre&gt;&lt;h2&gt;item 54. null이 아닌, 빈 컬렉션이나 배열을 반환하라&lt;/h2&gt;
&lt;p&gt;컬렉션이나 배열 같은 컨테이너가 비었을 때 null을 반환하는 메서드를 사용할 때면 항시 방어 코드를 넣어줘야 한다.&lt;/p&gt;
&lt;p&gt;클라이언트에서 방어 코드를 빼먹으면 오류가 발생할 수 있다.&lt;/p&gt;
&lt;p&gt;null을 반환하려면 반환하는 쪽에서도 이 상황을 특별히 취급해줘야 해서 코드가 더 복잡해진다.&lt;/p&gt;
&lt;h4&gt;null이 아닌 빈 배열이나 컬렉션을 반환하라.&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;null을 반환하는 API는 사용하기 어렵고 오류 처리 코드도 늘어난다. &lt;/li&gt;
&lt;li&gt;그렇다고 성능이 좋은 것도 아니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;빈 컨테이너를 할당하는 데 비용이 든다?&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;이 할당이 성능 저하의 주범이라고 확인되지 않는 한(item 67), 신경 안 써도 된다.&lt;/li&gt;
&lt;li&gt;빈 컬렉션과 배열은 굳이 새로 할당하지 않고도 반환할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;null 반환 - 따라 하지 말 것!&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;// 컬렉션이 비었으면 null을 반환한다. -따라 하지 말 것!
private final List&amp;lt;Cheese&amp;gt; cheesesInStock = ...;

public List&amp;lt;Cheese&amp;gt; getCheeses() {
    return cheesesInStock.isEmpty() ? null
        : new ArrayList&amp;lt;&amp;gt;(cheesesInStock);
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;빈 컬렉션 반환&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;// 빈 컬렉션을 반환하는 올바른 예
public List&amp;lt;Cheese&amp;gt; getCheeses() {
    return new ArrayList&amp;lt;&amp;gt;(cheesesInStock);
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;// 최적화 - 빈 컬렉션을 매번 새로 할당하지 않도록 했다.
// 매번 똑같은 빈 &amp;#39;불변&amp;#39; 컬렉션을 반환한다.
public List&amp;lt;Cheese&amp;gt; getCheeses() {
    return cheesesInStock.isEmpty() ? Collections.emptyList()
        : new ArrayList&amp;lt;&amp;gt;(cheesesInStock);
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;길이가 0일 수도 있는 배열 반환&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;// 길이가 0일 수도 있는 배열을 반환하는 올바른 방법
public Cheese[] getCheeses() {
    return cheesesInStock.toArray(new Cheese[0]);
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;// 최적화 - 빈 배열을 매번 새로 할당하지 않도록 했다.
private static final Cheese[] EMPTY_CHEESE_ARRAY = new Cheese[0];

public Cheese[] getCheeses() {
    return cheesesInStock.toArray(EMPTY_CHEESE_ARRAY);
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;cheesesInStock이 비었을 때면 언제나 EMPTY_CHEESE_ARRAY를 반환한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;단순히 성능을 개선할 목적이라면 toArray에 넘기는 배열을 미리 할당하는 건 추천하지 않는다. 오히려 성능이 떨어진다는 연구 결과도 있다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;// 나쁜 예 = 배열을 미리 할당하면 성능이 나빠진다.
return cheesesInStock.toArray(new Cheese[cheesesInStock.size()]);&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;item 55. 옵셔널 반환은 신중히 하라&lt;/h2&gt;
&lt;h3&gt;메서드가 특정 조건에서 값을 반환할 수 없을 때&lt;/h3&gt;
&lt;h4&gt;자바 8 이전&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;예외를 던짐&lt;ul&gt;
&lt;li&gt;예외는 진짜 예외적인 상황에서만 사용해야 한다.(item 69) &lt;/li&gt;
&lt;li&gt;예외를 생성할 때 스택 추적 전체를 캡처하므로 비용이 만만치 않다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;null을 반환&lt;ul&gt;
&lt;li&gt;별도의 null 처리 코드를 추가해야 한다.&lt;/li&gt;
&lt;li&gt;언젠가 NullPointerException이 발생할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;자바 8 이후&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Optional&amp;lt;T&amp;gt; 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Optional&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;null이 아닌 T 타입 참조를 하나 담거나, 혹은 아무것도 담지 않을 수 있다.&lt;/li&gt;
&lt;li&gt;원소를 최대 1개 가질 수 있는 &amp;#39;불변&amp;#39;컬렉션이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Optional 메서드&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;h4&gt;Optional.empty()&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;내부 값이 비어있는 Optional 객체 반환&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;h4&gt;Optional.of(T value)&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;내부 값이 value인 Optional 객체 반환&lt;/li&gt;
&lt;li&gt;만약 value가 null인 경우 NullPointerException 발생&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;h4&gt;Optional.ofNullable(T value)&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;가장 자주 쓰이는 Optional 생성 방법&lt;/li&gt;
&lt;li&gt;value가 null이면, empty Optional을 반환하고, 값이 있으면 Optional.of로 생성&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;h4&gt;T get()&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Optional 내의 값을 반환&lt;/li&gt;
&lt;li&gt;만약 Optional 내부 값이 null인 경우 NoSuchElementException 발생&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;h4&gt;boolean isPresent()&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Optional 내부 값이 null이면 false, 있으면 true&lt;/li&gt;
&lt;li&gt;Optional 내부에서만 사용해야하는 메서드라고 생각&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;h4&gt;boolean isEmpty()&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Optional 내부의 값이 null이면 true, 있으면 false&lt;/li&gt;
&lt;li&gt;isPresent() 메서드의 반대되는 메서드&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;h4&gt;void ifPresent(Consumer&amp;lt;? super T&amp;gt; consumer)&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Optional 내부의 값이 있는 경우 consumer 함수를 실행&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;h4&gt;Optional&amp;lt;T&amp;gt; filter(Predicate&amp;lt;T&amp;gt; predicate)&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Optional에 filter 조건을 걸어 조건에 맞을 때만 Optional 내부 값이 있음&lt;/li&gt;
&lt;li&gt;조건이 맞지 않으면 Optional.empty를 리턴&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;h4&gt;Optional&amp;lt;U&amp;gt; map(Funtion&amp;lt;? super T, ? extends U&amp;gt; f)&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Optional 내부의 값을 Function을 통해 가공&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;h4&gt;T orElse(T other)&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Optional 내부의 값이 있는 경우 그 값을 반환&lt;/li&gt;
&lt;li&gt;Optional 내부의 값이 null인 경우 other을 반환&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;h4&gt;T orElseGet(Supplier&amp;lt;? extends T&amp;gt; supplier)&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Optional 내부의 값이 있는 경우 그 값을 반환&lt;/li&gt;
&lt;li&gt;Optional 내부의 값이 null인 경우 supplier을 실행한 값을 반환&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;h4&gt;T orElseThrow()&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Optional 내부의 값이 있는 경우 그 값을 반환&lt;/li&gt;
&lt;li&gt;Optional 내부의 값이 null인 경우 NoSuchElementException 발생&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;h4&gt;T orElseThrow(Supplier&amp;lt;? extends X&amp;gt; exceptionSupplier)&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Optional 내부의 값이 있는 경우 그 값을 반환&lt;/li&gt;
&lt;li&gt;Optional 내부의 값이 null인 경우 exceptionSupplier을 실행하여 Exception 발생&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Optional을 사용하지 않은 예제&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;// 컬렉션에서 최댓값을 구한다(컬렉션이 비었으면 예외를 던진다).
public static &amp;lt;E extends Comparable&amp;lt;E&amp;gt;&amp;gt; E max(Collection&amp;lt;E&amp;gt; c) {
    if (c.isEmpty()) {
        throw new IllegalArgumentException(&amp;quot;빈 컬렉션&amp;quot;);
    }

    E result = null;
    for (E e : c) {
        if(result == null || e.compareTo(result) &amp;gt; 0) {
            result = Objects.requiredNonNull(e);
        }
    }

    return result;
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Optional을 사용한 예제&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;// 컬렉션에서 최댓값을 구해 Optional&amp;lt;E&amp;gt;로 반환한다.
public static &amp;lt;E extends Comparable&amp;lt;E&amp;gt;&amp;gt; Optional&amp;lt;E&amp;gt; max(Collection&amp;lt;E&amp;gt; c) {
    if (c.isEmpty()) {
        return Optional.empty();
    }

    E result = null;
    for (E e : c) {
        if (result == null || e.compareTo(result) &amp;gt; 0) {
            result = Objects.requiredNonNull(e);
        }
    }

    return Optional.of(result);
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Optional을 반환하여 client에서 더욱 더 유연하게 로직을 작성할 수 있다.&lt;/li&gt;
&lt;li&gt;Optional.of에 null을 넣으면 NullPointerException 이 발생하니 주의해야 한다.&lt;/li&gt;
&lt;li&gt;Optional을 리턴하는 메서드에서는 null을 리턴해서는 안된다. (Optional의 취지와 맞지 않기 때문)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Optional을 사용해야 하는 이유&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Optional은 검사 예외와 취지가 비슷하다. 즉, 반환값이 있을 수도 있고, 없을 수도 있음을 API 사용자에게 명확히 알려준다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Optional 활용1 - 기본값(defalut)를 정해둘 수 있다.&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;// 실제로 예외를 던진 것이 아니라, empty Optional이 리턴되기 때문에 예외 생성 비용이 들지 않는다.
String lastWordInLexicon = max(words).orElse(&amp;quot;단어 없음..&amp;quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Optional 활용2 - 원하는 예외를 던질 수 있다.&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;// 값이 없는 경우 원하는 예외를 던질 수 있다.
Toy myToy = max(toys).orElseThrow(TemperTantrumException::new);&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Optional 활용3 - 항상 값이 채워져 있는 경우&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;// 값이 없는 경우에는 NoSuchElementException이 발생하니 반드시 값이 있는 경우에만 사용해야 한다.
Element lastNobleGas = max(Elements.NOBLE_GASES).get();&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Optional 활용4 - 기본값을 설정하는 비용이 큰 경우&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;// 기본값을 설정하는 비용이 아주 커서 부담이 되는 경우 orElseGet을 사용하면,
// 값이 처음 필요할 때 Supplier를 사용해 생성하므로 초기 생성비용을 낮출 수 있다.
Element lastNobleGas = max(Elements.NOBLE_GASES).get();&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Optional 안티 패턴&lt;/h3&gt;
&lt;h4&gt;Collection, Stream, 배열은 Optional로 감싸지 말자&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Optional&amp;lt;List&lt;T&gt;&amp;gt;를 반환하기 보다는 빈 ArrayList를 반환하는 것이 좋다. 그렇게 하면 클라이언트 코드에서 Optional 처리 코드를 넣지 않아도 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Optional을 Map의 키나 값으로 사용하지 말자&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Optional은 Collection의 키, 값, 원소나 배열의 원소로 사용하는게 적절한 상황은 거의 없다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;isPresent()를 사용하지 말자&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;Optional.ofNullable(school).map(School::getClassRoom)    //Optional&amp;lt;School&amp;gt;
                           .map(ClassRoom::getTeacher)   //Optional&amp;lt;ClassRoom&amp;gt;
                           .map(Teacher::getSubject)     //Optional&amp;lt;Teacher&amp;gt;
                           .map(Subject::getSubjectName) //Optional&amp;lt;Subject&amp;gt;
                           .orElse(null);&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;반드시 이런식으로 사용하자.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;추가 내용&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;박싱된 기본타입을 사용할 때에는 OptionalInt, OptionalDouble, OptionalLong을 사용하자&lt;ul&gt;
&lt;li&gt;박싱된 기본타입을 담는 Optional은 기본타입 보다 무거울 수 밖에 없다.&lt;/li&gt;
&lt;li&gt;따라서 OptionalInt, OptionalDouble, OptionalLong을 사용하는 것이 조금 더 낫다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;값을 반환하지 못할 가능성이 있고, 호출할 때마다 반환값이 없을 가능성을 염두에 둬야 하는 메서드라면&lt;br&gt;Optional을 반환해야 하는 상황일 수 있다.&lt;/li&gt;
&lt;li&gt;Optional을 반환값 이외의 용도로 쓰는 경우는 거의 없다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;출처&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://jaehun2841.github.io/2019/02/24/effective-java-item55/#optional-%EC%9D%B4%EB%9E%80&quot;&gt;https://jaehun2841.github.io/2019/02/24/effective-java-item55/#optional-%EC%9D%B4%EB%9E%80&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;item 56. 공개된 API 요소에는 항상 문서화 주석을 작성하라&lt;/h2&gt;
&lt;p&gt;여러분의 API를 올바로 문서화하려면 공개된 모든 클래스, 인터페이스, 메서드, 필드 선언에 문서화 주석을 달아야 된다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;메서드 주석에서는 HOW가 아닌 WHAT을 기술해야 된다.&lt;/li&gt;
&lt;li&gt;한 클래스 안에 설명이 똑같은 맴버(혹은 생성자)가 둘 이상이면 안 된다.&lt;/li&gt;
&lt;li&gt;제네릭 타입이나 제네릭 메서드를 문서화할 때는 모든 타입 매개변수에 주석을 달아야 한다.&lt;/li&gt;
&lt;li&gt;열거 타입을 문서화할 때는 상수에도 주석을 달아야 한다.&lt;/li&gt;
&lt;li&gt;에너테이션 타입을 문서화할 때는 멤버들에도 모두 주석을 달아야 한다.&lt;/li&gt;
&lt;li&gt;클래스 혹은 정적 메서드의 쓰레드 안전수준을 반드시 API 설명에 포함해야 한다. (item 82)&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Java</category>
      <category>이펙티브 자바</category>
      <author>머래그로프</author>
      <guid isPermaLink="true">https://goldfishhead.tistory.com/106</guid>
      <comments>https://goldfishhead.tistory.com/106#entry106comment</comments>
      <pubDate>Mon, 21 Sep 2020 11:19:28 +0900</pubDate>
    </item>
    <item>
      <title>[이펙티브 자바] 제네릭</title>
      <link>https://goldfishhead.tistory.com/105</link>
      <description>&lt;h2&gt;item 26. 로 타입은 사용하지 말라&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;로 타입을 쓰면 제네릭이 안겨주는 안전성과 표현력을 모두 잃게 된다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;비한정적 와일드카드 타입인 Set&amp;lt;?&amp;gt;와 로 타입인 Set의 차이&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;와일드카드 타입은 안전하고, 로 타입은 안전하지 않다.&lt;/li&gt;
&lt;li&gt;Collection&amp;lt;?&amp;gt;에는 (null 외에는) 어떤 원소도 넣을 수 없고 컬렉션에서 꺼낼 수 있는 객체의 타입도 전혀 알 수 없다.&lt;/li&gt;
&lt;li&gt;이러한 제약을 받아들일 수 없다면 제너릭 메서드(item 30)나 한정적 와일드카드 타입(item 31)을 사용하면 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;로 타입을 쓰지 말라는 규칙에도 소소한 예외가 몇 개 있다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;첫 번쨰 예외. class 리터럴에는 로 타입을 써야 한다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;허용: List.class, String[].class, int.class&lt;/li&gt;
&lt;li&gt;허용하지 않음: List&lt;String&gt;.class, List&amp;lt;?&amp;gt;.class &lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;두 번째 예외. instanceof 연산자와 관련이 있다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;런타임에는 제네릭 타입 정보가 지워지므로 instanceof 연산자는 비 한정적 와일드카드 타입 이외의 매개변수화 타입에는 적용할 수 없다.&lt;/li&gt;
&lt;li&gt;로 타입이든 비한정적 와일드카드 타입이든 instanceof는 완전히 똑같이 동작한다.&lt;/li&gt;
&lt;li&gt;차라리 깔끔한 로 타입을 쓰자.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;item 27. 비검사 경고를 제거하라&lt;/h2&gt;
&lt;p&gt;비검사 경고는 중요하니 무시하지 말자. &lt;/p&gt;
&lt;p&gt;모든 비검사 경고는 런타임에 ClassCastException을 일으킬 수 있는 잠재적 가능성을 뜻하니 최선을 다해 제거하라.&lt;/p&gt;
&lt;p&gt;경고를 제거할 수는 없지만 타입이 안전하다고 확신할 수 있다면 @SuppressWarnings(&amp;quot;unchecked&amp;quot;) 에너테이션을 달아 경고를 숨기자.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;@SuppressWarnings 에너테이션은 항상 가능한 한 좁은 범위에 적용하자.&lt;/li&gt;
&lt;li&gt;한 줄이 넘는 메서드나 생성자에 달린 @SuppressWarnings 에너테이션을 발견하면 지역변수 선언 쪽으로 옮기자.&lt;/li&gt;
&lt;li&gt;경고를 무시해도 안전한 이유를 항상 주석으로 남겨야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;item 28. 배열보다는 리스트를 사용하라&lt;/h2&gt;
&lt;p&gt;배열은 공변이고 실체화되지만, 제네릭은 불공변이고 타입 정보가 소거된다.&lt;/p&gt;
&lt;p&gt;따라서 배열은 런타임에는 안전하지만, 컴파일타임에는 안전하지 않다.&lt;/p&gt;
&lt;p&gt;반면에 제네릭은 컴파일타임에 안전하다.&lt;/p&gt;
&lt;h3&gt;배열과 제네릭 타입의 차이 두가지&lt;/h3&gt;
&lt;p&gt;1) 배열은 함께 변하고 제네릭은 함께 변하지 않는다.&lt;/p&gt;
&lt;p&gt;2) 배열은실체화 된다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;배열은 런타임시에도 자신이 담기로 한 원소의 타입을 인지하고 확인하는 반면 제네릭은 타입정보가 런타임시에 하위호환성을 위해 제거된다.&lt;/li&gt;
&lt;li&gt;제네릭 타입안에 배열을 넣게 되면 런타임시 타입이 제거되기에 에러가 발생한다. 이러한 이유로 제네릭 배열이 생성되지 않도록 되어있다.&lt;/li&gt;
&lt;li&gt;E, List&lt;E&gt;, List&lt;String&gt; 같은 타입을 실체화 불가 타입이라 하며 실체화될 수 있는 타입은 List&amp;lt;?&amp;gt;같은 비 한정적 와일드 카드 타입 뿐이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;public class Chooser {
    private final Object[] choiceArray;

    public Chooser(Collection choices) {
        this.choiceArray = choices.toArray();
    }

    public Object choose() {
        Random rnd = ThreadLocalRandom.current();
        return choiceArray[rnd.nextInt(choiceArray.length)];
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;choose() 메서드를 사용하는 곳에서는 반환된 Object를 매번 형변환 해야 한다. &lt;/p&gt;
&lt;p&gt;혹시나 타입이 다른 원소가 들어 있었다면 런타임에 형변환 오류가 날 것이다.&lt;/p&gt;
&lt;p&gt;위의 코드를 제네릭을 사용하여 바꿔보자.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;public class Chooser&amp;lt;T&amp;gt; {
    private final T[] choiceArray;

    public Chooser(Collection&amp;lt;T&amp;gt; choices) {
        // incompatible types 오류를 해결하기 위해 형변환 사용
        this.choiceArray = (T[]) choices.toArray();
    }

    // choose 메소드는 동일하다.
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;위의 코드에서는 Unchecked Cast 경고가 발생한다. &lt;/p&gt;
&lt;p&gt;타입 매개변수 T가 어떤 타입인지 알 수 없으니 형변환이 런타임에도 안전한지 보장할 수가 없다는 메시지이다. &lt;/p&gt;
&lt;p&gt;제네릭은 런타임에는 타입 정보가 소거되므로 무슨 타입인지 알 수 없다. &lt;/p&gt;
&lt;p&gt;Unchecked Cast과 같은 비검사 형변환 경고를 제거하려면 배열 대신 리스트를 사용하면 된다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;// 리스트 기반 Chooser - 타입 안전성 확보 !
class Chooser&amp;lt;T&amp;gt; {
    private final List&amp;lt;T&amp;gt; choiceList;

    public Chooser(Collection&amp;lt;T&amp;gt; choices) {
        this.choiceList = new ArrayList&amp;lt;&amp;gt;(choices);
    }

    public T choose() {
        Random rnd = ThreadLocalRandom.current();
        return choiceList.get(rnd.nextInt(choiceList.size()));
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;배열과 제네릭을 섞어 쓰다가 컴파일 오류나 경고를 만나면, 가장 먼저 배열을 리스트로 대체하는 방법을 적용해보자 !&lt;/p&gt;
&lt;h2&gt;item 29. 이왕이면 제네릭 타입으로 만들라&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;// Object 기반 스택 - 제네릭이 절실한 강력 후보!
public class Stack {
    private Object[] elements;
    private int size = 0;
    private static final int DEFAULT_INITIAL_CAPACITY = 16;

    public Stack(){
        elements = new Object[DEFAULT_INITIAL_CAPACITY];
    }

    public void push(Object e){
        ensureCapacity();
        elements[size++] = e;
    }

    public Object pop(){
        if(size == 0)
            throw new EmptyStackException();
        Object result = elements[--size];
        elements[size] = null;
        return result;
    }

    public boolean isEmpty(){
        return size == 0;
    }

    private void ensureCapacity(){
        if(elements.length == size){
            elements = Arrays.copyOf(elements, 2 * size + 1);
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;E와 같은 실체화 불가 타입으로는 배열을 만들 수 없다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;이에 대한 적절한 해결책은 두 가지다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;제네릭 배열 생성을 금지하는 제약을 대놓고 우회하는 방법&lt;/li&gt;
&lt;li&gt;elements 필드의 타입을 E[]에서 Object[]로 바꾸는 방법&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;첫 번째 방식에서는 형변환을 배열 생성 시 단 한 번만 해주면 되지만, 두 번쨰 방식에서는 배열에서 원소를 읽을 때마다 해줘야 한다.&lt;/p&gt;
&lt;p&gt;현업에서는 첫 번째 방식을 더 선호하며 자주 사용한다.&lt;/p&gt;
&lt;p&gt;하지만 배열의 런타임 타입이 컴파일타임 타입과 달라 힙 오염(item 32)을 일으킨다.&lt;/p&gt;
&lt;p&gt;아래의 코드는 첫 번째 방법을 사용한 코드이다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;public class Stack&amp;lt;E&amp;gt; {
    private E[] elements;
    private int size = 0;
    private static final int DEFAULT_INITIAL_CAPACITY = 16;


    // 배열 elements 는 push(E)로 넘어온 E 인스턴스만 담는다.
    // 따라서 타입 안전성을 보장하지만,
    // 이 배열의 런타임 타입은 E[]가 아닌 Object[]다!
    @SuppressWarnings(&amp;quot;unchecked&amp;quot;)
    public Stack(){
        elements = (E[]) new Object [DEFAULT_INITIAL_CAPACITY]}
    }


    public void push(E e){
        ensureCapacity();
        elements[size++] = e;
    }

    public E pop(){
        if(size == 0){
            throw new EmptyStackException();
        }

        E result = elements[--size];
        elements[size] = null;
        return result;
    }

    public boolean isEmpty(){
        return size == 0;
    }

    private void ensureCapacity(){
        if(elements.length == size){
            elements = Arrays.copyOf(elements, 2 * size + 1);
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;제네릭 타입 안에서 리스트를 사용하는 게 항상 가능하지도, 꼭 더 좋은 것도 아니다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;자바가 리스트를 기본 타입으로 제공하지 않으므로 ArrayList 같은 제네릭 타입도 결국은 기본 타입인 배열을 사용해 구현해야 한다.&lt;/p&gt;
&lt;p&gt;또한 HashMap 같은 제네릭 타입은 성능을 높일 목적으로 배열을 사용하기도 한다.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;클라이언트에서 직접 형변환해야 하는 타입보다 제네릭 타입이 더 안전하고 쓰기 편하다. &lt;/p&gt;
&lt;p&gt;그러니 &lt;strong&gt;새로운 타입을 설계할 때는 형변환 없이도 사용할 수 있도록 하라.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;기존 클라이언트에는 아무 영향을 주지 않으면서, 새로운 사용자를 훨씬 편하게 해주는 길이다.(item 26)&lt;/p&gt;
&lt;h2&gt;item 30. 이왕이면 제너릭 메서드로 만들라&lt;/h2&gt;
&lt;p&gt;클래스와 마찬가지로, 메서드도 제네릭으로 만들 수 있다.&lt;/p&gt;
&lt;p&gt;매개변수화 타입을 받는 정적 유틸리티 메서드는 보통 제네릭이다.&lt;/p&gt;
&lt;p&gt;타입 매개변수 목록은 메서드의 제한자와 반환 타입 사이에 온다.&lt;/p&gt;
&lt;h3&gt;제네릭 싱글톤 팩터리&lt;/h3&gt;
&lt;p&gt;제네릭은 런타임에 타입 정보가 소거(item 28)되므로 하나의 객체를 어떤 타입으로든 매개변수화 할 수 있다. &lt;/p&gt;
&lt;p&gt;하지만 요청한 타입 매개변수에 맞도록 매번 그 객체의 타입을 바꿔주는 정적 팩터리를 만들어야 한다.&lt;br&gt;(예: Collections.reverseOrder, Collections.emptySet)&lt;/p&gt;
&lt;h3&gt;재귀적 타입 한정&lt;/h3&gt;
&lt;p&gt;자기 자신이 들어간 표현식을 사용하여 타입 매개변수의 허용 범위를 한정할 수 있다.&lt;/p&gt;
&lt;p&gt;주로 타입의 자연적 순서를 정하는 Comparable 인터페이스(item 14)와 함께 쓰인다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;// 재귀적 타입 한정을 이용해 상호 비교할 수 있음을 표현했다.
public static &amp;lt;E extends Comparable&amp;lt;E&amp;gt;&amp;gt; E max(Collection&amp;lt;E&amp;gt; c);&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;위의 코드에서 타입 한정인 &lt;code&gt;&amp;lt;E extends Comparable&amp;lt;E&amp;gt;&amp;gt;&lt;/code&gt;는 “모든 타입 E는 자신과 비교할 수 있다” 라고 읽을 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;형변환 해야할 일이 많다면 경우 제네릭 메서드를 사용하자.&lt;/strong&gt;&lt;/p&gt;
&lt;h3&gt;정리가 잘 되어있는 글&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://medium.com/@joongwon/java-java%EC%9D%98-generics-604b562530b3&quot;&gt;[ Java] Java의 Generics - Leopold Baik (백중원) - Medium&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;item 31. 한정적 와일드카드를 사용해 API 유연성을 높이라&lt;/h2&gt;
&lt;h3&gt;제네릭은 불공변&lt;/h3&gt;
&lt;p&gt;item 28에서 이야기했듯 매개변수화 타입은 불공변(invariant) 이다. &lt;/p&gt;
&lt;p&gt;즉, 서로 다른 타입 Type1과 Type2가 있을 때, List&lt;Type1&gt;은 List&lt;Type2&gt;의 하위 타입도 상위 타입도 아니다.&lt;/p&gt;
&lt;h3&gt;생산자(Producer)와 와일드카드&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;// 와일드카드 타입을 사용하지 않은 pushAll 메서드 - 결함이 있다!
public void pushAll(Iterable&amp;lt;E&amp;gt; src) {
    for (E e : src) {
        push(e);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;위의 코드는 컴파일은 정상적으로 수행되지만 아래와 같이 Number 타입으로 선언된 Stack 객체의 메서드에 Integer 타입의 매개변수를 전달하면 컴파일 오류가 발생한다.&lt;/p&gt;
&lt;p&gt;Integer는 Number의 하위 타입이니 정상적으로 잘 동작할 것 같지만 그렇지 않다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;제네릭의 매개변수화 타입은 불공변이기 때문에 상위-하위 자료형의 관계가 없다.&lt;/strong&gt; 이러한 문제를 해결하기 위해 한정적 와일드카드(bounded wildcard) 자료형을 사용한다. &lt;/p&gt;
&lt;p&gt;Integer 클래스는 Number를 상속한 구현체 이므로 아래와 같이 매개변수 부분에 선언한다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;// 생산자(producer) 매개변수에 와일드카드 타입 적용
public void pushAll(Iterable&amp;lt;? extends E&amp;gt; src) {
    for (E e : src) {
        push(e);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;선언한 부분은 &lt;strong&gt;매개변수는 E의 Iterable이 아니라 E의 하위 타입의 Iterable&lt;/strong&gt; 이라는 뜻이다.&lt;/p&gt;
&lt;p&gt;이제 Number 클래스를 상속하는 Integer, Long, Double 등의 타입 요소를 가질 수 있게 된다.&lt;/p&gt;
&lt;h3&gt;소비자(Consumer)와 와일드카드&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;// 와일드카드 타입을 사용하지 않은 popAll 메서드 - 결함이 있다!
public void popAll(Collection&amp;lt;E&amp;gt; dst) {
    while(!isEmpty()) {
        dst.add(pop());
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;pushAll 함수와 유사하게 Collection의 요소 타입과 Stack의 요소 타입이 일치하면 오류는 발생하지 않으나, 위에서 작성한 예제처럼 타입이 일치하지 않으면 컴파일 에러가 발생한다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;// 소비자(consumer) 매개변수에 와일드카드 타입 적용
public void popAll(Collection&amp;lt;? super E&amp;gt; dst) {
    while(!isEmpty()) {
        dst.add(pop());
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;선언한 부분은 &lt;strong&gt;매개변수는 E의 Collection이 아니라 E의 상위 타입인 Collection&lt;/strong&gt; 이라는 뜻이다.&lt;/p&gt;
&lt;p&gt;이제 받을 수 있는 모든 타입은 자기 자신의 상위 타입이다.&lt;/p&gt;
&lt;h3&gt;PECS&lt;/h3&gt;
&lt;p&gt;Producer-Extends-Consumer-Super의 약자이다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;메서드의 매개변수 타입이 생산자를 나타내면 &amp;lt;? extends T&amp;gt;를 사용하고 소비자의 역할을 한다면 &amp;lt;? super T&amp;gt;를 사용하면 된다.&lt;/strong&gt;&lt;/p&gt;
&lt;h3&gt;Advanced&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;메서드의 리턴값에는 와일드카드 타입을 사용하면 안된다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;왜냐하면 메서드를 사용하는 클라이언트 코드에서도 메서드 반환 값으로 와일드카드 자료형을 써야하기 때문이다.&lt;/p&gt;
&lt;p&gt;재귀적 타입 한정(Recursive Type Bound)을 사용한 메서드를 살펴보자.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;// 변경 전
public static &amp;lt;E extends Comparable&amp;lt;E&amp;gt;&amp;gt; E max(Collection&amp;lt;E&amp;gt; collection)

// 변경 후(PECS 공식 2번 적용)
public static &amp;lt;E extends Comparable&amp;lt;? super E&amp;gt;&amp;gt; E max(Collection&amp;lt;? extends E&amp;gt; collection)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;매개변수는 Producer 이므로 매개변수 선언 부분은 Collection&amp;lt;? extends E&amp;gt;가 되어야 한다. &lt;/p&gt;
&lt;p&gt;그리고 Comparable 은 E 인스턴스를 소비하는 Consumer이므로 super가 적용된다. 따라서 아래와 같이 PECS 공식을 2번 적용한 형태로 변경되어야 한다.&lt;/p&gt;
&lt;p&gt;복잡하지만 위와 같은 방식은 Comparable을 예로 들었을 때, Comparable을 직접 구현하지 않고 직접 구현한 다른 클래스를 확장한 타입을 지원할 때 필요하다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;List&amp;lt;ScheduledFuture&amp;lt;?&amp;gt;&amp;gt; scheduledFutures = ...;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;위에서 수정 전 max는 이 리스트를 처리할 수 없다. 그 이유를 알기 위해 ScheduledFuture 인터페이스의 구현 코드를 살펴보자.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;public interface ScheduledFuture&amp;lt;V&amp;gt; extends Delayed, Future&amp;lt;V&amp;gt; 
public interface Delayed extends Comparable&amp;lt;Delayed&amp;gt;
public interface Comparable&amp;lt;T&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;ScheduledFuture은 Delayed의 하위 인터페이스이며 Delayed는 Comparable&lt;Delayed&gt;를 확장했다.&lt;/p&gt;
&lt;p&gt;반면에 ScheduledFuture은 Comparable&lt;ScheduledFuture&gt;를 확장하지 않았다.&lt;/p&gt;
&lt;p&gt;ScheduledFuture의 인스턴스는 다른 ScheduledFuture 인스턴스뿐 아니라 Delayed 인스턴스와도 비교할 수 있어서 수정 전 max가 이 리스트를 거부하는 것이다.&lt;/p&gt;
&lt;p&gt;C&lt;strong&gt;omparable(혹은 Compatator)을 직접 구현하지 않고 직접 구현한 다른 타입을 확장한 타입을 지원하기 위해 와일드카드가 필요하다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;와일드카드와 관련해 하나 더 살펴보자.&lt;/p&gt;
&lt;p&gt;타입 매개변수와 와일드카드에는 공통되는 부분이 있어서, 메서드를 정의할 때 둘 중 어느 것을 사용해도 괜찮을 때가 많다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;public static &amp;lt;E&amp;gt; void swap(List&amp;lt;E&amp;gt; list, int i, int j);
public static void swap(List&amp;lt;?&amp;gt; list, in i, int j);&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;public API라면 간단한 두 번째가 낫다. &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;어떤 리스트든 이 메서드에 넘기면 명시한 인덱스의 원소들을 교환해 줄 것이다. &lt;/li&gt;
&lt;li&gt;신경 써야 할 타입 매개변수도 없다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;메서드 선언에 타입 매개변수가 한 번만 나오면 와일드카드로 대체하라.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;비한정적 타입 매개변수 -&amp;gt; 비한정적 와일드카드&lt;/li&gt;
&lt;li&gt;한정적 타입 매개변수 -&amp;gt; 한정적 와일드카드&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;하지만 아래와 같은 코드에서 컴파일되지 않는다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;public static void swap(List&amp;lt;?&amp;gt; list, in i, int j) {
  list.set(i, list.set(j, list.get(i)));
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;List&amp;lt;?&amp;gt;에는 null 외에는 어떤 값도 넣을 수 없다.&lt;/p&gt;
&lt;p&gt;이 상황에서 형변환이나 리스트의 로 타입을 사용하지 않고도 해결할 길이 있다.&lt;/p&gt;
&lt;p&gt;바로 &lt;strong&gt;와일드카드 타입의 실제 타입을 알려주는 메서드를 private 도우미 메서드로 따로 작성&lt;/strong&gt;하여 활용하는 방법이다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;public static void swap(List&amp;lt;?&amp;gt; list, in i, int j) {
  swapHelper(list, i, j);
}

// 와일드카드 타입을 실제 타입으로 바꿔주는 private 도우미 메서드
private static &amp;lt;E&amp;gt; void swapHelper(List&amp;lt;E&amp;gt; list, int i, int j) {
  list.set(i, list.set(j, list.get(i)));
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이 리스트에서 꺼낸 값의 타입은 항상 E이고, E 타입의 값이라면 이 리스트에 넣어도 안전함을 알고 있다.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;조금 복잡하더라도 와일드카드 타입을 적용시키면 API가 훨씬 유연해진다. 그러니 널리 쓰일 라이브러리를 작성한다면 반드시 와일드카드 타입을 적절히 사용해줘야 한다. PECS 공식을 기억하자 !&lt;/p&gt;</description>
      <category>Java</category>
      <category>이펙티브 자바</category>
      <author>머래그로프</author>
      <guid isPermaLink="true">https://goldfishhead.tistory.com/105</guid>
      <comments>https://goldfishhead.tistory.com/105#entry105comment</comments>
      <pubDate>Mon, 21 Sep 2020 11:16:22 +0900</pubDate>
    </item>
    <item>
      <title>[이펙티브 자바] 클래스와 인터페이스</title>
      <link>https://goldfishhead.tistory.com/104</link>
      <description>&lt;h2&gt;아이템 15. 클래스와 멤버의 접근 권한을 최소화하라&lt;/h2&gt;
&lt;p&gt;잘 설계된 컴포넌트는 모든 내부 구현을 완벽히 숨겨, 구현과 API를 깔끔히 분리한다.&lt;/p&gt;
&lt;p&gt;프로그램 요소의 접근성은 가능한 한 최소한으로 하라.&lt;/p&gt;
&lt;h4&gt;public 클래스는 상수용 public static final 필드 외에는 어떠한 public 필드도 가져서는 안 된다. public static final 필드가 참조하는 객체가 불변인지 확인하자.&lt;/h4&gt;
&lt;p&gt;public 가변 필드를 갖는 클래스는 일반적으로 스레드 안전하지 않다.&lt;/p&gt;
&lt;h4&gt;클래스에서 public static final 배열 필드를 두거나 이 필드를 반환하는 접근자 메서드를 제공해서는 안 된다. (내용을 수정할 수 있게 된다)&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;// 보안 허점이 숨어 있다.
public static final Thing[] VALUES = { ... };&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;해결책 1. public 불변 리스트 추가&lt;/h5&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;private static final Thing[] PRIVATE_VALUES = { ... };
public static final List&amp;lt;Thing&amp;gt; VALUES = Collections.unmodifiableList(Arrays.asList(PRIVATE_VALUES));&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;해결책 2. 복사본을 반환하는 public 메서드 추가&lt;/h5&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;private static final Thing[] PRIVATE_VALUES = { ... };
public static final List&amp;lt;Thing&amp;gt; values() {
  return PRIVATE_VALUES.clone();
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;java 9 에서는 모듈 시스템이라는 개념이 도입되었다.&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;모듈은 패키지들의 묶음&lt;/li&gt;
&lt;li&gt;모듈은 자신에 속하는 패키지 중 공개(export)할 것들을 (관례상 module-info.java 파일에) 선언한다.&lt;/li&gt;
&lt;li&gt;protected 혹은 public 멤버라도 해당 패키지를 공개하지 않았다면 모듈 외부에서는 접근할 수 없다.&lt;/li&gt;
&lt;li&gt;모듈 jar 파일을 자신의 모듈 경로가 아닌 애플리케이션의 classpath에 두면 그 모듈 안의 모든 패키지는 마치 모듈이 없는 것처럼 행동한다.(공개 여부와 상관없이 모듈 밖에서 접근할 수 있게 된다)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;아이템 16. public 클래스에서는 public 필드가 아닌 접근자 메소드를 사용하라&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;class Point {
  public double x;
  public double y;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이런 클래스는 캡슐화의 이점을 제공하지 못한다.&lt;/p&gt;
&lt;p&gt;API를 수정하지 않고는 내부 표현을 바꿀 수 없고, 불변식을 보장할 수 없다.&lt;/p&gt;
&lt;p&gt; 외부에서 필드에 접근할 때 부수 작업을 수행할 수도 없다.&lt;/p&gt;
&lt;p&gt;패키지 바깥에서 접근할 수 있는 클래스라면 접근자를 제공하자.&lt;/p&gt;
&lt;p&gt;package-private 클래스 혹은 private 중첩 클래스라면 데이터 필드를 노출한다 해도 하등의 문제가 없다. &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;이 방식은 클래스 선언 면에서나 이를 사용하는 클라이언트 코드 면에서나 접근자 방식보다 훨씬 깔끔하다.&lt;/li&gt;
&lt;li&gt;클라이언트 코드가 이 클래스 내부 표현에 묶이기는 하나, 클라이언트도 어차피 이 클래스를 포함하는 패키지 안에서만 동작하는 코드일 뿐이다.&lt;/li&gt;
&lt;li&gt;패키지 바깥 코드는 전혀 손대지 않고도 데이터표현 방식을 바꿀 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;불편 필드라면 노출해도 덜 위험하지만 완전히 안심할 수는 없다. 하지만 package-private 클래스나 private 중첩 클래스에서는 종종 (불변이든 가변이든) 필드를 노출하는 편이 나을 때도 있다.&lt;/p&gt;
&lt;h2&gt;아이템 17. 변경 가능성을 최소화하라&lt;/h2&gt;
&lt;h3&gt;클래스를 불변으로 만들기 위한 다섯가지 규칙&lt;/h3&gt;
&lt;p&gt;객체의 상태를 변경하는 메서드를 제공하지 않는다.&lt;/p&gt;
&lt;p&gt;클래스를 확장할 수 없도록 한다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;상속을 막는 대표적인 방법은 클래스를 final로 선언하는 것&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;모든 필드를 final로 선언한다.&lt;/p&gt;
&lt;p&gt;모든 필드를 private으로 선언한다.&lt;/p&gt;
&lt;p&gt;자신 외에는 내부의 가변 컴포넌트에 접근할 수 없도록 한다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;클래스에 가변 객체를 참조하는 필드가 하나라도 있다면 클라이언트에서 그 객체의 참조를 얻을 수 없도록 해야 한다.&lt;/li&gt;
&lt;li&gt;이런 필드는 절대 클라이언트가 제공한 객체 참조를 가리키게 해서는 안 되며, 접근자 메서드가 그 필드를 그대로 반환해서도 안 된다.&lt;/li&gt;
&lt;li&gt;생성자, 접근자, readObject 메서드(item 88) 모두에서 방어적 복사를 수행하라.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;피연산자에 함수를 적용해 그 결과를 반환하지만, 피연산자 자체는 그대로인 프로그래밍 패턴을 함수형 프로그래밍이라 한다.&lt;/p&gt;
&lt;p&gt;절자척 혹은 명령형 프로그래밍에서는 메서드에서 피연산자인 자신을 수정해 자신의 상태가 변하게 된다.&lt;/p&gt;
&lt;p&gt;메서드 이름으로 (add 같은) 동사 대신 (plus 같은) 전치사를 사용한다. 이는 해당 메서드가 객체의 값을 변경하지 않는다는 사실을 강조하려는 의도다.&lt;/p&gt;
&lt;p&gt;이 방식으로 프로그래밍하면 코드에서 불변이 되는 영역의 비율이 높아지는 장점을 누릴 수 있다.&lt;/p&gt;
&lt;h4&gt;불변 객체는 단순하다.&lt;/h4&gt;
&lt;p&gt;반면 가변 객체는 임의의 복잡한 상태에 놓일 수 있다.&lt;/p&gt;
&lt;h4&gt;불변 객체는 근본적으로 스레드 안전하여 따로 동기화할 필요 없다.&lt;/h4&gt;
&lt;p&gt;불변 객체에 대해서는 그 어떤 스레드도 다른 스레드에 영향을 줄 수 없으니 &lt;strong&gt;불변객체는 안심하고 공유할 수 있다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;따라서 불변 클래스라면 한번 만든 인스턴스를 최대한 재활용하기를 권한다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;정적 팩터리를 제공하자. (item 1)&lt;/li&gt;
&lt;li&gt;메모리 사용량과 가비지 컬렉션 비용이 줄어든다.&lt;/li&gt;
&lt;li&gt;새로운 클래스를 설계할 때 public 생성자 대신 정적 팩터리를 만들어두면, 클라이언트를 수정하지 않고도 필요에 따라 캐시 기능을 나중에 덧붙일 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;불변 객체는 자유롭게 공유할 수 있음은 물론, 불변 객체끼리는 내부 데이터를 공유할 수 있다.&lt;/h4&gt;
&lt;h4&gt;객체를 만들 때 다른 불변 객체들을 구성요소로 사용하면 이점이 많다.&lt;/h4&gt;
&lt;h4&gt;불변 객체는 그 자체로 실패 원자성(item 76)을 제공한다.&lt;/h4&gt;
&lt;h4&gt;단점. 값이 다르면 반드시 독립된 객체로 만들어야 한다.&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;원하는 객체를 완성하기까지의 단계가 많고, 그 중간 단계에서 만들어진 객체들이 모두 버려진다면 성능 문제가 더 불거진다.&lt;/li&gt;
&lt;li&gt;해결책. 다단계 연산들을 예측하여 기본 기능으로 제공하는 방법&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;모든 생성자를 priate 혹은 package-private 으로 만들고 public 정적 팩터리를 제공하자.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;public class Complex {
  private final double re;
  private final double im;

  private Complex(double re, double im) {
    this.re = re;
    this.im = im;
  }
  public static Complex valueOf(double re, double im) {
    return new Complex(re, im);
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;만약 신뢰할 수 없는 클라이언트로부터 BigInteger나 BigDecimal의 인스턴스를 인수로 받는다면 주의해야 한다.&lt;/strong&gt; 인수로 받은 객체가 진짜 BigInteger인지 반드시 확인해야 한다. &lt;/p&gt;
&lt;p&gt;다시 말해 신뢰할 수 없는 하위 클래스의 인스턴스라고 확인되면, 이 인수들은 가변이라 가정하고 방어적으로 복사해 사용해야 한다.(item 50)&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;getter가 있다고 해서 무조건 setter를 만들지는 말자.&lt;/p&gt;
&lt;h4&gt;클래스는 꼭 필요한 경우가 아니라면 불변이어야 한다.&lt;/h4&gt;
&lt;p&gt;성능 때문에 어쩔 수 없다면(item 67) 불변 클래스와 쌍을 이루는 가변 동반 클래스를 public 클래스로 제공하도록 하자.&lt;/p&gt;
&lt;p&gt;한편, 모든 클래스를 불변으로 만들 수는 없다.&lt;/p&gt;
&lt;p&gt;불변으로 만들 수 없는 클래스라도 변경할 수 있는 부분을 최소한으로 줄이자.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;해당 객체의 예측이 쉬워지고 오류가 생길 가능성이 줄어듬&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;다른 합당한 이유가 없다면 모든 필드는 private final 이어야 한다.&lt;/h4&gt;
&lt;h4&gt;생성자는 불변식 설정이 모두 완료된, 초기화가 완벽히 끝난 상태의 객체를 생성해야 한다.&lt;/h4&gt;
&lt;h2&gt;아이템 18. 상속보다는 컴포지션을 사용하라&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;상속은 잘못 사용하면 오류를 내기 쉬운 소프트웨어를 만들게 된다.&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;상위 클래스와 하위 클래스를 모두 같은 프로그래머가 통제하는 패키지 안에서라면 상속도 안전한 방법이다.&lt;/li&gt;
&lt;li&gt;확장할 목적으로 설계되었고 문서화도 잘 된 클래스(item 19)도 마찬가지로 안전하다.&lt;/li&gt;
&lt;li&gt;하지만 일반적인 구체 클래스를 패키지 경계를 넘어, 즉 다른 패키지의 구체 클래스를 상속하는 일은 위험하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;메서드 호출과 달리 상속은 캡슐화를 깨뜨린다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;상위 클래스를 릴리스마다 내부 구현이 달라질 수 있으며, &lt;strong&gt;그 여파로 코드 한 줄 건드리지 않은 하위 클래스가 오동작할 수 있다.&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;메서드 재정의로 인한 문제가 발생할 수 있다. &lt;/p&gt;
&lt;p&gt;그렇다면 클래스를 확장하더라도 메서드를 재정의하는 대신 새로운 메서드를 추가하면 괜찮을까? &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;이 방식이 훨씬 안전한 것은 맞지만, 위험이 전혀 없는 것은 아니다.&lt;/li&gt;
&lt;li&gt;다음 릴리스에서 상위 클래스에 새 메서드가 추가됐는데, 운 없게도 하필 내가 하위 클래스에 추가한 메서드와 시그니처가 같고 반환 타입은 다르다면 컴파일 조차 되지 않는다.&lt;/li&gt;
&lt;li&gt;상위 클래스의 메서드가 요구하는 규약을 만족하지 못할 가능성이 크다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;다행이 이상의 문제를 모두 피해 가는 묘안이 있다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;기존 클래스를 확장하는 대신, 새로운 클래스를 만들고 private 필드로 기존 클래스의 인스턴스를 참조하게 하자.&lt;/strong&gt; 기존 클래스가 새로운 클래스의 구성요소로 쓰인다는 뜻에서 이러한 설계를 컴포지션이라 한다.&lt;/p&gt;
&lt;p&gt;새 클래스의 인스턴스 메서드들은 (private 필드를 참조하는) 기존 클래스의 대응하는 메서드를 호출해 그 결과를 반환한다.&lt;/p&gt;
&lt;p&gt;새로운 클래스는 &lt;strong&gt;기존 클래스의 내부 구현 방식의 영향에서 벗어나며, 심지어 기존 클래스에 새로운 메서드가 추가되더라도 전혀 영향받지 않는다.&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;// 래퍼 클래스 - 상속 대신 컴포지션을 사용했다.
public class MySet&amp;lt;E&amp;gt; extends ForwardingSet&amp;lt;E&amp;gt;  {
    private int addCount = 0;

    public MySet(Set&amp;lt;E&amp;gt; set) {
        super(set);
    }

    @Override
    public boolean add(E e) {
        addCount++;
        return super.add(e);
    }

    @Override
    public boolean addAll(Collection&amp;lt;? extends E&amp;gt; collection) {
        addCount = addCount + collection.size();
        return super.addAll(collection);
    }

    public int getAddCount() {
        return addCount;
    }
}
// 재사용할 수 있는 전달 클래스
public class ForwardingSet&amp;lt;E&amp;gt; implements Set&amp;lt;E&amp;gt; {
    private final Set&amp;lt;E&amp;gt; set;
    public ForwardingSet(Set&amp;lt;E&amp;gt; set) { this.set = set; }
    public void clear() { set.clear(); }
    public boolean isEmpty() { return set.isEmpbty(); }
    public boolean add(E e) { return set.add(e); }
    public boolean addAll(Collection&amp;lt;? extends E&amp;gt; c) { return set.addAll(c); }
    ... 
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;InstrumentedSet은 Set 인터페이스를 구현했고, Set의 인스턴스를 인수로 받는 생성자를 하나 제공한다. 임의의 Set에 계측 기능을 덧씌워 새로운 Set으로 만드는 것이 이 클래스의 핵심이다.&lt;/p&gt;
&lt;p&gt;상속 방식은 구체 클래스 각각을 따로 확장해야 하며, 지원하고 싶은 상위 클래스의 생성자 각각에 대응하는 생성자를 별도로 정의해줘야 한다.&lt;/p&gt;
&lt;p&gt;하지만 &lt;strong&gt;컴포지션 방식은 한 번만 구현해두면 어떠한 Set 구현체라도 계측&lt;/strong&gt;할 수 있으며, &lt;strong&gt;기존 생성자들과도 함께 사용&lt;/strong&gt;할 수 있다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;Set&amp;lt;Instant&amp;gt; times = new InstrumentedSet&amp;lt;&amp;gt;(new TreeSet&amp;lt;&amp;gt;(cmp));
Set&amp;lt;E&amp;gt; s = new InstrumentedSet&amp;lt;&amp;gt;(new HashSet&amp;lt;&amp;gt;(INIT_CAPACITY));&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;InstrumnetedSet을 이용하면 대상 Set 인스턴스를 특정 조건하에서만 임시로 계측할 수 있다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;static void walk(Set&amp;lt;Dog&amp;gt; dogs) {
  InstrumentedSet&amp;lt;Dog&amp;gt; iDogs = new InstrumentedSet&amp;lt;&amp;gt;(dogs);
  ... // 이 메서드에서는 dogs 대신 iDogs를 사용한다.
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;다른 Set 인스턴스를 감싸고 있다는 뜻에서 InstrumentedSet 같은 클래스를 &lt;strong&gt;래퍼 클래스&lt;/strong&gt;라 하며, 다른 Set에 계측 기능을 덧씌운다는 뜻에서 &lt;strong&gt;데코레이터 패턴&lt;/strong&gt;이라고 한다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;컴포지션과 전달의 조합은 넓은 의미로 위임(deligation)이라고 부른다.&lt;/strong&gt; 단, 엄밀히 따지면 래퍼 객체가 내부 객체에 자기 자신의 참조를 넘기는 경우만 위임에 해당한다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;래퍼 클래스는 콜백 프레임워크와는 어울리지 않는다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;콜백 프레임워크에서는 자기 자신의 참조를 다른 객체에 넘겨서 다음 호출(콜백) 때 사용하도록 한다. 내부 객체는 자신을 감싸고 있는 래퍼의 존재를 모르니 대신 자신(this)의 참조를 넘기고, 콜백 때는 래퍼가 아닌 내부 객체를 호출하게 된다.(SELF 문제)&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;재사용할 수 있는 전달 클래스를 인터페이스당 하나씩만 만들어두면 원하는 기능을 덧씌우는 전달 클래스들을 아주 손쉽게 구현할 수 있다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;상속은 반드시 하위 클래스가 상위 클래스의 &amp;#39;진짜&amp;#39; 하위 타입인 상황에서만 쓰여야 한다. 다르게 말하면, &lt;strong&gt;클래스 B가 클래스 A와 is-a 관계일 때만 클래스 A를 상속해야 한다.&lt;/strong&gt; 클래스 A를 상속하는 클래스 B를 작성하려 한다면 &amp;quot;B가 정말 A인가?&amp;quot;하고 자문해보자. 대답이 &amp;quot;아니다&amp;quot;라면 A를 private 인스턴스로 두고, A와는 다른 API를 제공해야 하는 상황이 대다수다. 즉, A는 B의 필수 구성요소가 아니라 구현하는 방법 중 하나일 뿐이다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;컴포지션을 써야 할 상황에서 상속을 사용하는 건 내부 구현을 불필요하게 노출하는 꼴이다.&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;클라이언트가 노출된 내부에 직접 접근할 수 있어서 문제가 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;컴포지션 대신 상속을 사용하기로 결정하기 전에 마지막으로 자문해야 할 질문들&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;확장하려는 클래스의 API에 아무런 결함이 없는가?&lt;/li&gt;
&lt;li&gt;결함이 있다면, 이 결함이 여러분 클래스의 API까지 전파돼도 괜찮은가?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;컴포지션으로는 이런 결함을 숨기는 새로운 API를 설계할 수 있지만, &lt;strong&gt;상속은 상위 클래스의 API를 그 결함까지도 그래도 승계한다.&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;하위 클래스의 패키지가 상위 클래스와 다르고, 상위 클래스가 확장을 고려해 설계되지 않았다면 문제가 될 수 있다. &lt;/p&gt;
&lt;p&gt;상속의 취약점을 피하려면 상속 대신 컴포지션과 전달을 사용하자. 특히 래퍼 클래스로 구현할 적당한 인터페이스가 있다면 더욱 그렇다. &lt;/p&gt;
&lt;p&gt;래퍼 클래스는 하위 클래스보다 견고하고 강력하다.&lt;/p&gt;
&lt;h2&gt;아이템 19. 상속을 고려해 설계하고 문서화하라. 그러지 않았다면 상속을 금지하라&lt;/h2&gt;
&lt;p&gt;상속용 클래스를 설계하기란 결코 만만치 않다. &lt;/p&gt;
&lt;p&gt;상속용으로 설계한 클래스는 배포 전에 반드시 하위 클래스를 만들어 검증해야 한다.&lt;/p&gt;
&lt;p&gt;상속용 클래스의 생성자는 직접적으로든 간접적으로든 재정의 가능 메서드를 호출해서는 안 된다.&lt;/p&gt;
&lt;p&gt;클래스 내부에서 스스로를 어떻게 사용하는지(자기사용 패턴) 모두 문서로 남겨야 하며, 일단 문서화한 것은 그 클래스가 쓰이는 한 반드시 지켜야 한다. &lt;strong&gt;그러지 않으면 그 내부 구현 방식을 믿고 활용하던 하위 클래스를 오동작하게 만들 수 있다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;다른 이가 효율 좋은 하위 클래스를 만들 수 있도록 일부 메서드를 protected로 제공해야 할 수도 있다. 그러니 &lt;strong&gt;클래스를 확장해야 할 명확한 이유가 떠오르지 않으면 상속을 금지하는 편이 나을 것이다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;상속을 금지하려면 클래스를 final로 선언하거나 생성자 모두를 외부에서 접근할 수 없도록 만들면 된다.&lt;/p&gt;
&lt;h2&gt;아이템 20. 추상 클래스보다는 인터페이스를 우선하라&lt;/h2&gt;
&lt;h3&gt;골격 구현 클래스&lt;/h3&gt;
&lt;p&gt;인터페이스로는 타입을 정의하고, 필요하면 디폴트 메서드 몇 개도 함께 제공한다. 그리고 골격 구현 클래스는 나머지 메서드들까지 구현한다. 이렇게 해두면 단순히 골격을 확장하는 것만으로 이 인터페이스를 구현하는 데 필요한 일이 대부분 완료된다. 바로 템플릿 메서드 패턴이다.&lt;/p&gt;
&lt;p&gt;관례상 인터페이스 이름이 Interface라면 그 골격 구현 클래스의 이름은 AbstractInterface로 짓는다.&lt;/p&gt;
&lt;p&gt;다음 코드는 완벽히 동작하는 List 구현체를 반환하는 정적 팩터리 메서드로, AbstractList 골격 구현으로 활용했다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;static List&amp;lt;Integer&amp;gt; intArrayAsList(int[] a){
    Objects.requiredNonNull(a);

    return new AbstractList&amp;lt;Integer&amp;gt;(){
        @Override public Integer get(int i){
            return a[i]
        }

        @Override public Integer set(int i, Integer val){
            int oldVal = a[i];
            a[i] = val;
            return oldVal;
        }

        @Override public int size(){
            return a.length;
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;골격 구현 작성&lt;/h3&gt;
&lt;p&gt;가장 먼저 인터페이스를 잘 살펴 &lt;strong&gt;다른 메서드들의 구현에 사용되는 기반 메서드들을 선정한다. 이 기반 메서드들은 골격 구현에서는 추상 메서드가 될 것이다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt; 그 다음으로 &lt;strong&gt;기반 메서드들을 사용해 직접 구현할 수 있는 메서드를 모두 디폴트 메서드로 제공&lt;/strong&gt;한다. (단, equals 와 hashCode 같은 Object메서드는 디폴트 메서드로 제공하면 안된다는 사실을 항상 유념하자) &lt;/p&gt;
&lt;p&gt; 만약 인터페이스의 메서드 모두가 기반 메서드와 디폴트 메서드가 된다면 골격 구현 클래스를 별도로 만들 이유는 없다. &lt;/p&gt;
&lt;p&gt; &lt;strong&gt;기반 메서드나 디폴트 메서드로 만들지 못한 메서드가 남아 있다면, 이 인터페이스를 구현하는 골격 구현 클래스를 하나 만들어 남은 메서드들을 작성해 넣는다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;골격 구현 클래스에는 필요하면 public이 아닌 필드와 메서드를 추가 해도 된다.&lt;/p&gt;
&lt;p&gt;간단한 예로 &lt;strong&gt;Map.Entry 인터페이스&lt;/strong&gt;를 살펴보자. getKey, getValue는 확실히 기반 메서드이며, 선택적으로 setValue도 포함할 수 있다. 이 인터페이스는 equals 와 hashCode의 동작 방식도 정의해놨다. &lt;strong&gt;Object 메서드들은 디폴트 메서드로 제공해서는 안되므로, 해당 메서드들은 모두 골격 구현 클래스에 구현한다.&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;public abstract class AbstractMapEntry&amp;lt;K, V&amp;gt; implements Map.Entry&amp;lt;K,V&amp;gt;{

    // 변경 가능한 엔트리는 이 메서드를 반드시 재정의해야 한다.
    @Override public V setValue(V value){
        throw new UnsupportedOperationException();
    }

    // 생략.....
    // equals, hashCdoe, toString 구현
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;p&gt;일반적으로 다중 구현용 타입으로는 인터페이스가 가장 적합하다. &lt;/p&gt;
&lt;p&gt;복잡한 인터페이스라면 구현하는 수고를 덜어주는 &lt;strong&gt;골격 구현을 함께 제공하는 방법을 꼭 고려해보자.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;골격 구현은 &amp;#39;가능한 한&amp;#39; 인터페이스의 디폴트 메서드로 제공&lt;/strong&gt;하여 그 인터페이스를 구현한 모든 곳에서 활용하도록 하는 것이 좋다. &lt;/p&gt;
&lt;p&gt;&amp;#39;가능한 한&amp;#39;이라고 한 이유는, 인터페이스에 걸려 있는 구현상의 제약 때문에 골격 구현을 추상 클래스로 제공하는 경우가 더 흔하기 때문이다.&lt;/p&gt;
&lt;h2&gt;아이템 21. 인터페이스는 구현하는 쪽을 생각해 설계하라&lt;/h2&gt;
&lt;p&gt;디폴트 메서드를 선언하면, 그 인터페이스를 구현한 후 디폴트 메서드를 재정의하지 않은 모든 클래스에서 디폴트 구현이 쓰이게 된다.&lt;/p&gt;
&lt;p&gt;생각할 수 있는 모든 상황에서 불변식을 해치지 않는 디폴트 메서드를 작성하기랑 어려운 법이다.&lt;/p&gt;
&lt;p&gt;SynchronizedCollection 인스턴스를 여러 스레드가 공유하는 환경에서 한 스레드가 Collection 인터페이스의 디폴트 메서드인 removeIf를 호출하면 ConcurrentModificationException이 발생하거나 다른 예기치 못한 결과로 이어질 수 있다.&lt;/p&gt;
&lt;p&gt;자바 플랫폼 라이브러리에서도 이런 문제를 예방하기 위해 일련의 조치를 취했다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;구현한 인터페이스의 디폴트 메서드를 재정의&lt;/li&gt;
&lt;li&gt;다른 메서드에서는 디폴트 메서드를 호출하기 전에 필요한 작업을 수행&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;디폴트 메서드는 (컴파일에 성공하더라도) 기존 구현체에 런타임 오류를 일으킬 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;기존 인터페이스에 디폴트 메서드로 새 메서드를 추가하는 일은 꼭 필요한 경우가 아니면 피해야 한다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;반면, 새로운 인터페이스를 만드는 경우라면 표준적인 메서드 구현을 제공하는 데 아주 유용한 수단이며, 그 인터페이스를 더 쉽게 구현해 활용할 수 있게끔 해준다.(item 20)&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;인터페이스를 릴리스한 후라도 결함을 수정하는 게 가능한 경우도 있겠지만, 절대 그 가능성에 기대서는 안 된다.&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;아이템 22. 인터페이스는 타입을 정의하는 용도로만 사용하라&lt;/h2&gt;
&lt;p&gt;인터페이스는 자신을 구현한 클래스의 인스턴스를 참조할 수 있는 타입 역할을 한다. 인터페이스는 오직 이 용도로만 사용해야 한다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;인터페이스를 상수 공개용 수단으로 사용하지 말자.&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;// 상수 인터페이스 안티패턴 - 사용금지 !!
public interface PhysicalConstants {
  static final double AVOGADROS_NUMBER = 6.022
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;상수 인터페이스 안티패턴은 인터페이스를 잘못 사용한 예다. 클래스 내부에서 사용하는 상수는 외부 인터페이스가 아니라 내부 구현에 해당한다.&lt;/p&gt;
&lt;p&gt;final이 아닌 클래스가 상수 인터페이스를 구현한다면 모든 하위 클래스의 이름공간이 그 인터페이스가 정의한 상수들로 오염되어 버린다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;특정 클래스나 인터페이스와 강하게 연관된 상수일 경우&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;그 클래스나 인터페이스 자체에 추가해야 한다. &lt;/li&gt;
&lt;li&gt;enum 타입으로 나타내기 적합한 상수라면 enum 타입으로 만들어 공개하면 된다.(item 34) &lt;/li&gt;
&lt;li&gt;그것도 아니라면, 인스턴스화할 수 없는 유틸리티 클래스(item 4)에 담아 공개하자.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;유틸리티 클래스의 상수를 빈번히 사용한다면 static import를 하자.&lt;/p&gt;
&lt;h2&gt;아이템 23. 태그 달린 클래스보다는 클래스 계층구조를 활용하라&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;// 태그 달린 클래스 - 클래스 계층구조보다 훨씬 나쁘다 !!
class Figure {
    enum Shape { RECTANGLE, CIRCLE };
  // 태그 필드 - 현재 모양을 나타낸다.
    final Shape shape; 

    // 다음 필드들은 모양이 사각형(RECTANGLE)일 때만 쓰인다.
    double length;
    double width;

    // 다음 필드느 모양이 원(CIRCLE)일 때만 쓰인다.
    double radius;

    // 원용 생성자
    Figure(double radius) {
        shape = Shape.CIRCLE;
        this.radius = radius;
    }

    // 사각형용 생성자
    Figure(double length, double width) {
        shape = Shape.RECTANGLE;
        this.length = length;
        this.width = width;
    }

    double area() {
        switch(shape) {
            case RECTANGLE:
                return length * width;
            case CIRCLE:
                return Math.PI * (radius * radius);
            default:
                throw new AssertionError(shape);
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;태그 달린 클래스의 단점&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;장황하고, 오류를 내기 쉽고, 비효율적이다.&lt;/li&gt;
&lt;li&gt;클래스 계층구조를 어설프게 흉내낸 아류일 뿐이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;위의 태그 달린 클래스를 클래스 계층구조로 바꿔보자.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;가장 먼저 계층구조의 root가 될 추상 클래스를 정의하고, 태그 값에 따라 동작이 달라지는 메서드들을 root 클래스의 추상 메서드로 선언한다.&lt;/p&gt;
&lt;p&gt;그런 다음 태그 값에 상관없이 동작이 일정한 메서드들을 root 클래스에 일반 메서드로 추가한다.&lt;/p&gt;
&lt;p&gt;모든 하위 클래스에서 공통으로 사용하는 데이터 필드들도 전부 root 클래스로 올린다.&lt;/p&gt;
&lt;p&gt;root 클래스를 확장한 구체 클래스를 의미별로 하나씩 정의한다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;// 태그 달린 클래스를 클래스 계층구조로 변환
abstract class Figure {
    abstract double area();
}

class Circle extends Figure {
    final double radius;
    Circle(double radius) { this.radius = radius; }
    @Override double area() { return Math.PI * (radius * radius); }
}

class Rectangle extends Figure {
    final double length;
    final double width;
    Rectangle(double length, double width) {
        this.length = length;
        this.width = width;
    }
    @Override double area() { return length * width; }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;클래스 계층구조는 간결하고 명확하며, 쓸데없는 코드도 모두 사라졌다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;살아 남은 필드들은 모두 final이다.&lt;/p&gt;
&lt;p&gt;각 클래스의 생성자가 모든 필드를 남김없이 초기화하고 추상 메서드를 모두 구현했는지 컴파일러가 확인해준다.&lt;/p&gt;
&lt;p&gt;또한, 타입 사이의 자연스러운 계층 관계를 반영할 수 있어서 유연성은 물론 컴파일타임 타입 검사 능력을 높여준다는 장점도 있다.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;태그 달린 클래스를 써야 하는 상황은 거의 없다. 새로운 클래스를 작성하는 데 태그 필드가 등장한다면 태그를 없애고 계층구조로 대체하는 방법을 생각해보자.&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;아이템 24. 멤버 클래스는 되도록 static으로 만들라&lt;/h2&gt;
&lt;h3&gt;정적 멤버 클래스 &amp;amp;&amp;amp; 비정적 멤버 클래스&lt;/h3&gt;
&lt;p&gt;정적 멤버 클래스는 다른 클래스 안에 선언되고, 바깥 클래스의 private 멤버에도 접근할 수 있다는 점만 제외하고는 일반 클래스와 똑같다.&lt;/p&gt;
&lt;p&gt;정적 멤버 클래스는 흔히 바깥 클래스와 함께 쓰일 때만 유용한 public 도우미 클래스로 쓰인다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Operation 열거 타입은 Calculator 클래스의 public 정적 멤버 클래스가 되어야 한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;비정적 멤버 클래스의 인스턴스는 바깥 클래스의 인스턴스와 암묵적으로 연결된다. 그래서 정규화된 this를 사용해 바깥 인스턴스의 메서드를 호출하거나 바깥 인스턴스의 참조를 가져올 수 있다.&lt;/p&gt;
&lt;p&gt;따라서 개념상 &lt;strong&gt;중첩 클래스의 인스턴스가 바깥 인스턴스와 독립적으로 존재할 수 있다면 정적 멤버 클래스로 만들어야 한다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;비정적 멤버 클래스는 어댑터를 정의할 때 자주 쓰인다. 즉, 어떤 클래스의 인스턴스를 감싸 마치 다른 클래스의 인스턴스처럼 보이게 하는 뷰로 사용하는 것이다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;// 비정적 멤버 클래스의 흔한 쓰임 - 자신의 반복자 구현
public class MySet&amp;lt;E&amp;gt; extends AbstractSet&amp;lt;E&amp;gt; {
  ... // 생략

  @Override public Iterator&amp;lt;E&amp;gt; iterator() {
    return new MyIterator();
  }

  private class MyIterator implements Iterator&amp;lt;E&amp;gt; {
    ...
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;멤버 클래스에서 바깥 인스턴스에 접근할 일이 없다면 무조건 static을 부여서 정적 멤버 클래스로 만들자.&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;숨은 외부 참조 시에 시간과 공간이 소비됨.&lt;/li&gt;
&lt;li&gt;가비지 컬렉션이 바깥 클래스의 인스턴스를 수거하지 못하는 메모리 누수가 생길 수 있다.(item 7)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;익명 클래스&lt;/h3&gt;
&lt;p&gt;멤버와 달리, 쓰이는 시점에 선언과 동시에 인스턴스가 만들어진다.&lt;/p&gt;
&lt;p&gt;오직 비정적인 문맥에서 사용될 때만 바깥 클래스의 인스턴스를 참조할 수 있다.&lt;/p&gt;
&lt;p&gt;선언한 지점에서만 인스턴스를 만들 수 있다.&lt;/p&gt;
&lt;p&gt;여러 인터페이스를 구현할 수 없다.&lt;/p&gt;
&lt;p&gt;짧지 않으면 가독성이 떨어진다.&lt;/p&gt;
&lt;p&gt;람다를 지원하기 전에는 즉석에서 작은 함수 객체나 처리 객체를 만드는 데 익명 클래스를 주로 사용했다.&lt;/p&gt;
&lt;p&gt;또 다른 쓰임은 정적 팩터리 메서드를 구현할 때다.(코드 20-1)&lt;/p&gt;
&lt;h3&gt;지역 클래스&lt;/h3&gt;
&lt;p&gt;네 가지 중첩 클래스 중 가장 드물게 사용된다.&lt;/p&gt;
&lt;p&gt;지역변수를 선언할 수 있는 곳이면 실질적으로 어디서든 선언할 수 있고, 유효 범위도 지역변수와 같다.&lt;/p&gt;
&lt;p&gt;멤버 클래스처럼 이름이 있고 반복해서 사용할 수 있고, 익명 클래스처럼 비정적 문맥에서 사용될 때만 바깥 인스턴스를 참조할 수 있으며, 정적 멤버는 가질 수 없으며, 가독성을 위해 짧게 작성해야 한다.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;중첩 클래스에는 네 가지가 있으며, 각각의 쓰임이 다르다.&lt;/p&gt;
&lt;p&gt; 메서드 밖에서도 사용해야 하거나 메서드 안에 정의하기엔 너무 길다면 멤버 클래스로 만든다. &lt;/p&gt;
&lt;p&gt; 멤버 클래스의 인스턴스 각각이 바깥 인스턴스를 참조한다면 &lt;strong&gt;비정적&lt;/strong&gt;으로, 그렇지 않으면 &lt;strong&gt;정적&lt;/strong&gt;으로 만들자. &lt;/p&gt;
&lt;p&gt; 중첩 클래스가 한 메서드 안에서만 쓰이면서 그 인스턴스를 생성하는 지점이 단 한 곳이고 해당 타입으로 쓰기에 적합한 클래스나 인터페이스가 이미 있다면 &lt;strong&gt;익명 클래스&lt;/strong&gt;로 만들고, 그렇지 않으면 &lt;strong&gt;지역 클래스&lt;/strong&gt;로 만들자.&lt;/p&gt;
&lt;h2&gt;item 25. 톱레벨 클래스는 한 파일에 하나만 담으라&lt;/h2&gt;
&lt;p&gt;소스 파일 하나에는 반드시 톱레벨 클래스(혹은 톱레벨 인터페이스)를 하나만 담자. &lt;/p&gt;
&lt;p&gt;이 규칙만 따른다면 컴파일러가 한 클래스에 대한 정의를 여러 개 만들어내는 일은 사라진다. &lt;/p&gt;
&lt;p&gt;소스 파일을 어떤 순서로 컴파일하든 바이너리 파일이나 프로그램의 동작이 달라지는 일은 결코 일어나지 않을 것이다.&lt;/p&gt;</description>
      <category>Java</category>
      <category>effective java</category>
      <category>이펙티브 자바</category>
      <author>머래그로프</author>
      <guid isPermaLink="true">https://goldfishhead.tistory.com/104</guid>
      <comments>https://goldfishhead.tistory.com/104#entry104comment</comments>
      <pubDate>Thu, 10 Sep 2020 20:57:41 +0900</pubDate>
    </item>
  </channel>
</rss>