diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..7e77568
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,264 @@
+# Created by https://www.toptal.com/developers/gitignore/api/windows,macos,intellij,eclipse,java
+# Edit at https://www.toptal.com/developers/gitignore?templates=windows,macos,intellij,eclipse,java
+
+### Eclipse ###
+.metadata
+bin/
+tmp/
+*.tmp
+*.bak
+*.swp
+*~.nib
+local.properties
+.settings/
+.loadpath
+.recommenders
+
+# External tool builders
+.externalToolBuilders/
+
+# Locally stored "Eclipse launch configurations"
+*.launch
+
+# PyDev specific (Python IDE for Eclipse)
+*.pydevproject
+
+# CDT-specific (C/C++ Development Tooling)
+.cproject
+
+# CDT- autotools
+.autotools
+
+# Java annotation processor (APT)
+.factorypath
+
+# PDT-specific (PHP Development Tools)
+.buildpath
+
+# sbteclipse plugin
+.target
+
+# Tern plugin
+.tern-project
+
+# TeXlipse plugin
+.texlipse
+
+# STS (Spring Tool Suite)
+.springBeans
+
+# Code Recommenders
+.recommenders/
+
+# Annotation Processing
+.apt_generated/
+.apt_generated_test/
+
+# Scala IDE specific (Scala & Java development for Eclipse)
+.cache-main
+.scala_dependencies
+.worksheet
+
+# Uncomment this line if you wish to ignore the project description file.
+# Typically, this file would be tracked if it contains build/dependency configurations:
+#.project
+
+### Eclipse Patch ###
+# Spring Boot Tooling
+.sts4-cache/
+
+### Intellij ###
+# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
+# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
+
+# User-specific stuff
+.idea/**/workspace.xml
+.idea/**/tasks.xml
+.idea/**/usage.statistics.xml
+.idea/**/dictionaries
+.idea/**/shelf
+
+# AWS User-specific
+.idea/**/aws.xml
+
+# Generated files
+.idea/**/contentModel.xml
+
+# Sensitive or high-churn files
+.idea/**/dataSources/
+.idea/**/dataSources.ids
+.idea/**/dataSources.local.xml
+.idea/**/sqlDataSources.xml
+.idea/**/dynamic.xml
+.idea/**/uiDesigner.xml
+.idea/**/dbnavigator.xml
+
+# Gradle
+.idea/**/gradle.xml
+.idea/**/libraries
+
+# Gradle and Maven with auto-import
+# When using Gradle or Maven with auto-import, you should exclude module files,
+# since they will be recreated, and may cause churn. Uncomment if using
+# auto-import.
+# .idea/artifacts
+# .idea/compiler.xml
+# .idea/jarRepositories.xml
+# .idea/modules.xml
+# .idea/*.iml
+# .idea/modules
+# *.iml
+# *.ipr
+
+# CMake
+cmake-build-*/
+
+# Mongo Explorer plugin
+.idea/**/mongoSettings.xml
+
+# File-based project format
+*.iws
+
+# IntelliJ
+out/
+
+# mpeltonen/sbt-idea plugin
+.idea_modules/
+
+# JIRA plugin
+atlassian-ide-plugin.xml
+
+# Cursive Clojure plugin
+.idea/replstate.xml
+
+# SonarLint plugin
+.idea/sonarlint/
+
+# Crashlytics plugin (for Android Studio and IntelliJ)
+com_crashlytics_export_strings.xml
+crashlytics.properties
+crashlytics-build.properties
+fabric.properties
+
+# Editor-based Rest Client
+.idea/httpRequests
+
+# Android studio 3.1+ serialized cache file
+.idea/caches/build_file_checksums.ser
+
+### Intellij Patch ###
+# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721
+
+# *.iml
+# modules.xml
+# .idea/misc.xml
+# *.ipr
+
+# Sonarlint plugin
+# https://plugins.jetbrains.com/plugin/7973-sonarlint
+.idea/**/sonarlint/
+
+# SonarQube Plugin
+# https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin
+.idea/**/sonarIssues.xml
+
+# Markdown Navigator plugin
+# https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced
+.idea/**/markdown-navigator.xml
+.idea/**/markdown-navigator-enh.xml
+.idea/**/markdown-navigator/
+
+# Cache file creation bug
+# See https://youtrack.jetbrains.com/issue/JBR-2257
+.idea/$CACHE_FILE$
+
+# CodeStream plugin
+# https://plugins.jetbrains.com/plugin/12206-codestream
+.idea/codestream.xml
+
+### Java ###
+# Compiled class file
+*.class
+
+# Log file
+*.log
+
+# BlueJ files
+*.ctxt
+
+# Mobile Tools for Java (J2ME)
+.mtj.tmp/
+
+# Package Files #
+*.jar
+*.war
+*.nar
+*.ear
+*.zip
+*.tar.gz
+*.rar
+
+# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
+hs_err_pid*
+replay_pid*
+
+### macOS ###
+# General
+.DS_Store
+.AppleDouble
+.LSOverride
+
+# Icon must end with two \r
+Icon
+
+
+# Thumbnails
+._*
+
+# Files that might appear in the root of a volume
+.DocumentRevisions-V100
+.fseventsd
+.Spotlight-V100
+.TemporaryItems
+.Trashes
+.VolumeIcon.icns
+.com.apple.timemachine.donotpresent
+
+# Directories potentially created on remote AFP share
+.AppleDB
+.AppleDesktop
+Network Trash Folder
+Temporary Items
+.apdisk
+
+### macOS Patch ###
+# iCloud generated files
+*.icloud
+
+### Windows ###
+# Windows thumbnail cache files
+Thumbs.db
+Thumbs.db:encryptable
+ehthumbs.db
+ehthumbs_vista.db
+
+# Dump file
+*.stackdump
+
+# Folder config file
+[Dd]esktop.ini
+
+# Recycle Bin used on file shares
+$RECYCLE.BIN/
+
+# Windows Installer files
+*.cab
+*.msi
+*.msix
+*.msm
+*.msp
+
+# Windows shortcuts
+*.lnk
+
+# End of https://www.toptal.com/developers/gitignore/api/windows,macos,intellij,eclipse,java
diff --git a/.mvn/wrapper/maven-wrapper.jar b/.mvn/wrapper/maven-wrapper.jar
new file mode 100644
index 0000000..c1dd12f
Binary files /dev/null and b/.mvn/wrapper/maven-wrapper.jar differ
diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties
new file mode 100644
index 0000000..b7cb93e
--- /dev/null
+++ b/.mvn/wrapper/maven-wrapper.properties
@@ -0,0 +1,2 @@
+distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.4/apache-maven-3.8.4-bin.zip
+wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..0a59067
--- /dev/null
+++ b/README.md
@@ -0,0 +1,26 @@
+# 이펙티브 자바 스터디
+
+> 2022\. 05. 28 ~
+
+
+
+## 스터디원
+
+[김재호](https://github.com/chamominedev) [이민지](https://github.com/MinJee-lee) [임대진](https://github.com/fineapplepizza) [전선규](https://github.com/sungyujeon) [전희진](https://github.com/h2jinee)
+
+
+
+## 학습내용
+
+1장 들어가기
+
+2장 객체 생성과 파괴
+
+|아이템|내용|발표자|참고링크|
+|:-:|:---:|:---:|:---:|
+|1|생성자 대신 정적 팩터리 메서드를 고려하라|전희진|[LINK](https://h2jinee.notion.site/1-6657e62fab5948febb2fba00c3c53046)|
+|2|생성자에 매개변수가 많다면 빌더를 고려하라|김재호|[LINK](https://velog.io/@chamominedev/생성자에-매개변수가-많다면-빌더를-고려하라)|
+|3|private 생성자나 열거 타입으로 싱글턴임을 보증하라|전선규|[LINK](https://github.com/sungyujeon/TIL/blob/master/java/effective-java/01_creating-destroying-objects.md)|
+|4|인스턴스화를 막으려거든 private 생성자를 사용하라|이민지||
+|5|자원을 직접 명시하지 말고 의존 객체 주입을 사용하라|임대진||
+
diff --git a/effective-java.git/HEAD b/effective-java.git/HEAD
new file mode 100644
index 0000000..b870d82
--- /dev/null
+++ b/effective-java.git/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/main
diff --git a/effective-java.git/config b/effective-java.git/config
new file mode 100644
index 0000000..afffd3a
--- /dev/null
+++ b/effective-java.git/config
@@ -0,0 +1,10 @@
+[core]
+ repositoryformatversion = 0
+ filemode = false
+ bare = true
+ symlinks = false
+ ignorecase = true
+[remote "origin"]
+ url = https://github.com/h2jinee/effective-java/
+ fetch = +refs/*:refs/*
+ mirror = true
diff --git a/effective-java.git/description b/effective-java.git/description
new file mode 100644
index 0000000..498b267
--- /dev/null
+++ b/effective-java.git/description
@@ -0,0 +1 @@
+Unnamed repository; edit this file 'description' to name the repository.
diff --git a/effective-java.git/hooks/applypatch-msg.sample b/effective-java.git/hooks/applypatch-msg.sample
new file mode 100644
index 0000000..a5d7b84
--- /dev/null
+++ b/effective-java.git/hooks/applypatch-msg.sample
@@ -0,0 +1,15 @@
+#!/bin/sh
+#
+# An example hook script to check the commit log message taken by
+# applypatch from an e-mail message.
+#
+# The hook should exit with non-zero status after issuing an
+# appropriate message if it wants to stop the commit. The hook is
+# allowed to edit the commit message file.
+#
+# To enable this hook, rename this file to "applypatch-msg".
+
+. git-sh-setup
+commitmsg="$(git rev-parse --git-path hooks/commit-msg)"
+test -x "$commitmsg" && exec "$commitmsg" ${1+"$@"}
+:
diff --git a/effective-java.git/hooks/commit-msg.sample b/effective-java.git/hooks/commit-msg.sample
new file mode 100644
index 0000000..b58d118
--- /dev/null
+++ b/effective-java.git/hooks/commit-msg.sample
@@ -0,0 +1,24 @@
+#!/bin/sh
+#
+# An example hook script to check the commit log message.
+# Called by "git commit" with one argument, the name of the file
+# that has the commit message. The hook should exit with non-zero
+# status after issuing an appropriate message if it wants to stop the
+# commit. The hook is allowed to edit the commit message file.
+#
+# To enable this hook, rename this file to "commit-msg".
+
+# Uncomment the below to add a Signed-off-by line to the message.
+# Doing this in a hook is a bad idea in general, but the prepare-commit-msg
+# hook is more suited to it.
+#
+# SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p')
+# grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1"
+
+# This example catches duplicate Signed-off-by lines.
+
+test "" = "$(grep '^Signed-off-by: ' "$1" |
+ sort | uniq -c | sed -e '/^[ ]*1[ ]/d')" || {
+ echo >&2 Duplicate Signed-off-by lines.
+ exit 1
+}
diff --git a/effective-java.git/hooks/fsmonitor-watchman.sample b/effective-java.git/hooks/fsmonitor-watchman.sample
new file mode 100644
index 0000000..14ed0aa
--- /dev/null
+++ b/effective-java.git/hooks/fsmonitor-watchman.sample
@@ -0,0 +1,173 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+use IPC::Open2;
+
+# An example hook script to integrate Watchman
+# (https://facebook.github.io/watchman/) with git to speed up detecting
+# new and modified files.
+#
+# The hook is passed a version (currently 2) and last update token
+# formatted as a string and outputs to stdout a new update token and
+# all files that have been modified since the update token. Paths must
+# be relative to the root of the working tree and separated by a single NUL.
+#
+# To enable this hook, rename this file to "query-watchman" and set
+# 'git config core.fsmonitor .git/hooks/query-watchman'
+#
+my ($version, $last_update_token) = @ARGV;
+
+# Uncomment for debugging
+# print STDERR "$0 $version $last_update_token\n";
+
+# Check the hook interface version
+if ($version ne 2) {
+ die "Unsupported query-fsmonitor hook version '$version'.\n" .
+ "Falling back to scanning...\n";
+}
+
+my $git_work_tree = get_working_dir();
+
+my $retry = 1;
+
+my $json_pkg;
+eval {
+ require JSON::XS;
+ $json_pkg = "JSON::XS";
+ 1;
+} or do {
+ require JSON::PP;
+ $json_pkg = "JSON::PP";
+};
+
+launch_watchman();
+
+sub launch_watchman {
+ my $o = watchman_query();
+ if (is_work_tree_watched($o)) {
+ output_result($o->{clock}, @{$o->{files}});
+ }
+}
+
+sub output_result {
+ my ($clockid, @files) = @_;
+
+ # Uncomment for debugging watchman output
+ # open (my $fh, ">", ".git/watchman-output.out");
+ # binmode $fh, ":utf8";
+ # print $fh "$clockid\n@files\n";
+ # close $fh;
+
+ binmode STDOUT, ":utf8";
+ print $clockid;
+ print "\0";
+ local $, = "\0";
+ print @files;
+}
+
+sub watchman_clock {
+ my $response = qx/watchman clock "$git_work_tree"/;
+ die "Failed to get clock id on '$git_work_tree'.\n" .
+ "Falling back to scanning...\n" if $? != 0;
+
+ return $json_pkg->new->utf8->decode($response);
+}
+
+sub watchman_query {
+ my $pid = open2(\*CHLD_OUT, \*CHLD_IN, 'watchman -j --no-pretty')
+ or die "open2() failed: $!\n" .
+ "Falling back to scanning...\n";
+
+ # In the query expression below we're asking for names of files that
+ # changed since $last_update_token but not from the .git folder.
+ #
+ # To accomplish this, we're using the "since" generator to use the
+ # recency index to select candidate nodes and "fields" to limit the
+ # output to file names only. Then we're using the "expression" term to
+ # further constrain the results.
+ if (substr($last_update_token, 0, 1) eq "c") {
+ $last_update_token = "\"$last_update_token\"";
+ }
+ my $query = <<" END";
+ ["query", "$git_work_tree", {
+ "since": $last_update_token,
+ "fields": ["name"],
+ "expression": ["not", ["dirname", ".git"]]
+ }]
+ END
+
+ # Uncomment for debugging the watchman query
+ # open (my $fh, ">", ".git/watchman-query.json");
+ # print $fh $query;
+ # close $fh;
+
+ print CHLD_IN $query;
+ close CHLD_IN;
+ my $response = do {local $/; };
+
+ # Uncomment for debugging the watch response
+ # open ($fh, ">", ".git/watchman-response.json");
+ # print $fh $response;
+ # close $fh;
+
+ die "Watchman: command returned no output.\n" .
+ "Falling back to scanning...\n" if $response eq "";
+ die "Watchman: command returned invalid output: $response\n" .
+ "Falling back to scanning...\n" unless $response =~ /^\{/;
+
+ return $json_pkg->new->utf8->decode($response);
+}
+
+sub is_work_tree_watched {
+ my ($output) = @_;
+ my $error = $output->{error};
+ if ($retry > 0 and $error and $error =~ m/unable to resolve root .* directory (.*) is not watched/) {
+ $retry--;
+ my $response = qx/watchman watch "$git_work_tree"/;
+ die "Failed to make watchman watch '$git_work_tree'.\n" .
+ "Falling back to scanning...\n" if $? != 0;
+ $output = $json_pkg->new->utf8->decode($response);
+ $error = $output->{error};
+ die "Watchman: $error.\n" .
+ "Falling back to scanning...\n" if $error;
+
+ # Uncomment for debugging watchman output
+ # open (my $fh, ">", ".git/watchman-output.out");
+ # close $fh;
+
+ # Watchman will always return all files on the first query so
+ # return the fast "everything is dirty" flag to git and do the
+ # Watchman query just to get it over with now so we won't pay
+ # the cost in git to look up each individual file.
+ my $o = watchman_clock();
+ $error = $output->{error};
+
+ die "Watchman: $error.\n" .
+ "Falling back to scanning...\n" if $error;
+
+ output_result($o->{clock}, ("/"));
+ $last_update_token = $o->{clock};
+
+ eval { launch_watchman() };
+ return 0;
+ }
+
+ die "Watchman: $error.\n" .
+ "Falling back to scanning...\n" if $error;
+
+ return 1;
+}
+
+sub get_working_dir {
+ my $working_dir;
+ if ($^O =~ 'msys' || $^O =~ 'cygwin') {
+ $working_dir = Win32::GetCwd();
+ $working_dir =~ tr/\\/\//;
+ } else {
+ require Cwd;
+ $working_dir = Cwd::cwd();
+ }
+
+ return $working_dir;
+}
diff --git a/effective-java.git/hooks/post-update.sample b/effective-java.git/hooks/post-update.sample
new file mode 100644
index 0000000..ec17ec1
--- /dev/null
+++ b/effective-java.git/hooks/post-update.sample
@@ -0,0 +1,8 @@
+#!/bin/sh
+#
+# An example hook script to prepare a packed repository for use over
+# dumb transports.
+#
+# To enable this hook, rename this file to "post-update".
+
+exec git update-server-info
diff --git a/effective-java.git/hooks/pre-applypatch.sample b/effective-java.git/hooks/pre-applypatch.sample
new file mode 100644
index 0000000..4142082
--- /dev/null
+++ b/effective-java.git/hooks/pre-applypatch.sample
@@ -0,0 +1,14 @@
+#!/bin/sh
+#
+# An example hook script to verify what is about to be committed
+# by applypatch from an e-mail message.
+#
+# The hook should exit with non-zero status after issuing an
+# appropriate message if it wants to stop the commit.
+#
+# To enable this hook, rename this file to "pre-applypatch".
+
+. git-sh-setup
+precommit="$(git rev-parse --git-path hooks/pre-commit)"
+test -x "$precommit" && exec "$precommit" ${1+"$@"}
+:
diff --git a/effective-java.git/hooks/pre-commit.sample b/effective-java.git/hooks/pre-commit.sample
new file mode 100644
index 0000000..e144712
--- /dev/null
+++ b/effective-java.git/hooks/pre-commit.sample
@@ -0,0 +1,49 @@
+#!/bin/sh
+#
+# An example hook script to verify what is about to be committed.
+# Called by "git commit" with no arguments. The hook should
+# exit with non-zero status after issuing an appropriate message if
+# it wants to stop the commit.
+#
+# To enable this hook, rename this file to "pre-commit".
+
+if git rev-parse --verify HEAD >/dev/null 2>&1
+then
+ against=HEAD
+else
+ # Initial commit: diff against an empty tree object
+ against=$(git hash-object -t tree /dev/null)
+fi
+
+# If you want to allow non-ASCII filenames set this variable to true.
+allownonascii=$(git config --type=bool hooks.allownonascii)
+
+# Redirect output to stderr.
+exec 1>&2
+
+# Cross platform projects tend to avoid non-ASCII filenames; prevent
+# them from being added to the repository. We exploit the fact that the
+# printable range starts at the space character and ends with tilde.
+if [ "$allownonascii" != "true" ] &&
+ # Note that the use of brackets around a tr range is ok here, (it's
+ # even required, for portability to Solaris 10's /usr/bin/tr), since
+ # the square bracket bytes happen to fall in the designated range.
+ test $(git diff --cached --name-only --diff-filter=A -z $against |
+ LC_ALL=C tr -d '[ -~]\0' | wc -c) != 0
+then
+ cat <<\EOF
+Error: Attempt to add a non-ASCII file name.
+
+This can cause problems if you want to work with people on other platforms.
+
+To be portable it is advisable to rename the file.
+
+If you know what you are doing you can disable this check using:
+
+ git config hooks.allownonascii true
+EOF
+ exit 1
+fi
+
+# If there are whitespace errors, print the offending file names and fail.
+exec git diff-index --check --cached $against --
diff --git a/effective-java.git/hooks/pre-merge-commit.sample b/effective-java.git/hooks/pre-merge-commit.sample
new file mode 100644
index 0000000..399eab1
--- /dev/null
+++ b/effective-java.git/hooks/pre-merge-commit.sample
@@ -0,0 +1,13 @@
+#!/bin/sh
+#
+# An example hook script to verify what is about to be committed.
+# Called by "git merge" with no arguments. The hook should
+# exit with non-zero status after issuing an appropriate message to
+# stderr if it wants to stop the merge commit.
+#
+# To enable this hook, rename this file to "pre-merge-commit".
+
+. git-sh-setup
+test -x "$GIT_DIR/hooks/pre-commit" &&
+ exec "$GIT_DIR/hooks/pre-commit"
+:
diff --git a/effective-java.git/hooks/pre-push.sample b/effective-java.git/hooks/pre-push.sample
new file mode 100644
index 0000000..4ce688d
--- /dev/null
+++ b/effective-java.git/hooks/pre-push.sample
@@ -0,0 +1,53 @@
+#!/bin/sh
+
+# An example hook script to verify what is about to be pushed. Called by "git
+# push" after it has checked the remote status, but before anything has been
+# pushed. If this script exits with a non-zero status nothing will be pushed.
+#
+# This hook is called with the following parameters:
+#
+# $1 -- Name of the remote to which the push is being done
+# $2 -- URL to which the push is being done
+#
+# If pushing without using a named remote those arguments will be equal.
+#
+# Information about the commits which are being pushed is supplied as lines to
+# the standard input in the form:
+#
+#
+#
+# This sample shows how to prevent push of commits where the log message starts
+# with "WIP" (work in progress).
+
+remote="$1"
+url="$2"
+
+zero=$(git hash-object --stdin &2 "Found WIP commit in $local_ref, not pushing"
+ exit 1
+ fi
+ fi
+done
+
+exit 0
diff --git a/effective-java.git/hooks/pre-rebase.sample b/effective-java.git/hooks/pre-rebase.sample
new file mode 100644
index 0000000..6cbef5c
--- /dev/null
+++ b/effective-java.git/hooks/pre-rebase.sample
@@ -0,0 +1,169 @@
+#!/bin/sh
+#
+# Copyright (c) 2006, 2008 Junio C Hamano
+#
+# The "pre-rebase" hook is run just before "git rebase" starts doing
+# its job, and can prevent the command from running by exiting with
+# non-zero status.
+#
+# The hook is called with the following parameters:
+#
+# $1 -- the upstream the series was forked from.
+# $2 -- the branch being rebased (or empty when rebasing the current branch).
+#
+# This sample shows how to prevent topic branches that are already
+# merged to 'next' branch from getting rebased, because allowing it
+# would result in rebasing already published history.
+
+publish=next
+basebranch="$1"
+if test "$#" = 2
+then
+ topic="refs/heads/$2"
+else
+ topic=`git symbolic-ref HEAD` ||
+ exit 0 ;# we do not interrupt rebasing detached HEAD
+fi
+
+case "$topic" in
+refs/heads/??/*)
+ ;;
+*)
+ exit 0 ;# we do not interrupt others.
+ ;;
+esac
+
+# Now we are dealing with a topic branch being rebased
+# on top of master. Is it OK to rebase it?
+
+# Does the topic really exist?
+git show-ref -q "$topic" || {
+ echo >&2 "No such branch $topic"
+ exit 1
+}
+
+# Is topic fully merged to master?
+not_in_master=`git rev-list --pretty=oneline ^master "$topic"`
+if test -z "$not_in_master"
+then
+ echo >&2 "$topic is fully merged to master; better remove it."
+ exit 1 ;# we could allow it, but there is no point.
+fi
+
+# Is topic ever merged to next? If so you should not be rebasing it.
+only_next_1=`git rev-list ^master "^$topic" ${publish} | sort`
+only_next_2=`git rev-list ^master ${publish} | sort`
+if test "$only_next_1" = "$only_next_2"
+then
+ not_in_topic=`git rev-list "^$topic" master`
+ if test -z "$not_in_topic"
+ then
+ echo >&2 "$topic is already up to date with master"
+ exit 1 ;# we could allow it, but there is no point.
+ else
+ exit 0
+ fi
+else
+ not_in_next=`git rev-list --pretty=oneline ^${publish} "$topic"`
+ /usr/bin/perl -e '
+ my $topic = $ARGV[0];
+ my $msg = "* $topic has commits already merged to public branch:\n";
+ my (%not_in_next) = map {
+ /^([0-9a-f]+) /;
+ ($1 => 1);
+ } split(/\n/, $ARGV[1]);
+ for my $elem (map {
+ /^([0-9a-f]+) (.*)$/;
+ [$1 => $2];
+ } split(/\n/, $ARGV[2])) {
+ if (!exists $not_in_next{$elem->[0]}) {
+ if ($msg) {
+ print STDERR $msg;
+ undef $msg;
+ }
+ print STDERR " $elem->[1]\n";
+ }
+ }
+ ' "$topic" "$not_in_next" "$not_in_master"
+ exit 1
+fi
+
+<<\DOC_END
+
+This sample hook safeguards topic branches that have been
+published from being rewound.
+
+The workflow assumed here is:
+
+ * Once a topic branch forks from "master", "master" is never
+ merged into it again (either directly or indirectly).
+
+ * Once a topic branch is fully cooked and merged into "master",
+ it is deleted. If you need to build on top of it to correct
+ earlier mistakes, a new topic branch is created by forking at
+ the tip of the "master". This is not strictly necessary, but
+ it makes it easier to keep your history simple.
+
+ * Whenever you need to test or publish your changes to topic
+ branches, merge them into "next" branch.
+
+The script, being an example, hardcodes the publish branch name
+to be "next", but it is trivial to make it configurable via
+$GIT_DIR/config mechanism.
+
+With this workflow, you would want to know:
+
+(1) ... if a topic branch has ever been merged to "next". Young
+ topic branches can have stupid mistakes you would rather
+ clean up before publishing, and things that have not been
+ merged into other branches can be easily rebased without
+ affecting other people. But once it is published, you would
+ not want to rewind it.
+
+(2) ... if a topic branch has been fully merged to "master".
+ Then you can delete it. More importantly, you should not
+ build on top of it -- other people may already want to
+ change things related to the topic as patches against your
+ "master", so if you need further changes, it is better to
+ fork the topic (perhaps with the same name) afresh from the
+ tip of "master".
+
+Let's look at this example:
+
+ o---o---o---o---o---o---o---o---o---o "next"
+ / / / /
+ / a---a---b A / /
+ / / / /
+ / / c---c---c---c B /
+ / / / \ /
+ / / / b---b C \ /
+ / / / / \ /
+ ---o---o---o---o---o---o---o---o---o---o---o "master"
+
+
+A, B and C are topic branches.
+
+ * A has one fix since it was merged up to "next".
+
+ * B has finished. It has been fully merged up to "master" and "next",
+ and is ready to be deleted.
+
+ * C has not merged to "next" at all.
+
+We would want to allow C to be rebased, refuse A, and encourage
+B to be deleted.
+
+To compute (1):
+
+ git rev-list ^master ^topic next
+ git rev-list ^master next
+
+ if these match, topic has not merged in next at all.
+
+To compute (2):
+
+ git rev-list master..topic
+
+ if this is empty, it is fully merged to "master".
+
+DOC_END
diff --git a/effective-java.git/hooks/pre-receive.sample b/effective-java.git/hooks/pre-receive.sample
new file mode 100644
index 0000000..a1fd29e
--- /dev/null
+++ b/effective-java.git/hooks/pre-receive.sample
@@ -0,0 +1,24 @@
+#!/bin/sh
+#
+# An example hook script to make use of push options.
+# The example simply echoes all push options that start with 'echoback='
+# and rejects all pushes when the "reject" push option is used.
+#
+# To enable this hook, rename this file to "pre-receive".
+
+if test -n "$GIT_PUSH_OPTION_COUNT"
+then
+ i=0
+ while test "$i" -lt "$GIT_PUSH_OPTION_COUNT"
+ do
+ eval "value=\$GIT_PUSH_OPTION_$i"
+ case "$value" in
+ echoback=*)
+ echo "echo from the pre-receive-hook: ${value#*=}" >&2
+ ;;
+ reject)
+ exit 1
+ esac
+ i=$((i + 1))
+ done
+fi
diff --git a/effective-java.git/hooks/prepare-commit-msg.sample b/effective-java.git/hooks/prepare-commit-msg.sample
new file mode 100644
index 0000000..10fa14c
--- /dev/null
+++ b/effective-java.git/hooks/prepare-commit-msg.sample
@@ -0,0 +1,42 @@
+#!/bin/sh
+#
+# An example hook script to prepare the commit log message.
+# Called by "git commit" with the name of the file that has the
+# commit message, followed by the description of the commit
+# message's source. The hook's purpose is to edit the commit
+# message file. If the hook fails with a non-zero status,
+# the commit is aborted.
+#
+# To enable this hook, rename this file to "prepare-commit-msg".
+
+# This hook includes three examples. The first one removes the
+# "# Please enter the commit message..." help message.
+#
+# The second includes the output of "git diff --name-status -r"
+# into the message, just before the "git status" output. It is
+# commented because it doesn't cope with --amend or with squashed
+# commits.
+#
+# The third example adds a Signed-off-by line to the message, that can
+# still be edited. This is rarely a good idea.
+
+COMMIT_MSG_FILE=$1
+COMMIT_SOURCE=$2
+SHA1=$3
+
+/usr/bin/perl -i.bak -ne 'print unless(m/^. Please enter the commit message/..m/^#$/)' "$COMMIT_MSG_FILE"
+
+# case "$COMMIT_SOURCE,$SHA1" in
+# ,|template,)
+# /usr/bin/perl -i.bak -pe '
+# print "\n" . `git diff --cached --name-status -r`
+# if /^#/ && $first++ == 0' "$COMMIT_MSG_FILE" ;;
+# *) ;;
+# esac
+
+# SOB=$(git var GIT_COMMITTER_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p')
+# git interpret-trailers --in-place --trailer "$SOB" "$COMMIT_MSG_FILE"
+# if test -z "$COMMIT_SOURCE"
+# then
+# /usr/bin/perl -i.bak -pe 'print "\n" if !$first_line++' "$COMMIT_MSG_FILE"
+# fi
diff --git a/effective-java.git/hooks/push-to-checkout.sample b/effective-java.git/hooks/push-to-checkout.sample
new file mode 100644
index 0000000..af5a0c0
--- /dev/null
+++ b/effective-java.git/hooks/push-to-checkout.sample
@@ -0,0 +1,78 @@
+#!/bin/sh
+
+# An example hook script to update a checked-out tree on a git push.
+#
+# This hook is invoked by git-receive-pack(1) when it reacts to git
+# push and updates reference(s) in its repository, and when the push
+# tries to update the branch that is currently checked out and the
+# receive.denyCurrentBranch configuration variable is set to
+# updateInstead.
+#
+# By default, such a push is refused if the working tree and the index
+# of the remote repository has any difference from the currently
+# checked out commit; when both the working tree and the index match
+# the current commit, they are updated to match the newly pushed tip
+# of the branch. This hook is to be used to override the default
+# behaviour; however the code below reimplements the default behaviour
+# as a starting point for convenient modification.
+#
+# The hook receives the commit with which the tip of the current
+# branch is going to be updated:
+commit=$1
+
+# It can exit with a non-zero status to refuse the push (when it does
+# so, it must not modify the index or the working tree).
+die () {
+ echo >&2 "$*"
+ exit 1
+}
+
+# Or it can make any necessary changes to the working tree and to the
+# index to bring them to the desired state when the tip of the current
+# branch is updated to the new commit, and exit with a zero status.
+#
+# For example, the hook can simply run git read-tree -u -m HEAD "$1"
+# in order to emulate git fetch that is run in the reverse direction
+# with git push, as the two-tree form of git read-tree -u -m is
+# essentially the same as git switch or git checkout that switches
+# branches while keeping the local changes in the working tree that do
+# not interfere with the difference between the branches.
+
+# The below is a more-or-less exact translation to shell of the C code
+# for the default behaviour for git's push-to-checkout hook defined in
+# the push_to_deploy() function in builtin/receive-pack.c.
+#
+# Note that the hook will be executed from the repository directory,
+# not from the working tree, so if you want to perform operations on
+# the working tree, you will have to adapt your code accordingly, e.g.
+# by adding "cd .." or using relative paths.
+
+if ! git update-index -q --ignore-submodules --refresh
+then
+ die "Up-to-date check failed"
+fi
+
+if ! git diff-files --quiet --ignore-submodules --
+then
+ die "Working directory has unstaged changes"
+fi
+
+# This is a rough translation of:
+#
+# head_has_history() ? "HEAD" : EMPTY_TREE_SHA1_HEX
+if git cat-file -e HEAD 2>/dev/null
+then
+ head=HEAD
+else
+ head=$(git hash-object -t tree --stdin &2
+ echo " (if you want, you could supply GIT_DIR then run" >&2
+ echo " $0 )" >&2
+ exit 1
+fi
+
+if [ -z "$refname" -o -z "$oldrev" -o -z "$newrev" ]; then
+ echo "usage: $0 " >&2
+ exit 1
+fi
+
+# --- Config
+allowunannotated=$(git config --type=bool hooks.allowunannotated)
+allowdeletebranch=$(git config --type=bool hooks.allowdeletebranch)
+denycreatebranch=$(git config --type=bool hooks.denycreatebranch)
+allowdeletetag=$(git config --type=bool hooks.allowdeletetag)
+allowmodifytag=$(git config --type=bool hooks.allowmodifytag)
+
+# check for no description
+projectdesc=$(sed -e '1q' "$GIT_DIR/description")
+case "$projectdesc" in
+"Unnamed repository"* | "")
+ echo "*** Project description file hasn't been set" >&2
+ exit 1
+ ;;
+esac
+
+# --- Check types
+# if $newrev is 0000...0000, it's a commit to delete a ref.
+zero=$(git hash-object --stdin &2
+ echo "*** Use 'git tag [ -a | -s ]' for tags you want to propagate." >&2
+ exit 1
+ fi
+ ;;
+ refs/tags/*,delete)
+ # delete tag
+ if [ "$allowdeletetag" != "true" ]; then
+ echo "*** Deleting a tag is not allowed in this repository" >&2
+ exit 1
+ fi
+ ;;
+ refs/tags/*,tag)
+ # annotated tag
+ if [ "$allowmodifytag" != "true" ] && git rev-parse $refname > /dev/null 2>&1
+ then
+ echo "*** Tag '$refname' already exists." >&2
+ echo "*** Modifying a tag is not allowed in this repository." >&2
+ exit 1
+ fi
+ ;;
+ refs/heads/*,commit)
+ # branch
+ if [ "$oldrev" = "$zero" -a "$denycreatebranch" = "true" ]; then
+ echo "*** Creating a branch is not allowed in this repository" >&2
+ exit 1
+ fi
+ ;;
+ refs/heads/*,delete)
+ # delete branch
+ if [ "$allowdeletebranch" != "true" ]; then
+ echo "*** Deleting a branch is not allowed in this repository" >&2
+ exit 1
+ fi
+ ;;
+ refs/remotes/*,commit)
+ # tracking branch
+ ;;
+ refs/remotes/*,delete)
+ # delete tracking branch
+ if [ "$allowdeletebranch" != "true" ]; then
+ echo "*** Deleting a tracking branch is not allowed in this repository" >&2
+ exit 1
+ fi
+ ;;
+ *)
+ # Anything else (is there anything else?)
+ echo "*** Update hook: unknown type of update to ref $refname of type $newrev_type" >&2
+ exit 1
+ ;;
+esac
+
+# --- Finished
+exit 0
diff --git a/effective-java.git/info/exclude b/effective-java.git/info/exclude
new file mode 100644
index 0000000..a5196d1
--- /dev/null
+++ b/effective-java.git/info/exclude
@@ -0,0 +1,6 @@
+# git ls-files --others --exclude-from=.git/info/exclude
+# Lines that start with '#' are comments.
+# For a project mostly in C, the following would be a good set of
+# exclude patterns (uncomment them if you want to use them):
+# *.[oa]
+# *~
diff --git a/effective-java.git/objects/pack/pack-df45e6fb3b48ee1346b65d61b1d9bafc795fd097.idx b/effective-java.git/objects/pack/pack-df45e6fb3b48ee1346b65d61b1d9bafc795fd097.idx
new file mode 100644
index 0000000..73c3a1c
Binary files /dev/null and b/effective-java.git/objects/pack/pack-df45e6fb3b48ee1346b65d61b1d9bafc795fd097.idx differ
diff --git a/effective-java.git/objects/pack/pack-df45e6fb3b48ee1346b65d61b1d9bafc795fd097.pack b/effective-java.git/objects/pack/pack-df45e6fb3b48ee1346b65d61b1d9bafc795fd097.pack
new file mode 100644
index 0000000..3034049
Binary files /dev/null and b/effective-java.git/objects/pack/pack-df45e6fb3b48ee1346b65d61b1d9bafc795fd097.pack differ
diff --git a/effective-java.git/packed-refs b/effective-java.git/packed-refs
new file mode 100644
index 0000000..9bd8a51
--- /dev/null
+++ b/effective-java.git/packed-refs
@@ -0,0 +1,7 @@
+# pack-refs with: peeled fully-peeled sorted
+af592dd06cd89193d952ab8d852fb07082ea77c2 refs/heads/daejin
+af592dd06cd89193d952ab8d852fb07082ea77c2 refs/heads/heejin
+af592dd06cd89193d952ab8d852fb07082ea77c2 refs/heads/jaeho
+a5ee82bb39eb806b0129e0fc0f4103f2aeed3949 refs/heads/main
+af592dd06cd89193d952ab8d852fb07082ea77c2 refs/heads/minjee
+85e1ec02f7daa6772ce41d08f55d9f253e5e9ab7 refs/heads/sungyu
diff --git a/mvnw b/mvnw
new file mode 100755
index 0000000..8a8fb22
--- /dev/null
+++ b/mvnw
@@ -0,0 +1,316 @@
+#!/bin/sh
+# ----------------------------------------------------------------------------
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+# ----------------------------------------------------------------------------
+
+# ----------------------------------------------------------------------------
+# Maven Start Up Batch script
+#
+# Required ENV vars:
+# ------------------
+# JAVA_HOME - location of a JDK home dir
+#
+# Optional ENV vars
+# -----------------
+# M2_HOME - location of maven2's installed home dir
+# MAVEN_OPTS - parameters passed to the Java VM when running Maven
+# e.g. to debug Maven itself, use
+# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
+# MAVEN_SKIP_RC - flag to disable loading of mavenrc files
+# ----------------------------------------------------------------------------
+
+if [ -z "$MAVEN_SKIP_RC" ] ; then
+
+ if [ -f /usr/local/etc/mavenrc ] ; then
+ . /usr/local/etc/mavenrc
+ fi
+
+ if [ -f /etc/mavenrc ] ; then
+ . /etc/mavenrc
+ fi
+
+ if [ -f "$HOME/.mavenrc" ] ; then
+ . "$HOME/.mavenrc"
+ fi
+
+fi
+
+# OS specific support. $var _must_ be set to either true or false.
+cygwin=false;
+darwin=false;
+mingw=false
+case "`uname`" in
+ CYGWIN*) cygwin=true ;;
+ MINGW*) mingw=true;;
+ Darwin*) darwin=true
+ # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
+ # See https://developer.apple.com/library/mac/qa/qa1170/_index.html
+ if [ -z "$JAVA_HOME" ]; then
+ if [ -x "/usr/libexec/java_home" ]; then
+ export JAVA_HOME="`/usr/libexec/java_home`"
+ else
+ export JAVA_HOME="/Library/Java/Home"
+ fi
+ fi
+ ;;
+esac
+
+if [ -z "$JAVA_HOME" ] ; then
+ if [ -r /etc/gentoo-release ] ; then
+ JAVA_HOME=`java-config --jre-home`
+ fi
+fi
+
+if [ -z "$M2_HOME" ] ; then
+ ## resolve links - $0 may be a link to maven's home
+ PRG="$0"
+
+ # need this for relative symlinks
+ while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG="`dirname "$PRG"`/$link"
+ fi
+ done
+
+ saveddir=`pwd`
+
+ M2_HOME=`dirname "$PRG"`/..
+
+ # make it fully qualified
+ M2_HOME=`cd "$M2_HOME" && pwd`
+
+ cd "$saveddir"
+ # echo Using m2 at $M2_HOME
+fi
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched
+if $cygwin ; then
+ [ -n "$M2_HOME" ] &&
+ M2_HOME=`cygpath --unix "$M2_HOME"`
+ [ -n "$JAVA_HOME" ] &&
+ JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
+ [ -n "$CLASSPATH" ] &&
+ CLASSPATH=`cygpath --path --unix "$CLASSPATH"`
+fi
+
+# For Mingw, ensure paths are in UNIX format before anything is touched
+if $mingw ; then
+ [ -n "$M2_HOME" ] &&
+ M2_HOME="`(cd "$M2_HOME"; pwd)`"
+ [ -n "$JAVA_HOME" ] &&
+ JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
+fi
+
+if [ -z "$JAVA_HOME" ]; then
+ javaExecutable="`which javac`"
+ if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then
+ # readlink(1) is not available as standard on Solaris 10.
+ readLink=`which readlink`
+ if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then
+ if $darwin ; then
+ javaHome="`dirname \"$javaExecutable\"`"
+ javaExecutable="`cd \"$javaHome\" && pwd -P`/javac"
+ else
+ javaExecutable="`readlink -f \"$javaExecutable\"`"
+ fi
+ javaHome="`dirname \"$javaExecutable\"`"
+ javaHome=`expr "$javaHome" : '\(.*\)/bin'`
+ JAVA_HOME="$javaHome"
+ export JAVA_HOME
+ fi
+ fi
+fi
+
+if [ -z "$JAVACMD" ] ; then
+ if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ else
+ JAVACMD="`\\unset -f command; \\command -v java`"
+ fi
+fi
+
+if [ ! -x "$JAVACMD" ] ; then
+ echo "Error: JAVA_HOME is not defined correctly." >&2
+ echo " We cannot execute $JAVACMD" >&2
+ exit 1
+fi
+
+if [ -z "$JAVA_HOME" ] ; then
+ echo "Warning: JAVA_HOME environment variable is not set."
+fi
+
+CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher
+
+# traverses directory structure from process work directory to filesystem root
+# first directory with .mvn subdirectory is considered project base directory
+find_maven_basedir() {
+
+ if [ -z "$1" ]
+ then
+ echo "Path not specified to find_maven_basedir"
+ return 1
+ fi
+
+ basedir="$1"
+ wdir="$1"
+ while [ "$wdir" != '/' ] ; do
+ if [ -d "$wdir"/.mvn ] ; then
+ basedir=$wdir
+ break
+ fi
+ # workaround for JBEAP-8937 (on Solaris 10/Sparc)
+ if [ -d "${wdir}" ]; then
+ wdir=`cd "$wdir/.."; pwd`
+ fi
+ # end of workaround
+ done
+ echo "${basedir}"
+}
+
+# concatenates all lines of a file
+concat_lines() {
+ if [ -f "$1" ]; then
+ echo "$(tr -s '\n' ' ' < "$1")"
+ fi
+}
+
+BASE_DIR=`find_maven_basedir "$(pwd)"`
+if [ -z "$BASE_DIR" ]; then
+ exit 1;
+fi
+
+##########################################################################################
+# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
+# This allows using the maven wrapper in projects that prohibit checking in binary data.
+##########################################################################################
+if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Found .mvn/wrapper/maven-wrapper.jar"
+ fi
+else
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..."
+ fi
+ if [ -n "$MVNW_REPOURL" ]; then
+ jarUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar"
+ else
+ jarUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar"
+ fi
+ while IFS="=" read key value; do
+ case "$key" in (wrapperUrl) jarUrl="$value"; break ;;
+ esac
+ done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties"
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Downloading from: $jarUrl"
+ fi
+ wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar"
+ if $cygwin; then
+ wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"`
+ fi
+
+ if command -v wget > /dev/null; then
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Found wget ... using wget"
+ fi
+ if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
+ wget "$jarUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath"
+ else
+ wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath"
+ fi
+ elif command -v curl > /dev/null; then
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Found curl ... using curl"
+ fi
+ if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
+ curl -o "$wrapperJarPath" "$jarUrl" -f
+ else
+ curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f
+ fi
+
+ else
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Falling back to using Java to download"
+ fi
+ javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java"
+ # For Cygwin, switch paths to Windows format before running javac
+ if $cygwin; then
+ javaClass=`cygpath --path --windows "$javaClass"`
+ fi
+ if [ -e "$javaClass" ]; then
+ if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo " - Compiling MavenWrapperDownloader.java ..."
+ fi
+ # Compiling the Java class
+ ("$JAVA_HOME/bin/javac" "$javaClass")
+ fi
+ if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
+ # Running the downloader
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo " - Running MavenWrapperDownloader.java ..."
+ fi
+ ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR")
+ fi
+ fi
+ fi
+fi
+##########################################################################################
+# End of extension
+##########################################################################################
+
+export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}
+if [ "$MVNW_VERBOSE" = true ]; then
+ echo $MAVEN_PROJECTBASEDIR
+fi
+MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin; then
+ [ -n "$M2_HOME" ] &&
+ M2_HOME=`cygpath --path --windows "$M2_HOME"`
+ [ -n "$JAVA_HOME" ] &&
+ JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
+ [ -n "$CLASSPATH" ] &&
+ CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
+ [ -n "$MAVEN_PROJECTBASEDIR" ] &&
+ MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"`
+fi
+
+# Provide a "standardized" way to retrieve the CLI args that will
+# work with both Windows and non-Windows executions.
+MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@"
+export MAVEN_CMD_LINE_ARGS
+
+WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
+
+exec "$JAVACMD" \
+ $MAVEN_OPTS \
+ $MAVEN_DEBUG_OPTS \
+ -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
+ "-Dmaven.home=${M2_HOME}" \
+ "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
+ ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"
diff --git a/mvnw.cmd b/mvnw.cmd
new file mode 100644
index 0000000..1d8ab01
--- /dev/null
+++ b/mvnw.cmd
@@ -0,0 +1,188 @@
+@REM ----------------------------------------------------------------------------
+@REM Licensed to the Apache Software Foundation (ASF) under one
+@REM or more contributor license agreements. See the NOTICE file
+@REM distributed with this work for additional information
+@REM regarding copyright ownership. The ASF licenses this file
+@REM to you under the Apache License, Version 2.0 (the
+@REM "License"); you may not use this file except in compliance
+@REM with the License. You may obtain a copy of the License at
+@REM
+@REM https://www.apache.org/licenses/LICENSE-2.0
+@REM
+@REM Unless required by applicable law or agreed to in writing,
+@REM software distributed under the License is distributed on an
+@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+@REM KIND, either express or implied. See the License for the
+@REM specific language governing permissions and limitations
+@REM under the License.
+@REM ----------------------------------------------------------------------------
+
+@REM ----------------------------------------------------------------------------
+@REM Maven Start Up Batch script
+@REM
+@REM Required ENV vars:
+@REM JAVA_HOME - location of a JDK home dir
+@REM
+@REM Optional ENV vars
+@REM M2_HOME - location of maven2's installed home dir
+@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
+@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending
+@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
+@REM e.g. to debug Maven itself, use
+@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
+@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
+@REM ----------------------------------------------------------------------------
+
+@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
+@echo off
+@REM set title of command window
+title %0
+@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on'
+@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
+
+@REM set %HOME% to equivalent of $HOME
+if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
+
+@REM Execute a user defined script before this one
+if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
+@REM check for pre script, once with legacy .bat ending and once with .cmd ending
+if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %*
+if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %*
+:skipRcPre
+
+@setlocal
+
+set ERROR_CODE=0
+
+@REM To isolate internal variables from possible post scripts, we use another setlocal
+@setlocal
+
+@REM ==== START VALIDATION ====
+if not "%JAVA_HOME%" == "" goto OkJHome
+
+echo.
+echo Error: JAVA_HOME not found in your environment. >&2
+echo Please set the JAVA_HOME variable in your environment to match the >&2
+echo location of your Java installation. >&2
+echo.
+goto error
+
+:OkJHome
+if exist "%JAVA_HOME%\bin\java.exe" goto init
+
+echo.
+echo Error: JAVA_HOME is set to an invalid directory. >&2
+echo JAVA_HOME = "%JAVA_HOME%" >&2
+echo Please set the JAVA_HOME variable in your environment to match the >&2
+echo location of your Java installation. >&2
+echo.
+goto error
+
+@REM ==== END VALIDATION ====
+
+:init
+
+@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
+@REM Fallback to current working directory if not found.
+
+set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
+IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
+
+set EXEC_DIR=%CD%
+set WDIR=%EXEC_DIR%
+:findBaseDir
+IF EXIST "%WDIR%"\.mvn goto baseDirFound
+cd ..
+IF "%WDIR%"=="%CD%" goto baseDirNotFound
+set WDIR=%CD%
+goto findBaseDir
+
+:baseDirFound
+set MAVEN_PROJECTBASEDIR=%WDIR%
+cd "%EXEC_DIR%"
+goto endDetectBaseDir
+
+:baseDirNotFound
+set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
+cd "%EXEC_DIR%"
+
+:endDetectBaseDir
+
+IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
+
+@setlocal EnableExtensions EnableDelayedExpansion
+for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
+@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
+
+:endReadAdditionalConfig
+
+SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
+set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
+set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
+
+set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar"
+
+FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
+ IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B
+)
+
+@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
+@REM This allows using the maven wrapper in projects that prohibit checking in binary data.
+if exist %WRAPPER_JAR% (
+ if "%MVNW_VERBOSE%" == "true" (
+ echo Found %WRAPPER_JAR%
+ )
+) else (
+ if not "%MVNW_REPOURL%" == "" (
+ SET DOWNLOAD_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar"
+ )
+ if "%MVNW_VERBOSE%" == "true" (
+ echo Couldn't find %WRAPPER_JAR%, downloading it ...
+ echo Downloading from: %DOWNLOAD_URL%
+ )
+
+ powershell -Command "&{"^
+ "$webclient = new-object System.Net.WebClient;"^
+ "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^
+ "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^
+ "}"^
+ "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^
+ "}"
+ if "%MVNW_VERBOSE%" == "true" (
+ echo Finished downloading %WRAPPER_JAR%
+ )
+)
+@REM End of extension
+
+@REM Provide a "standardized" way to retrieve the CLI args that will
+@REM work with both Windows and non-Windows executions.
+set MAVEN_CMD_LINE_ARGS=%*
+
+%MAVEN_JAVA_EXE% ^
+ %JVM_CONFIG_MAVEN_PROPS% ^
+ %MAVEN_OPTS% ^
+ %MAVEN_DEBUG_OPTS% ^
+ -classpath %WRAPPER_JAR% ^
+ "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^
+ %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
+if ERRORLEVEL 1 goto error
+goto end
+
+:error
+set ERROR_CODE=1
+
+:end
+@endlocal & set ERROR_CODE=%ERROR_CODE%
+
+if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost
+@REM check for post script, once with legacy .bat ending and once with .cmd ending
+if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat"
+if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd"
+:skipRcPost
+
+@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
+if "%MAVEN_BATCH_PAUSE%"=="on" pause
+
+if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE%
+
+cmd /C exit /B %ERROR_CODE%
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..a5b1c70
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,69 @@
+
+
+ 4.0.0
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 2.6.3
+
+
+ me.whiteship
+ effective-java-part1
+ 0.0.1-SNAPSHOT
+ effective-java-part1
+ effective-java-part1
+
+ 11
+
+
+
+
+ com.google.guava
+ guava
+ 31.1-jre
+
+
+ com.google.auto.value
+ auto-value-annotations
+ 1.9
+
+
+ com.google.auto.value
+ auto-value
+ 1.9
+
+
+
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter
+
+
+
+ org.projectlombok
+ lombok
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+ 2.19.1
+
+
+
+
diff --git a/src/main/java/me/whiteship/chapter01/item01/ActionEnum.java b/src/main/java/me/whiteship/chapter01/item01/ActionEnum.java
new file mode 100644
index 0000000..4a13404
--- /dev/null
+++ b/src/main/java/me/whiteship/chapter01/item01/ActionEnum.java
@@ -0,0 +1,11 @@
+package me.whiteship.chapter01.item01;
+
+public enum ActionEnum {
+ ACTION_1(new App())
+ , ACTION_2(new App());
+
+ private final App action;
+
+ ActionEnum(App action) {this.action = action;}
+
+}
\ No newline at end of file
diff --git a/src/main/java/me/whiteship/chapter01/item01/AdvancedSettings.java b/src/main/java/me/whiteship/chapter01/item01/AdvancedSettings.java
new file mode 100644
index 0000000..fbe7028
--- /dev/null
+++ b/src/main/java/me/whiteship/chapter01/item01/AdvancedSettings.java
@@ -0,0 +1,8 @@
+package me.whiteship.chapter01.item01;
+
+public class AdvancedSettings {
+
+ Settings settings;
+
+
+}
diff --git a/src/main/java/me/whiteship/chapter01/item01/App.java b/src/main/java/me/whiteship/chapter01/item01/App.java
new file mode 100644
index 0000000..af910d4
--- /dev/null
+++ b/src/main/java/me/whiteship/chapter01/item01/App.java
@@ -0,0 +1,13 @@
+package me.whiteship.chapter01.item01;
+
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.annotation.AnnotationConfigApplicationContext;
+
+public class App {
+
+ public static void main(String[] args) {
+ ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
+ HelloService helloService = applicationContext.getBean(HelloService.class);
+ System.out.println(helloService.hello());
+ }
+}
diff --git a/src/main/java/me/whiteship/chapter01/item01/AppConfig.java b/src/main/java/me/whiteship/chapter01/item01/AppConfig.java
new file mode 100644
index 0000000..b7cff42
--- /dev/null
+++ b/src/main/java/me/whiteship/chapter01/item01/AppConfig.java
@@ -0,0 +1,15 @@
+package me.whiteship.chapter01.item01;
+
+//import me.whiteship.hello.ChineseHelloService;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+public class AppConfig {
+//
+// @Bean
+// public HelloService helloService() {
+// return new ChineseHelloService();
+// }
+
+}
diff --git a/src/main/java/me/whiteship/chapter01/item01/Difficulty.java b/src/main/java/me/whiteship/chapter01/item01/Difficulty.java
new file mode 100644
index 0000000..bb7c753
--- /dev/null
+++ b/src/main/java/me/whiteship/chapter01/item01/Difficulty.java
@@ -0,0 +1,6 @@
+package me.whiteship.chapter01.item01;
+
+public enum Difficulty {
+
+ EASY, NORMAL, HARD, HELL
+}
diff --git a/src/main/java/me/whiteship/chapter01/item01/HelloService.java b/src/main/java/me/whiteship/chapter01/item01/HelloService.java
new file mode 100644
index 0000000..9d8b45b
--- /dev/null
+++ b/src/main/java/me/whiteship/chapter01/item01/HelloService.java
@@ -0,0 +1,28 @@
+package me.whiteship.chapter01.item01;
+
+public interface HelloService {
+
+ String hello();
+
+ static String hi() {
+ prepareMessage();
+ return "hi";
+ }
+
+ static private void prepareMessage() {
+ }
+
+ static String hi1() {
+ prepareMessage();
+ return "hi";
+ }
+
+ static String hi2() {
+ prepareMessage();
+ return "hi";
+ }
+
+ default String bye() {
+ return "bye";
+ }
+}
diff --git a/src/main/java/me/whiteship/chapter01/item01/HelloServiceFactory.java b/src/main/java/me/whiteship/chapter01/item01/HelloServiceFactory.java
new file mode 100644
index 0000000..a552e61
--- /dev/null
+++ b/src/main/java/me/whiteship/chapter01/item01/HelloServiceFactory.java
@@ -0,0 +1,28 @@
+package me.whiteship.chapter01.item01;
+
+//import me.whiteship.hello.ChineseHelloService;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.util.Optional;
+import java.util.ServiceLoader;
+
+public class HelloServiceFactory {
+
+ public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
+ ServiceLoader loader = ServiceLoader.load(HelloService.class);
+ Optional helloServiceOptional = loader.findFirst();
+ helloServiceOptional.ifPresent(h -> {
+ System.out.println(h.hello());
+ });
+
+// HelloService helloService = new ChineseHelloService();
+// System.out.println(helloService.hello());
+
+// Class> aClass = Class.forName("me.whiteship.hello.ChineseHelloService");
+// Constructor> constructor = aClass.getConstructor();
+// HelloService helloService = (HelloService) constructor.newInstance();
+// System.out.println(helloService.hello());
+ }
+
+}
diff --git a/src/main/java/me/whiteship/chapter01/item01/ListQuiz.java b/src/main/java/me/whiteship/chapter01/item01/ListQuiz.java
new file mode 100644
index 0000000..5c3a549
--- /dev/null
+++ b/src/main/java/me/whiteship/chapter01/item01/ListQuiz.java
@@ -0,0 +1,24 @@
+package me.whiteship.chapter01.item01;
+
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+
+public class ListQuiz {
+
+ public static void main(String[] args) {
+ List numbers = new ArrayList();
+ numbers.add(100);
+ numbers.add(20);
+ numbers.add(44);
+ numbers.add(3);
+
+ System.out.println(numbers);
+
+ Comparator desc = (o1, o2) -> o2 - o1;
+
+ numbers.sort(desc.reversed());
+
+ System.out.println(numbers);
+ }
+}
diff --git a/src/main/java/me/whiteship/chapter01/item01/Order.java b/src/main/java/me/whiteship/chapter01/item01/Order.java
new file mode 100644
index 0000000..6c959ea
--- /dev/null
+++ b/src/main/java/me/whiteship/chapter01/item01/Order.java
@@ -0,0 +1,38 @@
+package me.whiteship.chapter01.item01;
+
+import java.util.*;
+
+public class Order {
+
+ private boolean prime;
+
+ private boolean urgent;
+
+ private Product product;
+
+ private OrderStatus orderStatus;
+
+ public static Order primeOrder(Product product) {
+ Order order = new Order();
+ order.prime = true;
+ order.product = product;
+
+ return order;
+ }
+
+ public static Order urgentOrder(Product product) {
+ Order order = new Order();
+ order.urgent = true;
+ order.product = product;
+ return order;
+ }
+
+ public static void main(String[] args) {
+
+ Order order = new Order();
+ if (order.orderStatus == OrderStatus.DELIVERED) {
+ System.out.println("delivered");
+ }
+ }
+
+}
diff --git a/src/main/java/me/whiteship/chapter01/item01/OrderStatus.java b/src/main/java/me/whiteship/chapter01/item01/OrderStatus.java
new file mode 100644
index 0000000..785b204
--- /dev/null
+++ b/src/main/java/me/whiteship/chapter01/item01/OrderStatus.java
@@ -0,0 +1,12 @@
+package me.whiteship.chapter01.item01;
+
+public enum OrderStatus {
+
+ PREPARING(0), SHIPPED(1), DELIVERING(2), DELIVERED(3);
+
+ private int number;
+
+ OrderStatus(int number) {
+ this.number = number;
+ }
+}
diff --git a/src/main/java/me/whiteship/chapter01/item01/Product.java b/src/main/java/me/whiteship/chapter01/item01/Product.java
new file mode 100644
index 0000000..7891629
--- /dev/null
+++ b/src/main/java/me/whiteship/chapter01/item01/Product.java
@@ -0,0 +1,18 @@
+package me.whiteship.chapter01.item01;
+
+import java.util.EnumSet;
+import java.util.Set;
+
+public class Product {
+
+ public static void main(String[] args) {
+ Settings settings1 = Settings.getInstance();
+ Settings settings2 = Settings.getInstance();
+
+ System.out.println(settings1);
+ System.out.println(settings2);
+
+ Boolean.valueOf(false);
+ EnumSet.allOf(Difficulty.class);
+ }
+}
diff --git a/src/main/java/me/whiteship/chapter01/item01/Settings.java b/src/main/java/me/whiteship/chapter01/item01/Settings.java
new file mode 100644
index 0000000..a5566ba
--- /dev/null
+++ b/src/main/java/me/whiteship/chapter01/item01/Settings.java
@@ -0,0 +1,23 @@
+package me.whiteship.chapter01.item01;
+
+/**
+ * 이 클래스의 인스턴스는 #getInstance()를 통해 사용한다.
+ * @see #getInstance()
+ */
+public class Settings {
+
+ private boolean useAutoSteering;
+
+ private boolean useABS;
+
+ private Difficulty difficulty;
+
+ private Settings() {}
+
+ private static final Settings SETTINGS = new Settings();
+
+ public static Settings getInstance() {
+ return SETTINGS;
+ }
+
+}
diff --git a/src/main/java/me/whiteship/chapter01/item02/builder/BuilderTest.java b/src/main/java/me/whiteship/chapter01/item02/builder/BuilderTest.java
new file mode 100644
index 0000000..f505b26
--- /dev/null
+++ b/src/main/java/me/whiteship/chapter01/item02/builder/BuilderTest.java
@@ -0,0 +1,10 @@
+package me.whiteship.chapter01.item02.builder;
+
+public class BuilderTest {
+
+ public static void main(String[] args) {
+ new NutritionFacts.Builder(10, 10)
+ .calories(10)
+ .build();
+ }
+}
diff --git a/src/main/java/me/whiteship/chapter01/item02/builder/NutritionFacts.java b/src/main/java/me/whiteship/chapter01/item02/builder/NutritionFacts.java
new file mode 100644
index 0000000..78d00c4
--- /dev/null
+++ b/src/main/java/me/whiteship/chapter01/item02/builder/NutritionFacts.java
@@ -0,0 +1,57 @@
+package me.whiteship.chapter01.item02.builder;
+
+// 코드 2-3 빌더 패턴 - 점층적 생성자 패턴과 자바빈즈 패턴의 장점만 취했다. (17~18쪽)
+public class NutritionFacts {
+ private final int servingSize;
+ private final int servings;
+ private final int calories;
+ private final int fat;
+ private final int sodium;
+ private final int carbohydrate;
+
+ public static void main(String[] args) {
+ NutritionFacts cocaCola = new Builder(240, 8)
+ .calories(100)
+ .sodium(35)
+ .carbohydrate(27).build();
+ }
+
+ public static class Builder {
+ // 필수 매개변수
+ private final int servingSize;
+ private final int servings;
+
+ // 선택 매개변수 - 기본값으로 초기화한다.
+ private int calories = 0;
+ private int fat = 0;
+ private int sodium = 0;
+ private int carbohydrate = 0;
+
+ public Builder(int servingSize, int servings) {
+ this.servingSize = servingSize;
+ this.servings = servings;
+ }
+
+ public Builder calories(int val)
+ { calories = val; return this; }
+ public Builder fat(int val)
+ { fat = val; return this; }
+ public Builder sodium(int val)
+ { sodium = val; return this; }
+ public Builder carbohydrate(int val)
+ { carbohydrate = val; return this; }
+
+ public NutritionFacts build() {
+ return new NutritionFacts(this);
+ }
+ }
+
+ private NutritionFacts(Builder builder) {
+ servingSize = builder.servingSize;
+ servings = builder.servings;
+ calories = builder.calories;
+ fat = builder.fat;
+ sodium = builder.sodium;
+ carbohydrate = builder.carbohydrate;
+ }
+}
diff --git a/src/main/java/me/whiteship/chapter01/item02/freeze/FreezeTest.js b/src/main/java/me/whiteship/chapter01/item02/freeze/FreezeTest.js
new file mode 100644
index 0000000..8991197
--- /dev/null
+++ b/src/main/java/me/whiteship/chapter01/item02/freeze/FreezeTest.js
@@ -0,0 +1,10 @@
+const keesun = {
+ 'name': 'Keesun',
+ 'age': 40
+};
+
+Object.freeze(keesun);
+
+keesun.kids = ["서연"];
+
+console.info(keesun.name);
\ No newline at end of file
diff --git a/src/main/java/me/whiteship/chapter01/item02/freeze/Person.java b/src/main/java/me/whiteship/chapter01/item02/freeze/Person.java
new file mode 100644
index 0000000..3d1781e
--- /dev/null
+++ b/src/main/java/me/whiteship/chapter01/item02/freeze/Person.java
@@ -0,0 +1,24 @@
+package me.whiteship.chapter01.item02.freeze;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class Person {
+
+ private final String name;
+
+ private final int birthYear;
+
+ private final List kids;
+
+ public Person(String name, int birthYear) {
+ this.name = name;
+ this.birthYear = birthYear;
+ this.kids = new ArrayList<>();
+ }
+
+ public static void main(String[] args) {
+ Person person = new Person("keesun", 1982);
+
+ }
+}
diff --git a/src/main/java/me/whiteship/chapter01/item02/hierarchicalbuilder/Calzone.java b/src/main/java/me/whiteship/chapter01/item02/hierarchicalbuilder/Calzone.java
new file mode 100644
index 0000000..5ef1df5
--- /dev/null
+++ b/src/main/java/me/whiteship/chapter01/item02/hierarchicalbuilder/Calzone.java
@@ -0,0 +1,31 @@
+package me.whiteship.chapter01.item02.hierarchicalbuilder;
+
+// 코드 2-6 칼초네 피자 - 계층적 빌더를 활용한 하위 클래스 (20~21쪽)
+public class Calzone extends Pizza {
+ private final boolean sauceInside;
+
+ public static class Builder extends Pizza.Builder {
+ private boolean sauceInside = false; // 기본값
+
+ public Builder sauceInside() {
+ sauceInside = true;
+ return this;
+ }
+
+ @Override public Calzone build() {
+ return new Calzone(this);
+ }
+
+ @Override protected Builder self() { return this; }
+ }
+
+ private Calzone(Builder builder) {
+ super(builder);
+ sauceInside = builder.sauceInside;
+ }
+
+ @Override public String toString() {
+ return String.format("%s로 토핑한 칼초네 피자 (소스는 %s에)",
+ toppings, sauceInside ? "안" : "바깥");
+ }
+}
diff --git a/src/main/java/me/whiteship/chapter01/item02/hierarchicalbuilder/NyPizza.java b/src/main/java/me/whiteship/chapter01/item02/hierarchicalbuilder/NyPizza.java
new file mode 100644
index 0000000..b0f04c1
--- /dev/null
+++ b/src/main/java/me/whiteship/chapter01/item02/hierarchicalbuilder/NyPizza.java
@@ -0,0 +1,32 @@
+package me.whiteship.chapter01.item02.hierarchicalbuilder;
+
+import java.util.Objects;
+
+// 코드 2-5 뉴욕 피자 - 계층적 빌더를 활용한 하위 클래스 (20쪽)
+public class NyPizza extends Pizza {
+ public enum Size { SMALL, MEDIUM, LARGE }
+ private final Size size;
+
+ public static class Builder extends Pizza.Builder {
+ private final Size size;
+
+ public Builder(Size size) {
+ this.size = Objects.requireNonNull(size);
+ }
+
+ @Override public NyPizza build() {
+ return new NyPizza(this);
+ }
+
+ @Override protected Builder self() { return this; }
+ }
+
+ private NyPizza(Builder builder) {
+ super(builder);
+ size = builder.size;
+ }
+
+ @Override public String toString() {
+ return toppings + "로 토핑한 뉴욕 피자";
+ }
+}
diff --git a/src/main/java/me/whiteship/chapter01/item02/hierarchicalbuilder/Pizza.java b/src/main/java/me/whiteship/chapter01/item02/hierarchicalbuilder/Pizza.java
new file mode 100644
index 0000000..edb3dd0
--- /dev/null
+++ b/src/main/java/me/whiteship/chapter01/item02/hierarchicalbuilder/Pizza.java
@@ -0,0 +1,33 @@
+package me.whiteship.chapter01.item02.hierarchicalbuilder;
+
+import java.util.EnumSet;
+import java.util.Objects;
+import java.util.Set;
+
+// 코드 2-4 계층적으로 설계된 클래스와 잘 어울리는 빌더 패턴 (19쪽)
+
+// 참고: 여기서 사용한 '시뮬레이트한 셀프 타입(simulated self-type)' 관용구는
+// 빌더뿐 아니라 임의의 유동적인 계층구조를 허용한다.
+
+public abstract class Pizza {
+ public enum Topping { HAM, MUSHROOM, ONION, PEPPER, SAUSAGE }
+ final Set toppings;
+
+ abstract static class Builder> {
+ EnumSet toppings = EnumSet.noneOf(Topping.class);
+ public T addTopping(Topping topping) {
+ toppings.add(Objects.requireNonNull(topping));
+ return self();
+ }
+
+ abstract Pizza build();
+
+ // 하위 클래스는 이 메서드를 재정의(overriding)하여
+ // "this"를 반환하도록 해야 한다.
+ protected abstract T self();
+ }
+
+ Pizza(Builder> builder) {
+ toppings = builder.toppings.clone(); // 아이템 50 참조
+ }
+}
diff --git a/src/main/java/me/whiteship/chapter01/item02/hierarchicalbuilder/PizzaTest.java b/src/main/java/me/whiteship/chapter01/item02/hierarchicalbuilder/PizzaTest.java
new file mode 100644
index 0000000..797365e
--- /dev/null
+++ b/src/main/java/me/whiteship/chapter01/item02/hierarchicalbuilder/PizzaTest.java
@@ -0,0 +1,20 @@
+package me.whiteship.chapter01.item02.hierarchicalbuilder;
+
+
+import static me.whiteship.chapter01.item02.hierarchicalbuilder.NyPizza.Size.SMALL;
+import static me.whiteship.chapter01.item02.hierarchicalbuilder.Pizza.Topping.*;
+
+// 계층적 빌더 사용 (21쪽)
+public class PizzaTest {
+ public static void main(String[] args) {
+ NyPizza pizza = new NyPizza.Builder(SMALL)
+ .addTopping(SAUSAGE)
+ .addTopping(ONION).build();
+
+ Calzone calzone = new Calzone.Builder()
+ .addTopping(HAM).sauceInside().build();
+
+ System.out.println(pizza);
+ System.out.println(calzone);
+ }
+}
diff --git a/src/main/java/me/whiteship/chapter01/item02/illegalargumentexception/Order.java b/src/main/java/me/whiteship/chapter01/item02/illegalargumentexception/Order.java
new file mode 100644
index 0000000..35ac437
--- /dev/null
+++ b/src/main/java/me/whiteship/chapter01/item02/illegalargumentexception/Order.java
@@ -0,0 +1,20 @@
+package me.whiteship.chapter01.item02.illegalargumentexception;
+
+import java.time.LocalDate;
+
+public class Order {
+
+ public void updateDeliveryDate(LocalDate deliveryDate) {
+ if (deliveryDate == null) {
+ throw new NullPointerException("deliveryDate can't be null");
+ }
+
+ if (deliveryDate.isBefore(LocalDate.now())) {
+ //TODO 과거로 배송 해달라고??
+ throw new IllegalArgumentException("deliveryDate can't be earlier than " + LocalDate.now());
+ }
+
+ // 배송 날짜 업데이트
+ }
+
+}
diff --git a/src/main/java/me/whiteship/chapter01/item02/javabeans/NutritionFacts.java b/src/main/java/me/whiteship/chapter01/item02/javabeans/NutritionFacts.java
new file mode 100644
index 0000000..9e829e8
--- /dev/null
+++ b/src/main/java/me/whiteship/chapter01/item02/javabeans/NutritionFacts.java
@@ -0,0 +1,53 @@
+package me.whiteship.chapter01.item02.javabeans;
+
+// 코드 2-2 자바빈즈 패턴 - 일관성이 깨지고, 불변으로 만들 수 없다. (16쪽)
+public class NutritionFacts {
+ // 필드 (기본값이 있다면) 기본값으로 초기화된다.
+ private int servingSize = -1; // 필수; 기본값 없음
+ private int servings = -1; // 필수; 기본값 없음
+ private int calories = 0;
+ private int fat = 0;
+ private int sodium = 0;
+ private int carbohydrate = 0;
+ private boolean healthy;
+
+ public NutritionFacts() { }
+
+ public void setServingSize(int servingSize) {
+ this.servingSize = servingSize;
+ }
+
+ public void setServings(int servings) {
+ this.servings = servings;
+ }
+
+ public void setCalories(int calories) {
+ this.calories = calories;
+ }
+
+ public void setFat(int fat) {
+ this.fat = fat;
+ }
+
+ public void setSodium(int sodium) {
+ this.sodium = sodium;
+ }
+
+ public void setCarbohydrate(int carbohydrate) {
+ this.carbohydrate = carbohydrate;
+ }
+
+ public void setHealthy(boolean healthy) {
+ this.healthy = healthy;
+ }
+
+ public static void main(String[] args) {
+ NutritionFacts cocaCola = new NutritionFacts();
+ cocaCola.setServingSize(240);
+ cocaCola.setServings(8);
+
+ cocaCola.setCalories(100);
+ cocaCola.setSodium(35);
+ cocaCola.setCarbohydrate(27);
+ }
+}
diff --git a/src/main/java/me/whiteship/chapter01/item02/telescopingconstructor/NutritionFacts.java b/src/main/java/me/whiteship/chapter01/item02/telescopingconstructor/NutritionFacts.java
new file mode 100644
index 0000000..e9bc38f
--- /dev/null
+++ b/src/main/java/me/whiteship/chapter01/item02/telescopingconstructor/NutritionFacts.java
@@ -0,0 +1,46 @@
+package me.whiteship.chapter01.item02.telescopingconstructor;
+
+// 코드 2-1 점층적 생성자 패턴 - 확장하기 어렵다! (14~15쪽)
+public class NutritionFacts {
+ private final int servingSize; // (mL, 1회 제공량) 필수
+ private final int servings; // (회, 총 n회 제공량) 필수
+ private final int calories; // (1회 제공량당) 선택
+ private final int fat; // (g/1회 제공량) 선택
+ private final int sodium; // (mg/1회 제공량) 선택
+ private final int carbohydrate; // (g/1회 제공량) 선택
+
+ public NutritionFacts(int servingSize, int servings) {
+ this(servingSize, servings, 0);
+ }
+
+ public NutritionFacts(int servingSize, int servings,
+ int calories) {
+ this(servingSize, servings, calories, 0);
+ }
+
+ public NutritionFacts(int servingSize, int servings,
+ int calories, int fat) {
+ this(servingSize, servings, calories, fat, 0);
+ }
+
+ public NutritionFacts(int servingSize, int servings,
+ int calories, int fat, int sodium) {
+ this(servingSize, servings, calories, fat, sodium, 0);
+ }
+
+ public NutritionFacts(int servingSize, int servings,
+ int calories, int fat, int sodium, int carbohydrate) {
+ this.servingSize = servingSize;
+ this.servings = servings;
+ this.calories = calories;
+ this.fat = fat;
+ this.sodium = sodium;
+ this.carbohydrate = carbohydrate;
+ }
+
+ public static void main(String[] args) {
+ NutritionFacts cocaCola =
+ new NutritionFacts(10, 10);
+ }
+
+}
diff --git a/src/main/java/me/whiteship/chapter01/item02/varargs/VarargsSamples.java b/src/main/java/me/whiteship/chapter01/item02/varargs/VarargsSamples.java
new file mode 100644
index 0000000..692780f
--- /dev/null
+++ b/src/main/java/me/whiteship/chapter01/item02/varargs/VarargsSamples.java
@@ -0,0 +1,17 @@
+package me.whiteship.chapter01.item02.varargs;
+
+import java.util.Arrays;
+
+public class VarargsSamples {
+
+ public void printNumbers(int... numbers) {
+ System.out.println(numbers.getClass().getCanonicalName());
+ System.out.println(numbers.getClass().getComponentType());
+ Arrays.stream(numbers).forEach(System.out::println);
+ }
+
+ public static void main(String[] args) {
+ VarargsSamples samples = new VarargsSamples();
+ samples.printNumbers(1, 20, 20, 39, 59);
+ }
+}
diff --git a/src/main/java/me/whiteship/chapter01/item03/enumtype/Elvis.java b/src/main/java/me/whiteship/chapter01/item03/enumtype/Elvis.java
new file mode 100644
index 0000000..2162896
--- /dev/null
+++ b/src/main/java/me/whiteship/chapter01/item03/enumtype/Elvis.java
@@ -0,0 +1,16 @@
+package me.whiteship.chapter01.item03.enumtype;
+
+// 열거 타입 방식의 싱글턴 - 바람직한 방법 (25쪽)
+public enum Elvis {
+ INSTANCE;
+
+ public void leaveTheBuilding() {
+ System.out.println("기다려 자기야, 지금 나갈께!");
+ }
+
+ // 이 메서드는 보통 클래스 바깥(다른 클래스)에 작성해야 한다!
+ public static void main(String[] args) {
+ Elvis elvis = Elvis.INSTANCE;
+ elvis.leaveTheBuilding();
+ }
+}
diff --git a/src/main/java/me/whiteship/chapter01/item03/enumtype/EnumElvisReflection.java b/src/main/java/me/whiteship/chapter01/item03/enumtype/EnumElvisReflection.java
new file mode 100644
index 0000000..67cffdf
--- /dev/null
+++ b/src/main/java/me/whiteship/chapter01/item03/enumtype/EnumElvisReflection.java
@@ -0,0 +1,15 @@
+package me.whiteship.chapter01.item03.enumtype;
+
+import java.lang.reflect.Constructor;
+
+public class EnumElvisReflection {
+
+ public static void main(String[] args) {
+ try {
+ Constructor declaredConstructor = Elvis.class.getDeclaredConstructor();
+ System.out.println(declaredConstructor);
+ } catch (NoSuchMethodException e) {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/src/main/java/me/whiteship/chapter01/item03/enumtype/EnumElvisSerialization.java b/src/main/java/me/whiteship/chapter01/item03/enumtype/EnumElvisSerialization.java
new file mode 100644
index 0000000..b67a348
--- /dev/null
+++ b/src/main/java/me/whiteship/chapter01/item03/enumtype/EnumElvisSerialization.java
@@ -0,0 +1,23 @@
+package me.whiteship.chapter01.item03.enumtype;
+
+import me.whiteship.chapter01.item03.field.Elvis;
+
+import java.io.*;
+
+public class EnumElvisSerialization {
+
+ public static void main(String[] args) {
+ try (ObjectOutput out = new ObjectOutputStream(new FileOutputStream("elvis.obj"))) {
+ out.writeObject(Elvis.INSTANCE);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
+ try (ObjectInput in = new ObjectInputStream(new FileInputStream("elvis.obj"))) {
+ Elvis elvis = (Elvis) in.readObject();
+ System.out.println(elvis == Elvis.INSTANCE);
+ } catch (IOException | ClassNotFoundException e) {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/src/main/java/me/whiteship/chapter01/item03/field/Concert.java b/src/main/java/me/whiteship/chapter01/item03/field/Concert.java
new file mode 100644
index 0000000..a357c35
--- /dev/null
+++ b/src/main/java/me/whiteship/chapter01/item03/field/Concert.java
@@ -0,0 +1,28 @@
+package me.whiteship.chapter01.item03.field;
+
+public class Concert {
+
+ private boolean lightsOn;
+
+ private boolean mainStateOpen;
+
+ private IElvis elvis;
+
+ public Concert(IElvis elvis) {
+ this.elvis = elvis;
+ }
+
+ public void perform() {
+ mainStateOpen = true;
+ lightsOn = true;
+ elvis.sing();
+ }
+
+ public boolean isLightsOn() {
+ return lightsOn;
+ }
+
+ public boolean isMainStateOpen() {
+ return mainStateOpen;
+ }
+}
diff --git a/src/main/java/me/whiteship/chapter01/item03/field/Elvis.java b/src/main/java/me/whiteship/chapter01/item03/field/Elvis.java
new file mode 100644
index 0000000..f72600a
--- /dev/null
+++ b/src/main/java/me/whiteship/chapter01/item03/field/Elvis.java
@@ -0,0 +1,40 @@
+package me.whiteship.chapter01.item03.field;
+
+import java.io.Serializable;
+
+// 코드 3-1 public static final 필드 방식의 싱글턴 (23쪽)
+public class Elvis implements IElvis, Serializable {
+
+ /**
+ * 싱글톤 오브젝트
+ */
+ public static final Elvis INSTANCE = new Elvis();
+ private static boolean created;
+
+ private Elvis() {
+ if (created) {
+ throw new UnsupportedOperationException("can't be created by constructor.");
+ }
+
+ created = true;
+ }
+
+ public void leaveTheBuilding() {
+ System.out.println("Whoa baby, I'm outta here!");
+ }
+
+ public void sing() {
+ System.out.println("I'll have a blue~ Christmas without you~");
+ }
+
+ // 이 메서드는 보통 클래스 바깥(다른 클래스)에 작성해야 한다!
+ public static void main(String[] args) {
+ Elvis elvis = Elvis.INSTANCE;
+ elvis.leaveTheBuilding();
+ }
+
+ private Object readResolve() {
+ return INSTANCE;
+ }
+
+}
diff --git a/src/main/java/me/whiteship/chapter01/item03/field/ElvisReflection.java b/src/main/java/me/whiteship/chapter01/item03/field/ElvisReflection.java
new file mode 100644
index 0000000..e9c0211
--- /dev/null
+++ b/src/main/java/me/whiteship/chapter01/item03/field/ElvisReflection.java
@@ -0,0 +1,21 @@
+package me.whiteship.chapter01.item03.field;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+
+// 생성자로 여러 인스턴스 만들기
+public class ElvisReflection {
+
+ public static void main(String[] args) {
+ try {
+ Constructor defaultConstructor = Elvis.class.getDeclaredConstructor();
+ defaultConstructor.setAccessible(true);
+ Elvis elvis1 = defaultConstructor.newInstance();
+ Elvis elvis2 = defaultConstructor.newInstance();
+ Elvis.INSTANCE.sing();
+ } catch (InvocationTargetException | NoSuchMethodException | InstantiationException | IllegalAccessException e) {
+ e.printStackTrace();
+ }
+ }
+
+}
diff --git a/src/main/java/me/whiteship/chapter01/item03/field/ElvisSerialization.java b/src/main/java/me/whiteship/chapter01/item03/field/ElvisSerialization.java
new file mode 100644
index 0000000..6507ed7
--- /dev/null
+++ b/src/main/java/me/whiteship/chapter01/item03/field/ElvisSerialization.java
@@ -0,0 +1,23 @@
+package me.whiteship.chapter01.item03.field;
+
+import java.io.*;
+
+// 역직렬화로 여러 인스턴스 만들기
+public class ElvisSerialization {
+
+ public static void main(String[] args) {
+ try (ObjectOutput out = new ObjectOutputStream(new FileOutputStream("elvis.obj"))) {
+ out.writeObject(Elvis.INSTANCE);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
+ try (ObjectInput in = new ObjectInputStream(new FileInputStream("elvis.obj"))) {
+ Elvis elvis3 = (Elvis) in.readObject();
+ System.out.println(elvis3 == Elvis.INSTANCE);
+ } catch (IOException | ClassNotFoundException e) {
+ e.printStackTrace();
+ }
+ }
+
+}
diff --git a/src/main/java/me/whiteship/chapter01/item03/field/IElvis.java b/src/main/java/me/whiteship/chapter01/item03/field/IElvis.java
new file mode 100644
index 0000000..fd60bfa
--- /dev/null
+++ b/src/main/java/me/whiteship/chapter01/item03/field/IElvis.java
@@ -0,0 +1,8 @@
+package me.whiteship.chapter01.item03.field;
+
+public interface IElvis {
+
+ void leaveTheBuilding();
+
+ void sing();
+}
diff --git a/src/main/java/me/whiteship/chapter01/item03/functionalinterface/DefaultFunctions.java b/src/main/java/me/whiteship/chapter01/item03/functionalinterface/DefaultFunctions.java
new file mode 100644
index 0000000..672c493
--- /dev/null
+++ b/src/main/java/me/whiteship/chapter01/item03/functionalinterface/DefaultFunctions.java
@@ -0,0 +1,24 @@
+package me.whiteship.chapter01.item03.functionalinterface;
+
+
+import me.whiteship.chapter01.item03.methodreference.Person;
+
+import java.time.LocalDate;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.function.Predicate;
+import java.util.function.Supplier;
+
+public class DefaultFunctions {
+
+ public static void main(String[] args) {
+ Function intToString = Object::toString;
+
+ Supplier personSupplier = Person::new;
+ Function personFunction = Person::new;
+
+ Consumer integerConsumer = System.out::println;
+
+ Predicate predicate;
+ }
+}
diff --git a/src/main/java/me/whiteship/chapter01/item03/functionalinterface/MyFunction.java b/src/main/java/me/whiteship/chapter01/item03/functionalinterface/MyFunction.java
new file mode 100644
index 0000000..521a650
--- /dev/null
+++ b/src/main/java/me/whiteship/chapter01/item03/functionalinterface/MyFunction.java
@@ -0,0 +1,11 @@
+package me.whiteship.chapter01.item03.functionalinterface;
+
+@FunctionalInterface
+public interface MyFunction {
+
+ String valueOf(Integer integer);
+
+ static String hello() {
+ return "hello";
+ }
+}
diff --git a/src/main/java/me/whiteship/chapter01/item03/functionalinterface/UsageOfFunctions.java b/src/main/java/me/whiteship/chapter01/item03/functionalinterface/UsageOfFunctions.java
new file mode 100644
index 0000000..ec8071c
--- /dev/null
+++ b/src/main/java/me/whiteship/chapter01/item03/functionalinterface/UsageOfFunctions.java
@@ -0,0 +1,21 @@
+package me.whiteship.chapter01.item03.functionalinterface;
+
+import java.time.LocalDate;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+public class UsageOfFunctions {
+
+ public static void main(String[] args) {
+ List dates = new ArrayList<>();
+ dates.add(LocalDate.of(1982, 7, 15));
+ dates.add(LocalDate.of(2011, 3, 2));
+ dates.add(LocalDate.of(2013, 1, 28));
+
+ List before2000 = dates.stream()
+ .filter(d -> d.isBefore(LocalDate.of(2000, 1, 1)))
+ .map(LocalDate::getYear)
+ .collect(Collectors.toList());
+ }
+}
diff --git a/src/main/java/me/whiteship/chapter01/item03/methodreference/Person.java b/src/main/java/me/whiteship/chapter01/item03/methodreference/Person.java
new file mode 100644
index 0000000..835ca38
--- /dev/null
+++ b/src/main/java/me/whiteship/chapter01/item03/methodreference/Person.java
@@ -0,0 +1,42 @@
+package me.whiteship.chapter01.item03.methodreference;
+
+import java.time.LocalDate;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+
+public class Person {
+
+ LocalDate birthday;
+
+ public Person() {
+
+ }
+
+ public Person(LocalDate birthday) {
+ this.birthday = birthday;
+ }
+
+ public static int compareByAge(Person a, Person b) {
+ return a.birthday.compareTo(b.birthday);
+ }
+
+ public static void main(String[] args) {
+ List people = new ArrayList<>();
+ people.add(new Person(LocalDate.of(1982, 7, 15)));
+ people.add(new Person(LocalDate.of(2011, 3, 2)));
+ people.add(new Person(LocalDate.of(2013, 1, 28)));
+
+ people.sort(new Comparator() {
+ @Override
+ public int compare(Person a, Person b) {
+ return a.birthday.compareTo(b.birthday);
+ }
+ });
+ }
+
+ public int getAge() {
+ return LocalDate.now().getYear() - birthday.getYear();
+ }
+
+}
diff --git a/src/main/java/me/whiteship/chapter01/item03/serialization/Book.java b/src/main/java/me/whiteship/chapter01/item03/serialization/Book.java
new file mode 100644
index 0000000..940329d
--- /dev/null
+++ b/src/main/java/me/whiteship/chapter01/item03/serialization/Book.java
@@ -0,0 +1,67 @@
+package me.whiteship.chapter01.item03.serialization;
+
+import java.io.Serializable;
+import java.time.LocalDate;
+
+public class Book implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+
+ private String isbn;
+
+ private String title;
+
+ private LocalDate published;
+
+ private String name;
+
+ private transient int numberOfSold;
+
+ public Book(String isbn, String title, String author, LocalDate published) {
+ this.isbn = isbn;
+ this.title = title;
+ this.published = published;
+ }
+
+ @Override
+ public String toString() {
+ return "Book{" +
+ "isbn='" + isbn + '\'' +
+ ", title='" + title + '\'' +
+ ", published=" + published +
+ ", numberOfSold=" + numberOfSold +
+ '}';
+ }
+
+ public String getIsbn() {
+ return isbn;
+ }
+
+ public void setIsbn(String isbn) {
+ this.isbn = isbn;
+ }
+
+ public String getTitle() {
+ return title;
+ }
+
+ public void setTitle(String title) {
+ this.title = title;
+ }
+
+ public LocalDate getPublished() {
+ return published;
+ }
+
+ public void setPublished(LocalDate published) {
+ this.published = published;
+ }
+
+ public int getNumberOfSold() {
+ return numberOfSold;
+ }
+
+ public void setNumberOfSold(int numberOfSold) {
+ this.numberOfSold = numberOfSold;
+ }
+}
diff --git a/src/main/java/me/whiteship/chapter01/item03/serialization/SerializationExample.java b/src/main/java/me/whiteship/chapter01/item03/serialization/SerializationExample.java
new file mode 100644
index 0000000..0fa0074
--- /dev/null
+++ b/src/main/java/me/whiteship/chapter01/item03/serialization/SerializationExample.java
@@ -0,0 +1,36 @@
+package me.whiteship.chapter01.item03.serialization;
+
+import java.io.*;
+import java.time.LocalDate;
+
+public class SerializationExample {
+
+ private void serialize(Book book) {
+ try (ObjectOutput out = new ObjectOutputStream(new FileOutputStream("book.obj"))) {
+ out.writeObject(book);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private Book deserialize() {
+ try (ObjectInput in = new ObjectInputStream(new FileInputStream("book.obj"))) {
+ return (Book) in.readObject();
+ } catch (IOException | ClassNotFoundException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static void main(String[] args) {
+// Book book = new Book("12345", "이팩티브 자바 완벽 공략", "백기선",
+// LocalDate.of(2022, 3, 21));
+// book.setNumberOfSold(200);
+
+ SerializationExample example = new SerializationExample();
+// example.serialize(book);
+ Book deserializedBook = example.deserialize();
+
+// System.out.println(book);
+ System.out.println(deserializedBook);
+ }
+}
diff --git a/src/main/java/me/whiteship/chapter01/item03/staticfactory/Concert.java b/src/main/java/me/whiteship/chapter01/item03/staticfactory/Concert.java
new file mode 100644
index 0000000..8abaf76
--- /dev/null
+++ b/src/main/java/me/whiteship/chapter01/item03/staticfactory/Concert.java
@@ -0,0 +1,16 @@
+package me.whiteship.chapter01.item03.staticfactory;
+
+import java.util.function.Supplier;
+
+public class Concert {
+
+ public void start(Supplier singerSupplier) {
+ Singer singer = singerSupplier.get();
+ singer.sing();
+ }
+
+ public static void main(String[] args) {
+ Concert concert = new Concert();
+ concert.start(Elvis::getInstance);
+ }
+}
diff --git a/src/main/java/me/whiteship/chapter01/item03/staticfactory/Elvis.java b/src/main/java/me/whiteship/chapter01/item03/staticfactory/Elvis.java
new file mode 100644
index 0000000..382c222
--- /dev/null
+++ b/src/main/java/me/whiteship/chapter01/item03/staticfactory/Elvis.java
@@ -0,0 +1,26 @@
+package me.whiteship.chapter01.item03.staticfactory;
+
+// 코드 3-2 정적 팩터리 방식의 싱글턴 (24쪽)
+public class Elvis implements Singer {
+ private static final Elvis INSTANCE = new Elvis();
+ private Elvis() { }
+ public static Elvis getInstance() { return INSTANCE; }
+
+ public void leaveTheBuilding() {
+ System.out.println("Whoa baby, I'm outta here!");
+ }
+
+ // 이 메서드는 보통 클래스 바깥(다른 클래스)에 작성해야 한다!
+ public static void main(String[] args) {
+ Elvis elvis = Elvis.getInstance();
+ elvis.leaveTheBuilding();
+
+ System.out.println(Elvis.getInstance());
+ System.out.println(Elvis.getInstance());
+ }
+
+ @Override
+ public void sing() {
+ System.out.println("my way~~~");
+ }
+}
diff --git a/src/main/java/me/whiteship/chapter01/item03/staticfactory/MetaElvis.java b/src/main/java/me/whiteship/chapter01/item03/staticfactory/MetaElvis.java
new file mode 100644
index 0000000..ce30e64
--- /dev/null
+++ b/src/main/java/me/whiteship/chapter01/item03/staticfactory/MetaElvis.java
@@ -0,0 +1,32 @@
+package me.whiteship.chapter01.item03.staticfactory;
+
+import java.util.Objects;
+
+// 코드 3-2 제네릭 싱글톤 팩토리 (24쪽)
+public class MetaElvis {
+
+ private static final MetaElvis