<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>dev-mage 알면 기술, 모르면 마술</title>
        <link>https://velog.io/</link>
        <description></description>
        <lastBuildDate>Tue, 10 Jan 2023 04:58:25 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>dev-mage 알면 기술, 모르면 마술</title>
            <url>https://velog.velcdn.com/images/dev-mage/profile/c5cd8aaa-232c-4b54-b41c-2513c1cc9dc3/image.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. dev-mage 알면 기술, 모르면 마술. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/dev-mage" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[섹션 2. 스프링 핵심 원리 이해1 - 예제 만들기(주문 도메인)]]></title>
            <link>https://velog.io/@dev-mage/spring-core-basic-sample-order-domain</link>
            <guid>https://velog.io/@dev-mage/spring-core-basic-sample-order-domain</guid>
            <pubDate>Tue, 10 Jan 2023 04:58:25 GMT</pubDate>
            <description><![CDATA[<p><a href="https://inf.run/VgfD">스프링 핵심 원리 - 기본편 - 인프런 | 강의</a></p>
<h1 id="주문과-할인-도메인">주문과 할인 도메인</h1>
<h3 id="주문과-할인-도메인-설계">주문과 할인 도메인 설계</h3>
<ul>
<li>주문 도메인 협력, 역할, 책임</li>
</ul>
<p><img src="https://velog.velcdn.com/images/dev-mage/post/4401a803-a228-4e52-b502-76f021d40f52/image.png" alt=""></p>
<ol>
<li><strong>주문 생성</strong>: 클라이언트는 주문 서비스에 주문 생성을 요청</li>
<li><strong>회원 조회</strong>: 할인을 위해서는 회원등급이 필요함 → 주문 서비스는 회원 저장소에서 회원을 조회</li>
<li><strong>할인 적용</strong>: 주문 서비스는 회원 등급에 따른 할인 여부를 할인 정책에 위임</li>
<li><strong>주문 결과 반환</strong>: 주문 서비스는 할인 결과를 포함한 주문 결과를 반환<ul>
<li>실제로는 상품 도메인도 있을 수 있고 주문 데이터를 데이터베이스에 저장도 하겠지만 여기서는 생략</li>
</ul>
</li>
</ol>
<ul>
<li>주문 도메인 전체(역할과 구현)<ul>
<li><strong>역할과 구현을 분리</strong>해서 자유롭게 구현 객체를 조립할 수 있게 설계됨</li>
<li>덕분에 회원 저장소는 물론이고 할인 정책도 유연하게 변경할 수 있음</li>
<li>단일 책임 원칙을 잘 따른 설계<ul>
<li>만약 주문 서비스에 할인 관련 로직을 넣었다면 유연하게 변경할 수 없었을 것</li>
</ul>
</li>
</ul>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/dev-mage/post/fc90c0b4-4625-47b3-9598-ecd0a104cc08/image.png" alt=""></p>
<ul>
<li>주문 도메인 클래스 다이어그램</li>
</ul>
<p><img src="https://velog.velcdn.com/images/dev-mage/post/065da013-83f8-48a9-b996-33b705baf5a0/image.png" alt=""></p>
<ul>
<li>주문 도메인 객체 다이어그램1<ul>
<li>회원을 메모리에서 조회하고 정액 할인 정책(고정 금액)을 지원해도 주문 서비스를 변경하지 않아도 됨</li>
<li>역할들의 협력 관계를 그대로 사용할 수 있음</li>
</ul>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/dev-mage/post/c5b9d624-f968-40c7-95c5-14e5a422fc4c/image.png" alt=""></p>
<ul>
<li>주문 도메인 객체 다이어그램2<ul>
<li>회원을 메모리가 아닌 실제 데이터베이스에서 조회하고 정률 할인 정책(주문 금액에 따라 % 할인)을 지원해도 주문 서비스를 변경하지 않아도 됨</li>
<li>협력 관계를 그대로 재사용할 수 있음</li>
</ul>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/dev-mage/post/cc16b58a-5b5a-4979-bb28-e15e7177d637/image.png" alt=""></p>
<h3 id="주문과-할인-도메인-개발">주문과 할인 도메인 개발</h3>
<ul>
<li>할인 정책 인터페이스</li>
</ul>
<pre><code class="language-java">package hello.core.discount;

import hello.core.member.Member;

public interface DiscountPolicy {

    /**
     * @return 할인 금액
    * */
    int discount(Member member, int price);
}</code></pre>
<ul>
<li>정액 할인 정책 구현체</li>
</ul>
<pre><code class="language-java">package hello.core.discount;

import hello.core.member.Grade;
import hello.core.member.Member;

public class FixDiscountPolicy implements DiscountPolicy {
    private int discountFixAmount = 1000; // 1000원 할인

    @Override
    public int discount(Member member, int price) {
        if (member.getGrade() == Grade.VIP) {
            return discountFixAmount;
        } else {
            return 0;
        }
    }
}</code></pre>
<ul>
<li>주문 엔티티</li>
</ul>
<pre><code class="language-java">package hello.core.order;

public class Order {
    private Long memberId;
    private String itemName;
    private int itemPrice;
    private int discountPrice;

    public Order(Long memberId, String itemName, int itemPrice, int discountPrice) {
        this.memberId = memberId;
        this.itemName = itemName;
        this.itemPrice = itemPrice;
        this.discountPrice = discountPrice;
    }

    public int calculatePrice() {
        return itemPrice - discountPrice;
    }

    public Long getMemberId() {
        return memberId;
    }

    public void setMemberId(Long memberId) {
        this.memberId = memberId;
    }

    public String getItemName() {
        return itemName;
    }

    public void setItemName(String itemName) {
        this.itemName = itemName;
    }

    public int getItemPrice() {
        return itemPrice;
    }

    public void setItemPrice(int itemPrice) {
        this.itemPrice = itemPrice;
    }

    public int getDiscountPrice() {
        return discountPrice;
    }

    public void setDiscountPrice(int discountPrice) {
        this.discountPrice = discountPrice;
    }

    @Override
    public String toString() {
        return &quot;Order{&quot; +
                &quot;memberId=&quot; + memberId +
                &quot;, itemName=&#39;&quot; + itemName + &#39;\&#39;&#39; +
                &quot;, itemPrice=&quot; + itemPrice +
                &quot;, discountPrice=&quot; + discountPrice +
                &#39;}&#39;;
    }
}</code></pre>
<ul>
<li>주문 서비스 인터페이스</li>
</ul>
<pre><code class="language-java">package hello.core.order;

public interface OrderService {
    Order createOrder(Long memberId, String itemName, int itemPrice);
}</code></pre>
<ul>
<li>주문 서비스 구현체</li>
</ul>
<pre><code class="language-java">package hello.core.order;

import hello.core.discount.DiscountPolicy;
import hello.core.discount.FixDiscountPolicy;
import hello.core.member.Member;
import hello.core.member.MemberRepository;
import hello.core.member.MemoryMemberRepository;

public class OrderServiceImpl implements OrderService {
    private final MemberRepository memberRepository = new MemoryMemberRepository();
    private final DiscountPolicy discountPolicy = new FixDiscountPolicy();

    @Override
    public Order createOrder(Long memberId, String itemName, int itemPrice) {
        Member member = memberRepository.findById(memberId);
        // 단일 책임 원칙을 잘 지켰기 때문에 할인에 문제가 생기면 할인만 수정하면 됨
        int discountPrice = discountPolicy.discount(member, itemPrice);
        return new Order(memberId, itemName, itemPrice, discountPrice);
    }
}</code></pre>
<ul>
<li>주문 생성 요청이 오면<ol>
<li>회원 정보 조회</li>
<li>할인 정책 적용</li>
<li>주문 객체 생성 후 반환</li>
</ol>
<ul>
<li>메모리 회원 저장소와 고정 금액 할인 정책을 구현체로 사용</li>
</ul>
</li>
</ul>
<h3 id="주문과-할인-도메인-테스트">주문과 할인 도메인 테스트</h3>
<pre><code class="language-java">package hello.core.order;

import hello.core.member.Grade;
import hello.core.member.Member;
import hello.core.member.MemberService;
import hello.core.member.MemberServiceImpl;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;

public class OrderServiceTest {
    MemberService memberService = new MemberServiceImpl();
    OrderService orderService = new OrderServiceImpl();

    @Test
    void createOrder() {
        Long memberId = 1L;
        Member member = new Member(memberId, &quot;memberA&quot;, Grade.VIP);
        memberService.join(member);

        Order order = orderService.createOrder(memberId, &quot;itemA&quot;, 10000);

        Assertions.assertThat(order.getDiscountPrice()).isEqualTo(1000);
    }
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[섹션 2. 스프링 핵심 원리 이해1 - 예제 만들기(요구사항과 회원 도메인)]]></title>
            <link>https://velog.io/@dev-mage/spring-core-basic-sample-requirements-user-domain</link>
            <guid>https://velog.io/@dev-mage/spring-core-basic-sample-requirements-user-domain</guid>
            <pubDate>Wed, 28 Dec 2022 02:57:31 GMT</pubDate>
            <description><![CDATA[<p><a href="https://inf.run/VgfD">스프링 핵심 원리 - 기본편 - 인프런 | 강의</a></p>
<h1 id="비즈니스-요구사항과-설계">비즈니스 요구사항과 설계</h1>
<h3 id="요구사항">요구사항</h3>
<ul>
<li>회원<ul>
<li>회원은 가입하고 조회할 수 있음</li>
<li>회원은 일반과 VIP 두 가지 등급이 있음</li>
<li>회원 데이터는 자체 DB를 구축할 수 있고 외부 시스템과 연동될 수 있음(미 확정)</li>
</ul>
</li>
<li>주문과 할인 정책<ul>
<li>회원은 상품 주문 가능</li>
<li>회원 등급에 따라 할인 정책 적용 가능성 있음<ul>
<li>할인 정책은 모든 VIP는 1000원을 할인해주는 고정 금액 할인을 적용(나중에 변경될 수 있음)</li>
</ul>
</li>
<li>할인 정책은 변경 가능성이 높음. 회상의 기본 할인 정책을 아직 정하지 못했고, 오픈 직전까지 고민을 미루고 있음.<ul>
<li>최악의 경우 할인을 적용하지 않을 수도 있음(미확정)</li>
</ul>
</li>
</ul>
</li>
</ul>
<h1 id="회원-도메인">회원 도메인</h1>
<h3 id="회원-도메인-설계">회원 도메인 설계</h3>
<ul>
<li>회원 도메인 협력 관계</li>
</ul>
<p><img src="https://velog.velcdn.com/images/dev-mage/post/01ad8ec7-256b-49d9-b617-fb470a5a119b/image.png" alt=""></p>
<ul>
<li>회원 클래스 다이어그램</li>
</ul>
<p><img src="https://velog.velcdn.com/images/dev-mage/post/49d4ad03-d6fa-4fe6-8dbd-adc1eb60af64/image.png" alt=""></p>
<ul>
<li><p>회원 객체 다이어그램</p>
<ul>
<li><p>서비스될 때 객체 간 메모리에서 실제로 어떻게 참조될지를 나타낸 것 ≠ 클래스 다이어그램</p>
</li>
<li><p>생성된 인스턴스끼리의 참조를 나타낸다고 볼 수 있음</p>
</li>
<li><p>구현체는 동적으로 할당되기 때문에 클래스 다이어그램으로 나타내지 않음</p>
<p><img src="https://velog.velcdn.com/images/dev-mage/post/bf58155c-be99-4d5f-947d-1e9b71dddd65/image.png" alt=""></p>
</li>
</ul>
</li>
</ul>
<h3 id="회원-도메인-구현">회원 도메인 구현</h3>
<ul>
<li><p>회원</p>
<ul>
<li><p>회원 등급</p>
<pre><code class="language-java">package hello.core.member;

public enum Grade { BASIC, VIP }</code></pre>
</li>
<li><p>회원 엔티티</p>
<pre><code class="language-java">package hello.core.member;

public class Member {
  private Long id;
  private String name;
  private Grade grade;

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

  public Long getId() {
      return id;
  }

  public void setId(Long id) {
      this.id = id;
  }

  public String getName() {
      return name;
  }

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

  public Grade getGrade() {
      return grade;
  }

  public void setGrade(Grade grade) {
      this.grade = grade;
  }
}</code></pre>
</li>
</ul>
</li>
<li><p>회원 저장소</p>
<ul>
<li><p>회원 저장소 인터페이스</p>
<pre><code class="language-java">package hello.core.member;

public interface MemberRepository {
  void save(Member member);

  Member findById(Long memberId);
}</code></pre>
</li>
<li><p>메모리 회원 저장소 구현체</p>
<ul>
<li>참고: HashMap은 동시성 이슈가 발생할 수 있음. 이런 경우 ConcurrentHashMap을 사용할 것</li>
</ul>
<pre><code class="language-java">package hello.core.member;

import java.util.HashMap;
import java.util.Map;

public class MemoryMemberRepository implements MemberRepository {
  private static Map&lt;Long, Member&gt; store = new HashMap&lt;&gt;();

  @Override
  public void save(Member member) {
      store.put(member.getId(), member);
  }

  @Override
  public Member findById(Long memberId) {
      return store.get(memberId);
  }
}</code></pre>
</li>
</ul>
</li>
<li><p>회원 서비스</p>
<ul>
<li><p>회원 서비스 인터페이스</p>
<pre><code class="language-java">package hello.core.member;

public interface MemberService {
  void join(Member member);

  Member findMember(Long memberId);
}</code></pre>
</li>
<li><p>회원 서비스 구현체</p>
<pre><code class="language-java">package hello.core.member;

public class MemberServiceImpl implements MemberService{
  private final MemberRepository memberRepository = new MemoryMemberRepository();

  @Override
  public void join(Member member) {
      memberRepository.save(member);
  }

  @Override
  public Member findMember(Long memberId) {
      return memberRepository.findById(memberId);
  }
}</code></pre>
</li>
</ul>
</li>
</ul>
<h3 id="회원-도메인-실행과-테스트">회원 도메인 실행과 테스트</h3>
<ul>
<li>애플리케이션 로직으로(main 메서드에서 실행하는 것) 테스트하는 것은 좋은 방법이 아님.(콘솔에서 일일이 확인? → X) 테스트 프레임워크를 활용할 것</li>
<li>회원 도메인 - 회원 가입 테스트</li>
</ul>
<pre><code class="language-java">package hello.core.member;

import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;

public class MemberServiceTest {
    MemberService memberService = new MemberServiceImpl();
    @Test
    void join() {
        // given
        Member member = new Member(1L, &quot;memberA&quot;, Grade.VIP);

        // when
        memberService.join(member);
        Member foundMember = memberService.findMember(member.getId());

        // then
        Assertions.assertThat(member).isEqualTo(foundMember);
    }
}</code></pre>
<h3 id="회원-도메인-설계의-문제점">회원 도메인 설계의 문제점</h3>
<ul>
<li><p>위 코드는 설계상 문제점을 가지고 있음</p>
</li>
<li><p>만약 메모리에서 다른 저장소로 변경하려고 하면 개방-폐쇄 원칙을 위배하게 됨</p>
</li>
<li><p>의존성 역전 원칙 또한 위배</p>
</li>
<li><p>이는 회원 서비스가 회원 저장소 인터페이스 뿐만 아니라 회원 저장소의 구현체까지 의존하고 있기 때문에 발생</p>
<pre><code class="language-java">  public class MemberServiceImpl implements MemberService {
      private final MemberRepository memberRepository = new MemoryMemberRepository();
          ...
  }</code></pre>
<ul>
<li>개방-폐쇄 원칙: 기존 코드의 <code>MemoryMemberRepository()</code> 부분이 수정됨</li>
<li>의존성 역전 원칙: <code>MemoryMemberRepository()</code> 에 의존</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[섹션 1. 객체 지향 설계와 스프링 - SOLID와 스프링]]></title>
            <link>https://velog.io/@dev-mage/spring-core-basic-oop-solid</link>
            <guid>https://velog.io/@dev-mage/spring-core-basic-oop-solid</guid>
            <pubDate>Mon, 19 Dec 2022 13:23:52 GMT</pubDate>
            <description><![CDATA[<p><a href="https://inf.run/VgfD">스프링 핵심 원리 - 기본편 - 인프런 | 강의</a></p>
<h1 id="좋은-객체지향-설계의-5가지-원칙solid">좋은 객체지향 설계의 5가지 원칙(SOLID)</h1>
<h3 id="solid">SOLID?</h3>
<ul>
<li>클린 코드로 유명한 로버트 마틴이 좋은 객체지향 설계의 5가지 원칙을 정리</li>
<li>SRP(Single Responsibility Principle): 단일 책임 원칙</li>
<li>OCP(Open/Closed Principle): 개방-폐쇄 원칙</li>
<li>LSP(Liskov Substitution Principle): 리스코프 치환 원칙</li>
<li>ISP(Interface Segregation Principle): 인터페이스 분리 원칙</li>
<li>DIP(Dependency Inversion Principle): 의존 관계 역전 원칙</li>
</ul>
<h3 id="srpsingle-responsibility-principle-단일-책임-원칙">SRP(Single Responsibility Principle): 단일 책임 원칙</h3>
<ul>
<li>하나의 클래스는 하나의 책임만 가져야 함</li>
<li>그러나 하나의 책임이라는 것은 모호함<ul>
<li>클 수도, 작을 수도 있음</li>
<li>문맥과 상황에 따라 다름</li>
</ul>
</li>
<li><strong>중요한 기준은 변경</strong><ul>
<li>변경이 있을 때 <strong>파급 효과가 적으면</strong> 단일책임 원칙을 잘 따른 것</li>
<li>예) UI 변경, 객체의 생성과 사용을 분리</li>
</ul>
</li>
</ul>
<h3 id="️-ocpopenclosed-principle-개방-폐쇄-원칙">‼️ OCP(Open/Closed Principle): 개방-폐쇄 원칙</h3>
<ul>
<li>소프트웨어 요소는 <strong>확장에는 열려</strong> 있으나, <strong>변경에는 닫혀</strong> 있어야 함</li>
<li>확장을 하려면 당연히 기존 코드를 변경해야 하지 않나?<ul>
<li><strong>다형성</strong>을 활용해 보면 됨</li>
<li>인터페이스를 구현한 새로운 클래스를 하나 만들어서 새로운 기능을 구현</li>
<li>⇒ 기존 코드를 변경하는 것이 아님</li>
</ul>
</li>
<li>역할과 구현의 분리로 실현 가능</li>
</ul>
<h3 id="ocp개방-폐쇄-원칙의-문제점">OCP(개방-폐쇄 원칙)의 문제점</h3>
<pre><code class="language-java">public class MemeberService {
        // private MemberRepository mr = new MemoryMemberRepository();
        private MemberRepository mr = new JDBCMemberRepository();
}</code></pre>
<ul>
<li>그러나 MemeberService의 코드를 보면 MemberRepository의 구현체가 달라지므로써 코드가 변경되는 것을 볼 수 있음</li>
<li>왜? 그것은 클라이언트(MemeberService)가 직접 구현 클래스를 선택하고 있기 때문</li>
</ul>
<pre><code class="language-java">private MemberRepository mr = new MemoryMemberRepository(); // 기존 코드
private MemberRepository mr = new JDBCMemberRepository(); // 변경 코드</code></pre>
<ul>
<li><strong>구현 객체를 변경하려면 클라이언트 코드를 변경해야 함</strong></li>
<li><strong>분명 다형성을 사용했지만 개방-폐쇄 원칙을 지킬 수 없음</strong></li>
<li>결국 이 문제를 해결하려면 객체를 생성하고, 연관 관계를 맺어주는 별도의 조립, 설정자가 필요(= 스프링 컨테이너)</li>
</ul>
<h3 id="lspliskov-substitution-principle-리스코프-치환-원칙">LSP(Liskov Substitution Principle): 리스코프 치환 원칙</h3>
<ul>
<li>프로그램의 객체는 프로그램의 정확성을 깨뜨리지 않으면서 하위 타입의 인스턴스로 바꿀 수 있어야 함<ul>
<li>다형성에서 하위 클래스는 인터페이스 규약을 다 지켜야 한다는 것</li>
<li>다형성을 지원하기 위한 원칙</li>
<li>인터페이스를 구현한 구현체를 믿고 사용하려면 이 원칙이 필요함</li>
</ul>
</li>
<li>단순히 컴파일에 성공하는 것을 넘어서는 이야기</li>
<li>예) 자동차 인터페이스의 엑셀 = 앞으로 전진하는 기능.<ul>
<li>뒤로 가게 구현하면 리스코프 치환 원칙에 위배됨</li>
<li>이는 컴파일 상 오류가 아니지만 인터페이스 규약을 위반했기 때문에 좋은 객체지향 코드가 아님</li>
<li>느리더라도 앞으로 가야함</li>
<li>기능적으로 위반되지 않는 것이 보장 되어야 함</li>
</ul>
</li>
</ul>
<h3 id="ispinterface-segregation-principle-인터페이스-분리-원칙">ISP(Interface Segregation Principle): 인터페이스 분리 원칙</h3>
<ul>
<li>특정 클라이언트를 위한 인터페이스 여러 개가 범용 인터페이스 하나보다 나음<ul>
<li>= 적당한 크기로 인터페이스를 잘 쪼개라</li>
</ul>
</li>
<li>예) 자동차 인터페이스 → 운전 인터페이스, 정비 인터페이스로 분리<ul>
<li>이렇게 인터페이스를 분리하면 클라이언트 또한 사용자 클라이언트 → 운전자 클라이언트, 정비사 클라이언트로 분리 가능</li>
<li>분리하면 정비 인터페이스 자체가 변해도 운전자 클라이언트에 영향을 주지 않음</li>
</ul>
</li>
<li>인터페이스가 명확해지고 대체 가능성이 높아짐</li>
</ul>
<h3 id="️-dipdependency-inversion-principle-의존-관계-역전-원칙">‼️ DIP(Dependency Inversion Principle): 의존 관계 역전 원칙</h3>
<ul>
<li>프로그래머는 “추상화에 의존해야 하지 구체화에 의존해서는 안 된다.” → 의존성 주입 원칙은 이 원칙을 따르는 방법 중 하나</li>
<li>쉽게 말해서 클라이언트가 구현 클래스에 의존하지 말고 인터페이스에 의존하라는 뜻</li>
<li>앞서 이야기한 역할(Role)에 의존하게 해야 한다는 것과 같음<ul>
<li>객체 세상도 클라이언트가 인터페이스에 의존해야 유연하게 구현체를 변경할 수 있음</li>
<li>구현체에 의존하게 되면 변경이 아주 어려워 짐</li>
</ul>
</li>
<li>그런데 개방-폐쇄 원칙에서 설명한 MemberService는 인터페이스에 의존하긴 하지만, 동시에 구현 클래스도 의존함<ul>
<li>MemberService 클라이언트가 구현 클래스를 직접 선택하기 때문(<code>new JDBCMemberRepository()</code>)</li>
<li>이는 <strong>의존 관계 역전 원칙에 위반</strong><ul>
<li>추상화에 의존하지 않고 구체화에 의존함</li>
</ul>
</li>
<li>다형성 만으로 DIP에 위배되지 않게 하려면 결국 코드는 동작하지 않을 것임<ul>
<li>인터페이스만 있고 구현체가 없기 때문에</li>
</ul>
</li>
</ul>
</li>
</ul>
<h3 id="정리">정리</h3>
<ul>
<li>객체 지향의 핵심은 다형성</li>
<li>다형성 만으로는 쉽게 부품을 갈아 끼우듯이 개발할 수 없음</li>
<li>다형성 만으로는 구현 객체를 변경할 때 클라이언트도 함께 변경되어야 함</li>
<li><strong>다형성 만으로는 OCP, DIP를 지킬 수 없음</strong></li>
<li>뭔가 더 필요함 ⇒ 스프링 프레임워크!!</li>
</ul>
<h1 id="객체지향-설계와-스프링">객체지향 설계와 스프링</h1>
<h3 id="스프링-이야기에-왜-객체지향-이야기가-나오는가">스프링 이야기에 왜 객체지향 이야기가 나오는가?</h3>
<ul>
<li><strong>스프링은 다음 기술로 다형성 + OCP, DIP를 가능하게 지원</strong><ul>
<li>DI(Dependency Injection): 의존 관계, 의존성 주입</li>
<li>DI 컨테이너 제공</li>
</ul>
</li>
<li><strong>해당 기술로써 클라이언트 코드의 변경 없이 기능 확장</strong><ul>
<li>쉽게 부품을 교체하듯이 개발</li>
</ul>
</li>
</ul>
<h3 id="스프링이-없던-시절">스프링이 없던 시절</h3>
<ul>
<li>객체지향적인 개발을 위해 OCP, DIP를 지키며 개발을 하려고 보니 원칙들을 지키기 위해 필요한 코드가 더 많아져 배보다 배꼽이 더 큰 상황 발생<ul>
<li>그래서 프레임워크로 만들어버림</li>
</ul>
</li>
<li>순수하게 자바로 OCP, DIP를 지키면서 개발을 하다 보면 결국 스프링 프레임워크(더 정확히는 DI 컨테이너)를 만들게 됨</li>
</ul>
<h3 id="정리-1">정리</h3>
<ul>
<li>모든 설계에 역할과 구현을 분리할 것(자동차, 공연처럼)</li>
<li>애플리케이션 설계도 공연을 기획하듯이 배역을 두고 배우는 언제든지 유연하게 변경할 수 있도록 만드는 것이 좋은 객체지향 설계</li>
<li>제일 이상적인 것은 모든 설계에 인터페이스를 두는 것<ul>
<li>‘이상적’이라고 표현한 이유는 인터페이스를 도입했을 때 추상화라는 비용이 발생하기 때문</li>
<li>추상화는 구체화하는 과정이 추가로 요구됨</li>
<li>장점이 단점을 상쇄할 때 선택</li>
</ul>
</li>
<li>기능을 확장할 가능성이 없다면 구체 클래스를 직접 사용하고, 향후 꼭 필요할 때 리팩토링해서 인터페이스를 도입하는 것도 방법</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[10) 제네릭2 - 제네릭의 활용]]></title>
            <link>https://velog.io/@dev-mage/hello-java-world-generics-advances</link>
            <guid>https://velog.io/@dev-mage/hello-java-world-generics-advances</guid>
            <pubDate>Tue, 13 Dec 2022 05:32:11 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>와일드 카드와 제네릭의 활용</p>
</blockquote>
<h3 id="제한된-타입-변수bounded-type-parameters">제한된 타입 변수(<strong><strong>Bounded Type Parameters</strong></strong>)</h3>
<p>타입 문자로 사용할 타입을 명시하면 한 종류의 타입만 저장하도록 제한할 수 있지만, 여전히 모든 종류의 타입을 지정할 수 있다. 아예 지정할 수 있는 타입에 제한을 두고 싶다면 타입 매개변수 T에 지정할 수 있는 타입의 종류를 제한하면 된다.</p>
<p>예를 들어 카페에서 식재료를 보관하는 보관함들이 있다. 하지만 따로 제한을 두지 않을 경우 엉뚱한 물건을 담게 될 수도 있다.</p>
<pre><code class="language-java">StorageBox&lt;Tool&gt; storageBox = new StorageBox&lt;Tool&gt;();
storageBox.add(new Tool()); // 식재료 대신 공구를 저장하게 됨</code></pre>
<p>다음과 같이 제네릭 타입에 <code>extends</code>를 사용하면 특정 타입의 자식들만 대입할 수 있게 제한할 수 있다. 여전히 한 종류의 타입만 담을 수 있지만 담을 수 있는 타입의 종류에 제한이 추가된 것이다.</p>
<pre><code class="language-java">public class StorageBox&lt;T extends Ingredient&gt; { // Ingredient의 자식 타입만 지정 가능
    ArrayList&lt;T&gt; list = new ArrayList&lt;T&gt;();
        ...
}

public class CoffeeBeans extends Ingredient{

////////////////////////////////////////////////////////////////////////

StorageBox&lt;CoffeeBeans&gt; storageBox1 = new StorageBox&lt;CoffeeBeans&gt;();
StorageBox&lt;Tool&gt; storageBox2 = new Serving&lt;Tool&gt;(); // 에러</code></pre>
<p>만일 클래스가 아니라 인터페이스를 구현해야 한다는 제약이 필요한 경우 역시 <code>implements</code> 대신 <code>extends</code>를 사용한다. 자식 클래스이면서 특정 인터페이스도 구현해야 한다면 기호 <code>&amp;</code>로 연결한다.</p>
<pre><code class="language-java">public interface Brewable { ... }
public class CoffeeBeans extends Ingredient implements Brewable {
public class StorageBox&lt;T extends Ingredient &amp; Brewable&gt; { ... }</code></pre>
<h3 id="와일드카드wildcards">와일드카드(Wildcards)</h3>
<p>제네릭 클래스를 생성할 때, 참조 변수에 지정된 제네릭 타입과 생성자에 지정된 타입은 일치해야 하고 상속 관계에 있더라도 일치하지 않는다면 컴파일 에러가 발생한다. 이런 제네릭 타입에 다형성을 적용하기 위해 도입된 것이 와일드카드이다. 와일드카드는 기호 <code>?</code>를 사용하며 <code>extends</code>와 <code>super</code>로 상한과 하한을 제한할 수 있다. </p>
<ul>
<li><code>&lt;? extends T&gt;</code>: 와일드카드의 상한(upper bound) 제한. T와 그 자식들만 가능.</li>
<li><code>&lt;? super T&gt;</code>: 와일드카드의 하한(lower bound) 제한. T와 그 부모들만 가능.</li>
<li><code>&lt;?&gt;</code>: 제한 없음. 모든 타입이 가능(<code>&lt;? extends Object&gt;</code>와 동일).</li>
</ul>
<p>제네릭 타입으로 와일드카드를 사용하면 다음과 같이 하나의 참조 변수로 다른 제네릭 타입이 지정된 객체를 다룰 수 있다.</p>
<pre><code class="language-java">public class FreshIngredient extends Ingredient{ ... }
public class Fruit extends FreshIngredient{ ... }
public class Vegetable extends FreshIngredient{ ... }

////////////////////////////////////////////////////////////////////////

StorageBox&lt;? extends FreshIngredient&gt; fruitBox = new StorageBox&lt;Fruit&gt;();
StorageBox&lt;? extends FreshIngredient&gt; vegeBox = new StorageBox&lt;Vegetable&gt;();</code></pre>
<p>와일드카드를 메서드의 매개변수에 적용하면 제네릭 타입이 다른 여러 객체를 매개변수로 지정할 수 있다.</p>
<pre><code class="language-java">public class FreshIngredient extends Ingredient{ String state; }

public class Freezer {
    static void freeze(StorageBox&lt;? extends FreshIngredient&gt; storageBox) {
        for(FreshIngredient i : storageBox.list) {
            i.state = &quot;frozen&quot;;
        }
    }
}

////////////////////////////////////////////////////////////////////////

Freezer.freeze(new StorageBox&lt;Fruit&gt;());
Freezer.freeze(new StorageBox&lt;Vegetable&gt;());</code></pre>
<h3 id="제네릭-타입-제거type-erasure">제네릭 타입 제거(type erasure)</h3>
<p>컴파일러는 제네릭 타입을 이용해서 소스 파일을 체크하고 필요한 곳에 형변환을 추가한 후 제네릭 타입을 제거한다. 즉 컴파일된 파일(*.class)에는 제네릭 타입과 관련된 정보가 없어지는 것이다. 이렇게 하는 이유는 제네릭이 도입되기 이전의 소스 코드와의 호환성을 유지하고 제네릭 타입을 호출할 때마다 대입된 타입의 새로운 클래스가 생성되는 것을 방지하여 런타임 오버헤드를 줄이기 위함이다.</p>
<ul>
<li><p>제네릭 타입 제거 과정</p>
<ol>
<li><p>제네릭 타입의 경계(bound) 제거</p>
<ul>
<li><p>제네릭 타입에 제한이 있다면 타입 매개변수 T는 제한된 타입으로 치환. 경계가 없는 경우 Object로 치환.</p>
</li>
<li><p>치환 후 제네릭 타입 선언은 제거됨.</p>
<pre><code class="language-java">public class StorageBox&lt;T extends Ingredient&gt; {
      ArrayList&lt;T&gt; list = new ArrayList&lt;T&gt;();
  void add(T item) { list.add(item); }
      T get(int i) { return list.get(i); }
}

////////////////////////////////////////////////////////////////////////

public class StorageBox {
      ArrayList list = new ArrayList();
  void add(Ingredient item) { list.add(item); }
      Ingredient get(int i) { return list.get(i); }
}</code></pre>
</li>
</ul>
</li>
<li><p>제네릭 타입을 제거한 후 타입이 일치하지 않으면 형변환을 추가</p>
<ul>
<li><p>List의 get()은 Object 타입을 반환하므로 형변환 필요.</p>
<pre><code class="language-java">public class StorageBox {
      ...
      Ingredient get(int i) { return (Ingredient) list.get(i); }
}</code></pre>
</li>
</ul>
</li>
</ol>
</li>
</ul>
<h3 id="제네릭의-제약">제네릭의 제약</h3>
<p>모든 객체가 공유하는 static 멤버에는 타입 변수 T를 사용할 수 없다. T는 인스턴스를 생성할 때마다 지정되어 인스턴스 변수로 간주되기 때문이다. </p>
<pre><code class="language-java">public class StorageBox&lt;T extends Ingredient&gt; {
    static T item; // 에러
        ...
}</code></pre>
<p><code>static T item;</code>에서 발생하는 에러는 static 멤버가 인스턴스 멤버를 참조하려는 경우에 발생하는 에러이다(<code>&#39;StorageBox.this&#39; cannot be referenced from a static context</code>). 인스턴스 생성 시 각 인스턴스마다 다른 타입을 지정할 수 있도록 제네릭스가 도입된 것인데 모든 인스턴스가 공유하는 static 멤버에 타입 변수를 적용할 경우 동일한 값을 유지할 수 없게 된다. </p>
<pre><code class="language-java">StorageBox&lt;CoffeeBeans&gt; coffeeBeansBox = new StorageBox&lt;&gt;();
StorageBox&lt;Fruit&gt; fruitBox = new StorageBox&lt;&gt;();
StorageBox&lt;Vegetable&gt; vegeBox = new StorageBox&lt;&gt;();</code></pre>
<p>만약 static 멤버에 타입 변수가 허용된다면 위 코드의 static 변수 item은 StorageBox<CoffeeBeans>에서 CoffeeBeans가 되고 StorageBox<Fruit>에서 Fruit이 되고, StorageBox<Vegetable>에서는 Vegetable이 된다. 모든 인스턴스가 더이상 공유할 수 없게 되는 것이다.</p>
<p>또한 제네릭 타입의 배열을 생성할 수 없다. 이는 <code>new</code> 연산자가 컴파일 시점에 타입 변수가 어떤 타입이 될지 알 수 없기 때문이다. <code>instanceof</code> 연산자도 같은 이유로 타입 변수를 피연산자로 쓸 수 없다. 만약 배열을 생성할 수 있다면 아래와 같이 될 것이다.</p>
<pre><code class="language-java">ArrayList&lt;String&gt;[] stringLists = new ArrayList&lt;String&gt;[1]; // 실제론 컴파일 에러

////////////////////////////////////////////////////////////////////////

// 타입 변수는 컴파일 시 제거되므로 컴파일 후 다음과 같이 됨.
ArrayList[] stringLists = new ArrayList[1];</code></pre>
<p>결국 문자열 대신 다른 타입이 저장되는 일을 방지할 수 없게 된다.</p>
<h3 id="제네릭-메서드">제네릭 메서드</h3>
<p>메서드 선언부에 제네릭 타입이 선언된 메서드를 제네릭 메서드라고 하며 리턴 타입과 매개변수 타입으로 타입 파라미터를 가질 수 있다. 제네릭 타입의 선언 위치는 반환 타입 바로 앞이다. 대표적으로 Collections.sort()가 있다. 제네릭 메서드는 제네릭 클래스가 아닌 클래스에도 정의될 수 있다.</p>
<pre><code class="language-java">public class Collections {
        ...
        public static &lt;T&gt; void sort(List&lt;T&gt; list, Comparator&lt;? super T&gt; c) {
            list.sort(c);
        }
        ...
}</code></pre>
<p>제네릭 클래스에 정의된 타입 매개변수 T와 제네릭 메서드에 정의된 타입 매개변수 T는 별개이다.</p>
<pre><code class="language-java">public class StorageBox&lt;T extends Ingredient&gt; {
    static &lt;T&gt; void sort(List&lt;T&gt; list, Comparator&lt;? super T&gt; c) {
                ...
    }
}</code></pre>
<p>제네릭 클래스 StorageBox에 선언된 타입 매개변수 T와 제네릭 메서드 sort()에 선언된 타입 매개변수 T는 타입 문자만 같을 뿐 전혀 다른 변수이다. 제네릭 클래스에 선언된 타입 매개변수가 인스턴스 변수로 취급되는 것처럼 메서드에 선언된 제네릭 타입은 지역 변수와 같다. 해당 타입 매개변수는 메서드 내에서 지역적으로 사용될 것이므로  메서드가 <code>static</code>이더라도 제네릭 타입을 선언하고 사용하는 것이 가능하다. 앞서 나왔던 freeze 메서드를 제네릭 메서드로 바꾸면 다음과 같다.</p>
<pre><code class="language-java">public class Freezer {
    static &lt;T extends FreshIngredient&gt; void freeze(StorageBox&lt;T&gt; storageBox) {
        for(FreshIngredient i : storageBox.list) {
            i.state = &quot;frozen&quot;;
        }
    }
}</code></pre>
<p>제네릭 메서드를 호출할 때는 타입 변수에 타입을 대입해야 하지만 컴파일러가 대입된 타입을 추정할 수 있는 경우 생략 가능하다.</p>
<pre><code class="language-java">StorageBox&lt;Fruit&gt; fruit = new StorageBox&lt;Fruit&gt;();
StorageBox&lt;Vegetable&gt; vege = new StorageBox&lt;Vegetable&gt;();

Freezer.freeze(fruit); // Freezer.&lt;Fruit&gt;freeze(fruit);
Freezer.freeze(vege); // Freezer.&lt;Vegetable&gt;freeze(vege);</code></pre>
<hr>
<h3 id="references">References</h3>
<ul>
<li>자바의 정석 CHAPTER 12</li>
<li><a href="https://dev.java/learn/type-erasure/">https://dev.java/learn/type-erasure/</a></li>
<li><a href="https://dev.java/learn/restriction-on-generics/">https://dev.java/learn/restriction-on-generics/</a></li>
<li><a href="https://pompitzz.github.io/blog/Java/whyCantCreateGenericsArray.html#%E1%84%8C%E1%85%A6%E1%84%82%E1%85%A6%E1%84%85%E1%85%B5%E1%86%A8%E1%84%80%E1%85%AA-%E1%84%87%E1%85%A2%E1%84%8B%E1%85%A7%E1%86%AF%E1%84%8B%E1%85%B4-%E1%84%8E%E1%85%A1%E1%84%8B%E1%85%B5%E1%84%8C%E1%85%A5%E1%86%B7">https://pompitzz.github.io/blog/Java/whyCantCreateGenericsArray.html#제네릭과-배열의-차이점</a></li>
<li><a href="https://yaboong.github.io/java/2019/01/19/java-generics-1/">https://yaboong.github.io/java/2019/01/19/java-generics-1/</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[섹션 1. 객체 지향 설계와 스프링 - 객체지향과 다형성]]></title>
            <link>https://velog.io/@dev-mage/spring-core-basic-oop-polymorphism</link>
            <guid>https://velog.io/@dev-mage/spring-core-basic-oop-polymorphism</guid>
            <pubDate>Fri, 09 Dec 2022 07:46:22 GMT</pubDate>
            <description><![CDATA[<p><a href="https://inf.run/VgfD">스프링 핵심 원리 - 기본편 - 인프런 | 강의</a></p>
<h1 id="좋은-객체지향-프로그래밍">좋은 객체지향 프로그래밍</h1>
<h3 id="객체지향-특징">객체지향 특징</h3>
<ul>
<li>추상화, 캡슐화, 상속, <strong>다형성</strong></li>
</ul>
<h3 id="객체지향-프로그래밍의-정의위키백과">객체지향 프로그래밍의 정의(위키백과)</h3>
<ul>
<li>객체 지향 프로그래밍은 컴퓨터 프로그램을 명령어의 목록으로 보는 시각에서 벗어나 여러 개의 독립된 단위, 즉 <strong>&quot;객체&quot;들의 모임</strong>으로 파악하고자 하는 것이다. 각각의 <strong>객체는 메시지를 주고받고</strong>, 데이터를 처리할 수 있다. (<strong>협력</strong>)</li>
<li>객체 지향 프로그래밍은 <strong>프로그램을 유연하고 변경이 용이하게</strong> 만들기 때문에 대규모 소프트웨어 개발에 많이 사용된다.</li>
</ul>
<h3 id="유연하고-변경이-용이하다">유연하고 변경이 용이하다?</h3>
<p>이 뜻은…</p>
<ul>
<li>레고블럭 조립하듯이</li>
<li>키보드, 마우스 갈아끼우듯이</li>
<li>컴퓨터 부품 갈아끼우듯이</li>
<li>컴포넌트를 쉽고 유연하게 변경하면서 개발할 수 있음</li>
</ul>
<h3 id="유연하고-변경-용이하게-해주는-궁극적인-방법-다형성">유연하고 변경 용이하게 해주는 궁극적인 방법: 다형성</h3>
<ul>
<li>실세계와 객체지향을 일대일로 매칭할 수는 없지만 실세계에 비유하면 이해하기 쉬움.</li>
<li>다형성을 실세계에 비유하기 위해서는 일단 <strong>역할</strong>(인터페이스)과 그 역할을 실제 행하는 <strong>구현</strong>으로 세상을 구분해야 함</li>
</ul>
<h3 id="다형성-실세계-비유-예시">다형성 실세계 비유 예시</h3>
<ul>
<li>운전자 - 자동차</li>
<li>공연</li>
<li>키보드, 마우스, 세상의 표준 인터페이스들</li>
<li>정렬 알고리즘(기능만 똑같으면 성능이 더 좋은 알고리즘으로 교체 가능)</li>
<li>할인 정책 로직</li>
</ul>
<h3 id="운전자---자동차">운전자 - 자동차</h3>
<ul>
<li>운전자 역할, 자동차 역할과 그 역할을 서로 다른 자동차들이 구현함</li>
</ul>
<p><img src="https://velog.velcdn.com/images/dev-mage/post/d80cb750-4864-49f8-a5cc-febac0bdd478/image.png" alt=""></p>
<ul>
<li>운전자는 차가 바뀌어도 당연히 운전할 수 있음</li>
<li>그렇기에 자동차가 바뀌어도 운전자한테 영향을 주지 않음<ul>
<li>자동차 모델이 바뀌어도 운전자는 해당 모델에 대한 운전면허를 딸 필요가 없듯이 운전자에게 영향을 끼치지 않는 이유는 운전자는 자동차 인터페이스만을 알고 있고 그런 자동차 인터페이스에 맞춰 자동차들이 구현되었기 때문.</li>
</ul>
</li>
<li>이것이 바로 자동차의 역할과 구현을 분리해 놓은 이유 → 운전자를 위해서 (운전자 = 클라이언트)</li>
<li>클라이언트는 자동차의 내부 구조를 몰라도 되고 자동차 내부가 바뀌어도 자동차 역할을 제대로 수행하고 있다면 영향을 받지 않음. 심지어 자동차 모델 자체가 바뀌어도 상관 없음</li>
<li>→ 이는 자동차 세상의 무한 확장 가능성을 뜻함<ul>
<li>= 세상을 바꾸지 않고 새로운 자동차 출시 가능</li>
<li>= 클라이언트에게 영향을 주지 않고 새로운 기능 추가 가능</li>
<li>유연하고 변경 용이함: 역할과 구현을 구분해 놓았기 때문에 가능한 일</li>
</ul>
</li>
<li>여기서 핵심은 자동차를 여러 개 구현(= 새로운 자동차 출시)하는 게 아님. <strong>새로운 자동차가 나오더라도 운전자는 무언가를 새로 배우지 않아도 된다는 것(= 클라이언트의 변경)</strong></li>
</ul>
<h3 id="공연-무대---로미오와-줄리엣-공연">공연 무대 - 로미오와 줄리엣 공연</h3>
<ul>
<li>공연에 로미오라는 역할과 줄리엣이라는 역할이 있음</li>
</ul>
<p><img src="https://velog.velcdn.com/images/dev-mage/post/6e4478be-edd2-428c-9628-85f3576eaeab/image.png" alt=""></p>
<ul>
<li>그 역할은 장동건이 할 수도 원빈이 할 수도, 김태희가 할 수도, 송혜교가 할 수도 있는 것</li>
<li>아니면 다른 무명 배우로도 대체할 수 있음</li>
<li>공연을 할 때 배우는 대체 가능해야 함. 어떠한 사정이 있더라도 공연은 진행되어야 하기 때문</li>
<li>이 또한 역할과 구현을 나누었기 때문에 대체가 가능함 → 유연하고 변경 용이</li>
<li>로미오 역할을 맡은 사람은 줄리엣을 누가 맡든지 그저 대본에 충실하면 됨</li>
<li>로미오가 클라이언트고 줄리엣이 서버라면<ul>
<li>줄리엣 배우(구현된 서버)가 바뀌더라도 로미오 배우에게 영향을 끼치지 않음</li>
<li>→ 다른 대상으로 대체 가능 → 유연하고 변경 용이</li>
</ul>
</li>
</ul>
<h3 id="역할과-구현을-분리">역할과 구현을 분리</h3>
<ul>
<li><strong>역할</strong>과 <strong>구현</strong>으로 구분하면 세상이 <strong>단순</strong>해지고 <strong>유연</strong>해지며 <strong>변경</strong>도 편리해짐</li>
<li>장점<ul>
<li><strong>클라이언트</strong>는 대상의 역할(인터페이스)만 알면 됨</li>
<li><strong>클라이언트</strong>는 구현 대상의 <strong>내부 구조를 몰라도</strong> 됨</li>
<li><strong>클라이언트</strong>는 구현 대상의 <strong>내부 구조가 변경</strong>되어도 영향을 받지 않음</li>
<li><strong>클라이언트</strong>는 구현 <strong>대상 자체를 변경</strong>해도 영향을 받지 않음</li>
<li><strong>⇒ 구현 대상이 바뀌어도 세상(클라이언트)를 바꿀 필요가 없음</strong></li>
</ul>
</li>
<li>자바에서<ul>
<li>자바 언어의 다형성을 활용<ul>
<li>역할 = 인터페이스</li>
<li>구현 = 인터페이스를 구현한 클래스, 구현 객체</li>
</ul>
</li>
<li>객체를 설계할 때 <strong>역할</strong>과 <strong>구현</strong>을 명확히 분리</li>
<li>객체 설계 시 역할(인터페이스)을 먼저 부여하고, 그 역할을 수행하는 구현 객체 만들기</li>
</ul>
</li>
</ul>
<h1 id="좋은-객체지향-프로그래밍-1">좋은 객체지향 프로그래밍</h1>
<h3 id="객체의-협력이라는-관계부터-생각">객체의 협력이라는 관계부터 생각</h3>
<ul>
<li>혼자 있는 객체는 없음</li>
<li>클라이언트: <strong>요청</strong>, 서버: <strong>응답</strong></li>
<li>수 많은 객체 클라이언트와 객체 서버는 서로 협력관계를 가짐</li>
</ul>
<p><img src="https://velog.velcdn.com/images/dev-mage/post/9b021d50-15a1-424b-a460-7fadc403893d/image.png" alt=""></p>
<ul>
<li>동시에 클라이언트와 서버가 될 수도 있음</li>
</ul>
<h3 id="자바-언어의-다형성">자바 언어의 다형성</h3>
<p><img src="https://velog.velcdn.com/images/dev-mage/post/7a0fa1ce-fd55-40d7-bdd9-8d6a92ee8450/image.png" alt=""></p>
<ul>
<li><strong>오버라이딩</strong>을 떠올려보자</li>
<li>오버라이딩은 자바 기본 문법</li>
<li>오버라이딩된 메서드가 실행됨</li>
<li>다형성으로 인터페이스를 구현한 객체를 실행 시점에 유연하게 변경할 수 있음</li>
<li>물론 클래스 상속 관계도 다형성, 오버라이딩 적용 가능</li>
</ul>
<p><img src="https://velog.velcdn.com/images/dev-mage/post/d5bf58b5-6584-4726-b642-4e763127860c/image.png" alt=""></p>
<ul>
<li>클라이언트(MemberService)는 MemberRepository에 의존하고 있음<ul>
<li>= 클라이언트는 MemberRepository를 알고 있음</li>
<li>MemberRepository를 구현한 MemoryMemberRepository와 JDBCMemberRepository를 할당할 수 있음</li>
</ul>
</li>
</ul>
<pre><code class="language-java">public class MemeberService {
        // private MemberRepository mr = new MemoryMemberRepository();
        private MemberRepository mr = new JDBCMemberRepository();
}</code></pre>
<ul>
<li>MemberRepository와 전혀 관련없는 것은 할당할 수 없음</li>
</ul>
<p><img src="https://velog.velcdn.com/images/dev-mage/post/30040b4d-a2bc-4f4e-9ff6-c5af10c52550/image.png" alt=""></p>
<h3 id="다형성의-본질">다형성의 본질</h3>
<ul>
<li>인터페이스를 구현한 객체 인스턴스를 <strong>실행 시점</strong>에 <strong>유연</strong>하게 <strong>변경</strong>할 수 있음</li>
<li>다형성의 본질을 이해하려면 <strong>협력</strong>이라는 객체 사이의 관계에서 시작해야함</li>
<li><strong>클라이언트를 변경하지 않고, 서버의 구현 기능을 유연하게 변경할 수 있음</strong></li>
</ul>
<h3 id="역할과-구현을-분리한다를-정리해-보자면">역할과 구현을 분리한다를 정리해 보자면…</h3>
<ul>
<li>실세계의 역할과 구현이라는 편리한 컨셉을 다형성을 통해 객체 세상으로 가져올 수 있음</li>
<li>덕분에) 유연하고 변경이 용이</li>
<li>덕분에) 확장 가능한 설계</li>
<li>클라이언트에 영향을 주지 않고 변경 가능</li>
<li><strong>관건은 인터페이스를 안정적으로 잘 설계하는 것</strong></li>
</ul>
<h3 id="역할과-구현을-분리하는-것의-한계점">역할과 구현을 분리하는 것의 한계점</h3>
<ul>
<li>역할(인터페이스) 자체가 변하면, 클라이언트, 서버 모두에 큰 변경이 발생<ul>
<li>자동차 → 비행기로 변경해야 하는 경우</li>
<li>공연 대본이 변경되는 경우</li>
<li>USB 인터페이스가 변경되는 경우</li>
<li><strong>결국 인터페이스를 안정적으로 잘 설계하는 것이 중요</strong></li>
</ul>
</li>
</ul>
<h3 id="스프링과-객체지향">스프링과 객체지향</h3>
<ul>
<li>다형성이 가장 중요!</li>
<li>스프링은 다형성을 극대화해서 이용할 수 있게 도와줌</li>
<li>스프링에서 이야기하는 제어의 역전(IoC), 의존관계 주입(DI)은 다형성을 활용해서 역할과 구현을 편리하게 다룰 수 있도록 지원함</li>
<li>스프링을 사용하면 마치 레고 블럭을 조립하듯이, 공연 무대의 배우를 선택하듯이 구현을 편리하게 변경할 수 있음</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[10) 제네릭1 - 제네릭과 타입 변수]]></title>
            <link>https://velog.io/@dev-mage/hello-java-world-generics-type-parameter</link>
            <guid>https://velog.io/@dev-mage/hello-java-world-generics-type-parameter</guid>
            <pubDate>Fri, 09 Dec 2022 07:37:50 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>Java의 제네릭과 타입 변수</p>
</blockquote>
<h3 id="제네릭generics">제네릭(Generics)</h3>
<p>제네릭은 다양한 타입의 객체들을 다루는 메서드나 컬렉션 클래스에 컴파일 시 타입 체크를 제공하는 기능(compile-time type check)이다. 객체의 타입을 컴파일 시에 체크하기 때문에 객체의 타입 안정성(type safety)을 높이고 형변환의 번거로움이 줄어든다. 타입 안정성을 높인다는 것은 의도하지 않은 타입의 객체를 저장하는 것을 막고, 저장된 객체를 꺼낼 때 원래의 타입과 다른 타입으로 형변환되어 발생할 수 있는 오류를 줄여주는 것을 뜻한다.</p>
<p>예를 들어 ArrayList를 생성할 때, 저장할 객체의 타입을 지정해 주면 지정한 타입 외에 다른 타입의 객체가 저장하려는 경우 에러가 발생한다.</p>
<pre><code class="language-java">ArrayList&lt;Coffee&gt; coffees = new ArrayList&lt;&gt;();
coffees.add(new Coffee());
coffees.add(new Bread()); // 컴파일 에러. Coffee 타입만 저장 가능.</code></pre>
<p>그리고 이미 어떤 타입의 객체가 저장되어 있는지 알고 있기 때문에 저장된 객체를 꺼낼 때는 형변환할 필요가 없다.</p>
<pre><code class="language-java">ArrayList coffees = new ArrayList();
coffees.add(new Coffee());
Coffee coffee = (Coffee) coffees.get(0);

////////////////////////////////////////////////////////////////////////

ArrayList&lt;Coffee&gt; coffees = new ArrayList&lt;&gt;();
coffees.add(new Coffee());
Coffee coffee = coffees.get(0); // 형변환 불필요.</code></pre>
<ul>
<li><p>제네릭의 장점</p>
<ul>
<li>타입 안정성 제공.</li>
<li>타입 체크와 형변환 생략 가능으로 간결해진 코드.</li>
</ul>
</li>
<li><p>제네릭 용어</p>
<p>  <img src="https://velog.velcdn.com/images/dev-mage/post/192ec4f5-05c0-4cee-a305-58368d4ab0bf/image.png" alt=""></p>
</li>
</ul>
<pre><code>- Menu&lt;T&gt;: 제네릭 클래스. ‘T의 Menu’ 또는 ‘T Menu’라고 읽음.
- T: 타입 변수 또는 타입 매개변수(T는 타입 문자).
- Menu: 원시 타입(raw type).

타입 문자 T는 제네릭 클래스 Menu&lt;T&gt;의 타입 변수 또는 타입 매개변수라고 하는데, 메서드의 매개변수와 유사한 면이 있기 때문이다. 그래서 다음과 같이 타입 매개변수에 타입을 지정하는 것을 ‘제네릭 타입 호출’이라 하며 지정된 타입 ‘Coffee’를 매개변수화된 타입이라고 한다.

![](https://velog.velcdn.com/images/dev-mage/post/d6acd9df-48e8-46dd-84cb-dbd372b9690b/image.png)


예를 들어 Menu&lt;Coffee&gt;와 Menu&lt;Bread&gt;는 제네릭 클래스 Menu&lt;T&gt;에 서로 다른 타입을 대입하여 호출한 것일 뿐, 이 둘이 별개의 클래스를 의미하는 것이 아니다. 이는 마치 매개변수의 값이 다른 메서드 호출, 즉 add(1, 2)와 add(3, 5)가 서로 다른 메서드를 호출하는 것이 아님과 같다.

컴파일 후에 Menu&lt;Coffee&gt;와 Menu&lt;Bread&gt;는 이들의 원시 타입인 Menu로 바뀐다. 즉 제네릭 타입이 제거된다.</code></pre><h3 id="타입-변수type-variable">타입 변수(type variable)</h3>
<p>ArrayList 클래스의 선언에서 클래스 이름 옆의 ‘&lt;&gt;’안에 명시된 클래스를 타입 변수라고 한다.</p>
<pre><code class="language-java">public class ArrayList&lt;E&gt; extends AbstractList&lt;E&gt;
        implements List&lt;E&gt;, RandomAccess, Cloneable, java.io.Serializable
{ ... }</code></pre>
<p>타입 변수가 여러 개인 경우에는 Map&lt;K, V&gt;와 같이 콤마(,)를 구분자로 나열하면 된다. 제네릭이 도입되기 이전에는 다양한 종류의 타입을 다루는 메서드의 매개변수나 리턴 타입으로 Object 타입의 참조 변수를 많이 사용했기에 형변환이 불가피했지만, 제네릭 덕분에 원하는 타입의 타입 변수를 지정해 주기만 하면 된다.</p>
<p>ArrayList와 같은 제네릭 클래스를 생성할 때는 다음과 같이 참조 변수와 생성자에 타입 변수 E 대신 실제 타입을 지정해 주어야 한다.</p>
<pre><code class="language-java">ArrayList&lt;Coffee&gt; coffees = new ArrayList&lt;Coffee&gt;();</code></pre>
<p>이때 타입 변수 E 대신 지정된 타입 Coffee를 매개변수화된 타입 또는 대입된 타입(parameterized type)이라고 한다. 타입이 대입되고 나면 ArrayList의 선언에 포함된 타입 변수 E가 아래와 같이 대입된 타입으로 바뀐다고 생각하면 된다.</p>
<pre><code class="language-java">public class ArrayList&lt;E&gt; extends AbstractList&lt;E&gt; ... {
        ...
        transient E[] elementData;
        public boolean add(E e) { ... }
        public E get(int index) { ... }
        ...
}

////////////////////////////////////////////////////////////////////////

public class ArrayList extends AbstractList ... {
        ...
        transient Coffee[] elementData;
        public boolean add(Coffee e) { ... }
        public Coffee get(int index) { ... }
        ...
}</code></pre>
<h3 id="제네릭-타입과-다형성">제네릭 타입과 다형성</h3>
<p>제네릭 클래스의 객체를 생성할 때 참조 변수에 지정해준 제네릭 타입과 생성자에 지정해 준 제네릭 타입은 상속 관계의 부모-자식과 상관 없이 일치해야 한다.</p>
<pre><code class="language-java">public class Menu { ... }
public class CoffeeMenu extends Menu{ ... }

////////////////////////////////////////////////////////////////////////

ArrayList&lt;Menu&gt; menus1 = new ArrayList&lt;Menu&gt;();
ArrayList&lt;Menu&gt; menus2 = new ArrayList&lt;CoffeeMenu&gt;(); // 에러. 타입 불일치</code></pre>
<p>그러나 제네릭 타입이 아닌 클래스 타입 간 다형성을 적용하는 것은 가능하다. 이 경우에도 제네릭 타입은 일치해야 한다.</p>
<pre><code class="language-java">List&lt;Menu&gt; menus = new ArrayList&lt;Menu&gt;(); // ArrayList가 List를 구현</code></pre>
<p>제네릭 클래스의 객체를 생성할 때는 참조 변수와 생성자에 지정한 제네릭 타입이 같아야 하지만 값을 저장할 때는 상속 관계에 있는 자식 객체를 저장하는 것이 가능하다. 대신 저장된 객체를 꺼낼 때 형변환이 필요하다.</p>
<pre><code class="language-java">List&lt;Menu&gt; menus = new ArrayList&lt;Menu&gt;();
menus.add(new CoffeeMenu());
CoffeeMenu coffeeMenu = (CoffeeMenu) menus.get(0);</code></pre>
<hr>
<h3 id="references">References</h3>
<ul>
<li>자바의 정석 CHAPTER 12</li>
<li><a href="https://www.oracle.com/technical-resources/articles/java/juneau-generics.html">https://www.oracle.com/technical-resources/articles/java/juneau-generics.html</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[섹션 1. 객체 지향 설계와 스프링 - 스프링이란?]]></title>
            <link>https://velog.io/@dev-mage/spring-core-basic-what-is-spring</link>
            <guid>https://velog.io/@dev-mage/spring-core-basic-what-is-spring</guid>
            <pubDate>Thu, 01 Dec 2022 13:04:52 GMT</pubDate>
            <description><![CDATA[<p><a href="https://inf.run/VgfD">스프링 핵심 원리 - 기본편 - 인프런 | 강의</a></p>
<blockquote>
<p>스프링의 탄생 배경과 정의</p>
</blockquote>
<h1 id="스프링-탄생-배경"><strong>스프링 탄생 배경</strong></h1>
<h3 id="ejb-지옥">EJB 지옥</h3>
<ul>
<li>2000년 초반 자바 진영은 자바 표준 기술인 EJB가 대세였음. 당시 오픈 소스 기술은 별로 좋은 인식을 갖고 있지 못함.)<ul>
<li>EJB(Enterprise Java Beans): 기업에서 시스템을 개발하기 위해 주로 사용된 서버 애플리케이션. 지금으로 따지면 Spring + JPA와 같은 종합 선물(?) 세트 (왜 물음표가 붙었는지는 뒤에…)</li>
<li>표준 기술을 그것을 구현한 구현체를 만드는 회사가 존재함.<ul>
<li>IBM - 웹스피어</li>
<li>구 BEA 현 오라클 - 웹로직</li>
</ul>
</li>
</ul>
</li>
<li>자바의 표준 기술이었기 때문에 많은 기업에서 도입함. 영업도 잘 됐음.(듣보잡 오픈 소스 기술 No!)</li>
<li>왜 대세였나?<ul>
<li>다양한 고급 기술 제공.<ul>
<li>컨테이너 기술</li>
<li>설정에 의한(선언적) 트랜잭션 관리</li>
<li>분산 계층 기술</li>
<li>엔티티빈 기술(ORM) 등…</li>
</ul>
</li>
<li>= 이론적으로 괜찮았음.</li>
</ul>
</li>
<li>근데 왜 지금은…?<ul>
<li>당시 매우 비쌌음. 수천만원을 호가하기도. 지금 무료로 풀려있는 Spring Boot를 생각해보자. 내장 서버까지 포함하고 있다.</li>
<li>느림</li>
<li>매우 어렵고 복잡함 - 그때 당시 개발자들을 EJB 지옥에 빠져 불타게 만든 주범.<ul>
<li>이론상 괜찮았지만 사용하기 매우 어렵고 EJB에 의존적으로 개발해야 했음.</li>
<li>코드가 지저분해 지고 유지보수하기 어려워짐.</li>
</ul>
</li>
<li>못써먹을 수준의 기술인 엔티티빈.</li>
</ul>
</li>
<li>그래서 등장했다. POJO!<ul>
<li>POJO(Plain Old Java Object): EJB에 종속되어 복잡하고 무거워진 코드 말고 오래된 방식의 순수한 옛날 자바 코드로 돌아가자!</li>
</ul>
</li>
</ul>
<h3 id="그래서-등장했습니다-spring과-jpa">그래서 등장했습니다. Spring과 JPA</h3>
<p>이건 아니다라고 생각했던 개빈 킹과 로드 존슨이라는 개발자가 2가지 기술을 내놓음.</p>
<ul>
<li><p>하이버네이트</p>
<ul>
<li>EJB의 엔티티빈을 대체할 기술인 하이버네이트를 개빈 킹이 개발함.</li>
<li>하이버네이트의 인기가 치솟자 망해가던 엔티티빈을 보던 자바 진영에서 개빈 킹을 데려와 하이버네이트를 정제하여 자바 표준 ORM 기술인 JPA를 개발.</li>
<li>현재 JPA라는 표준 인터페이스가 있고 자바 진영의 ORM 시장을 장악함.</li>
<li>다양한 JPA 구현체들이 있지만 하이버네이트가 압도적으로 우세.</li>
</ul>
</li>
<li><p>스프링</p>
<ul>
<li><p>2002년, 로드 존슨이 EJB를 비판하는 책을 출판하고 해당 책의 내용이 현재 스프링의 핵심 개념과 개발 코드의 근간이 됨.</p>
<p>  “EJB 없이도 충분히 고품질의 확장 가능한 애플리케이션 개발이 가능하다!”</p>
</li>
<li><p>책 출간 후 히트를 침. 이후 유겐 휠러와 얀 카로프라는 2명의 개발자들이 오픈 소스 프로젝트를 제안함.</p>
</li>
<li><p>스프링이라는 이름은 얀 카로프가 전통적인 J2EE(EJB)라는 겨울이 지나고 새로운 봄이 왔다는 의미로 지음.</p>
</li>
</ul>
</li>
</ul>
<h1 id="스프링이란">스프링이란?</h1>
<h3 id="스프링-생태계">스프링 생태계</h3>
<ul>
<li>스프링: 여러 가지 기술들의 모음<ul>
<li>필수: 스프링 프레임워크, 스프링 부트</li>
<li>선택: 스프링 데이터, 스프링 세션, 스프링 시큐리티, 스프링 배치, 그 외 다수 프로젝트들</li>
</ul>
</li>
</ul>
<h3 id="스프링-프레임워크">스프링 프레임워크</h3>
<ul>
<li>핵심 기술: 스프링 DI 컨테이너, AOP, 이벤트, 기타…</li>
<li>웹 기술: 스프링 MVC, 스프링 WebFlux</li>
<li>데이터 접근 기술: 트랜잭션, JDBC, ORM 지원, XML 지원</li>
<li>기술 통합: 캐시, 이메일, 원격 접근, 스케줄링</li>
<li>테스트: 스프링 기반 테스트 지원</li>
<li>언어: 코틀린, 그루비 지원</li>
<li>최근에는 스프링 부트를 통해서 스프링 프레임워크의 기술들을 편리하게 사용</li>
</ul>
<h3 id="스프링-부트">스프링 부트</h3>
<ul>
<li><strong>스프링을 편리하게 사용할 수 있도록 지원, 최근에는 기본으로 사용</strong></li>
<li>단독으로 실행할 수 있는 스프링 애플리케이션을 쉽게 생성</li>
<li>Tomcat 같은 웹 서버를 내장해서 별도의 웹 서버를 설치하지 않아도 됨</li>
<li>손쉬운 빌드 구성을 위한 starter 종속성 제공</li>
<li>스프링과 3rd party(외부) 라이브러리 자동 구성</li>
<li>메트릭, 상태 확인, 외부 구성 같은 프로덕션 준비 기능 제공(모니터링 기능)</li>
<li>관례에 의한 간결한 설정 → 하나하나 설정해 줄 필요 없음</li>
<li>스프링 프레임워크와 별도로 사용할 수 있는 게 아님! 스프링 프레임워크 기반으로 다양한 기술을 편리하게 사용할 수 있도록 도와주는 것</li>
</ul>
<h3 id="스프링이란-단어">‘스프링’이란 단어</h3>
<ul>
<li>스프링이라는 단어는 문맥에 따라 다르게 사용됨<ul>
<li>스프링 DI 컨테이너 기술</li>
<li>스프링 프레임워크</li>
<li>스프링 부트, 스프링 프레임워크 등을 모두 포함한 스프링 생태계</li>
</ul>
</li>
</ul>
<h3 id="스프링은-왜-만들었나요-스프링의-핵심-컨셉">스프링은 왜 만들었나요? 스프링의 핵심 컨셉</h3>
<p>어떤 기술이든 왜 만들었는지에 대한 핵심 컨셉은 간단함. 그래서 왜 이 기술을 만들었는지, 핵심 컨셉은 무엇인지와 같은 것들이 중요함.</p>
<ul>
<li>스프링이 나오기 전, EJB를 사용하려면 EJB에 의존적으로 개발해야 했음</li>
<li>그러다 보니 객체지향이 가진 좋은 장점을 다 버린 채 EJB 스타일로 개발하게 됨</li>
<li>그래서 다시 순수한 자바 스타일(객체지향)로 돌아가자는 POJO라는 단어까지 생김(이는 스프링 DI 개념과 연관이 깊음)</li>
<li>스프링은 자바 언어 기반의 프레임워크</li>
<li>자바 언어의 가장 큰 특징 - 객체지향 언어</li>
<li>스프링은 객체지향 언어가 가진 강력한 특징을 살려내는 프레임워크</li>
<li><strong>스프링은 좋은 객체지향 애플리케이션을 개발할 수 있게 도와주는 프레임워크</strong></li>
<li>따라서 좋은 객체지향 프로그래밍은 무엇인지 알아야 스프링을 제대로 이해할 수 있음</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[9) 컬렉션 프레임워크6 - 이진 탐색 트리와 TreeSet]]></title>
            <link>https://velog.io/@dev-mage/hello-java-world-the-collections-framework-binary-search-tree</link>
            <guid>https://velog.io/@dev-mage/hello-java-world-the-collections-framework-binary-search-tree</guid>
            <pubDate>Wed, 30 Nov 2022 06:56:10 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>이진 탐색 트리와 TreeSet</p>
</blockquote>
<h3 id="treeset">TreeSet</h3>
<p>TreeSet은 이진 탐색 트리라는 자료 구조 형태로 데이터를 저장하는 컬렉션 클래스이다. 이진 탐색 트리는 정렬, 검색, 범위 검색(range search)에 높은 성능을 보이는 자료 구조이며 TreeSet은 이진 탐색 트리의 성능을 향상시킨 레드-블랙 트리(Red-Black tree)로 구현되어 있다. Set 인터페이스를 구현했으므로 데이터를 중복으로 저장하지 않고 값을 정렬하여 저장하므로 저장 순서를 유지하지도 않는다.</p>
<p>이진 트리(binary tree)는 연결 리스트처럼 여러 개의 노드가 서로 연결된 구조로, 각 노드에 최대 2개의 노드를 연결할 수 있으며 루트(root)라고 불리는 하나의 노드에서부터 시작해 계속 확장해 나간다. 위 아래로 연결된 두 노드를 부모-자식 관계에 있다고 하며 위의 노드를 부모 노드, 아래 노드를 자식 노드라 한다. 부모-자식 관계는 상대적이며 하나의 부모는 최대 2개의 자식 노드와 연결될 수 있다. 이진 트리의 노드를 코드로 표현하면 다음과 같다.</p>
<pre><code class="language-java">class TreeNode {
        TreeNode left;  // 왼쪽 자식 노드
        Object element; // 객체를 저장하기 위한 참조 변수
        TreeNode right; // 오른쪽 자식 노드
}</code></pre>
<p><img src="https://velog.velcdn.com/images/dev-mage/post/5d2adcbf-6e3d-4fc7-afe8-6ce67425d926/image.png" alt=""></p>
<h3 id="이진-탐색-트리binary-search-tree">이진 탐색 트리(binary search tree)</h3>
<p>이진 탐색 트리는 부모 노드의 왼쪽에는 부모 노드의 값보다 작은 값의 자식 노드를, 오른쪽에는 큰 값의 자식 노드를 저장하는 이진 트리이다.</p>
<p>예를 들어 데이터를 5, 1, 7의 순서로 저장한 이진 탐색 트리의 구조는 아래와 같이 표현할 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/dev-mage/post/cd2f91f7-3bf3-49d4-88ae-17e17339455c/image.png" alt=""></p>
<p>왼쪽 마지막 값에서부터 오른쪽 값까지 값을 ‘왼쪽 노드 → 부모 노드 → 오른쪽 노드’ 순으로 읽어오면 오름차순으로 정렬된 순서를 얻을 수 있다. TreeSet은 이처럼 정렬된 상태를 유지하기 때문에 단일 값 검색과 범위 검색(range search, 예를 들어 3과 7사이의 범위에 있는 값을 검색)이 매우 빠르다. 저장된 값의 개수에 비례해서 검색 시간이 증가하긴 하지만 값의 개수가 10배 증가해도 특정 값을 찾는데 필요한 비교횟수가 3~4번만 증가할 정도로 검색 효율이 뛰어난 자료 구조이다.</p>
<p>트리는 데이터를 순차적으로 저장하는 것이 아니라 저장 위치를 찾아서 저장해야 하고, 삭제하는 경우 트리의 일부를 재구성해야 하므로 연결 리스트보다 데이터의 추가 / 삭제 시간은 더 걸린다. 대신 배열이나 연결 리스트에 비해 검색과 정렬 기능이 더 뛰어나다.</p>
<ul>
<li>특징<ul>
<li>모든 노드는 최대 2개의 자식 노드를 가질 수 있음.</li>
<li>왼쪽 자식 노드의 값은 부모 노드의 값보다 작고 오른쪽 자식 노드의 값은 부모 노드의 값보다 커야 함.</li>
<li>노드의 추가, 삭제에 시간이 걸림(순차적으로 저장하지 않으므로).</li>
<li>검색(범위 검색)과 정렬에 유리.</li>
<li>중복된 값을 저장하지 못함.</li>
</ul>
</li>
</ul>
<h3 id="이진-탐색-트리의-저장-과정">이진 탐색 트리의 저장 과정</h3>
<p>예를 들어 이진 탐색 트리에 7, 4, 9, 1, 5의 순서로 값을 저장한다고 가정하면 다음과 같은 순서로 진행된다.</p>
<p><img src="https://velog.velcdn.com/images/dev-mage/post/8557498b-c388-44af-9b8c-7f438788357b/image.png" alt=""></p>
<ul>
<li>첫 번째로 저장되는 값은 루트가 됨.</li>
<li>두 번째 값은 트리의 루트부터 시작해 값의 크기를 비교하면서 트리를 따라 내려감.</li>
<li>작은 값은 왼쪽에 큰 값은 오른쪽에 저장.</li>
<li>이렇게 트리를 구성하면, 왼쪽 마지막 레벨이 제일 작은 값이 되고 오른쪽 마지막 레벨의 값이 제일 큰 값이 됨.</li>
</ul>
<pre><code class="language-java">public static void main(String[] args) {
    Integer[] ints = {7, 4, 9, 1, 5};
    TreeSet set = new TreeSet(Arrays.asList(ints));
    for (Object o : set) {
        System.out.print(o + &quot; &quot;);
    }

    // 실행 결과
    // 1 4 5 7 9 
}</code></pre>
<p>TreeSet에 저장되는 객체가 Comparable을 구현하든지 Comparator를 제공해서 두 객체를 비교할 방법을 알려주지 않으면 TreeSet에 객체를 저장할 때 예외가 발생한다. 다음과 같은 Menu 클래스 있고 이 Menu 클래스의 객체를 TreeSet에 추가하려는 경우 에러가 발생한다.</p>
<pre><code class="language-java">public class Menu {
    private String menuName;
    private int price;
    public Menu(String menuName, int price) {
        this.menuName = menuName;
        this.price = price;
    }

        ...

    @Override
    public String toString() {
        return &quot;&lt;&quot; + menuName + &quot;: &quot; + price + &quot;&gt;&quot;;
    }
}</code></pre>
<pre><code class="language-java">public static void main(String[] args) {
    Menu[] menus = {
            new Menu(&quot;coffee&quot;, 3000),
            new Menu(&quot;tea&quot;, 3500),
            new Menu(&quot;ade&quot;, 4500)
    };
    TreeSet set = new TreeSet(Arrays.asList(menus));
    for (Object o : set) {
        System.out.print(o + &quot; &quot;);
    }
}</code></pre>
<ul>
<li><code>Exception in thread &quot;main&quot; java.lang.ClassCastException: collection_framework.Menu cannot be cast to java.lang.Comparable</code> 발생</li>
</ul>
<p>따라서 Comparable을 구현하여 비교할 수 있도록 해주어야 한다.</p>
<pre><code class="language-java">public class Menu implements Comparable&lt;Menu&gt; {
    ...
    @Override
    public int compareTo(Menu menu) {
        return this.getPrice() - menu.getPrice();
    }
}</code></pre>
<pre><code class="language-java">public static void main(String[] args) {
    Menu[] menus = {
            new Menu(&quot;coffee&quot;, 3000),
            new Menu(&quot;tea&quot;, 3500),
            new Menu(&quot;ade&quot;, 4500)
    };
    TreeSet set = new TreeSet(Arrays.asList(menus));
    for (Object o : set) {
        System.out.print(o + &quot; &quot;);
    }

    // 실행 결과
    // &lt;coffee: 3000&gt; &lt;tea: 3500&gt; &lt;ade: 4500&gt; 
}</code></pre>
<hr>
<h3 id="references">References</h3>
<ul>
<li>자바의 정석 CHAPTER 11</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[9) 컬렉션 프레임워크5 - Hash와 Iterator]]></title>
            <link>https://velog.io/@dev-mage/hello-java-world-the-collections-framework-hash-iterator</link>
            <guid>https://velog.io/@dev-mage/hello-java-world-the-collections-framework-hash-iterator</guid>
            <pubDate>Mon, 28 Nov 2022 04:22:56 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>HashMap, HashSet과 Iterator</p>
</blockquote>
<h3 id="hashmap">HashMap</h3>
<p>HashMap은 Map 인터페이스를 구현한 컬렉션 클래스로 Map의 특징인 키와 값을 묶어서 하나의 데이터(entry)로 저장하며 해싱을 사용하기 때문에 많은 양의 데이터를 검색하는데 있어서 뛰어난 성능을 보인다. HashMap은 키와 값을 각각 Object 타입으로 저장하고 키와 값을 저장하기 위해 Map의 내부 인터페이스인 Map.Entry 인터페이스를 구현한다. 이는 키와 값이 서로 연관되었기 때문에 이들을 별개로 다루지 않고 Entry라는 타입으로 묶어서 취급하는 것이다. </p>
<ul>
<li>비객체지향적인 코드</li>
</ul>
<pre><code class="language-java">Object[] key;
Object[] value;</code></pre>
<ul>
<li>객체지향적인 코드</li>
</ul>
<pre><code class="language-java">Entry[] table;
class Entry {
        Object key;
        Object value;
}</code></pre>
<h3 id="hashset">HashSet</h3>
<p>HashSet은 Set 인터페이스를 구현한 가장 대표적인 컬렉션 클래스이며 Set 인터페이스의 특징대로 중복된 요소를 저장하지 않고 저장 순서를 유지하지 않는다. 만약 저장 순서를 유지하고자 한다면 LinkedHashSet을 사용해야 한다.</p>
<p>List 인터페이스를 구현하는 컬렉션들은 인덱스를 통해 값에 접근할 수 있었다. 하지만 HashSet의 경우 저장 순서가 없으므로 인덱스로 값을 읽어 올 수 없다. 대신 Iterator 인터페이스를 이용하면 된다.</p>
<h3 id="컬렉션에-저장된-요소-읽어-오기---iterator-listiterator-enumeration">컬렉션에 저장된 요소 읽어 오기 - Iterator, ListIterator, Enumeration</h3>
<p>컬렉션 프레임워크에서는 컬렉션에 저장된 요소들을 읽어오는 방법을 표준화하였다. List와 Set 인터페이스의 부모 인터페이스인 Collection 인터페이스는 Iterable 인터페이스를 상속 받고있으며 이 Iterable 인터페이스는 Iterator를 생성하도록 하고 있다. Iterator, ListIterator, Enumeration 모두 컬렉션에 저장된 요소를 접근하는데 사용되는 인터페이스이다.</p>
<ul>
<li>Iterator: 컬렉션에 저장된 요소를 접근하는데 사용되는 인터페이스.</li>
<li>ListIterator: Iterator에 양방향 조회 기능을 추가한 인터페이스(List를 구현한 경우만 사용 가능).</li>
<li>Enumeration: Iterator의 구버전 → Iterable 인터페이스 구현 X</li>
</ul>
<p>Collection 인터페이스에는 Iterable 인터페이스에 정의된 ‘Iterator를 구현한 클래스의 인스턴스’를 반환하는 iterator()가 구현되어 있다.</p>
<pre><code class="language-java">public interface Iterable&lt;T&gt; {
    Iterator&lt;T&gt; iterator();
        ...
}</code></pre>
<pre><code class="language-java">public interface Iterator&lt;E&gt; {
    boolean hasNext();
    E next();
        ...
}</code></pre>
<pre><code class="language-java">public interface Collection&lt;E&gt; extends Iterable&lt;E&gt; {
    ...
    Iterator&lt;E&gt; iterator();
        ...
}</code></pre>
<pre><code class="language-java">public interface Set&lt;E&gt; extends Collection&lt;E&gt; {
    ...
    Iterator&lt;E&gt; iterator();
        ...
}</code></pre>
<p>List나 Set 인터페이스를 구현하는 컬렉션 클래스는 iterator()가 각 컬렉션의 특징에 알맞게 작성되어 있다.</p>
<pre><code class="language-java">public class ArrayList&lt;E&gt; extends AbstractList&lt;E&gt;
        implements List&lt;E&gt;, RandomAccess, Cloneable, java.io.Serializable
{
        ...
        public Iterator&lt;E&gt; iterator() {
        return new Itr();
    }

    private class Itr implements Iterator&lt;E&gt; {
        int cursor;       // index of next element to return
        int lastRet = -1; // index of last element returned; -1 if no such
        int expectedModCount = modCount;

        Itr() {}

        public boolean hasNext() {
            return cursor != size;
        }
                ...
    }
        ...
}</code></pre>
<pre><code class="language-java">public class HashSet&lt;E&gt;
    extends AbstractSet&lt;E&gt;
    implements Set&lt;E&gt;, Cloneable, java.io.Serializable
{
        ...
    public Iterator&lt;E&gt; iterator() {
        return map.keySet().iterator();
    }
        ...
}</code></pre>
<p>컬렉션 클래스에 대해 iterator()를 호출하여 Iterator를 얻은 다음 반복문을 사용해서 컬렉션 클래스의 요소들을 읽어 올 수 있다.</p>
<pre><code class="language-java">public static void main(String[] args) {
    Object[] objects = {&quot;1&quot;, new Integer(1), &quot;2&quot;, &quot;2&quot;, &quot;3&quot;, &quot;3&quot;, &quot;3&quot;, &quot;4&quot;, &quot;4&quot;};
    HashSet set = new HashSet&lt;&gt;(Arrays.asList(objects));

    Iterator it = set.iterator();
    while (it.hasNext()) {
        System.out.print(it.next() + &quot; &quot;);
    }

    // 실행 결과
    // 1 1 2 3 4
}</code></pre>
<p>실행 결과 1이 두 번 출력된 것을 알 수 있다. 하나는 String 인스턴스이고 다른 하나는 Integer 인스턴스로 서로 다른 객체이기 때문에 중복으로 간주되지 않은 것이다.</p>
<h3 id="map과-iterator">Map과 Iterator</h3>
<p>Map 인터페이스를 구현한 컬렉션 클래스는 키와 값을 쌍(pair)으로 저장하고 있기 때문에 iterator를 직접 호출할 수 없고, 대신 keySet()이나 entrySet()과 같은 메서드를 통해 키와 값을 각각 따로 Set의 형태로 얻어온 후에 다시 iterator()를 호출해야 Iterator를 얻을 수 있다.</p>
<pre><code class="language-java">HashMap map = new HashMap();
        ...
Iterator it = map.entrySet().iterator();</code></pre>
<h3 id="사용자-정의-객체-중복-제거">사용자 정의 객체 중복 제거</h3>
<p>Customer 클래스는 이름(name)과 휴대전화 번호 끝 4자리(phoneNum)를 멤버 변수로 갖는다. 이름과 휴대전화 번호가 같으면 같은 사람으로 인식해 중복으로 고객 리스트를 작성하지 않으려고 한다.</p>
<pre><code class="language-java">import java.util.*;

public class Customer {
    String name;
    String phoneNum;

    public Customer(String name, String phoneNum) {
        this.name = name;
        this.phoneNum = phoneNum;
    }

    @Override
    public String toString() {
        return &quot;[&quot; + name + &quot; - &quot; + phoneNum + &quot;]&quot;;
    }

    public static void main(String[] args) {
        Customer[] customers = {
                new Customer(&quot;David&quot;, &quot;1111&quot;),
                new Customer(&quot;David&quot;, &quot;1111&quot;),
                new Customer(&quot;Alice&quot;, &quot;2222&quot;),
                new Customer(&quot;Kelly&quot;, &quot;6777&quot;)
        };
        HashSet customerList = new HashSet&lt;&gt;(Arrays.asList(customers));

        System.out.println(customerList);
    }
}</code></pre>
<ul>
<li>실행 결과</li>
</ul>
<p><img src="https://velog.velcdn.com/images/dev-mage/post/7bbe21d8-92ed-4030-914f-bed5548d2c2d/image.png" alt=""></p>
<p>하지만 실행 결과, 이름과 번호가 같음에도 서로 다른 사람으로 인식하여 David가 2번 저장되었다. 이런 경우 Customer 클래스는 equals()와 hashCode()를 오버라이딩해야 한다. HashSet에서 새로운 요소를 추가하기 전에 기존에 저장된 요소와 같은 것인지 판별하기 위해 추가하려는 요소의 equals()와 hashCode()를 호출하기 때문이다.</p>
<pre><code class="language-java">import java.util.*;

public class Customer {
    ...
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Customer customer = (Customer) o;
        return name.equals(customer.name) &amp;&amp; phoneNum.equals(customer.phoneNum);
    }
    @Override
    public int hashCode() {
        return Objects.hash(name, phoneNum);
    }
        ...
}</code></pre>
<ul>
<li>실행 결과</li>
</ul>
<p><img src="https://velog.velcdn.com/images/dev-mage/post/56fcabd1-0961-4c93-a3f9-6803e00bdd08/image.png" alt=""></p>
<h3 id="hash">hash</h3>
<p>HashMap과 HashSet에 공통적으로 ‘hash’라는 단어가 들어가는 이유는 바로 중복되는 값을 찾기 위해 해싱 알고리즘을 사용하기 때문이다. Set에서는 같은 객체인지 판별하기 위해, Map에서는 같은 키인지 판별하기 위해 <a href="https://velog.io/@dev-mage/hello-java-world-java-lang-package-object#hashcode">hashCode()</a>를 사용한다.</p>
<hr>
<h3 id="reference">Reference</h3>
<ul>
<li>자바의 정석 CHAPTER 11</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[9) 컬렉션 프레임워크4 - 정렬]]></title>
            <link>https://velog.io/@dev-mage/hello-java-world-the-collections-framework-sorting</link>
            <guid>https://velog.io/@dev-mage/hello-java-world-the-collections-framework-sorting</guid>
            <pubDate>Fri, 25 Nov 2022 02:28:38 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>Comparable과 Comparator로 컬렉션 정렬</p>
</blockquote>
<h3 id="comparable과-comparator">Comparable과 Comparator</h3>
<p>Comparable과 Comparator 모두 컬렉션을 정렬하는데 필요한 메서드를 정의하고 있는 인터페이스이다.</p>
<ul>
<li><p>Comparable</p>
<ul>
<li><p>기본 정렬 기준을 구현하는데 사용.</p>
</li>
<li><p>Comparable을 구현하는 클래스들: 같은 타입의 인스턴스끼리 서로 비교할 수 있는 클래스들(wrapper, String, Date, File 클래스 등). ⇒ 정렬이 가능함을 의미.</p>
</li>
<li><p>기본적으로 오름차순으로 정렬되도록 구현되어 있음.</p>
</li>
<li><p><code>java.lang</code> 패키지 소속.</p>
</li>
<li><p>멤버로 오직 compareTo()만 가짐.</p>
<pre><code class="language-java">public interface Comparable&lt;T&gt; {
      public int compareTo(T o); // 객체 자신(this)과 o를 비교
}</code></pre>
</li>
<li><p>compareTo()의 반환값은 int이지만 실제로는 비교하는 두 객체가 같으면 0, 비교하는 값보다 작으면 음수, 크면 양수를 반환하도록 구현해야 함.</p>
</li>
<li><p>예)</p>
<pre><code class="language-java">public final class Integer extends Number implements Comparable&lt;Integer&gt; {
      ...
      public int compareTo(Integer anotherInteger) {
          return compare(this.value, anotherInteger.value);
      }
      public static int compare(int x, int y) {
          return (x &lt; y) ? -1 : ((x == y) ? 0 : 1);
      }
      ...
}</code></pre>
</li>
</ul>
</li>
<li><p>Comparator</p>
<ul>
<li><p>기본 정렬 기준 외에 다른 기준으로 정렬하고자 할 때 사용.</p>
</li>
<li><p><code>java.util</code> 패키지 소속.</p>
<pre><code class="language-java">public interface Comparator&lt;T&gt; {
      int compare(T o1, T o2); // o1과 o2를 비교
      boolean equals(Object obj);
      ...
}</code></pre>
</li>
<li><p>compareTo()와 마찬가지로 객체를 비교해서 음수, 0, 양수 중의 하나를 반환하도록 구현.</p>
</li>
<li><p>equals()는 오버라이딩 할 수 있지만 구현하지 않아도 무방.</p>
</li>
<li><p>예)</p>
<pre><code class="language-java">public class Arrays {
      ...
      static final class NaturalOrder implements Comparator&lt;Object&gt; {
          public int compare(Object first, Object second) {
              return ((Comparable&lt;Object&gt;)first).compareTo(second);
          }
          ...
      }
      ...
}</code></pre>
</li>
</ul>
</li>
</ul>
<h3 id="arrayssort">Arrays.sort()</h3>
<p>Arrays.sort()는 배열 정렬을 위한 메서드이다. Comparator를 지정해주지 않으면 저장하는 객체(Comparable을 구현한 클래스의 객체)에 구현된 내용에 따라 정렬된다.</p>
<p>String의 Comparable 구현은 문자열이 사전 순(오름차순)으로 정렬되도록 작성되어 있다(공백 → 숫자 → 대문자 → 소문자와 같이 문자의 유니코드 순서가 작은 값부터 큰 값으로 정렬).</p>
<pre><code class="language-java">public static void main(String[] args) {
    String[] arr = {&quot;cake&quot;, &quot;banana&quot;, &quot;oreo&quot;, &quot;apple&quot;, &quot;app1e&quot;, &quot;0re0&quot;};
    Arrays.sort(arr); // [0re0, app1e, apple, banana, cake, oreo]
}</code></pre>
<p>만약 문자열의 내림차순으로 정렬하고 싶다면 다음과 같이 Comparator를 구현하면 된다.</p>
<pre><code class="language-java">public static void main(String[] args) {
    String[] arr = {&quot;cake&quot;, &quot;banana&quot;, &quot;oreo&quot;, &quot;apple&quot;, &quot;app1e&quot;, &quot;0re0&quot;};
    Arrays.sort(arr, new Comparator() {
        @Override
        public int compare(Object o1, Object o2) {
            if (o1 instanceof String &amp;&amp; o2 instanceof String)
                return ((String) o2).compareTo((String) o1);
            return 0;
        }
    }); // [oreo, cake, banana, apple, app1e, 0re0]
}</code></pre>
<h3 id="사용자-정의-객체-정렬">사용자 정의 객체 정렬</h3>
<p>만약 사용자가 정의한 객체를 정렬하려면 어떻게 해야할까? 앞서 언급한 것처럼 비교할 수 있는 객체는 Comparable을 구현해야 한다. 따라서 사용자 정의 객체에 Comparable을 구현하고 compareTo()를 오버라이딩 하면 된다. 아래 예제는 메뉴 가격이 적은 순서대로 정렬되도록 구현하였다.</p>
<pre><code class="language-java">public class Menu implements Comparable&lt;Menu&gt; {
    private String menuName;
    private int price;
    public Menu(String menuName) {
        this.menuName = menuName;
    }
    public Menu(String menuName, int price) {
        this.menuName = menuName;
        this.price = price;
    }
    public String getMenuName() {
        return menuName;
    }
    public int getPrice() {
        return price;
    }
    @Override
    public String toString() {
        return &quot;&lt;&quot; + menuName + &quot;: &quot; + price + &quot;&gt;&quot;;
    }
    @Override
    public int compareTo(Menu menu) {
        return this.getPrice() - menu.getPrice();
    }
}</code></pre>
<pre><code class="language-java">public static void main(String[] args) {
    Menu[] menus = {
            new Menu(&quot;coffee&quot;, 3500),
            new Menu(&quot;tea&quot;, 3000),
            new Menu(&quot;ade&quot;, 4500),
    };
    System.out.println(Arrays.toString(menus));
        // 실행 결과
        // [&lt;coffee: 3500&gt;, &lt;tea: 3000&gt;, &lt;ade: 4500&gt;]

        Arrays.sort(menus);
    System.out.println(Arrays.toString(menus));

        // 실행 결과
        // [&lt;tea: 3000&gt;, &lt;coffee: 3500&gt;, &lt;ade: 4500&gt;]
}</code></pre>
<hr>
<h3 id="reference">Reference</h3>
<ul>
<li>자바의 정석 CHAPTER 11</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[9) 컬렉션 프레임워크3 - Stack과 Queue]]></title>
            <link>https://velog.io/@dev-mage/hello-java-world-the-collections-framework-stack-queue</link>
            <guid>https://velog.io/@dev-mage/hello-java-world-the-collections-framework-stack-queue</guid>
            <pubDate>Fri, 18 Nov 2022 13:58:00 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>Stack과 Queue</p>
</blockquote>
<h3 id="자료-구조-스택stack과-큐queue">자료 구조 스택(Stack)과 큐(Queue)</h3>
<p>자료 구조(data structure)란 <a href="https://ko.wikipedia.org/wiki/%EC%9E%90%EB%A3%8C_%EA%B5%AC%EC%A1%B0">데이터 값의 모임, 또 데이터 간의 관계, 그리고 데이터에 적용할 수 있는 함수나 명령을 의미한다</a>. 마지막에 저장한 데이터가 가장 먼저 나오는 후입선출(LIFO: Last In First Out) 자료 구조인 스택과 처음에 저장한 데이터가 가장 먼저 나오는 선입선출(FIFO: First In First Out) 자료 구조인 큐가 대표적이다.</p>
<ul>
<li>스택</li>
</ul>
<p><img src="https://velog.velcdn.com/images/dev-mage/post/d6c629c4-b76f-4708-b98f-3313a0dd2ec5/image.png" alt=""></p>
<p><a href="https://ko.wikipedia.org/wiki/%EC%8A%A4%ED%83%9D">https://ko.wikipedia.org/wiki/스택</a></p>
<ul>
<li>큐</li>
</ul>
<p><img src="https://velog.velcdn.com/images/dev-mage/post/0120aa1f-199f-4d00-be37-21e3ad1b3518/image.png" alt=""></p>
<p><a href="https://ko.wikipedia.org/wiki/%ED%81%90_(%EC%9E%90%EB%A3%8C_%EA%B5%AC%EC%A1%B0)">https://ko.wikipedia.org/wiki/큐_(자료_구조)</a></p>
<p>자바에서는 스택은 Stack 클래스로 큐는 Queue 인터페이스로 제공하고 있으나 Stack 클래스의 경우 사용을 지양하고 있다(참고: <a href="https://dev.java/learn/storing-data-using-the-collections-framework/#deprecated">https://dev.java/learn/storing-data-using-the-collections-framework/#deprecated</a>). 대신 ArrayList와 같은 순차적으로 데이터를 추가하고 삭제하는 배열 기반의 컬렉션 클래스나 ArrayDeque 클래스로 대체할 수 있다. 큐의 경우 Queue 인터페이스를 구현하는 LinkedList 클래스를 사용하면 된다.</p>
<p>예를 들어 하루에 3잔만 판매하는 카페가 있다고 가정했다. 이 카페에서는 주문 목록을 Stack에 저장하였다.</p>
<pre><code class="language-java">public class Cafe {
    public static void main(String[] args) {
        Stack&lt;Order&gt; orderList = new Stack&lt;&gt;();
        System.out.println(&quot;============= OPEN =============&quot;);
        Scanner ordering = new Scanner(System.in);
        while (orderList.size() &lt; 3) {
            System.out.print(&quot;주문하실 메뉴를 입력하세요: &quot;);
            Order order = new Order(ordering.next());
            orderList.push(order);
        }

        int orderNo = 1;
        while (!orderList.isEmpty()) {
            System.out.println(&quot;\n&gt;&gt;&gt; 주문 확인 중...&quot;);
            System.out.println(&quot;주문번호 &quot; + orderNo + &quot;번 &quot; + &quot;메뉴: &quot; + orderList.peek().getMenu());

            System.out.println(&quot;\n&gt;&gt;&gt; 음료 제조 중...&quot;);

            Order order = orderList.pop();
            System.out.println(order.getMenu() + &quot; 나왔습니다.&quot;);
            orderNo++;
        }
        System.out.println(&quot;============= CLOSE =============&quot;);
    }
}</code></pre>
<ul>
<li>실행 결과</li>
</ul>
<p><img src="https://velog.velcdn.com/images/dev-mage/post/7810ab4d-13bf-4293-bba0-e5f277c7ceac/image.png" alt=""></p>
<p>문제는 주문한 음료를 고객에게 전달할 때 나타났다. Stack에 주문 목록을 작성해버려 제일 먼저 주문한 고객이 가장 늦게 음료를 받고 말았다. 그래서 Stack 대신 Queue로 주문 목록을 작성해 문제를 해결하였다.</p>
<pre><code class="language-java">public class Cafe {
    public static void main(String[] args) {
        Queue&lt;Order&gt; orderList = new LinkedList&lt;&gt;();
        System.out.println(&quot;============= OPEN =============&quot;);
        Scanner ordering = new Scanner(System.in);
        while (orderList.size() &lt; 3) {
            System.out.print(&quot;주문하실 메뉴를 입력하세요: &quot;);
            Order order = new Order(ordering.next());
            orderList.offer(order);
        }

        int orderNo = 1;
        while (!orderList.isEmpty()) {
            System.out.println(&quot;\n&gt;&gt;&gt; 주문 확인 중...&quot;);
            System.out.println(&quot;주문번호 &quot; + orderNo + &quot;번 &quot; + &quot;메뉴: &quot; + orderList.peek().getMenu());

            System.out.println(&quot;\n&gt;&gt;&gt; 음료 제조 중...&quot;);

            Order order = orderList.poll();
            System.out.println(order.getMenu() + &quot; 나왔습니다.&quot;);
            orderNo++;
        }
        System.out.println(&quot;============= CLOSE =============&quot;);
    }
}</code></pre>
<p><img src="https://velog.velcdn.com/images/dev-mage/post/e55b3d54-af00-49c3-849a-05e9f0abd0c7/image.png" alt=""></p>
<hr>
<h3 id="reference">Reference</h3>
<ul>
<li>자바의 정석 CHAPTER 11</li>
<li><a href="https://docs.oracle.com/javase/8/docs/api/java/util/Stack.html">https://docs.oracle.com/javase/8/docs/api/java/util/Stack.html</a></li>
<li><a href="https://docs.oracle.com/javase/8/docs/api/java/util/Queue.html">https://docs.oracle.com/javase/8/docs/api/java/util/Queue.html</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[프로그래머스 레벨 1 - 이상한 문자 만들기]]></title>
            <link>https://velog.io/@dev-mage/httpsvelog.iodev-mageprogrammers-level1-generate-weird-words</link>
            <guid>https://velog.io/@dev-mage/httpsvelog.iodev-mageprogrammers-level1-generate-weird-words</guid>
            <pubDate>Fri, 18 Nov 2022 03:34:49 GMT</pubDate>
            <description><![CDATA[<p><a href="https://school.programmers.co.kr/learn/courses/30/lessons/12930">코딩테스트 연습 - 이상한 문자 만들기</a></p>
<h3 id="문제-설명"><strong>문제 설명</strong></h3>
<p>문자열 s는 한 개 이상의 단어로 구성되어 있습니다. 각 단어는 하나 이상의 공백문자로 구분되어 있습니다. 각 단어의 짝수번째 알파벳은 대문자로, 홀수번째 알파벳은 소문자로 바꾼 문자열을 리턴하는 함수, solution을 완성하세요.</p>
<h3 id="제한-사항">제한 사항</h3>
<ul>
<li>문자열 전체의 짝/홀수 인덱스가 아니라, 단어(공백을 기준)별로 짝/홀수 인덱스를 판단해야합니다.</li>
<li>첫 번째 글자는 0번째 인덱스로 보아 짝수번째 알파벳으로 처리해야 합니다.</li>
</ul>
<hr>
<h3 id="입출력-예">입출력 예</h3>
<table>
<thead>
<tr>
<th>s</th>
<th>return</th>
</tr>
</thead>
<tbody><tr>
<td>&quot;try hello world&quot;</td>
<td>&quot;TrY HeLlO WoRlD&quot;</td>
</tr>
</tbody></table>
<h3 id="입출력-예-설명">입출력 예 설명</h3>
<p>&quot;try hello world&quot;는 세 단어 &quot;try&quot;, &quot;hello&quot;, &quot;world&quot;로 구성되어 있습니다. 각 단어의 짝수번째 문자를 대문자로, 홀수번째 문자를 소문자로 바꾸면 &quot;TrY&quot;, &quot;HeLlO&quot;, &quot;WoRlD&quot;입니다. 따라서 &quot;TrY HeLlO WoRlD&quot; 를 리턴합니다.</p>
<hr>
<h3 id="풀이">풀이</h3>
<pre><code class="language-jsx">class Solution {
  public String solution(String s) {
        String answer = &quot;&quot;;
        String[] strArr = s.split(&quot; &quot;, -1);
        StringBuilder sb = new StringBuilder();

        for(int i = 0; i &lt; strArr.length; i++) {
            String word = strArr[i];
            if(word.equals(&quot;&quot;) &amp;&amp; i != strArr.length - 1) {
                sb.append(&quot; &quot;);
            } else {
                for(int j = 0; j &lt; word.length(); j++) {
                    char tempChar = word.charAt(j);
                    if(j % 2 == 0) {
                        sb.append(Character.toUpperCase(tempChar));
                    } else {
                        sb.append(Character.toLowerCase(tempChar));
                    }
                }
                if(i != strArr.length - 1) {
                    sb.append(&quot; &quot;);
                }
            }
        }

        answer = sb.toString();
        return answer;
    }
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[프로그래머스 레벨 1 - 소수 만들기]]></title>
            <link>https://velog.io/@dev-mage/programmers-level1-generate-prime-numbers</link>
            <guid>https://velog.io/@dev-mage/programmers-level1-generate-prime-numbers</guid>
            <pubDate>Fri, 18 Nov 2022 03:28:35 GMT</pubDate>
            <description><![CDATA[<p><a href="https://school.programmers.co.kr/learn/courses/30/lessons/12977">코딩테스트 연습 - 소수 만들기</a></p>
<h3 id="문제-설명"><strong>문제 설명</strong></h3>
<p>주어진 숫자 중 3개의 수를 더했을 때 소수가 되는 경우의 개수를 구하려고 합니다. 숫자들이 들어있는 배열 nums가 매개변수로 주어질 때, nums에 있는 숫자들 중 서로 다른 3개를 골라 더했을 때 소수가 되는 경우의 개수를 return 하도록 solution 함수를 완성해주세요.</p>
<h3 id="제한사항">제한사항</h3>
<ul>
<li>nums에 들어있는 숫자의 개수는 3개 이상 50개 이하입니다.</li>
<li>nums의 각 원소는 1 이상 1,000 이하의 자연수이며, 중복된 숫자가 들어있지 않습니다.</li>
</ul>
<hr>
<h3 id="입출력-예">입출력 예</h3>
<table>
<thead>
<tr>
<th>nums</th>
<th>result</th>
</tr>
</thead>
<tbody><tr>
<td>[1,2,3,4]</td>
<td>1</td>
</tr>
<tr>
<td>[1,2,7,6,4]</td>
<td>4</td>
</tr>
</tbody></table>
<h3 id="입출력-예-설명">입출력 예 설명</h3>
<p>입출력 예 #1[1,2,4]를 이용해서 7을 만들 수 있습니다.</p>
<p>입출력 예 #2[1,2,4]를 이용해서 7을 만들 수 있습니다.[1,4,6]을 이용해서 11을 만들 수 있습니다.[2,4,7]을 이용해서 13을 만들 수 있습니다.[4,6,7]을 이용해서 17을 만들 수 있습니다.</p>
<hr>
<h3 id="풀이">풀이</h3>
<pre><code class="language-java">class Solution {
    public int solution(int[] nums) {
        int answer = 0;
        int numLeng = nums.length;
        for (int i = 0; i &lt; numLeng; i++) {
            for (int j = i + 1; j &lt; numLeng; j++) {
                for (int k = j + 1; k &lt; numLeng; k++) {
                    if (isPrime(nums[i] + nums[j] + nums[k])) answer++;
                }
            }
        }

        return answer;
    }

    public static boolean isPrime(int n) {
        boolean flag = true;
        for (int i = 2; i &lt;= Math.sqrt(n); i++) {
            if (n % i == 0) {
                flag = false;
                break;
            }
        }
        return flag;
    }
}</code></pre>
<h3 id="리뷰">리뷰</h3>
<ul>
<li>소수 관련은 <a href="https://velog.io/@dev-mage/programmers-level1-find-prime-numbers">https://velog.io/@dev-mage/programmers-level1-find-prime-numbers</a> 참고</li>
<li>3중 for문을 사용해서 3개를 뽑을 수 있는 경우의 수 구하기</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[프로그래머스 레벨 1 - 소수 찾기]]></title>
            <link>https://velog.io/@dev-mage/programmers-level1-find-prime-numbers</link>
            <guid>https://velog.io/@dev-mage/programmers-level1-find-prime-numbers</guid>
            <pubDate>Fri, 18 Nov 2022 03:22:21 GMT</pubDate>
            <description><![CDATA[<p><a href="https://school.programmers.co.kr/learn/courses/30/lessons/12921">코딩테스트 연습 - 소수 찾기</a></p>
<h3 id="문제-설명"><strong>문제 설명</strong></h3>
<p>1부터 입력받은 숫자 n 사이에 있는 소수의 개수를 반환하는 함수, solution을 만들어 보세요.</p>
<p>소수는 1과 자기 자신으로만 나누어지는 수를 의미합니다.(1은 소수가 아닙니다.)</p>
<hr>
<h3 id="제한-조건">제한 조건</h3>
<ul>
<li>n은 2이상 1000000이하의 자연수입니다.</li>
</ul>
<hr>
<h3 id="입출력-예">입출력 예</h3>
<table>
<thead>
<tr>
<th>n</th>
<th>result</th>
</tr>
</thead>
<tbody><tr>
<td>10</td>
<td>4</td>
</tr>
<tr>
<td>5</td>
<td>3</td>
</tr>
</tbody></table>
<h3 id="입출력-예-설명">입출력 예 설명</h3>
<p>입출력 예 #11부터 10 사이의 소수는 [2,3,5,7] 4개가 존재하므로 4를 반환</p>
<p>입출력 예 #21부터 5 사이의 소수는 [2,3,5] 3개가 존재하므로 3를 반환</p>
<hr>
<h3 id="풀이">풀이</h3>
<pre><code class="language-java">class Solution {
    public int solution(int n) {
        int answer = 0;
        for (int i = 2; i &lt;= n; i++) {
            boolean isPrime = true;
            for (int j = 2; j &lt;= Math.sqrt(i); j++) {
                if (i % j == 0) {
                    isPrime = false;
                    break;
                }
            }
            if (isPrime) answer++;
        }
        return answer;
    }
}</code></pre>
<h3 id="리뷰">리뷰</h3>
<ul>
<li>소수를 찾는 문제는 <a href="https://ko.wikipedia.org/wiki/%EC%97%90%EB%9D%BC%ED%86%A0%EC%8A%A4%ED%85%8C%EB%84%A4%EC%8A%A4%EC%9D%98_%EC%B2%B4">에라토스테네스의 체</a> 또는 제곱근을 구하는 것으로 품.</li>
<li>주어진 수 N의 제곱근 범위 안에서 소수 판별하는 법<ul>
<li>N의 약수는 무조건 N의 제곱근 범위 안에 존재.<ul>
<li>예) N = 36</li>
</ul>
<ol>
<li>약수: 1, 2, 3, 4, 6, 9, 12, 18, 36</li>
<li>$36 = 1 * 36$ / $2 * 18$ / $3 * 12$ / $4 * 9$ / $6 * 6$ / …</li>
<li>$36 = 1 * 2^2 * 3^2$  </li>
</ol>
<ul>
<li>결국 N은 제곱근인 6의 약수로 이루어져 있음.</li>
<li>따라서 N의 제곱근까지만 판별하면 됨.</li>
</ul>
</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[9) 컬렉션 프레임워크2 - List]]></title>
            <link>https://velog.io/@dev-mage/hello-java-world-the-collections-framework-list</link>
            <guid>https://velog.io/@dev-mage/hello-java-world-the-collections-framework-list</guid>
            <pubDate>Thu, 17 Nov 2022 03:46:40 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>ArrayList와 LinkedList</p>
</blockquote>
<h3 id="arraylist">ArrayList</h3>
<p>ArrayList는 List 인터페이스를 구현하는 컬렉션 클래스로 데이터의 저장순서가 유지되고 중복을 허용한다. 내부의Object 배열을 이용해서 데이터를 순차적으로 저장하는데 배열에 더 이상 저장할 공간이 없으면 보다 큰 새로운 배열을 생성해서 기존의 배열에 저장된 내용을 새로운 배열로 복사한 다음에 저장한다. 기존의 Vector를 개선한 것으로 Vector와 구현 원리와 기능적인 측면에서 비슷하지만 동기화 되지 않는다는 점이 다르다. 하지만 자바에서는 Vector와 같이 오래된 컬렉션 클래스 사용을 지양하도록 권고하고 있다(참고: <a href="https://dev.java/learn/storing-data-using-the-collections-framework/#deprecated">Avoiding Using Old Interfaces and Implementations</a>). </p>
<p>ArrayList의 요소를 삭제하는 경우 삭제할 객체의 바로 다음 데이터를 한 칸씩 앞으로 복사해서 삭제할 객체를 덮은 후 마지막 데이터는 null로 변경하는 방식으로 처리한다. 만일 삭제할 객체가 마지막 데이터라면 복사할 필요 없이 단순히 null로 대체한다. 새로운 요소를 추가할 때도 먼저 추가할 위치 이후의 요소들을 모두 한 칸씩 이동시킨 후 저장한다. 따라서 객체를 순차적으로 저장할 때와 마지막 데이터부터 삭제하면 데이터를 복사해서 옮기지 않아도 되기 때문에 작업 시간이 단축되지만, 배열의 중간에 위치한 객체에 대한 작업인 경우 다른 데이터의 위치를 이동시켜야 하기 때문에 다루는 데이터가 많을수록 작업 시간이 길어지게 된다.</p>
<pre><code class="language-java">ArrayList arrayList = new ArrayList&lt;&gt;(5);
for (int i = 0; i &lt; 5; i++) {
    arrayList.add(i); // ArrayList에 요소 추가
}
System.out.println(Arrays.toString(arrayList.toArray()));

while (!arrayList.isEmpty()) {
    arrayList.remove(0); // ArrayList에 요소 삭제
    System.out.println(Arrays.toString(arrayList.toArray()));
}</code></pre>
<ul>
<li>실행 결과</li>
</ul>
<p><img src="https://velog.velcdn.com/images/dev-mage/post/98cbf4ab-6605-4b86-931e-e0d483be7c4a/image.png" alt=""></p>
<h3 id="linkedlist">LinkedList</h3>
<p>배열은 가장 기본적인 형태의 자료구조로 구조가 간단하며 사용하기 쉽고 데이터를 읽어 오는데 걸리는 시간(접근 시간, access time)이 가장 빠르다는 장점을 가지고 있지만 다음과 같은 단점도 가지고 있다.</p>
<ul>
<li>크기를 변경할 수 없음.<ul>
<li>크기를 변경할 수 없으므로 새로운 배열을 생성해서 데이터를 복사해야 함.</li>
<li>새로운 배열로 복사해서 옮기는 작업을 줄이고 실행 속도를 향상시키기 위해서는 충분히 큰 크기의 배열을 생성해야 하므로 메모리가 낭비됨.</li>
</ul>
</li>
<li>비순차적인 데이터의 추가 또는 삭제가 오래 걸림.<ul>
<li>차례대로 데이터를 추가하거나 마지막에서부터 데이터를 삭제하지 않고 배열의 중간에서 작업하려면 다른 데이터의 복사 및 이동이 필요</li>
</ul>
</li>
</ul>
<p>이러한 배열의 단점을 보완하기 위해 연결 리스트(Linked List)라는 자료구조가 고안되었다. 배열은 모든 데이터가 연속적으로 존재하지만 연결 리스트는 불연속적으로 존재하는 데이터를 서로 연결한 형태의 구조를 가지고 있다.</p>
<p>연결 리스트의 각 요소(node)들은 자신과 연결된 다음 요소에 대한 참조(주소값)와 데이터로 구성되어 있다.</p>
<p><img src="https://velog.velcdn.com/images/dev-mage/post/334326f0-ef30-436f-94ee-887163428e58/image.png" alt=""></p>
<p>연결 리스트에서 데이터를 삭제하려면 삭제하고자 하는 요소의 이전 요소가 삭제하고자 하는 요소의 다음 요소를 참조하도록 변경하면 된다. 배열처럼 데이터를 이동하기 위해 복사하는 과정 없이 단 하나의 참조만 변경하면 삭제되므로 처리 속도가 빠르다</p>
<p><img src="https://velog.velcdn.com/images/dev-mage/post/cc44acc8-08c4-4c8a-b248-b956ae771bc1/image.png" alt=""></p>
<p>새로운 데이터를 추가할 때는 새로운 요소를 생성한 다음 추가하고자 하는 위치의 이전 요소의 참조를 새로운 요소에 대한 참조로 변경한 후 새로운 요소가 그 다음 요소를 참조하도록 하면 된다.</p>
<p><img src="https://velog.velcdn.com/images/dev-mage/post/88962756-321a-49f3-97c9-32be664e0cb0/image.png" alt=""></p>
<h3 id="arraylist-vs-linkedlist">ArrayList vs. LinkedList</h3>
<p>배열은 각 요소들이 연속적으로 메모리상에 존재하기 때문에 간단한 계산으로 원하는 요소의 주소를 얻어 데이터에 접근할 수 있지만 연결 리스트는 각 요소의 위치가 불연속적이기 때문에 처음부터 n번째 데이터까지 차례대로 따라가야만 원하는 값을 얻을 수 있다. 따라서 연결 리스트는 저장해야하는 데이터의 개수가 많아질수록 데이터를 읽어 오는 시간이 길어지는 단점이 있다.</p>
<table>
<thead>
<tr>
<th>컬렉션</th>
<th>읽기(접근 시간)</th>
<th>추가 / 삭제</th>
<th>비고</th>
</tr>
</thead>
<tbody><tr>
<td>ArrayList</td>
<td>빠름</td>
<td>느림</td>
<td>- 순차적인 추가 / 삭제는 더 빠름</td>
</tr>
<tr>
<td>- 비효율적인 메모리 사용</td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>LinkedList</td>
<td>느림</td>
<td>빠름</td>
<td>- 데이터가 많을 수록 접근성이 떨어짐</td>
</tr>
</tbody></table>
<hr>
<h3 id="references">References</h3>
<ul>
<li>자바의 정석 CHAPTER 11</li>
<li><a href="https://docs.oracle.com/javase/8/docs/api/java/util/ArrayList.html">https://docs.oracle.com/javase/8/docs/api/java/util/ArrayList.html</a></li>
<li><a href="https://docs.oracle.com/javase/8/docs/api/java/util/Vector.html">https://docs.oracle.com/javase/8/docs/api/java/util/Vector.html</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[9) 컬렉션 프레임워크1 - 컬렉션 프레임워크란]]></title>
            <link>https://velog.io/@dev-mage/hello-java-world-the-collections-framework</link>
            <guid>https://velog.io/@dev-mage/hello-java-world-the-collections-framework</guid>
            <pubDate>Thu, 17 Nov 2022 03:44:55 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>Java 컬렉션 프레임워크</p>
</blockquote>
<h3 id="컬렉션-프레임워크collection-framework">컬렉션 프레임워크(Collection Framework)</h3>
<p>컬렉션의 본래 뜻은 관련된 물건들의 집합이라고 할 수 있다. 프레임워크란 표준화된 프로그래밍 방식을 제공하기 위해 설계된 틀(구조)이다. 즉 자바의 컬렉션 프레임워크는 여러 요소가 담긴 컨테이너 객체(컬렉션)를 다루기 위한 표준 방법(프레임워크)을 지칭한다. 어플리케이션 개발 시에 여러 데이터를 메모리에 저장하고 처리해야 할 일이 있을 때를 대비해 자바는 컬렉션 프레임워크를 제공한다.</p>
<h3 id="컬렉션-프레임워크-인터페이스">컬렉션 프레임워크 인터페이스</h3>
<p>컬렉션 프레임워크는 크게 다음과 같은 3가지 타입이 있다. </p>
<p><img src="https://velog.velcdn.com/images/dev-mage/post/8bfee166-388c-4548-9f68-45f1640eb29a/image.png" alt=""></p>
<table>
<thead>
<tr>
<th>인터페이스</th>
<th>특징</th>
<th>구현 클래스</th>
</tr>
</thead>
<tbody><tr>
<td>List</td>
<td>- 순서가 있는 데이터의 집합.</td>
<td></td>
</tr>
<tr>
<td>- 데이터의 중복 허용.</td>
<td>ArrayList, LinkedList, Vector, Stack 등</td>
<td></td>
</tr>
<tr>
<td>Set</td>
<td>- 순서를 유지하지 않는 데이터의 집합.</td>
<td></td>
</tr>
<tr>
<td>- 데이터의 중복 불허.</td>
<td>HashSet, TreeSet 등</td>
<td></td>
</tr>
<tr>
<td>Map</td>
<td>- 키(key)와 값(value)의 쌍으로 이루어진 데이터의 집합.</td>
<td></td>
</tr>
<tr>
<td>- 순서는 유지되지 않음.</td>
<td></td>
<td></td>
</tr>
<tr>
<td>- 키는 중복을 허용하지 않고 값의 중복은 허용.(중복된 키를 저장하면 기존의 값은 없어지고 마지막에 입력한 값이 저장됨)</td>
<td>HashMap, TreeMap, Hashtable, Properties 등</td>
<td></td>
</tr>
</tbody></table>
<h3 id="list">List</h3>
<p><img src="https://velog.velcdn.com/images/dev-mage/post/c446cd39-f1d8-4ce5-bc58-3534245b60cd/image.png" alt=""></p>
<h3 id="set">Set</h3>
<p><img src="https://velog.velcdn.com/images/dev-mage/post/87151e6a-9433-4161-8034-4aa487d03390/image.png" alt=""></p>
<h3 id="map">Map</h3>
<p><img src="https://velog.velcdn.com/images/dev-mage/post/3627f804-0955-4935-ad72-505a36be7712/image.png" alt=""></p>
<hr>
<h3 id="references">References</h3>
<ul>
<li>자바의 정석 CHAPTER 11</li>
<li><a href="https://docs.oracle.com/javase/tutorial/collections/intro/index.html">https://docs.oracle.com/javase/tutorial/collections/intro/index.html</a></li>
<li><a href="https://dev.java/learn/storing-data-using-the-collections-framework/">https://dev.java/learn/storing-data-using-the-collections-framework/</a></li>
<li><a href="https://docs.oracle.com/javase/8/docs/api/java/util/List.html">https://docs.oracle.com/javase/8/docs/api/java/util/List.html</a></li>
<li><a href="https://docs.oracle.com/javase/8/docs/api/java/util/Set.html">https://docs.oracle.com/javase/8/docs/api/java/util/Set.html</a></li>
<li><a href="https://docs.oracle.com/javase/8/docs/api/java/util/Map.html">https://docs.oracle.com/javase/8/docs/api/java/util/Map.html</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[8) java.lang 패키지3 - 기본형]]></title>
            <link>https://velog.io/@dev-mage/hello-java-world-java-lang-package-primitive-type</link>
            <guid>https://velog.io/@dev-mage/hello-java-world-java-lang-package-primitive-type</guid>
            <pubDate>Mon, 14 Nov 2022 03:46:30 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>Java에서 기본형을 객체처럼 다루기</p>
</blockquote>
<h3 id="래퍼wrapper-클래스">래퍼(Wrapper) 클래스</h3>
<p><a href="https://velog.io/@dev-mage/hello-java-world-oop-object-and-class#%EA%B0%9D%EC%B2%B4">객체지향 프로그래밍에서는 모든 것을 객체로 다루어야 하는데</a> 자바는 성능을 위해 8가지 기본형이라는 예외를 두었다. 그렇지만 기본형 변수도 때로 객체처럼 다루어야 하는 경우가 생긴다. 가령 매개변수로 객체를 요구하거나, 기본형 값이 아닌 객체로 저장해야 하거나, 객체간의 비교가 필요할 경우에는 기본형 값을 객체로 변환하여 작업을 수행해야 한다. 이럴 때 사용되는 것이 래퍼 클래스이며 기본형 데이터를 객체로 포장(wrap)해주는 8개의 래퍼 클래스가 있다. 래퍼 클래스는 각 자료형에 해당하는 값을 인자로 받아 해당 값을 가지는 객체로 만들어 준다. 8가지 기본형에 대응하는 래퍼 클래스는 다음과 같다.</p>
<table>
<thead>
<tr>
<th>기본형</th>
<th>래퍼 클래스</th>
</tr>
</thead>
<tbody><tr>
<td>byte</td>
<td>Byte</td>
</tr>
<tr>
<td>short</td>
<td>Short</td>
</tr>
<tr>
<td>int</td>
<td>Integer</td>
</tr>
<tr>
<td>long</td>
<td>Long</td>
</tr>
<tr>
<td>char</td>
<td>Character</td>
</tr>
<tr>
<td>float</td>
<td>Float</td>
</tr>
<tr>
<td>double</td>
<td>Double</td>
</tr>
<tr>
<td>boolean</td>
<td>Boolean</td>
</tr>
</tbody></table>
<h3 id="오토박싱autoboxing과-언박싱unboxing">오토박싱(autoboxing)과 언박싱(unboxing)</h3>
<p>JDK 5 이전에는 기본형과 참조형 간의 연산이 불가능했기 때문에 래퍼 클래스로 기본형을 객체로 변환 후 연산해야 했다.</p>
<pre><code class="language-java">int int1 = 10;
Integer int2 = new Integer(10);

int int3 = int1 + int2; // JDK 5 이전: 에러! 기본형과 참조형 간의 연산 불가</code></pre>
<p>그러나 JDK 5부터 기본형과 참조형 간의 연산이 자동적으로 되게끔 컴파일러가 동작한다. 기본형 값을 래퍼 클래스의 객체로 자동 변환해주는 것을 <strong>오토박싱</strong>이라고 하며 그 반대로 변환하는 것을 <strong>언박싱</strong>이라고 한다.</p>
<pre><code class="language-java">int int1 = 10;
Integer int2 = new Integer(10);

int1 = int2;
int2 = int1;</code></pre>
<p><img src="https://velog.velcdn.com/images/dev-mage/post/baa376cd-7bc9-46aa-a727-25fae6eedf6d/image.png" alt=""></p>
<p>위 코드에서 <code>int1 = int2;</code>는 intValue()로 언박싱을, <code>int2 = int1;</code>는 valueOf()로 오토박싱을 컴파일러가 자동적으로 수행한 것을 볼 수 있다.</p>
<p>또한 래퍼 클래스는 모두 객체가 가지고 있는 값을 비교하도록 equals 메서드를 오버라이딩하고 있다. 기본형과 비교가 아닌 객체끼리의 비교는 equals 메서드를 사용해야 한다.</p>
<pre><code class="language-java">int int1 = 10;
Integer int2 = 10;
Integer int3 = new Integer(10);

System.out.println(int1 == int3); // true
System.out.println(int2 == int3); // false
System.out.println(int2.equals(int3)); // true

char char1 = &#39;a&#39;;
Character char2 = &#39;a&#39;;
Character char3 = new Character(&#39;a&#39;);

System.out.println(char1 == char3); // true
System.out.println(char2 == char3); // false
System.out.println(char2.equals(char3)); // true

boolean bool1 = true;
Boolean bool2 = true;
Boolean bool3 = new Boolean(true);

System.out.println(bool1 == bool3); // true
System.out.println(bool2 == bool3); // false
System.out.println(bool2.equals(bool3)); // true</code></pre>
<h3 id="valueof-메서드">valueOf 메서드</h3>
<pre><code class="language-java">public static void main(String[] args) {
    int int1 = 10;

    Integer int2_1 = 10;
    Integer int2_2 = Integer.valueOf(10);

    Integer int3 = new Integer(10);
}</code></pre>
<p><img src="https://velog.velcdn.com/images/dev-mage/post/a54d6c4a-b0eb-4969-b458-fb22216fb20f/image.png" alt=""></p>
<p>위 코드의 바이트코드를 확인해보면 <code>int2_1</code>과 <code>int2_2</code>가 똑같이 valueOf 메서드를 호출하는 것으로 컴파일된 사실을 알 수 있다. <code>new</code> 연산자를 사용해 Integer 객체를 생성하지 않고 바로 정수형 리터럴을 할당하는 경우 컴파일러가 내부적으로 valueOf 메서드를 추가하는데, 이는 valueOf 메서드가 자주 쓰이는 값의 캐싱을 통해 성능적인 측면에서 생성자보다 나아 높은 우선순위를 가지기 때문이다. </p>
<p>Integer와 Short 클래스의 valueOf 메서드는 다른 정수형 래퍼 클래스들과 다른 점을 하나 가지고 있다. 아래 코드에서 <code>int1</code>과 <code>int2</code>의 동등비교 결과는 <code>true</code>이지만 <code>int3</code>와 <code>int4</code>의 결과는 <code>false</code>이다. </p>
<pre><code class="language-java">Integer int1 = 10;
Integer int2 = 10;

System.out.println(int1 == int2); // true

Integer int3 = 10000;
Integer int4 = 10000;

System.out.println(int3 == int4); // false</code></pre>
<p>API를 확인해보면 -128 ~ 127까지의 값은 항상 캐싱돼 있어 같은 값을 가져와 쓰게 되기 때문에 <code>int1</code>과 <code>int2</code>는 같은 값을 참조하게 된다. 따라서 동등비교 결과가 <code>true</code>인 것이다.</p>
<p><img src="https://velog.velcdn.com/images/dev-mage/post/974acc69-6b9b-4a89-bcdb-7961b9132ffa/image.png" alt=""></p>
<p>Character 클래스의 valueOf 메서드도 u0000 ~ u007F까지의 값을 캐싱한다.</p>
<p><img src="https://velog.velcdn.com/images/dev-mage/post/d99b9e19-f65c-49b9-b07b-275b52c0187a/image.png" alt=""></p>
<hr>
<h3 id="references">References</h3>
<ul>
<li>자바의 정석 CHAPTER 9</li>
<li><a href="https://docs.oracle.com/javase/8/docs/api/java/lang/Integer.html">https://docs.oracle.com/javase/8/docs/api/java/lang/Integer.html</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[8) java.lang 패키지2 - 문자열]]></title>
            <link>https://velog.io/@dev-mage/hello-java-world-java-lang-package-string</link>
            <guid>https://velog.io/@dev-mage/hello-java-world-java-lang-package-string</guid>
            <pubDate>Thu, 10 Nov 2022 05:04:19 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>Java의 String, StringBuffer, StringBuilder 클래스</p>
</blockquote>
<h3 id="변경-불가능한immutable-string-클래스">변경 불가능한(immutable) String 클래스</h3>
<p>String 클래스는 문자열 관련 기능을 제공하는 클래스이다. 문자열을 저장하기 위해 문자형 배열 참조변수(<code>char[]</code>) value를 인스턴스 변수로 정의하고 있다. 인스턴스 생성 시 생성자의 매개변수로 받는 문자열은 이 인스턴스 변수 value에 문자형 배열(<code>char[]</code>)로 저장된다. </p>
<pre><code class="language-java">public final class String
    implements java.io.Serializable, Comparable&lt;String&gt;, CharSequence {
    /** The value is used for character storage. */
    private final char value[];

        ...
}</code></pre>
<pre><code class="language-java">String str1 = &quot;abc&quot;; // 문자열 리터럴
String str2 = new String(&quot;abc&quot;);</code></pre>
<p><img src="https://velog.velcdn.com/images/dev-mage/post/c2ebfd2f-0ce1-4265-84aa-6bb868524f84/image.png" alt=""></p>
<p>문자열을 만들 때는 문자열 리터럴을 지정하는 방법과 String 클래스의 생성자를 통해서 만드는 방법이 있다. 문자열 리터럴은 똑같은 문자열이 있다면 그것을 재사용하지만 String 클래스의 생성자를 사용하는 경우 <code>new</code> 연산자에 의해 메모리 할당이 이루어지기 때문에 항상 새로운 String 인스턴스가 생성된다.</p>
<p><img src="https://velog.velcdn.com/images/dev-mage/post/548625cc-92d0-44e5-b890-f86e43b58f40/image.png" alt=""></p>
<p>문자열 리터럴은 상수 저장소(constant pool)란 곳에 저장되는데 같은 내용의 문자열 리터럴이 이 constant pool에 있는지 찾은 후 있다면 값을 참조하고 없다면 새로 등록한다. 이처럼 문자열 리터럴은 똑같은 내용일 경우 같은 객체를 가리키므로 동등비교 연산자(<code>==</code>)로 비교하면 <code>true</code>를 얻는다. 반면 <code>new</code> 연산자로 생성한 문자열은 내용은 같지만 서로 다른 객체를 가리키므로 동등비교 연산자를 사용할 경우 <code>false</code>를 반환한다. 내용이 같은 문자열인지 비교하려면 equals 메서드를 사용해야 한다. equals 메서드는 String 클래스에서 내용 비교를 위해 오버라이딩 되어 있다.</p>
<pre><code class="language-java">String str1 = &quot;abc&quot;;
String str2 = &quot;abc&quot;;
String str3 = new String(&quot;abc&quot;);
String str4 = new String(str1);

System.out.println(str1 == str2); // true
System.out.println(str1 == str3); // false
System.out.println(str1 == str4); // false
System.out.println(str3 == str4); // false

System.out.println(str1.equals(str3)); // true
System.out.println(str1.equals(str4)); // true
System.out.println(str3.equals(str4)); // true</code></pre>
<p><img src="https://velog.velcdn.com/images/dev-mage/post/850a0c70-cec0-4d25-a1d0-16ab51ae887b/image.png" alt=""></p>
<p>한번 생성된 String 인스턴스는 값을 읽어 올 수만 있고 변경할 수는 없다. 결합 연산자(<code>+</code>)를 이용해 문자열을 합치는 경우 인스턴스 내의 문자열이 바뀌는 것이 아니라 새로운 문자열이 담긴 String 인스턴스가 생성된다. 이처럼 한번 생성된 후 상태를 변경할 수 없는 객체를 <strong>불변(immutable) 객체</strong>라고 한다. (반대되는 개념으로 가변(mutable) 객체가 있다.) 결합 연산자를 사용할 때마다 새로운 문자열을 만들어 메모리 공간을 차지하게 되므로 가능한 이런 연산을 줄이고 문자열을 다루는 작업이 많을 경우 String 클래스 대신 StringBuffer 클래스를 사용하는 것이 좋다. StringBuffer 인스턴스에 저장된 문자열은 변경이 가능하므로 하나의 인스턴스만으로도 문자열을 다루는 것이 가능하다.</p>
<p><img src="https://velog.velcdn.com/images/dev-mage/post/3a32a0d6-c753-46c6-9be2-8c9b642afab2/image.png" alt=""></p>
<ul>
<li>참고: 문자열의 결합 연산은 내부적으로 StringBuilder 클래스의 append메서드를 통해 구현된다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/dev-mage/post/c135232b-5255-40d6-bd48-3b48179bb194/image.png" alt=""></p>
<h3 id="stringbuffer-클래스">StringBuffer 클래스</h3>
<p>불변 클래스인 String을 대신해 내부적으로 문자열 편집을 위한 버퍼(buffer)를 가지고 있는 StringBuffer 클래스를 이용하면 쉽게 문자열을 변경할 수 있다. StringBuffer 인스턴스를 생성할 때 버퍼 크기를 지정할 수 있는데, 편집할 문자열의 길이를 고려하여 버퍼의 길이를 충분히 잡아주는 것이 좋다. 편집 중인 문자열이 버퍼의 길이를 넘어서면 버퍼의 길이를 늘려주는 작업이 추가로 수행되어 작업 효율이 떨어지기 때문이다.</p>
<p>StringBuffer 클래스도 String 클래스처럼 내부에 문자형 배열을 갖는다. 이 배열은 인스턴스를 생성할 때 문자열을 저장하고 편집하기 위한 공간(buffer)로 사용된다. StringBuffer 인스턴스에 저장된 문자열의 길이를 지정해 주지 않으면 기본적으로 16개의 문자를 저장할 수 있는 크기의 버퍼를 생성한다.</p>
<p><img src="https://velog.velcdn.com/images/dev-mage/post/fe1a2f4a-e0df-407e-827a-5cb3ee90532b/image.png" alt=""></p>
<p>String과 달리 StringBuffer 인스턴스는 가변 객체이므로 내용을 변경할 수 있다. 다음과 같이 코드를 작성했다고 가정하자.</p>
<pre><code class="language-java">StringBuffer sb = new StringBuffer(&quot;abc&quot;);
sb.append(&quot;123&quot;);
StringBuffer sb2 = sb.append(&quot;ZZ&quot;);

System.out.println(sb); // abc123ZZ
System.out.println(sb2); // abc123ZZ</code></pre>
<p>append 메서드의 반환타입은 StringBuffer인데 자기 자신을 반환한다. 그러므로 새로운 인스턴스를 생성하지 않고 계속해서 자신을 가지고 연산하는 것이다.</p>
<p><img src="https://velog.velcdn.com/images/dev-mage/post/42918bf1-4b34-417d-955b-6571d1b2f6ce/image.png" alt=""></p>
<p>String 클래스에서는 equals 메서드를 문자열의 내용을 비교하도록 오버라이딩하였지만 StringBuffer 클래스에서는 오버라이딩되지 않았기 때문에 StringBuffer 클래스의 equals 메서드를 사용할 경우 동등비교 연산자로 비교한 것과 같은 결과를 얻는다.</p>
<pre><code class="language-java">StringBuffer sb1 = new StringBuffer(&quot;abc&quot;);
StringBuffer sb2 = new StringBuffer(&quot;abc&quot;);

System.out.println(sb1 == sb2); // false
System.out.println(sb1.equals(sb2)); // false</code></pre>
<p>반면 toString 메서드는 오버라이딩되어 있어 StringBuffer 클래스에서 이를 호출하면 담고 있는 문자열을 String으로 변환하여 반환한다. 따라서 StringBuffer 인스턴스에 담긴 문자열을 비교하기 위해서는 StringBuffer 인스턴스의 toString 메서드로 String 인스턴스를 얻어 String 클래스의 equals 메서드를 호출해야 한다.</p>
<pre><code class="language-java">System.out.println(sb1.toString().equals(sb2.toString())); // true</code></pre>
<h3 id="stringbuilder-클래스">StringBuilder 클래스</h3>
<p>StringBuffer 클래스는 멀티 스레드 환경에서 안전하도록(tread-safe) 연산을 수행할 때 동기화되도록 설계되었다. 동기화는 성능을 떨어뜨리는 요인이 되므로 멀티 스레드 환경이 아닌 경우에는 불필요하다. 그래서 싱글 스레드 환경에 적합한, StringBuffer의 스레드 동기화 기능을 뺀 StringBuilder 클래스가 추가되었다. StringBuilder는 단지 동기화만 보장하지 않을 뿐 StringBuffer와 같은 기능을 가지고 있다.</p>
<hr>
<h3 id="references">References</h3>
<ul>
<li>자바의 정석 CHAPTER 9</li>
<li><a href="https://docs.oracle.com/javase/8/docs/api/java/lang/String.html">https://docs.oracle.com/javase/8/docs/api/java/lang/String.html</a></li>
<li><a href="https://docs.oracle.com/javase/specs/jls/se8/html/jls-12.html#jls-12.5">https://docs.oracle.com/javase/specs/jls/se8/html/jls-12.html#jls-12.5</a></li>
<li><a href="https://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.18.1">https://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.18.1</a></li>
<li><a href="https://docs.oracle.com/javase/8/docs/api/java/lang/StringBuffer.html">https://docs.oracle.com/javase/8/docs/api/java/lang/StringBuffer.html</a></li>
<li><a href="https://docs.oracle.com/javase/8/docs/api/java/lang/StringBuilder.html">https://docs.oracle.com/javase/8/docs/api/java/lang/StringBuilder.html</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[8) java.lang 패키지1 - 객체]]></title>
            <link>https://velog.io/@dev-mage/hello-java-world-java-lang-package-object</link>
            <guid>https://velog.io/@dev-mage/hello-java-world-java-lang-package-object</guid>
            <pubDate>Tue, 08 Nov 2022 01:51:10 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>Java의 Object 클래스와 메서드</p>
</blockquote>
<h3 id="object-클래스-모든-클래스의-최고-부모">Object 클래스: 모든 클래스의 최고 부모</h3>
<p>Object 클래스는 모든 클래스 상속 계층도의 최상위에 있는 부모 클래스이다. 다른 클래스로부터 상속 받지 않는 모든 클래스들은 자동적으로 Object 클래스를 상속 받게 된다. 예를 들어 문자열을 나타내는 String 클래스도 찾아보면 Object 클래스를 상속 받고 있다.</p>
<p><img src="https://velog.velcdn.com/images/dev-mage/post/b65c5857-5257-47d5-994f-37e826adc9ed/image.png" alt=""></p>
<p><a href="https://docs.oracle.com/javase/8/docs/api/java/lang/String.html">https://docs.oracle.com/javase/8/docs/api/java/lang/String.html</a></p>
<p>아래 바이트코드에서 Child 클래스의 부모 클래스인 Parent 클래스에 컴파일러가 자동으로 Object 클래스를 추가한 것을  볼 수 있다. 이미 어떤 클래스로부터 상속을 받는 클래스에 대해서는 컴파일러가 따로 Object 클래스를 추가하지 않아 Child 클래스는 Parent 클래스를 상속 받는 것을 확인할 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/dev-mage/post/8b7056f9-62f9-4922-bc82-12a3deaee592/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/dev-mage/post/e8d937d2-bdb3-41b5-9100-9fb6cd742c42/image.png" alt=""></p>
<p>Object 클래스에는 멤버 변수 없이 오직 11개의 메서드만 있다.</p>
<p><img src="https://velog.velcdn.com/images/dev-mage/post/df4c8076-b134-41b4-9369-da622e4997b6/image.png" alt=""></p>
<h3 id="equals">equals()</h3>
<p>매개변수로 객체의 참조 변수를 받아서 비교하여 그 결과를 boolean값으로 알려 준다. Object 클래스에 아래와 같이 정의되어 있다. <code>this</code>를 출력해보면 클래스의 FQCN과 16진수의 해시 코드(고유값)를 확인할 수 있다.</p>
<pre><code class="language-java">public boolean equals(Object obj) { return (this == obj); }</code></pre>
<p>두 개의 참조 변수가 같은 객체를 참조하고 있는지를 참조 변수의 고유값으로 판단하기 때문에 서로 다른 객체가 같은 내용을 가질지라도 비교 결과는 <code>false</code>가 된다. </p>
<pre><code class="language-java">class Value {
    int value;
    Value(int value) {
        System.out.println(this);
        this.value = value;
    }
}

public class ObjectTest {
    public static void main(String[] args) {
        Value v1 = new Value(10);
        Value v2 = new Value(10);
        System.out.println(&quot;v1.equals(v2): &quot; + v1.equals(v2));

        v2 = v1;
        System.out.println(&quot;v2: &quot; + v2);
        System.out.println(&quot;v1.equals(v2): &quot; + v1.equals(v2));
    }
}</code></pre>
<ul>
<li>실행 결과</li>
</ul>
<p><img src="https://velog.velcdn.com/images/dev-mage/post/f5ea764e-8a1b-48ef-8ebe-41da9f6ff9e2/image.png" alt=""></p>
<h3 id="hashcode">hashCode()</h3>
<p>해싱(hashing) 기법에 사용되는 <a href="https://ko.wikipedia.org/wiki/%ED%95%B4%EC%8B%9C_%ED%95%A8%EC%88%98">해시 함수(hash function)</a>를 구현한 메서드이다. 해시 함수는 찾고자하는 값을 입력하면 그 값이 저장된 위치를 알려주는 해시 코드(hash code)를 반환한다. 일반적으로 해시 코드가 같은 두 객체가 존재할 수 있지만 Object 클래스에 정의된 hashCode()는 객체의 고유 정수값을 이용한 해시 코드를 반환하기 때문에 서로 다른 객체는 결코 같은 해시 코드를 가질 수 없다.</p>
<pre><code class="language-java">public native int hashCode();</code></pre>
<ul>
<li>참고: <code>native</code> 메서드는 다른 프로그래밍 언어로 작성된 플랫폼에 종속적인 메서드이다.</li>
</ul>
<pre><code class="language-java">class Value {
    int value;
    Value(int value) {
        System.out.println(this);
        this.value = value;
    }
}

public class ObjectTest {
    public static void main(String[] args) {
        Value v1 = new Value(10);
        Value v2 = new Value(10);
        System.out.println(&quot;v1.hashCode(): &quot; + v1.hashCode());
        System.out.println(&quot;v2.hashCode(): &quot; + v2.hashCode());

        v2 = v1;
        System.out.println(&quot;v2.hashCode(): &quot; + v2.hashCode());
        System.out.println(&quot;v2: &quot; + Integer.decode(String.valueOf(v2.hashCode())));
    }
}</code></pre>
<ul>
<li>실행 결과</li>
</ul>
<p><img src="https://velog.velcdn.com/images/dev-mage/post/45036936-7335-43ff-8ba4-eca4ace30dee/image.png" alt=""></p>
<p>객체 값은 사실 해당 객체의 해시 코드를 16진수로 표현한 값임을 알 수 있다.</p>
<h3 id="tostring">toString()</h3>
<p>인스턴스에 대한 정보를 문자열로 제공할 목적으로 정의된 메서드이다. 인스턴스 변수에 저장된 값을 문자열로 표현할 때 사용되는데 아래와 같이 Object 클래스에 정의된 내용은 클래스명과 해시코드만 나올 뿐 다른 정보는 보여주지 않는다. 따라서 클래스를 작성할 때 해당 클래스에 맞게 오버라이딩해주어야 한다.</p>
<pre><code class="language-java">public String toString() {
    return getClass().getName() + &quot;@&quot; + Integer.toHexString(hashCode());
}</code></pre>
<pre><code class="language-java">class Value {
    int value;
    String name;
    Value(int value, String name) {
        this.value = value;
        this.name = name;
    }

    @Override
    public String toString() {
        return &quot;Value{&quot; +
                &quot;value=&quot; + value +
                &quot;, name=&#39;&quot; + name + &#39;\&#39;&#39; +
                &#39;}&#39;;
    }
}

public class ObjectTest {
    public static void main(String[] args) {
        Value v1 = new Value(10, &quot;v1&quot;);
        Value v2 = new Value(20, &quot;v2&quot;);
        System.out.println(v1.toString());
        System.out.println(v2.toString());
    }
}</code></pre>
<ul>
<li>실행 결과</li>
</ul>
<p><img src="https://velog.velcdn.com/images/dev-mage/post/b8ab210e-4a88-4406-be4d-689362db3b8d/image.png" alt=""></p>
<ul>
<li>참고: 부모에 정의된 메서드를 재정의할 때는 부모에 정의된 접근 범위보다 같거나 더 넓어야 하기 때문에 <code>public</code>으로 오버라이딩 되었다.</li>
</ul>
<hr>
<h3 id="references">References</h3>
<ul>
<li>자바의 정석 CHAPTER 9</li>
<li><a href="https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html">https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html</a></li>
<li><a href="https://docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-8.4.3.4">https://docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-8.4.3.4</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[7) 예외2 - 예외의 종류]]></title>
            <link>https://velog.io/@dev-mage/hello-java-world-exception-types</link>
            <guid>https://velog.io/@dev-mage/hello-java-world-exception-types</guid>
            <pubDate>Sun, 30 Oct 2022 14:57:13 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>예외의 발생과 checked, unchecked 및 사용자 정의 예외</p>
</blockquote>
<h3 id="throwable-클래스의-메서드">Throwable <strong>클래스의 메서드</strong></h3>
<p>모든 에러와 예외의 부모 클래스는 Throwable 클래스이다. 예외가 발생했을 때 생성되는 예외 클래스의 인스턴스에는 발생한 예외에 대한 정보가 담겨 있는데 이는 Throwable 클래스의 getMessage와 printStackTrace 메서드로 접근할 수 있다. try-catch문으로 예외를 처리하여 예외가 발생해도 비정상적으로 종료되지 않도록 해주는 동시에 앞서 말한 메서드로 예외의 발생 원인을 알 수 있다.</p>
<ul>
<li><p>getMessage(): 발생한 예외 클래스의 인스턴스에 저장된 메시지를 얻을 수 있음.</p>
</li>
<li><p>printStackTrace(): 예외 발생 당시의 호출 스택에 있었던 메서드의 정보와 예외 메시지를 화면에 출력.</p>
<pre><code class="language-java">  public class ExceptionFlow {
      public static void main(String[] args) {
          try {
              System.out.println(0/0);
          } catch (ArithmeticException ae) {
              ae.printStackTrace();
          }
      }
  }</code></pre>
<p>  <img src="https://velog.velcdn.com/images/dev-mage/post/ecce3cef-5173-4070-8a05-745a66716e14/image.png" alt=""></p>
</li>
</ul>
<h3 id="throw-예외-발생시키기">throw: 예외 발생시키기</h3>
<p>키워드 <code>throw</code>를 사용하면 프로그래머가 고의로 예외를 발생시킬 수 있다.</p>
<ol>
<li>연산자 <code>new</code>를 이용해서 발생시키려는 예외 클래스의 객체 생성.</li>
<li>키워드 <code>throw</code>를 이용해서 예외 발생.</li>
</ol>
<pre><code class="language-java">public class ExceptionFlow {
    public static void main(String[] args) {
        try {
            // Exception e = new Exception(&quot;고의로 발생시킨 예외&quot;);
            // throw e;
            throw new Exception(&quot;고의로 발생시킨 예외&quot;); // 한 줄로 작성 가능
        } catch (Exception e) {
            System.out.println(&quot;에러 메세지: &quot; + e.getMessage());
            e.printStackTrace();
        }
    }
}</code></pre>
<p><img src="https://velog.velcdn.com/images/dev-mage/post/2f688aba-a3ee-4234-81a6-d1daf6986b47/image.png" alt=""></p>
<p>예외 클래스의 객체를 생성할 때 생성자의 매개변수에 <code>String</code>을 넣어 주면 해당 <code>String</code>이 객체의 메시지로 저장된다. 이 메시지는 getMessage()로 접근할 수 있다.</p>
<h3 id="throws-메서드에-예외-선언하기">throws: 메서드에 예외 선언하기</h3>
<p>예외를 처리하는 방법엔 try-catch문 외에 메서드와 같이 선언하는 방법도 있다. 메서드에 예외를 선언하려면 메서드 선언부에 키워드 <code>throws</code>를 사용해 메서드 내에서 발생할 수 있는 예외를 작성하면 된다.</p>
<pre><code class="language-java">void method() throws Exception1, Exception2, ... , ExceptionN {
        ...
}</code></pre>
<p>만약 Exception 클래스를 선언한다면 해당 메서드는 모든 종류의 예외가 발생할 수 있다는 의미가 된다.</p>
<pre><code class="language-java">void method() throws Exception { ... } // 어떤 예외든 발생 가능</code></pre>
<p>따라서 메서드에 예외를 선언할 경우 선언한 예외의 자식 타입 예외까지 발생할 수 있다는 점을 주위해야 한다. 이렇게 메서드의 선언부에 예외를 작성함으로써 메서드를 사용하기 위해 어떤 예외들이 처리되어야 하는지 쉽게 알 수 있다.</p>
<h3 id="checked-와-unchecked-예외">checked 와 unchecked 예외</h3>
<p>예외는 다음과 같이 크게 2가지로 나뉜다. </p>
<ol>
<li><strong>checked 예외</strong><ul>
<li>Exception 클래스와 자식 클래스들(RuntimeException 제외).</li>
<li>발생 예측 가능 → <strong><a href="https://dev.java/learn/what-is-an-exception/#anchor_2">The Catch or Specify Requirement</a></strong>를 따라야함.<ul>
<li>반드시 try-catch문이나 <code>throws</code>로 예외 처리를 해주어야 함.</li>
<li>예외 처리하지 않으면 컴파일 불가능.</li>
</ul>
</li>
</ul>
</li>
<li><strong>unchecked 예외</strong><ul>
<li>Error 클래스 및 RuntimeException 클래스와 그 자식 클래스들.</li>
<li>프로그램 실행 중 발생하기 때문에 예측하기 어려움 → 예외 처리를 강제하지 않음.</li>
</ul>
</li>
</ol>
<h3 id="사용자-정의-예외">사용자 정의 예외</h3>
<p>기존에 정의된 예외 클래스 이외에 필요에 따라 프로그래머가 새로운 예외 클래스를 정의하여 사용할 수 있다. 보통은 Exception 클래스 또는 RuntimeException 클래스로부터 상속 받는 클래스를 만든다.</p>
<pre><code class="language-java">public class UserException extends Exception { ... }</code></pre>
<h3 id="호출-스택과-예외-처리">호출 스택과 예외 처리</h3>
<pre><code class="language-java">public class ExceptionFlow {
    static void method1() throws Exception {
        method2();
    }
    static void method2() throws Exception {
        throw new Exception();
    }
    public static void main(String[] args) throws Exception {
        method1();
    }
}</code></pre>
<p>위의 코드를 실행하면 다음과 같은 결과와 <a href="https://velog.io/@dev-mage/hello-java-world-oop-method#%ED%98%B8%EC%B6%9C-%EC%8A%A4%ED%83%9Dcall-stack">호출 스택</a>을 확인할 수 있다.</p>
<ul>
<li>예외가 발생했을 때 3개의 메서드(main, method1, method2)가 호출 스택에 있었음.</li>
<li>예외가 발생한 곳은 method2 메서드.</li>
<li>main 메서드가 method1()을, method1()은 method2()를 호출</li>
</ul>
<p><img src="https://velog.velcdn.com/images/dev-mage/post/0b0dd725-2eba-4415-b34a-81c5f669c01f/image.png" alt=""></p>
<ul>
<li>흐름<ol>
<li>method2()에서 예외가 발생(<code>throw new Exception();</code>) </li>
<li>try-catch문으로 예외 처리를 해주지 않았으므로 종료되면서 자신을 호출한 method1()에게 예외를 넘김.</li>
<li>method1()에서도 따로 예외 처리를 해주지 않았으므로 종료되면서 main 메서드에게 예외를 넘김.</li>
<li>main 메서드에서도 예외 처리를 하지 않아 main 메서드가 종료되면서 프로그램도 예외로 인해 비정상적으로 종료</li>
</ol>
</li>
</ul>
<p>이처럼 예외가 발생한 메서드에서 예외 처리를 하지 않고 자신을 호출한 메서드에게 예외를 넘겨줄 순 있지만 예외를 처리하지 않고 단순히 전달만 하는 것이다. 결국 어느 한 곳에서는 반드시 try-catch문으로 예외 처리를 해주어야 한다.</p>
<p>다음은 사용자로부터 입력 받은 이름으로 파일을 생성하는 코드이다. generateFile 메서드는 입력값이 유효하지 않으면 예외를 발생시키고 유효하다면 해당 값으로 파일을 생성한다. </p>
<pre><code class="language-java">package file;

import java.io.File;
import java.util.Scanner;

public class FileGenerator {
    static File generateFile(String fileName) throws Exception {
        if(fileName == null || fileName.equals(&quot;&quot;)) {
            throw new Exception(&quot;파일 이름이 유효하지 않습니다.&quot;);
        }
        File file = new File(&quot;./src/file/files/&quot;, fileName);
        file.createNewFile();
        return file;
    }

    public static void main(String[] args) {
        System.out.println(&quot;파일 이름을 입력하세요.&quot;);
        Scanner scanner = new Scanner(System.in);
        try {
            String fileName = scanner.nextLine();
            File file = generateFile(fileName);
            System.out.println(file.getName() + &quot; 파일이 성공적으로 생성되었습니다.&quot;);
        } catch (Exception e) {
            System.out.println(e.getMessage() + &quot; 다시 실행해 주시기 바랍니다.&quot;);
        } finally {
            scanner.close();
        }
    }
}</code></pre>
<p>하지만 예외는 generateFile 메서드를 호출한 main 메서드의 try-catch문에서 처리한다. 이는 generateFile 메서드가 작업을 수행하는 과정에서 문제가 생기자 예외를 발생시켜 main 메서드에게 알리는 것과 같다. 만약 generateFile 메서드가 예외를 처리하면 main 메서드에서는 예외를 발생했다는 사실을 알 수 없다. 예외가 발생했을 때 예외가 발생한 메서드 내에서 자체적으로 처리해도 되는 경우 메서드 내에 try-catch문을 넣어서 처리하고 메서드 내에서 자체적으로 해결이 안되는 경우(파일 이름을 다시 받아와야 하는 경우)에는 예외를 선언해서 호출한 메서드가 처리하도록 해야 한다.</p>
<h3 id="try-with-resources문">try-with-resources문</h3>
<p>위 예제를 보면 스캐너를 생성해 사용자 입력을 받은 후 finally 블럭에서 닫아주는 것을 알 수 있다. Scanner 클래스는 데이터를 입력 받으려고 할 때 사용되는데 마치 수도꼭지로 물(자원)을 틀고 더 이상 쓸 일이 없으면 잠그는 것처럼 입력(자원)을 다 받았다면 이를 해제해 주어야 한다. 때문에 자원을 낭비하지 않기 위해 무조건 실행되는 finally 블럭에서 스캐너를 닫아 준 것이다. Java 7부터는 이를 쉽게 해주는 try-with-resources문이 도입되었다. try-with-resources문을 활용하면 다음과 같이 코드를 작성할 수 있다.</p>
<pre><code class="language-java">public static void main(String[] args) {
        System.out.println(&quot;파일 이름을 입력하세요.&quot;);
        try (Scanner scanner = new Scanner(System.in)) {
            String fileName = scanner.nextLine();
            File file = generateFile(fileName);
            System.out.println(file.getName() + &quot; 파일이 성공적으로 생성되었습니다.&quot;);
        } catch (Exception e) {
            System.out.println(e.getMessage() + &quot; 다시 실행해 주시기 바랍니다.&quot;);
        }
    }</code></pre>
<p>try-with-resources문은 try 블럭을 벗어나면 close()를 호출해 자동으로 자원을 해제해 준다.</p>
<ul>
<li>참고: 자동으로 close()를 호출하려면 AutoCloseable를 구현한 객체여야 한다.</li>
</ul>
<h3 id="예외-되던지기exception-re-throwing">예외 되던지기(exception re-throwing)</h3>
<p>한 메서드에서 발생할 수 있는 예외가 여럿인 경우, 일부는 해당 메서드 내에서 자체적으로 처리하고 나머지는 선언부에 지정한 뒤 인위적으로 예외를 다시 발생시켜 호출한 메서드에서 나누어 처리할 수 있다. 이것을 <strong>예외 되던지기</strong>라고 한다. 하나의 예외에 대해 예외가 발생한 메서드와 이를 호출한 메서드 양쪽 모두에서 처리해줘야 할 작업이 있을 때 사용된다.</p>
<pre><code class="language-java">public class ExceptionFlow {
    static void method1() throws Exception {
        try {
            throw new Exception();
        } catch (Exception e) {
            System.out.println(&quot;method1 메서드에서 예외 처리됨.&quot;);
            throw e; // 다시 예외 발생
        }
    }
    public static void main(String[] args) {
        try {
            method1();
        } catch (Exception e) {
            System.out.println(&quot;main 메서드에서 예외 처리됨.&quot;);
        }
    }
}</code></pre>
<p><img src="https://velog.velcdn.com/images/dev-mage/post/db06d825-6602-478a-9576-31f1d6804d1b/image.png" alt=""></p>
<p>반환값이 있는 return문의 경우, catch 블럭에도 return문이 있어야 한다. 예외가 발생해도 값을 반환해야 하기 때문이다.</p>
<h3 id="연결된-예외chained-exception">연결된 예외(chained exception)</h3>
<p>한 예외가 다른 예외를 발생하게 할 수 있다. 예외 A가 예외 B를 발생시켰다면 A를 B의 <strong>원인 예외(cause exception)</strong>라고 한다. 먼저 예외를 생성한 후 Throwable 클래스의 initCause 메서드로 다른 예외를 원인 예외로 설정한다. 그리고 <code>throw</code>로 생성한 예외를 던진다.</p>
<ul>
<li><code>Throwable initCause(Throwable cause)</code>: 지정한 예외를 원인 예외로 등록</li>
<li><code>Throwable getCause()</code>: 원인 예외를 반환</li>
</ul>
<pre><code class="language-java">try {
    throw new CauseException(&quot;원인 예외 발생&quot;);
} catch (CauseException ce) {
    ResultException re = new ResultException(&quot;예외 발생&quot;);
    re.initCause(ce);
    throw re;
}</code></pre>
<p>발생한 예외를 그냥 처리하지 않고 원인 예외로 등록해서 다시 예외를 발생시키는 이유는 여러 가지 예외를 하나의 큰 분류의 예외로 묶어서 다루기 위함이다.</p>
<p>예를 들어 작은 카페가 있고 메뉴로는 커피, 차, 에이드를 판매한다고 하자. 커피는 3000원, 차는 3500원, 에이드는 4000원이다.</p>
<pre><code class="language-java">public class JavaCafe {
    static final String[][] MENU = {{&quot;coffee&quot;, &quot;3000&quot;}, {&quot;tea&quot;, &quot;3500&quot;}, {&quot;ade&quot;, &quot;4000&quot;}};
}</code></pre>
<p>만약 없는 메뉴를 주문하거나 있는 메뉴를 주문해도 지불한 금액이 맞지 않는 경우를 확인하기 위해 checkMenu()와 checkMoney()를 정의했다. 주문이 실패한 경우 각각 NoSuchMenuException과 WrongAmountOfMoneyException이 발생한다.</p>
<pre><code class="language-java">public class NoSuchMenuException extends Exception{ ... }

////////////////////////////////////////////////////////////////////

public class WrongAmountOfMoneyException extends Exception { ... }

////////////////////////////////////////////////////////////////////

public class JavaCafe {
    static final String[][] MENU = {
                {&quot;coffee&quot;, &quot;3000&quot;}, {&quot;tea&quot;, &quot;3500&quot;}, {&quot;ade&quot;, &quot;4000&quot;}
        };

    static int checkMenu(int order) throws NoSuchMenuException {
        switch (order) {
            case 1 :
                return Integer.parseInt(MENU[0][1]);
            case 2 :
                return Integer.parseInt(MENU[1][1]);
            case 3 :
                return Integer.parseInt(MENU[2][1]);
        }
        throw new NoSuchMenuException(&quot;없는 메뉴&quot;);
    }
    static boolean checkMoney(int price, int money) 
            throws WrongAmountOfMoneyException {
        if(price == money) {
            return true;
        }
        throw new WrongAmountOfMoneyException(&quot;잘못된 금액&quot;);
    }
}</code></pre>
<p>두 경우 모두 주문 실패에 해당하기 때문에 FailedOrderException으로 묶으려 하는데 NoSuchMenuException과 WrongAmountOfMoneyException의 조상으로 FailedOrderException을 지정하면 실제로 발생한 예외가 어떤 것인지 알 수 없고 두 예외의 상속 관계를 변경해야 하는 문제가 생긴다. 이 때 원인 예외를 포함하게 한다면 두 예외는 상속 관계가 아니어도 상관 없게 된다.</p>
<pre><code class="language-java">import java.util.Scanner;
public class JavaCafe {
    static final String[][] MENU = {{&quot;coffee&quot;, &quot;3000&quot;}, {&quot;tea&quot;, &quot;3500&quot;}, {&quot;ade&quot;, &quot;4000&quot;}};

    static void order() throws FailedOrderException {
        System.out.println(&quot;---- 메뉴 ----&quot;);
        for(int i = 0; i &lt; MENU.length; i++) {
            System.out.printf(&quot;%d. %s: %s원 %n&quot;, i+1, MENU[i][0], MENU[i][1]);
        }
        System.out.print(&quot;선택: &quot;);
        try (Scanner scanner = new Scanner(System.in)) {
            int price = checkMenu(scanner.nextInt());
            System.out.print(&quot;지불 금액: &quot;);
            int money = scanner.nextInt();
            if (checkMoney(price, money)) {
                System.out.println(&quot;주문 완료&quot;);
            }
        } catch (NoSuchMenuException | WrongAmountOfMoneyException e) {
            FailedOrderException fe = new FailedOrderException(&quot;주문 실패: &quot; + e.getMessage());
            fe.initCause(e);
            throw fe;
        }

    }
    static int checkMenu(int order) throws NoSuchMenuException {
        switch (order) {
            case 1 :
                return Integer.parseInt(MENU[0][1]);
            case 2 :
                return Integer.parseInt(MENU[1][1]);
            case 3 :
                return Integer.parseInt(MENU[2][1]);
        }
        throw new NoSuchMenuException(&quot;없는 메뉴&quot;);
    }
    static boolean checkMoney(int price, int money) throws WrongAmountOfMoneyException {
        if(price == money) {
            return true;
        }
        throw new WrongAmountOfMoneyException(&quot;잘못된 금액&quot;);
    }

    public static void main(String[] args) {
        try {
            order();
        } catch (FailedOrderException fe) {
            System.out.println(fe.getMessage());
            System.out.println(&quot;다시 주문해 주세요.&quot;);
        }
    }
}</code></pre>
<ul>
<li><p>없는 메뉴를 주문한 경우</p>
<p>  <img src="https://velog.velcdn.com/images/dev-mage/post/68f92d2a-44f1-4bc0-b441-415ff70eb5df/image.png" alt=""></p>
</li>
</ul>
<ul>
<li><p>지불한 금액이 잘못된 경우</p>
<p>  <img src="https://velog.velcdn.com/images/dev-mage/post/aca0e9e9-89af-4788-b311-f9081697787f/image.png" alt=""></p>
</li>
</ul>
<ul>
<li><p>주문에 성공한 경우</p>
<p>  <img src="https://velog.velcdn.com/images/dev-mage/post/5c0f37a3-9c13-438c-99f3-5db45d61a19e/image.png" alt=""></p>
</li>
</ul>
<hr>
<h3 id="references">References</h3>
<ul>
<li>자바의 정석 CHAPTER 8</li>
<li><a href="https://dev.java/learn/what-is-an-exception/">https://dev.java/learn/what-is-an-exception/</a></li>
<li><a href="https://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html">https://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html</a></li>
</ul>
]]></description>
        </item>
    </channel>
</rss>