<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>Hello world</title>
    <link>https://shine-17.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Sun, 23 Aug 2026 00:13:42 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>말차쟁이</managingEditor>
    <item>
      <title>Docker IP 대역 변경</title>
      <link>https://shine-17.tistory.com/14</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;IP 대역을 변경해야 하는 이유&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리 회사의 서비스는 Docker 기반이며 고객사 서버에 배포하는 방식으로 구축한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어느 한 고객사에도 마찬가지로 Docker 기반으로 배포하여 개발을 하던 중 고객사에서 우리가 사용 중인 특정 IP 대역을 다른 IP 대역으로 변경해 달라는 요청이 들어왔다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특정 IP 대역은 서비스를 배포한 Docker에서 사용 중이었기 때문에 Docker의 IP 대역을 변경해야 한다는 뜻이었다. Docker의 IP 대역을 변경해본 적이 없어서... 가능한건가? 라는 생각이 들었지만 여러 블로그 글들을 보며 해결하여 사내에도 공유했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;요청사항&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;`172.17.x.x` `172.18.x.x` 대역 모두 고객사 사내망에서 사용 중이므로 변경요청&lt;/li&gt;
&lt;li&gt;변경할 IP 대역 : `100.64.x.x`&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서비스가 설치된 호스트의 라우팅 테이블&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1286&quot; data-origin-height=&quot;151&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bFrkmn/dJMcajtTtxF/SMTBvhdBZPpwNqSZLt4imK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bFrkmn/dJMcajtTtxF/SMTBvhdBZPpwNqSZLt4imK/img.png&quot; data-alt=&quot;라우팅 테이블&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bFrkmn/dJMcajtTtxF/SMTBvhdBZPpwNqSZLt4imK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbFrkmn%2FdJMcajtTtxF%2FSMTBvhdBZPpwNqSZLt4imK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1286&quot; height=&quot;151&quot; data-origin-width=&quot;1286&quot; data-origin-height=&quot;151&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;라우팅 테이블&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;`172.17.x.x`&lt;b&gt; :&lt;/b&gt; docker0&lt;/li&gt;
&lt;li&gt;`172.18.x.x`&lt;b&gt; :&lt;/b&gt; docker_gwbride&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;docker0, docker_gwbridge에서 `172.17.x.x` `172.18.x.x` 대역을 사용 중이므로 모두 라우팅 테이블에서 제외해야 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;804&quot; data-origin-height=&quot;265&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/EnmzL/dJMcaiBKTee/FKWbYm2jytFvffniLoiJu1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/EnmzL/dJMcaiBKTee/FKWbYm2jytFvffniLoiJu1/img.png&quot; data-alt=&quot;docker0, docker_gwbridge의 IP&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/EnmzL/dJMcaiBKTee/FKWbYm2jytFvffniLoiJu1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FEnmzL%2FdJMcaiBKTee%2FFKWbYm2jytFvffniLoiJu1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;804&quot; height=&quot;265&quot; data-origin-width=&quot;804&quot; data-origin-height=&quot;265&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;docker0, docker_gwbridge의 IP&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;docker0&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;docker를 설치하면 `docker0`의 IP는 자동으로 `172.17.0.1`로 할당되며, 호스트의 eth0 네트워크 인터페이스와 직접적으로 연결되는 네트워크 인터페이스&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;docker_gwbridge&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;docker container 간 통신을 위한 docker swarm에 사용되는 네트워크 인터페이스&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Docker IP 대역 변경&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. daemon.json 수정&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;docker 설정파일 수정 (없으면 생성)&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #2c2c2b;&quot; data-token-index=&quot;0&quot;&gt;/etc/docker/daemon.json&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;{
	&quot;log-driver&quot;: &quot;json-file&quot;,
	&quot;log-opts&quot;: {
		&quot;max-size&quot;: &quot;10m&quot;,
		&quot;max-file&quot;: &quot;3&quot;
	},
	&quot;bip&quot;: &quot;100.64.x.x&quot;,
	&quot;default-address-pools&quot;: [
		{
			&quot;base&quot;: &quot;100.64.x.x&quot;,
			&quot;size&quot;: 24
		}
	]
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;bip&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;docker가 사용하는 기본 bridge network 대역&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;default-address-pools.base&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;docker network가 사용하는 대역 (새로운 Docker network를 생성할 때 이 설정을 기반으로 IP 대역을 할당함)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;docker 재시작&lt;/p&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;systemctl restart docker
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;docker0 인터페이스의 IP 대역이 변경된 것을 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1529&quot; data-origin-height=&quot;355&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/9BkSJ/dJMcaiogimV/BLOY4OLiuw3ek3LKzKVrIk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/9BkSJ/dJMcaiogimV/BLOY4OLiuw3ek3LKzKVrIk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/9BkSJ/dJMcaiogimV/BLOY4OLiuw3ek3LKzKVrIk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F9BkSJ%2FdJMcaiogimV%2FBLOY4OLiuw3ek3LKzKVrIk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1529&quot; height=&quot;355&quot; data-origin-width=&quot;1529&quot; data-origin-height=&quot;355&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span data-token-index=&quot;0&quot;&gt;2. 설정 및 설치파일 수정&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컨테이너 배포 설정 관련 파일 내에서 IP 변경(`100.64.x.x`)&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;.properties&lt;/li&gt;
&lt;li&gt;.conf&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span data-token-index=&quot;0&quot;&gt;3. docker_gwbridge 삭제 및 swarm 재설치&lt;/span&gt;&lt;/h3&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;docker swarm leave --force
docker network rm docker_gwbridge&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;master, slave 구조라면 각각 위처럼 docker_gwbridge를 삭제한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 후 master, slave 모두 swarm 재설치 및 join&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span data-token-index=&quot;0&quot;&gt;4. 컨테이너 배포 및 재배포&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컨테이너를 신규로 배포하거나 재배포한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;docker0, docker_gwbridge 인터페이스 모두 IP 대역이 변경된 것을 확인할 수 있다.&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1522&quot; data-origin-height=&quot;698&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dmjisQ/dJMcag43P77/Yffk1fk6wlZMmgRAqx4LV0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dmjisQ/dJMcag43P77/Yffk1fk6wlZMmgRAqx4LV0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dmjisQ/dJMcag43P77/Yffk1fk6wlZMmgRAqx4LV0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdmjisQ%2FdJMcag43P77%2FYffk1fk6wlZMmgRAqx4LV0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1522&quot; height=&quot;698&quot; data-origin-width=&quot;1522&quot; data-origin-height=&quot;698&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1743&quot; data-origin-height=&quot;315&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dxcjaE/dJMcafrxQV4/Z6ycxfFrdAsTe6qce93Dkk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dxcjaE/dJMcafrxQV4/Z6ycxfFrdAsTe6qce93Dkk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dxcjaE/dJMcafrxQV4/Z6ycxfFrdAsTe6qce93Dkk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdxcjaE%2FdJMcafrxQV4%2FZ6ycxfFrdAsTe6qce93Dkk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1743&quot; height=&quot;315&quot; data-origin-width=&quot;1743&quot; data-origin-height=&quot;315&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;참고&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://waspro.tistory.com/635&quot;&gt;https://waspro.tistory.com/635&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://bluese05.tistory.com/15&quot;&gt;https://bluese05.tistory.com/15&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://velog.io/@lijahong/0부터-시작하는-Docker-Swarm-공부-더-자세한-Docker-Swarm-Network&quot;&gt;https://velog.io/@lijahong/0부터-시작하는-Docker-Swarm-공부-더-자세한-Docker-Swarm-Network&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://velog.io/@loveelf1/Docker-default-network-대역-변경&quot;&gt;https://velog.io/@loveelf1/Docker-default-network-대역-변경&lt;/a&gt;&lt;/p&gt;</description>
      <category>Trouble Shooting</category>
      <author>말차쟁이</author>
      <guid isPermaLink="true">https://shine-17.tistory.com/14</guid>
      <comments>https://shine-17.tistory.com/14#entry14comment</comments>
      <pubDate>Wed, 3 Dec 2025 18:04:27 +0900</pubDate>
    </item>
    <item>
      <title>Gson으로 파싱할 때 double 자동변환</title>
      <link>https://shine-17.tistory.com/13</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;158&quot; data-origin-height=&quot;137&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oDQXE/dJMcabbBBUC/517y60ZiAJtiHjQXmyuga1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oDQXE/dJMcabbBBUC/517y60ZiAJtiHjQXmyuga1/img.png&quot; data-alt=&quot;실수형 데이터&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oDQXE/dJMcabbBBUC/517y60ZiAJtiHjQXmyuga1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FoDQXE%2FdJMcabbBBUC%2F517y60ZiAJtiHjQXmyuga1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;158&quot; height=&quot;137&quot; data-origin-width=&quot;158&quot; data-origin-height=&quot;137&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;실수형 데이터&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;회사에서 테스트하던 도중 데이터를 확인했는데 정수형(`Integer`)이어야 할 데이터가 실수형(`Double`)으로 들어가 있었다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;분명 클라이언트에서 넘어오는 파라미터도 정수형이었는데 뭐가 문제일까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;String json = &quot;{\&quot;params\&quot;:{\&quot;num\&quot;:9999}}&quot;;
Host host = gson.fromJson(json, Host.class);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;코드를 추적해보니 json 문자열 파라미터를 전달받은 후 `gson`을 이용해 객체로 변환할 때 객체의 필드가 실수로 변환되고 있었다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;186&quot; data-origin-height=&quot;27&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dswmo7/dJMcagxc9Jn/hKTQDCaEVTrNulslaa5pEk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dswmo7/dJMcagxc9Jn/hKTQDCaEVTrNulslaa5pEk/img.png&quot; data-alt=&quot;실행결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dswmo7/dJMcagxc9Jn/hKTQDCaEVTrNulslaa5pEk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdswmo7%2FdJMcagxc9Jn%2FhKTQDCaEVTrNulslaa5pEk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;186&quot; height=&quot;27&quot; data-origin-width=&quot;186&quot; data-origin-height=&quot;27&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;실행결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;원인&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;`Gson`의 &lt;b&gt;Object to Number&lt;/b&gt; 정책의 기본값이 `Double`이므로 따로 설정을 세팅해주지 않으면 `Double`로 변환되는 것이었다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1370&quot; data-origin-height=&quot;525&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/NMRY5/dJMcah3XqIf/IbV1mSWCrkV8oiFcvYNEs0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/NMRY5/dJMcah3XqIf/IbV1mSWCrkV8oiFcvYNEs0/img.png&quot; data-alt=&quot;Gson의 기본 정책&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/NMRY5/dJMcah3XqIf/IbV1mSWCrkV8oiFcvYNEs0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNMRY5%2FdJMcah3XqIf%2FIbV1mSWCrkV8oiFcvYNEs0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1370&quot; height=&quot;525&quot; data-origin-width=&quot;1370&quot; data-origin-height=&quot;525&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Gson의 기본 정책&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;해결&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Gson을 커스텀해서 따로 util 클래스로 만들어 사용하고 있었는데 `gson` 인스턴스를 빌드할 때 Object to Number 변환정책을&amp;nbsp; `LONG_OR_DOUBLE`로 세팅함&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;aspectj&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;public static Gson makeGson() {
    GsonBuilder builder = new GsonBuilder();
    ...
    builder.setObjectToNumberStrategy(ToNumberPolicy.LONG_OR_DOUBLE);

    return builder.create();
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;`LONG_OR_DOUBLE` &lt;b&gt;:&lt;/b&gt; 숫자로 변환될 객체가 `Long` 또는 `Double` 타입으로 변환될 수 있을 때 해당 타입으로 변환&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;변환 우선순위&lt;/b&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;`Long`&lt;/li&gt;
&lt;li&gt;`Double`&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;참고&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://velog.io/@kimnow/LocalDate-Gson-jackson&quot;&gt;https://velog.io/@kimnow/LocalDate-Gson-jackson&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://jcchu.medium.com/gson변환-시-int가-double로-773e5e81c105&quot;&gt;https://jcchu.medium.com/gson변환-시-int가-double로-773e5e81c105&lt;/a&gt;&lt;/p&gt;</description>
      <category>Trouble Shooting</category>
      <author>말차쟁이</author>
      <guid isPermaLink="true">https://shine-17.tistory.com/13</guid>
      <comments>https://shine-17.tistory.com/13#entry13comment</comments>
      <pubDate>Tue, 2 Dec 2025 22:37:57 +0900</pubDate>
    </item>
    <item>
      <title>CAS (Compare And Swap)</title>
      <link>https://shine-17.tistory.com/12</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;비동기적 동시성 제어 기법 중 하나로, 여러 스레드가 동시에 데이터를 수정하려고 할 때 데이터의 일관성을 보장하는 방법이다. CAS는 원자적 연산(atomic operation)으로, 멀티스레드 환경에서 안전하게 데이터를 읽고 쓸 수 있게 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;원자적 연산&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컴퓨터 과학에서 사용하는 원자적 연산(atomic operation)은 해당 연산이 더 이상 나눌 수 없는 단위로 수행된다는 것을 의미한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;int i = 0;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같은 변수가 있을 때&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;`i = 1`&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 연산은 둘로 쪼갤 수 없는 원자적 연산이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단 하나의 순서로 실행되기 때문이다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;오른쪽에 있는 1을 왼쪽의 `i`변수에 대입한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;`i = i + 1`&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 이 연산은 원자적 연산이 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 순서로 나누어 실행되기 때문이다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;오른쪽에 있는 `i`의 값을 읽는다. `i`의 값은 10이다.&lt;/li&gt;
&lt;li&gt;읽은 10에 1을 더해서 11을 만든다.&lt;/li&gt;
&lt;li&gt;11을 왼쪽의 `i`변수에 대입한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;멀티스레드 환경&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원자적 연산은 멀티스레드 환경에서 아무런 문제가 발생하지 않는다. 하지만 원자적 연산이 아닌 경우에는 synchronized나 Lock 등을 사용해서 안전한 임계 영역을 만들어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;원자적이지 않은 연산&lt;/h3&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;public class AtomicInteger {
    private int value;

    public void increment() {
        value++;
    }

    public int get() {
        return value;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;public class AtomicMain {
    private static final int THREAD_COUNT = 10;
    private static AtomicInteger atomicInteger = new AtomicInteger();

    public static void main(String[] args) throws InterruptedException {
        List&amp;lt;Thread&amp;gt; threads = new ArrayList&amp;lt;&amp;gt;();

        for (int i=0; i&amp;lt;THREAD_COUNT; i++) {
            Thread thread = new Thread(new AtomicTask());
            threads.add(thread);
            thread.start();
        }

        for (Thread thread : threads) {
            thread.join();
        }

        System.out.println(&quot;atomicInteger: &quot; + atomicInteger.get());
    }

    static class AtomicTask implements Runnable {
        @Override
        public void run() {
            for (int i=0; i&amp;lt;1000; i++) {
                atomicInteger.increment();
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드를 실행했을 때 스레드 10개가 각각 1000번씩 `increment()`하기 때문에 기댓값은 10,000이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 코드를 돌려보면 다른 결과가 나온다.&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;atomicInteger: 9690
atomicInteger: 9993
atomicInteger: 9874&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;왜 그럴까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;`AtomicInteger`의 `increment()`가 원자적이지 않기 때문이다.&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;public void increment() {
    value++;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;`value++`은 `value = value + 1`와 같으며, 다음 순서로 실행된다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;`value`의 값을 읽는다.&lt;/li&gt;
&lt;li&gt;`value`에 1을 더한다.&lt;/li&gt;
&lt;li&gt;`value`에 1을 더한 값을 `value`에 대입한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;여러 스레드들이 increment()를 실행할 때 어떤 스레드가 `value`에 1을 더해 `value`에 대입하기 전에 다른 스레드가 `value`&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;의 값을 읽어서 연산을 진행한다면 1을 더하는 연산이 누락되는 문제가 발생한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;원자적 연산&lt;/h2&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;public class AtomicInteger {
    private int value;

    public synchronized void increment() {
        value++;
    }

    public int get() {
        return value;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;`increment()`에 `synchronized`를 붙여준다면 기댓값인 10,000이 출력된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;`synchronized`로 임계 영역을 설정해 연산을 원자적으로 수행하도록 했기 때문에 멀티 스레드 환경에서도 안전하게 동작했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Lock 기반 방식&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;멀티스레드 환경에서 &lt;b&gt;공유 자원에 동시에 접근하는 문제(Race Condition)&lt;/b&gt;를 해결하기 위해 &lt;b&gt;Lock&lt;/b&gt;을 사용하여 동시에 하나의 스레드만 접근하도록 제한하는 방식이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Lock&lt;/b&gt;이 걸려 있는 동안 다른 스레드들은 공유 자원에 접근할 수 없고, &lt;b&gt;Lock&lt;/b&gt;이 해제될 때까지 대기해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대표적으로 `synchronized` `Lock(ReentrantLock)`이 &lt;b&gt;Lock &lt;/b&gt;기반 방식에 속한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;동작 방식&lt;/h4&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Lock이 있는지 확인&lt;/li&gt;
&lt;li&gt;Lock을 획득하고 임계 영역에 들어감&lt;/li&gt;
&lt;li&gt;작업 수행&lt;/li&gt;
&lt;li&gt;Lock을 반납&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Lock 기반 방식은 임계 구역을 Lock으로 제어하는 구조이기 때문에 직관적이고 유지보수가 용이하다는 장점이 있지만, 임계 영역에 접근할 때마다 &lt;span data-token-index=&quot;1&quot;&gt;Lock을 획득하고 해제하는 비용이 발생&lt;/span&gt;하기 때문에 상대적으로 비용이 비싼 방식이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Lock 프리(Free) 방식&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Lock 기반 방식과 반대로 멀티스레드 환경에서 Lock을 사용하지 않고 동기화하는 방식이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Lock 대신 &lt;b&gt;원자적 연산&lt;/b&gt;을 통해 공유 자원을 안전하게 변경한다. Lock을 완전히 대체하는 것은 아니고, &lt;b&gt;작은 단위의 일부 영역에 적용&lt;/b&gt;할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대표적으로 &lt;b&gt;CAS(Compare And Swap) 연산&lt;/b&gt;이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;CAS (Compare And Swap)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메모리의 값이 &lt;b&gt;기댓값&lt;/b&gt;과 같다면 &lt;b&gt;새로운 값&lt;/b&gt;으로 &lt;b&gt;교체(Swap)&lt;/b&gt;하고, 그렇지 않으면 실패를 알리는 &lt;b&gt;원자적(atomic)&lt;/b&gt; 연산이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;성공/실패 여부가 반환되므로 실패하면 다시 시도하는 재시도 전략을 적용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쉽게 말하면, &lt;b&gt;현재 값이 내가 예상한 값과 같다면 교체&lt;/b&gt;하는 연산이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;동작 원리&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;CAS에 필요한 3가지 값&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;메모리 주소&lt;/b&gt; : 변경하고자 하는 데이터의 위치&lt;/li&gt;
&lt;li&gt;&lt;b&gt;기댓값 (expected value)&lt;/b&gt; : 현재 메모리 주소에 저장된 값으로 예상되는 값&lt;/li&gt;
&lt;li&gt;&lt;b&gt;새로운 값 (new value)&lt;/b&gt; : &lt;b&gt;메모리 주소&lt;/b&gt;에 저장하고자 하는 새로운 값&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;public class AtomicInteger extends Number implements java.io.Serializable {
	...

	public final boolean compareAndSet(int expectedValue, int newValue) {
	    return U.compareAndSetInt(this, VALUE, expectedValue, newValue);
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CAS를 사용하는 `AtomicInteger` 클래스의 `compareAndSet()`를 보면 기댓값(`expectedValue`), 새로운 값(`newValue`)을 인자로 받아서 CAS를 수행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바에서 대표적으로 CAS를 사용하는 사례&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;java.util.concurrent.atomic 패키지의 클래스들 (`AtomicInteger` `AtomicLong` 등)&lt;/li&gt;
&lt;li&gt;java.util.concurrent 패키지의 `ConcurrentHashMap`&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내부적으로는 `Unsafe` 클래스의 CAS 메서드를 사용한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;`compareAndSetReference()`&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;CAS 연산 과정&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt; &lt;span data-token-index=&quot;0&quot;&gt;1. 읽기 (Read)&lt;/span&gt; &lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;메모리 주소의 현재 값을 읽는다.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. 비교 (Compare)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;읽은 현재 값이 &lt;b&gt;기댓값(`expected value`)&lt;/b&gt;과 같은지 비교한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3. 교체 (Swap)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다르다면 교체하지 않고 실패로 반환한다. (다른 스레드가 그 사이에 값을 변경했음을 의미함)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같다면 현재 값을 &lt;b&gt;새로운 값(`new value`)&lt;/b&gt;으로 &lt;b&gt;원자적(`Atomic`)&lt;/b&gt;으로 교체한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;4. 결과 반환 (Return)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실패 시 : `false` (재시도 전략을 적용하거나, 다른 적절한 처리를 할 수 있도록 함)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;성공 시 : `true`&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;어떻게 원자적일까?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대부분의 CPU(예: x86)는 `CMPXCHG` 같은 특수한 명령어를 제공하는데, 이 명령어는 메모리 주소에 대한 비교와 교체를 단일 명령어로 처리한다. CAS 과정 중 &lt;b&gt;읽기, 비교, 교체&lt;/b&gt;를 원자적으로 수행한다는 뜻이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;단일 명령어(원자적)&lt;/b&gt;으로 처리되기 때문에 CAS 중에는 다른 스레드나 CPU가 끼어들거나 값을 변경할 수 없다. 이것은 &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;하드웨어 수준에서 원자성을 보장&lt;/b&gt;&lt;/span&gt;해준다는 것을 의미한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하드웨어 수준에서 보장해주는 원자성 덕분에 Lock 없이도 동시성 제어(Lock-Free)를 구현할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 CAS는 &lt;b&gt;메모리 가시성&lt;/b&gt; 문제와 결합되므로 프로그래밍 언어/플랫폼의 메모리 모델(예: Java Memory Model)에서 `volatile`혹은 메모리 배리어(memory barrier)를 통해 가시성을 보장해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;왜 CAS 연산 중 다른 CPU가 값을 변경하지 못할까?&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CPU에서 `CMPXCHG`를 수행할 때, &lt;b&gt;해당 메모리 주소가 속한 캐시 라인&lt;/b&gt;에 대해서 &lt;b&gt;독점적 접근 권한&lt;/b&gt;을 얻는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 해당 캐시 라인이 다른 CPU의 캐시에 포함되어 있다면 접근 불가 상태로 변경되어 해당 CPU는 접근하지 못한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 메모리 주소에 대한 &lt;b&gt;독점적 접근 권한&lt;/b&gt;은 &lt;b&gt;MESI 캐시 일관성 프로토콜&lt;/b&gt;을 통해 얻게 되며, `CMPXCHG` 명령어가 처리될 때까지 해당 메모리 주소는 보호된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;장점&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Lock을 사용하지 않고 동시성를 제어할 수 있다.&lt;/li&gt;
&lt;li&gt;Lock을 사용하지 않기 때문에 Lock 관련 오버헤드(문맥 교환, 교착 상태, Lock 획득/해제 비용)가 발생하지 않는다.&lt;/li&gt;
&lt;li&gt;임계 영역에서 Lock을 사용하지 않기 때문에 스레드들이 블로킹되지 않기 때문에 성능이 더 좋아질 수 있다. 하지만 장점이자 단점이 될 수 있는데, 상황에 따라 다르다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;충돌 가능성이 낮은 상황&lt;/b&gt; : 반복적으로 재시도하는 스핀 비용이 발생하지 않을 가능성이 높기 때문에 성능이 향상된다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;충돌 가능성이 높은 상황&lt;/b&gt; : 반복적으로 재시도하는 스핀 비용이 발생할 가능성이 높기 때문에 성능이 저하된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;단점&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Busy-wait&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;CAS는 CPU가 메모리를 확인하고 수정하기 위해 반복적으로 재시도하기 때문에, 값이 변경될 때까지 CPU 자원이 소모된다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;CPU 자원을 소모하면서 대기하기 때문에 바쁜 대기라고 하며, CPU 자원이 낭비될 수 있다.&lt;/li&gt;
&lt;li&gt;특히 경쟁이 치열할수록 반복적인 재시도로 인한 스핀 비용이 더 많이 발생한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;CAS를 계속 실패하면 Lock을 잠깐 사용하거나, 스핀 횟수를 제한한 뒤 Thread.yield()로 양보하는 방법으로 CPU 자원 낭비를 방지할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;ABA 문제&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;스레드1이 값이 `A`인지 확인 후 `C`로 변경하려고 하는데 다른 스레드2가 `A &amp;rarr; B &amp;rarr; A`로 값을 변경했을 때, 스레드1이 봤을 때는 변경을 감지하지 못하고 CAS를 성공 처리하게 됨.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;하지만 실제로 중간에 변경이 있었음&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;값에 버전을 붙여 버전 관리를 하는 방법으로 해결 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;언제 사용하는 것이 좋을까?&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;공유 데이터에 대한 경쟁이 약할 때&lt;/li&gt;
&lt;li&gt;Lock을 사용하기에는 오버헤드가 큰, 짧고 단순한 연산일 때&lt;/li&gt;
&lt;li&gt;숫자 카운팅, 자료 구조의 데이터 추가 및 삭제 등 CPU 사이클이 금방 끝나지만 안전한 임계 영역 또는 원자적인 연산이 필요한 경우에 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터베이스를 기다리거나 다른 서버의 API를 기다리는 것처럼 오래 기다리는 작업에 CAS를 사용하면 CPU를 계속 사용하며 기다리는 최악의 결과가 나올 수도 있다. 이런 경우는 동기화 Lock을 사용해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;충돌 가능성이 낮은 환경에서는 매우 효율적이지만, 충돌 가능성이 높은 환경에서는 성능 저하가 발생할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;참고&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.inflearn.com/course/%EA%B9%80%EC%98%81%ED%95%9C%EC%9D%98-%EC%8B%A4%EC%A0%84-%EC%9E%90%EB%B0%94-%EA%B3%A0%EA%B8%89-1&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;김영한의 실전 자바 고급 1편&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://curiousjinan.tistory.com/entry/java-concurrent-hash-map-cas&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://curiousjinan.tistory.com/entry/java-concurrent-hash-map-cas&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://velog.io/@jinh2352/멀티코어에서의-캐시-coherency-보장-프로토콜&quot;&gt;https://velog.io/@jinh2352/멀티코어에서의-캐시-coherency-보장-프로토콜&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://bbagwang.com/posts/game-server-development/2-atomic/&quot;&gt;https://bbagwang.com/posts/game-server-development/2-atomic/&lt;/a&gt;&lt;/p&gt;</description>
      <category>Computer Science</category>
      <author>말차쟁이</author>
      <guid isPermaLink="true">https://shine-17.tistory.com/12</guid>
      <comments>https://shine-17.tistory.com/12#entry12comment</comments>
      <pubDate>Thu, 13 Nov 2025 17:03:49 +0900</pubDate>
    </item>
    <item>
      <title>상속이 캡슐화를 위반하는 이유</title>
      <link>https://shine-17.tistory.com/11</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;객체지향 공부를 하던 중 객체지향의 특징인 상속이 캡슐화를 위반한다는 글을 보게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결론부터 말하자면 상속이 항상 캡슐화를 위반하는 것은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 어떤 상속이 캡슐화를 위반할까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한번 알아보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;상속&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체 지향에서의 상속은 상위 클래스의 특성을 하위 클래스에서 상속(특성 상속)받고 거기에 더해 필요한 특성을 추가하는 것.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;즉, 확장해서 사용할 수 있다는 뜻이다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;캡슐화&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체의 데이터와 동작을 하나로 묶어 실제 구현 내용을 외부에 공개하지 않는 것&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;즉, 객체가 내부적으로 기능을 어떻게 구현하는지를 감추는 것이다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상속과 캡슐화는 객체지향의 4대 특성이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만, 아이러니하게도 &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;상속은 캡슐화를 위반&lt;/b&gt;&lt;/span&gt;한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 통해 알아보자.&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;// Broken - Inappropriate use of inheritance!
public class InstrumentedHashSet&amp;lt;E&amp;gt; extends HashSet&amp;lt;E&amp;gt; {
    // The number of attempted element insertions
    private int addCount = 0;
    
    public InstrumentedHashSet() {
    }
    
    public InstrumentedHashSet(int initCap, float loadFactor) {
        super(initCap, loadFactor);
    }
    
    @Override
    public boolean add(E e) {
        addCount++;
        return super.add(e);
    }
    
    @Override
    public boolean addAll(Collection&amp;lt;? extends E&amp;gt; c) {
        addCount += c.size();
        return super.addAll(c);
    }
    
    public int getAddCount() {
        return addCount;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;`InstrumentedHashSet`는 `HashSet`에 추가되는 요소 개수를 계산하는 클래스이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요소가 추가될 때마다 계산하기 위해 `add()`와 `addAll()`을 오버라이딩하여 addCount에 요소 개수를 카운팅한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;public class ExtendsTest {
    public static void main(String[] args) {
        InstrumentedHashSet&amp;lt;String&amp;gt; set = new InstrumentedHashSet&amp;lt;&amp;gt;();
        set.addAll(List.of(&quot;Snap&quot;, &quot;Crackle&quot;, &quot;Pop&quot;));

        System.out.println(&quot;addCount: &quot; + set.getAddCount());
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&quot;Snap&quot;, &quot;Crackle&quot;, &quot;Pop&quot;&lt;/b&gt; 3개의 요소를 추가했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 `InstrumentedHashSet`에서 오버라이딩한 `addAll()`이 호출될 것이고, `addCount`의 기댓값은 3일 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;220&quot; data-origin-height=&quot;48&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rPirf/dJMcaeFZMBF/u3abf4YhNf2LsWiyA77361/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rPirf/dJMcaeFZMBF/u3abf4YhNf2LsWiyA77361/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rPirf/dJMcaeFZMBF/u3abf4YhNf2LsWiyA77361/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrPirf%2FdJMcaeFZMBF%2Fu3abf4YhNf2LsWiyA77361%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;220&quot; height=&quot;48&quot; data-origin-width=&quot;220&quot; data-origin-height=&quot;48&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만, `addCount`의 값은 6이 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;분명 3개의 요소를 추가했는데 `addCount`가 6이 된다니? 왜일까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div&gt;&lt;span style=&quot;color: #333333;&quot; data-token-index=&quot;0&quot;&gt;`addAll()`&lt;/span&gt;의 코드를 살펴보면 이유를 알 수 있다.&lt;/div&gt;
&lt;pre class=&quot;aspectj&quot;&gt;&lt;code&gt;@Override
public boolean addAll(Collection&amp;lt;? extends E&amp;gt; c) {
    addCount += c.size();
    return super.addAll(c);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;`addCount += c.size()`&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;addCount = 3&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;`super.addAll(c)`&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상위 클래스(`HashSet`)의 `addAll()`를 호출한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;aspectj&quot;&gt;&lt;code&gt;public boolean addAll(Collection&amp;lt;? extends E&amp;gt; c) {
    boolean modified = false;
    for (E e : c)
        if (add(e))
            modified = true;
    return modified;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드는 상위 클래스 `HashSet`의 `addAll()`이다. 살펴보면 내부 구현에서는 `add()`를 호출하는 것을 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 인스턴스는 `HashSet`이 아닌 `InstrumentedHashSet`이기 때문에 `add()`는 오버라이딩된 `InstrumentedHashSet`의 `add()`가 호출된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;요소 3개만큼 반복문을 진행하기 때문에 `add()`가 3번 더 호출되어 `addCount`는 6이 되는 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;문제점&lt;/h4&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;`InstrumentedHashSet`는 `HashSet`의 `addAll()`의 내부 구현을 모르는 상태로 &lt;b&gt;오버라이딩&lt;/b&gt;했지만 메서드가 오작동함.&lt;/li&gt;
&lt;li&gt;코드를 수정하려면 `HashSet`의 `addAll()`의 내부 구현을 알아야 함.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 문제점은 이러한&amp;nbsp;&lt;b&gt;문제를 해결하려면 부모 클래스의 구현 내용을 알아야 하고, 이를 해결에 반영하면 그 내용을 노출할 가능성이 발생&lt;/b&gt;한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;부모 클래스의 구현 내용을 노출하게 된다면&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;캡슐화 위반&lt;/b&gt;&lt;/span&gt;이 되는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;그럼 오버라이딩때문에 캡슐화를 위반한 것일까?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;`addAll()`을 오버라이딩함으로써 캡슐화를 위반하게 됐다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만, 오버라이딩으로 인해 무조건 캡슐화를 위반하는 것은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;scala&quot;&gt;&lt;code&gt;class Animal {
    public void breathe() {
        System.out.println(&quot;호흡하다&quot;);
    }
}

class Whale extends Animal {
    public void breathe() {
        System.out.println(&quot;폐로 호흡하다&quot;);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;`breathe()`는 하위 클래스에서 오버라이딩했지만, 상위 클래스와 하위 클래스에서 각각 다르게 구현됐다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 경우 오버라이딩으로 인해 상위 클래스의 내부 동작이 노출되거나 변경되지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 하위 클래스에서 오버라이딩을 해도, &lt;span style=&quot;color: #2c2c2b;&quot; data-token-index=&quot;1&quot;&gt;상위 클래스와 하위 클래스 모두 해당 메서드를 독립적으로 구현해서 &lt;/span&gt;&lt;b&gt;&lt;span style=&quot;color: #2c2c2b;&quot; data-token-index=&quot;2&quot;&gt;상위 클래스의 내부 동작이 노출되거나 변경되지 않는다면 캡슐화를 위반하지 않는다&lt;/span&gt;&lt;span data-token-index=&quot;3&quot;&gt;.&lt;/span&gt; &lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span data-token-index=&quot;3&quot;&gt;결론&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상속 관계에서 &lt;b&gt;상위 클래스의 내부 동작이 노출 또는 변경되지 않으면&lt;/b&gt; 캡슐화를 위반하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만, 오버라이딩으로 인해 상위 클래스의 내부 동작이 노출되거나 변경될 가능성이 발생한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;상속이 캡슐화는 위반하는 이유는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;캡슐화를 위반할 가능성이 있는 오버라이딩을 상속이 제공&lt;/b&gt;&lt;/span&gt;해주기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 제임스 고슬링도 상속보다는 구현을 권장한다고 한다!&lt;/p&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;참고&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://inpa.tistory.com/entry/OOP-&quot;&gt;https://inpa.tistory.com/entry/OOP-&lt;/a&gt;&lt;a href=&quot;https://inpa.tistory.com/entry/OOP-%F0%9F%92%A0-%EA%B0%9D%EC%B2%B4-%EC%A7%80%ED%96%A5%EC%9D%98-%EC%83%81%EC%86%8D-%EB%AC%B8%EC%A0%9C%EC%A0%90%EA%B3%BC-%ED%95%A9%EC%84%B1Composition-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0&quot;&gt; -객체-지향의-상속-문제점과-합성Composition-이해하기&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://unluckyjung.github.io/oop/2021/03/17/Inheritance-and-Encapsulation/&quot;&gt;https://unluckyjung.github.io/oop/2021/03/17/Inheritance-and-Encapsulation/&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://dev-rootable.tistory.com/156&quot;&gt;https://dev-rootable.tistory.com/156&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://blogs.oracle.com/javamagazine/post/java-inheritance-composition&quot;&gt;&lt;s&gt;https://blogs.oracle.com/javamagazine/post/java-inheritance-composition&lt;/s&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Joshua Bloch의 글에 있는 예제를 인용했는데 글이 삭제되었다&amp;hellip;&lt;/p&gt;</description>
      <category>Object-oriented programming</category>
      <author>말차쟁이</author>
      <guid isPermaLink="true">https://shine-17.tistory.com/11</guid>
      <comments>https://shine-17.tistory.com/11#entry11comment</comments>
      <pubDate>Tue, 4 Nov 2025 23:00:35 +0900</pubDate>
    </item>
    <item>
      <title>hashCode()와 equals()를 같이 오버라이딩해야 하는 이유</title>
      <link>https://shine-17.tistory.com/10</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;`hashCode()`와 `equals()`는 Object 클래스에 선언된 메서드며, 모든 객체가 사용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 메서드는 같이 오버라이딩해야 하는 이유가 있다. 그 이유를 알아보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;일단 `hashCode()`와 `equals()`부터 알아보자.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt; &lt;span data-token-index=&quot;0&quot;&gt;JDK 21 기준&lt;/span&gt; &lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;public class Object {

    @IntrinsicCandidate
    public native int hashCode();

    public boolean equals(Object obj) {
        return (this == obj);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;hashCode()&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체의&amp;nbsp;&lt;b&gt;해시 값을 반환&lt;/b&gt;한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;메모리 주소와 무관한 난수 값을 사용해서 해시를 생성&lt;/b&gt;하는 해시 함수다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해시가 뭘까? 해시 함수부터 알아보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;해시 함수&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;임의의 길이의 데이터를 입력으로 받아, &lt;b&gt;고정된 길이의 해시 값(해시 코드)&lt;/b&gt;을 출력하는 함수&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;public int testHashCode() {
	...
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 &lt;b&gt;고정된 길이&lt;/b&gt;는 저장 공간의 크기를 뜻한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;아래 메서드의 반환 값처럼 4byte(`int`)를 차지하는 고정된 길이&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;좋은 해시 함수일수록 해시 충돌 확률이 낮아진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;주요 특징&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;일관성&lt;/b&gt; : 동일한 입력에 대해서는 항상 동일한 결과가 나온다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;단방향성&lt;/b&gt; : 해시 값으로부터 원본 데이터를 유추하기 어렵다. (사실상 복호화 불가능)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;주요 용도&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;데이터 무결성 검사&lt;/b&gt; : 원본 데이터의 해시 값을 미리 저장해두고, 데이터가 변경되었을 때 해시 값을 비교해 데이터의 변조 여부를 확인한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;자료구조&lt;/b&gt; : 해시 테이블과 같은 해시 기반 자료구조에서 데이터를 효율적으로 저장하고 검색하기 위해 사용된다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;암호화&lt;/b&gt; : 비밀번호 등 중요한 정보를 안전하게 저장하기 위해 사용된다. 원본 비밀번호 대신 해시 값을 저장하고, 로그인 시 입력된 비밀번호의 해시 값과 저장된 해시 값을 비교한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt; &lt;span data-token-index=&quot;0&quot;&gt;해시&lt;/span&gt;&lt;/b&gt;는&lt;span data-token-index=&quot;2&quot;&gt; &lt;/span&gt;해시 함수를 통해 반환되는 결과이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;equals()&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문자열 비교에 자주 사용해서 문자열을 비교하는 메서드라고 알고 있을 수도 있겠지만,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;엄밀히 말하면 &lt;b&gt;객체 비교&lt;/b&gt;에 사용하는 메서드다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체 비교는 두 가지 방식이 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt; &lt;span data-token-index=&quot;0&quot;&gt;동일성(Identity)&lt;/span&gt;&lt;/b&gt; : 두 객체가 동일한지 비교한다. (두 객체가 동일한 메모리 주소를 참조하는지 비교) &lt;br /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;비교 연산 : `==`&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;동등성(Equality)&lt;/b&gt; : 두 객체가 논리적으로 동일한 값(내용)을 가지는지 비교한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;비교 연산 : `equals()`&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;public static void main(String[] args) {
    Member member1 = new Member(&quot;test&quot;);
    Member member2 = member1;
    Member member3 = new Member(&quot;test&quot;);

    // 동일성 비교
    boolean identity = member1 == member2;
    System.out.println(identity); // true

    // 동등성 비교
    boolean equals = member1.equals(member3);
    System.out.println(equals); // true
}

static class Member {
    private String name;

    public Member(String name) {
        this.name = name;
    }
    
    @Override
    public boolean equals(Object object) {
        Member member = (Member) object;
        return name.equals(member.name);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;동일성 비교&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;member1과 member2가 동일한 인스턴스인지 비교한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;member2에 member1의 인스턴스를 대입했으니 `true`가 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;동등성 비교&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;member1과 member3이 논리적으로 동일한지 비교한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Member 클래스는 `equals()`를 오버라이딩하여 &lt;b&gt;name&lt;/b&gt;이 같으면 같은 객체라고 판단한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;member1과 member3의 &lt;b&gt;name&lt;/b&gt;이&amp;nbsp;같으므로 `true`가 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;String의 equals()&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문자열 비교에 흔히 사용하는 메서드다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Object 클래스의 equals()를 오버라이딩했다.&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;public boolean equals(Object anObject) {
    if (this == anObject) {
        return true;
    }
    return (anObject instanceof String aString)
            &amp;amp;&amp;amp; (!COMPACT_STRINGS || this.coder == aString.coder)
            &amp;amp;&amp;amp; StringLatin1.equals(value, aString.value);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;동일성 비교&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;`if (this == anObject)`&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 문자열이 같은 인스턴스인지 비교한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;동등성 비교&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;`StringLatin1.equals(value, aString.value)`&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 객체의 값(문자열)을 비교한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;// 문자열 비교
String str1 = &quot;hello&quot;;
String str2 = &quot;hello&quot;;
String str3 = new String(&quot;hello&quot;);

System.out.println(str1 == str2);       // true
System.out.println(str1 == str3);       // false
System.out.println(str1.equals(str3));  // true
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바에서는 일반적으로 객체는 `new` 키워드를 사용해 인스턴스를 생성한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 String 클래스는 조금 다른데, 2가지의 객체 생성 방식이 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;new 키워드 사용&lt;/b&gt;&amp;nbsp;: `String str3 = new String(&amp;rdquo;hello&amp;rdquo;);`&lt;/li&gt;
&lt;li&gt;&lt;b&gt;문자열 리터럴&lt;/b&gt;&amp;nbsp;: `String str1 = &quot;hello&quot;;`&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;`new` 키워드를 사용하는 방식은 일반적으로 사용되지 않고, 대부분 &lt;b&gt;문자열 리터럴(literal)&lt;/b&gt; 방식을 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리터럴 방식으로 생성한 `hello` 문자열은 &lt;b&gt;String Constant pool(문자열 상수 풀)&lt;/b&gt;이라는 특별한 메모리 영역에 저장되어 JVM 종료까지 하나의 `hello` 문자열 인스턴스만 존재한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;때문에 같은 `hello` 문자열을 리터럴 방식으로 생성한 `str1`와 `str2`는 같은 인스턴스를 참조하게 되고, 동일성 비교했을 때 &lt;b&gt;true&lt;/b&gt;인 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;하지만 `new` 키워드를 사용하면 &lt;b&gt;String Constant pool&lt;/b&gt;에 저장되지 않고, 다른 인스턴스처럼 Heap 영역에 저장된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;따라서 `str1`와 `str3`은 같은 `hello`문자열이더라도 다른 인스턴스를 참조하고 있기 때문에 &lt;b&gt;false&lt;/b&gt;이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;그래서 hashCode()와 equals()를 같이 오버라이딩해야 하는 이유는 뭘까?&lt;/h3&gt;
&lt;div&gt;&lt;span style=&quot;color: #333333;&quot;&gt;`hashCode()`는 해시 기반 자료구조에서 데이터를 효율적으로 저장하고 검색하기 위해 사용된다고 했다.&lt;/span&gt;&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div&gt;&lt;span style=&quot;color: #333333;&quot;&gt; 대표적인 해시 기반 자료구조인 `&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;HashMap`을&lt;/span&gt;&amp;nbsp;살펴보자.&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;/span&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;public class HashMap&amp;lt;K,V&amp;gt; extends AbstractMap&amp;lt;K,V&amp;gt;
    implements Map&amp;lt;K,V&amp;gt;, Cloneable, Serializable {
    
    public V get(Object key) {
        Node&amp;lt;K,V&amp;gt; e;
        return (e = getNode(key)) == null ? null : e.value;
    }

    final Node&amp;lt;K,V&amp;gt; getNode(Object key) {
        Node&amp;lt;K,V&amp;gt;[] tab; Node&amp;lt;K,V&amp;gt; first, e; int n, hash; K k;
        if ((tab = table) != null &amp;amp;&amp;amp; (n = tab.length) &amp;gt; 0 &amp;amp;&amp;amp;
            (first = tab[(n - 1) &amp;amp; (hash = hash(key))]) != null) {
            if (first.hash == hash &amp;amp;&amp;amp; // always check first node
                ((k = first.key) == key || (key != null &amp;amp;&amp;amp; key.equals(k))))
                return first;
            if ((e = first.next) != null) {
                if (first instanceof TreeNode)
                    return ((TreeNode&amp;lt;K,V&amp;gt;)first).getTreeNode(hash, key);
                do {
                    if (e.hash == hash &amp;amp;&amp;amp;
                        ((k = e.key) == key || (key != null &amp;amp;&amp;amp; key.equals(k))))
                        return e;
                } while ((e = e.next) != null);
            }
        }
        return null;
    }
		
    static final int hash(Object key) {
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h &amp;gt;&amp;gt;&amp;gt; 16);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;color: #333333;&quot;&gt;`HashMap`에서 값을 가져오는 메서드인 `get()`을 살펴 보자.&lt;/span&gt;&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;return (e = getNode(key)) == null ? null : e.value;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;전달 받은 key를 `getNode()`로 전달한다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;final Node&amp;lt;K,V&amp;gt; getNode(Object key) {
    Node&amp;lt;K,V&amp;gt;[] tab; Node&amp;lt;K,V&amp;gt; first, e; int n, hash; K k;
    if (... &amp;amp;&amp;amp; (hash = hash(key))) != null) {
    
        ...
        if (e.hash == hash &amp;amp;&amp;amp; ((k = e.key) == key || (key != null &amp;amp;&amp;amp; key.equals(k))))
              return e;
        }
        ...
        
    }
    return null;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;bash&quot;&gt;&lt;code&gt;hash = hash(key)
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;`hash()`로 해시를 생성하여 &lt;b&gt;hash&lt;/b&gt; 변수에 대입한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;processing&quot;&gt;&lt;code&gt;static final int hash(Object key) {
    int h;
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h &amp;gt;&amp;gt;&amp;gt; 16);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해시 값을 생성하는 `hash()`에서 `key.hashCode()`가 사용된다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;해시 테이블의 해시 인덱스를 생성하는 용도&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;if (e.hash == hash &amp;amp;&amp;amp; ((k = e.key) == key || (key != null &amp;amp;&amp;amp; key.equals(k))))
      return e;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체 간 비교한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;`key` : 클라이언트에서 값을 찾으려고 전달한 키&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;`hash` : `key`로 생성한 해시&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;`e` : 버킷에 담겨 있는 노드&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;`k` : 버킷에 담겨 있는 노드의 키&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 버킷에 담겨 있는 노드(`e`)와 `key`로 생성한 해시(`hash`)를 비교&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;`e.hash == hash`&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 버킷에 담겨 있는 노드의 키(`k`)와 `key`를 동일성 비교&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;`(k = e.key) == key`&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. `key`가 null이 아니라면, `key`와 `k`를 동등성 비교&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;`key != null &amp;amp;&amp;amp; key.equals(k)`&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 동등성 비교인 `equals()`가 사용된다. 여기까지 진행됐다는 것은 두 객체의 해시가 같다는 뜻이다. 그 다음 객체 간 비교할 수 있는 수단이 필요한데, 그 수단이 `equals()`인 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;결론&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;`hashCode()` : 해시 기반 자료구조에서 객체를 찾기 위한 힌트 (버킷 입장권 느낌)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;`equals()` : 두 객체가 내용적으로 같은지 비교 (논리적으로 동등한지)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해시 충돌이 적을 수록 좋은 해시 함수지만 해시의 범위에는 한계가 있고, 객체의 수가 많아질수록 해시 충돌 가능성이 높아져 해시 값이 같은 두 객체가 나타날 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그때 해시 값이 같은 두 객체 간 비교에 `equals()`로 논리적으로 동등한지 판단해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 `hashCode()`와 `equals()`를 같이 오버라이딩해야 해시 기반 자료구조(ex. 컬렉션 프레임워크)에서 &lt;b&gt;객체 간 논리적 동등성&lt;/b&gt;이 일치한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;`hashCode()`를 항상 오버라이딩해야 하는 것은 아니다. 해시 기반 자료 구조를 사용할 경우에만 `hashCode()`와 `equals()`를 같이 오버라이딩하면 될 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;직접 오버라이딩하는 것은 쉽지 않기 때문에 IDE의 도움을 받아도 좋을 것 같다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;219&quot; data-origin-height=&quot;296&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bF6RNb/dJMcaaQ6G6A/biYPDY0wUwVApLPkrTSie0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bF6RNb/dJMcaaQ6G6A/biYPDY0wUwVApLPkrTSie0/img.png&quot; data-alt=&quot;IntelliJ IDE에서 자동 생성할 수 있다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bF6RNb/dJMcaaQ6G6A/biYPDY0wUwVApLPkrTSie0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbF6RNb%2FdJMcaaQ6G6A%2FbiYPDY0wUwVApLPkrTSie0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;219&quot; height=&quot;296&quot; data-origin-width=&quot;219&quot; data-origin-height=&quot;296&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;IntelliJ IDE에서 자동 생성할 수 있다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;참고&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://jiwondev.tistory.com/113&quot;&gt;https://jiwondev.tistory.com/113&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://velog.io/@hsh111366/Java-hashCode-메모리-주소일까&quot;&gt;https://velog.io/@hsh111366/Java-hashCode-메모리-주소일까&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.inflearn.com/course/%EA%B9%80%EC%98%81%ED%95%9C%EC%9D%98-%EC%8B%A4%EC%A0%84-%EC%9E%90%EB%B0%94-%EC%A4%91%EA%B8%89-2&quot;&gt;김영한의 실전 자바 중급 2편&lt;/a&gt;&lt;/p&gt;</description>
      <category>Java</category>
      <author>말차쟁이</author>
      <guid isPermaLink="true">https://shine-17.tistory.com/10</guid>
      <comments>https://shine-17.tistory.com/10#entry10comment</comments>
      <pubDate>Thu, 30 Oct 2025 22:31:39 +0900</pubDate>
    </item>
    <item>
      <title>ConcurrentHashMap의 동작원리</title>
      <link>https://shine-17.tistory.com/9</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;ConcurrentHashMap은 실무에서 처음 사용해 봤었다. 코드리뷰를 진행하다가 HashMap을 사용한 코드에 대해서 Thread-Safe한 HashMap인 ConcurrentHashMap을 사용해야 한다고 피드백을 받았다. 그 후로 동시성 문제가 발생할 만한 곳은 ConcurrentHashMap을 사용하여 해결했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러다 문득 동시성 문제를 어떻게 해결하는지 궁금해져서 파헤쳐 보기로 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt; &lt;span data-token-index=&quot;0&quot;&gt;JDK 21 기준&lt;/span&gt; &lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일단 키-값 쌍 구조인 HashTable을 구현한 클래스는 크게 3가지가 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;HashMap (Thread-Unsafe)&lt;/li&gt;
&lt;li&gt;HashTable (Thread-Safe)&lt;/li&gt;
&lt;li&gt;ConcurrentHashMap (Thread-Safe)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt; &lt;span data-token-index=&quot;0&quot;&gt;ConcurrentHashMap이 Thread-Safe한 HashMap이라고 했는데, HashTable은 뭘까?&lt;/span&gt; &lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 ConcurrentHashMap 이전에 JDK 1.0 버전에 Thread-Safe한 HashMap이 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;바로 `HashTable`이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;왜 Thread-Safe한 HashMap이 두 개나 있는 걸까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HashTable부터 파헤쳐 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;HashTable&lt;/h2&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;public class Hashtable&amp;lt;K,V&amp;gt; extends Dictionary&amp;lt;K,V&amp;gt;
    implements Map&amp;lt;K,V&amp;gt;, Cloneable, java.io.Serializable {

	public synchronized int size() {
    	...
	}
		
    public synchronized boolean isEmpty() {
        ...
    }

    public synchronized boolean containsKey(Object key) {
        ...
    }

    public synchronized V get(Object key) {
        ...
    }

    public synchronized V put(K key, V value) {
        ...
    }

    ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HashTable의 코드를 파헤쳐 보면 일부 메서드를 제외하고 모두 `synchronized`가 붙어있는 것을 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이것은 무엇을 의미할까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바의 모든 인스턴스는 각각의 모니터 락을 보유하고 있다. 스레드가 `&lt;span style=&quot;color: #333333;&quot; data-token-index=&quot;1&quot;&gt;synchronized`&lt;/span&gt;가 적용된 메서드에 접근하려면 해당 인스턴스의 모니터 락을 획득해야 접근할 수 있고, 그동안 다른 스레드들은 블로킹된다. 심지어 읽기 메서드인 `get()`조차도 `synchronized`가 적용됐기 때문에 블로킹된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;블로킹된 스레드들은 모니터 대기 큐(Entry Queue)에서 무한 대기하게 된다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;무한 대기 : 블로킹된 스레드들은 모니터 락을 획득할 때까지 무한 대기한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;타임아웃 X&lt;/li&gt;
&lt;li&gt;인터럽트 X&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;BLOCKED 상태로 아무 일도 못한 채 대기하게 되어 스레드의 유휴시간이 많아지고, 결과적으로 성능 저하가 발생한다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 HashTable은 동시성 문제를 해결함과 동시에 성능저하가 발생한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;ConcurrentHashMap&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JDK 1.5에 HashTable에서 성능이 개선된 클래스인 ConcurrentHashMap이 나왔다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ConcurrentHashMap은 HashMap을 사용하면서 발생할 수 있는 동시성 문제를 해결하는 Thread-Safe한 HashMap이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;643&quot; data-origin-height=&quot;173&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cTBQff/dJMcagX485X/JBn2MVKAsdsoc3ykKP4TO1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cTBQff/dJMcagX485X/JBn2MVKAsdsoc3ykKP4TO1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cTBQff/dJMcagX485X/JBn2MVKAsdsoc3ykKP4TO1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcTBQff%2FdJMcagX485X%2FJBn2MVKAsdsoc3ykKP4TO1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;643&quot; height=&quot;173&quot; data-origin-width=&quot;643&quot; data-origin-height=&quot;173&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Table&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ConcurrentHashMap의 해시 테이블로, 키-값 쌍이 저장되는 노드(`&lt;span style=&quot;color: #333333;&quot; data-token-index=&quot;1&quot;&gt;Node&amp;lt;K, V&amp;gt;`&lt;/span&gt;) 들의 배열이다. 이 배열의 각 요소는 버킷(bucket)이다. 버킷에는 노드가 저장되며, 해시 충돌이 발생하면 노드들이 연결 리스트(체인)나 트리 구조로 구성된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HashMap와 동일한 Node 배열이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;678&quot; data-origin-height=&quot;238&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cUMhIq/dJMcacBnwzl/ye2ZFIkcNK1jUQYQ1FMTk1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cUMhIq/dJMcacBnwzl/ye2ZFIkcNK1jUQYQ1FMTk1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cUMhIq/dJMcacBnwzl/ye2ZFIkcNK1jUQYQ1FMTk1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcUMhIq%2FdJMcacBnwzl%2Fye2ZFIkcNK1jUQYQ1FMTk1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;678&quot; height=&quot;238&quot; data-origin-width=&quot;678&quot; data-origin-height=&quot;238&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Node&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주석에 나와있듯이 ConcurrentHashMap 내에서 엔트리를 나타내는 객체다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;hash : 해시&lt;/li&gt;
&lt;li&gt;key : 키&lt;/li&gt;
&lt;li&gt;val : 값&lt;/li&gt;
&lt;li&gt;next : 다음 노드&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;next(다음 노드)는 버킷이 연결 리스트나 트리 구조를 구성할 때 사용된다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;get()&lt;/h3&gt;
&lt;pre id=&quot;code_1761656837507&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public V get(Object key) {
    Node&amp;lt;K,V&amp;gt;[] tab; Node&amp;lt;K,V&amp;gt; e, p; int n, eh; K ek;
    
    int h = spread(key.hashCode());
    
    if ((tab = table) != null &amp;amp;&amp;amp; (n = tab.length) &amp;gt; 0 &amp;amp;&amp;amp;
        (e = tabAt(tab, (n - 1) &amp;amp; h)) != null) {
        if ((eh = e.hash) == h) {
            if ((ek = e.key) == key || (ek != null &amp;amp;&amp;amp; key.equals(ek)))
                return e.val;
        }
        else if (eh &amp;lt; 0)
            return (p = e.find(h, key)) != null ? p.val : null;
        while ((e = e.next) != null) {
            if (e.hash == h &amp;amp;&amp;amp;
                ((ek = e.key) == key || (ek != null &amp;amp;&amp;amp; key.equals(ek))))
                return e.val;
        }
    }
    return null;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;int h = spread(key.hashCode());&lt;/code&gt;&lt;/pre&gt;
&lt;div&gt;
&lt;div&gt;&lt;span style=&quot;color: #333333;&quot; data-token-index=&quot;0&quot;&gt;`spread()`&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;를 통해 해시를 더 균등하게 분산시킨다.&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;해시 충돌을 줄여 해시 테이블 성능 최적화&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;e = tabAt(tab, (n - 1) &amp;amp; h)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt; &lt;span data-token-index=&quot;0&quot;&gt;원자적으로&lt;/span&gt;&lt;/b&gt; 해시 인덱스에 해당하는 버킷의 노드를 읽어온다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;&lt;span style=&quot;color: #333333;&quot; data-token-index=&quot;0&quot;&gt;`tabAt()`&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;를 좀 더 파헤쳐 보면 아래 코드를 볼 수 있다.&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;public final class Unsafe {
    /**
     * Fetches a reference value from a given Java variable, with **volatile**
     * load semantics. Otherwise identical to {@link #getReference(Object, long)}
     */
    @IntrinsicCandidate
    public native Object getReferenceVolatile(Object o, long offset);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Unsafe의 getReferenceVolatile()는 원자성과 가시성을 보장해준다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;원자성 : CPU 수준에서 원자성을 보장&lt;/li&gt;
&lt;li&gt;가시성 : 메모리 배리어를 사용해 가시성을 보장&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 다음은 조건에 따라 분기된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt; &lt;span data-token-index=&quot;0&quot;&gt;tabAt()로 읽어온 노드의 해시와 파라미터로 전달받은 키로 만든 해시가 같을 때&lt;/span&gt; &lt;/b&gt;&lt;/p&gt;
&lt;div&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;if ((eh = e.hash) == h) {
    if ((ek = e.key) == key || (ek != null &amp;amp;&amp;amp; key.equals(ek)))
        return e.val;
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;노드의 키가 파라미터로 전달받은 키와 같은 인스턴스거나 &lt;b&gt;== (동일성)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;노드의 키가 파라미터로 전달받은 키와 같다면 &lt;b&gt;equals (동등성)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;노드의 값을 반환한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해시 충돌이 발생하지 않아 버킷에 노드가 한 개인 경우다. 대부분 이 경우에 속한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt; &lt;span data-token-index=&quot;0&quot;&gt;노드의 해시값이 음수일 때&lt;/span&gt; &lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;else if (eh &amp;lt; 0)
	return (p = e.find(h, key)) != null ? p.val : null;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 코드를 보면 음수 값을 가지는 `&lt;span style=&quot;color: #333333;&quot;&gt;MOVED` `TREEBIN` &lt;/span&gt;상수를 볼 수 있다.&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;static final int MOVED     = -1; // hash for forwarding nodes
static final int TREEBIN   = -2; // hash for roots of trees

static final class ForwardingNode&amp;lt;K,V&amp;gt; extends Node&amp;lt;K,V&amp;gt; {
	...
}
static final class TreeNode&amp;lt;K,V&amp;gt; extends Node&amp;lt;K,V&amp;gt; {
	...
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;ForwardingNode : 리사이징 중 새 테이블로 전송되는 것을 도와주는 노드&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;TreeNode : 해시충돌이 많아지면서 임계점 초과 시 성능을 위해 버킷이 트리 구조로 변환됨&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TreeNode, ForwardingNode는 해시를 저장하지 않고, 음수 해시 값을 가지고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 &lt;b&gt;노드의 해시값이 음수라는 것은 버킷의 구조가 트리&lt;/b&gt;라는 걸 의미한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 후 트리 구조를 순회하면서 노드의 값을 반환한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt; &lt;span data-token-index=&quot;0&quot;&gt;해시 충돌이 발생하여 버킷에 노드가 여러 개일 때&lt;/span&gt; &lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;sas&quot;&gt;&lt;code&gt;while ((e = e.next) != null) {
    if (e.hash == h &amp;amp;&amp;amp; ((ek = e.key) == key || (ek != null &amp;amp;&amp;amp; key.equals(ek))))
        return e.val;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;노드의 next를 따라가며 null이 아닐 때까지 순차적으로 탐색한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 해시가 같고, 같은 인스턴스거나(동일성), equals()로 같다면(동등성) 노드의 값을 반환한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로 키에 해당하는 노드가 없을 경우 null을 반환한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;그런데 HashTable의 `&lt;span style=&quot;color: #666666; font-size: 1em; letter-spacing: 0px;&quot;&gt;get()`&lt;/span&gt;와 달리 `&lt;span style=&quot;color: #666666; font-size: 1em; letter-spacing: 0px;&quot;&gt;synchronized`&lt;/span&gt;를 찾아볼 수 없는 데다가 다른 동기화 기법도 찾아볼 수 없다. 왜일까? &lt;br /&gt;&lt;br /&gt;
&lt;div&gt;&lt;span style=&quot;color: #666666;&quot; data-token-index=&quot;0&quot;&gt;`get()`&lt;/span&gt;&lt;span style=&quot;font-size: 1em; letter-spacing: 0px;&quot;&gt;은 내부에 값을 담는 버킷 배열의 구조를 변경하지 않기 때문에 동기화 없이 여러 스레드에서 동시에 호출되어도 문제가 없다.&lt;br /&gt;&lt;br /&gt;1. 메모리 가시성 문제는 내부에 값을 담는 버킷 배&lt;span style=&quot;color: #666666;&quot;&gt;열(`table`)&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #666666;&quot;&gt;&lt;span style=&quot;font-size: 1em; letter-spacing: 0px;&quot;&gt;을 `volatile`&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;font-size: 1em; letter-spacing: 0px;&quot;&gt;로 선언해 해결했다.&lt;br /&gt;&lt;/span&gt;`synchronized`는 메모리 가시성을 보장해 준다.&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;
2. ConcurrentHashMap은&lt;span style=&quot;color: #333333;&quot;&gt;&lt;b&gt; Lock Striping(분할 &lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;color: #666666;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;b&gt;잠금: 버킷 단위로 잠금을 사용하는 방식)&lt;/b&gt;&lt;/span&gt;을 사용하기 때문에 특정 버킷만 읽으면 되기 때문에 `&lt;span style=&quot;font-size: 1em; letter-spacing: 0px;&quot;&gt;get()`&lt;/span&gt;전체에 동기화를 할 필요가 없다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #666666;&quot;&gt;이렇게 `&lt;span style=&quot;font-size: 1em; letter-spacing: 0px;&quot;&gt;synchronized`&lt;/span&gt;를 적용하지 않음으로써 읽기 성능을 최적화했다.&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;put()&lt;/h3&gt;
&lt;div&gt;
&lt;div&gt;&lt;span style=&quot;color: #333333;&quot; data-token-index=&quot;0&quot;&gt;`put()`&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;은 ConcurrentHashMap의 핵심이다.&lt;/span&gt;&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;pre class=&quot;cs&quot;&gt;&lt;code&gt;public V put(K key, V value) {
    return putVal(key, value, false);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;클라이언트는 공개 인터페이스인 `put()`을 호출하지만, 내부적으로는 `putVal()`이 호출된다. &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;/** Implementation for put and putIfAbsent */
final V putVal(K key, V value, boolean onlyIfAbsent) {
    if (key == null || value == null) throw new NullPointerException();
    int hash = spread(key.hashCode());
    int binCount = 0;
    for (Node&amp;lt;K,V&amp;gt;[] tab = table;;) {
        Node&amp;lt;K,V&amp;gt; f; int n, i, fh; K fk; V fv;
        if (tab == null || (n = tab.length) == 0)
            tab = initTable();
        // 버킷이 비어있으면 CAS 연산으로 삽입
        else if ((f = tabAt(tab, i = (n - 1) &amp;amp; hash)) == null) {
            if (casTabAt(tab, i, null, new Node&amp;lt;K,V&amp;gt;(hash, key, value)))
                break;                   // no lock when adding to empty bin
        }
        else if ((fh = f.hash) == MOVED)
            tab = helpTransfer(tab, f);
        else if (onlyIfAbsent // check first node without acquiring lock
                 &amp;amp;&amp;amp; fh == hash
                 &amp;amp;&amp;amp; ((fk = f.key) == key || (fk != null &amp;amp;&amp;amp; key.equals(fk)))
                 &amp;amp;&amp;amp; (fv = f.val) != null)
            return fv;
        // 버킷이 비어있지 않으면 버킷을 동기화하고 삽입
        else {
            V oldVal = null;
            synchronized (f) {
                if (tabAt(tab, i) == f) {
                    if (fh &amp;gt;= 0) {
                        binCount = 1;
                        for (Node&amp;lt;K,V&amp;gt; e = f;; ++binCount) {
                            K ek;
                            if (e.hash == hash &amp;amp;&amp;amp;
                                ((ek = e.key) == key ||
                                 (ek != null &amp;amp;&amp;amp; key.equals(ek)))) {
                                oldVal = e.val;
                                if (!onlyIfAbsent)
                                    e.val = value;
                                break;
                            }
                            Node&amp;lt;K,V&amp;gt; pred = e;
                            if ((e = e.next) == null) {
                                pred.next = new Node&amp;lt;K,V&amp;gt;(hash, key, value);
                                break;
                            }
                        }
                    }
                    else if (f instanceof TreeBin) {
                        Node&amp;lt;K,V&amp;gt; p;
                        binCount = 2;
                        if ((p = ((TreeBin&amp;lt;K,V&amp;gt;)f).putTreeVal(hash, key,
                                                       value)) != null) {
                            oldVal = p.val;
                            if (!onlyIfAbsent)
                                p.val = value;
                        }
                    }
                    else if (f instanceof ReservationNode)
                        throw new IllegalStateException(&quot;Recursive update&quot;);
                }
            }
            if (binCount != 0) {
                if (binCount &amp;gt;= TREEIFY_THRESHOLD)
                    treeifyBin(tab, i);
                if (oldVal != null)
                    return oldVal;
                break;
            }
        }
    }
    addCount(1L, binCount);
    return null;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;for (Node&amp;lt;K,V&amp;gt;[] tab = table;;) {&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특정 조건이 충족될 때까지 무한 반복한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;CAS 연산 실패 시 재시도하는 전략&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;if (key == null || value == null) throw new NullPointerException();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HashMap과 달리 ConcurrentHashMap은 key와 value 모두 null을 허용하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 다음은 조건에 따라 분기된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt; &lt;span data-token-index=&quot;0&quot;&gt;테이블이 없을 때&lt;/span&gt; &lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;if (tab == null || (n = tab.length) == 0) tab = initTable();&lt;/code&gt;&lt;/pre&gt;
&lt;div&gt;&lt;span style=&quot;color: #333333;&quot;&gt;`table`이 아직 초기화되지 않았거나 크기가 0이면 CAS 연산으로 초기화한다.&lt;/span&gt;&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt; &lt;span data-token-index=&quot;0&quot;&gt;버킷이 비어있을 때&lt;/span&gt; &lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;else if ((f = *tabAt*(tab, i = (n - 1) &amp;amp; hash)) == null) {&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;key의 해시인덱스에 해당하는 버킷이 비어있다면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;if (casTabAt(tab, i, null, new Node&amp;lt;K,V&amp;gt;(hash, key, value)))
	break;                   // no lock when adding to empty bin&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt; CAS 연산&lt;/b&gt;으로 해당 버킷에 노드를 추가한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt; &lt;span data-token-index=&quot;0&quot;&gt;노드가 Forwarding Node일 때&lt;/span&gt; &lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;else if ((fh = f.hash) == MOVED)&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;static final int MOVED     = -1; // hash for forwarding nodes&lt;/code&gt;&lt;/pre&gt;
&lt;div&gt;&lt;span style=&quot;color: #333333;&quot;&gt;`MOVED`는 Node가 Forwarding Node라는 것을 나타낸다. &lt;/span&gt;&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;tab = helpTransfer(tab, f);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그것은 현재 테이블이 리사이징 중이라는 의미이기 때문에 리사이징을 돕는 메서드를 호출한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt; &lt;span data-token-index=&quot;0&quot;&gt;노드가 비어있지 않을 때&lt;/span&gt; &lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;else if (onlyIfAbsent &amp;amp;&amp;amp; fh == hash &amp;amp;&amp;amp;
         ((fk = f.key) == key || (fk != null &amp;amp;&amp;amp; key.equals(fk))) &amp;amp;&amp;amp;
         (fv = f.val) != null)
return fv;&lt;/code&gt;&lt;/pre&gt;
&lt;div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;`putIfAbsent()`를 호출하면 `onlyIfAbsent`가 true이기 때문에 이 조건을 검사하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;key에 해당하는 값이 없을 때만 put 하게 되고, 값이 있을 때는 그 값을 반환한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt; &lt;span data-token-index=&quot;0&quot;&gt;그 외 (버킷이 비어있지 않을 때)&lt;/span&gt; &lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;버킷이 비어있지 않다면 버킷을 `synchronized`로 동기화한다. &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div&gt;그 후 버킷의 구조에 따라 분기된다.&lt;/div&gt;
&lt;div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;버킷이 연결 리스트 구조일 때&lt;/li&gt;
&lt;li&gt;버킷이 Red-Black 트리 구조일 때&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&lt;b&gt; &lt;span data-token-index=&quot;0&quot;&gt;버킷이 연결 리스트 구조일 때&lt;/span&gt; &lt;/b&gt;&lt;/div&gt;
&lt;div&gt;
&lt;pre id=&quot;code_1761660557181&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 해시가 음수면 트리 구조
if (fh &amp;gt;= 0) {
    binCount = 1;
    
    // 버킷을 순회한다.
    for (Node&amp;lt;K,V&amp;gt; e = f;; ++binCount) {
        K ek;
        
        // 동일한 key를 찾았을 때
        // put() 일 경우 값 교체
        // putIfAbsent()를 호출했을 경우 값 유지
        if (e.hash == hash &amp;amp;&amp;amp;
            ((ek = e.key) == key ||
             (ek != null &amp;amp;&amp;amp; key.equals(ek)))) {
            oldVal = e.val;
            if (!onlyIfAbsent)
                e.val = value;
            break;
        }
        Node&amp;lt;K,V&amp;gt; pred = e;
        
        // key가 동일하지 않을 때 다음 노드가 없으면 노드 추가 (연결 리스트)
        if ((e = e.next) == null) {
            pred.next = new Node&amp;lt;K,V&amp;gt;(hash, key, value);
            break;
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt; &lt;span data-token-index=&quot;0&quot;&gt;버킷이 Red-Black 트리 구조일 때&lt;/span&gt; &lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1761660575761&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;else if (f instanceof TreeBin) {
    Node&amp;lt;K,V&amp;gt; p;
    binCount = 2;
    // 노드가 추가되지 않았을 때
    if ((p = ((TreeBin&amp;lt;K,V&amp;gt;)f).putTreeVal(hash, key, value)) != null) {
        oldVal = p.val;
        // put()를 호출했을 경우 값 교체
        if (!onlyIfAbsent)
            p.val = value;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;div&gt;&lt;span style=&quot;color: #333333;&quot;&gt;`putTreeVal()`는 노드를 찾거나 추가한다.&lt;/span&gt;&lt;/div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;null을 반환 : 노드가 추가됨&lt;/li&gt;
&lt;li&gt;null이 아닌 노드를 반환 : key에 해당하는 노드를 찾음&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;버킷을 동기화하고 노드를 추가하는 작업을 수행하고 노드 수를 확인한다.&lt;/p&gt;
&lt;pre id=&quot;code_1761660606922&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;if (binCount != 0) {
    if (binCount &amp;gt;= TREEIFY_THRESHOLD)
        treeifyBin(tab, i);
    if (oldVal != null)
        return oldVal;
    break;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;노드를 추가하면서 `binCount`를 증가시키는데 `binCount`가 임계값(`TREEIFY_THRESHOLD`)을 넘기면 버킷의 구조를 변경한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;버킷의 사이즈가 커질수록 검색 속도가 느려지기 때문에 성능을 위해서 아래 조건을 모두 충족할 경우 버킷의 구조를 &lt;span data-token-index=&quot;1&quot;&gt;연결 리스트 구조&lt;/span&gt;에서 &lt;span data-token-index=&quot;3&quot;&gt;Red-Black 트리 구조&lt;/span&gt;로 변경한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;하나의 버킷에 저장된 노드 수가 8개 이상(`TREEIFY_THRESHOLD = 8`)일 경우&lt;/li&gt;
&lt;li&gt;전체 배열 크기(&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;`capacity`&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;)가 64 이상(&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;`MIN_TREEIFY_CAPACITY = 64`&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;)일 경우&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;버킷에 저장된 노드 수(연결 리스트)가 8 이상이어도 전체 배열 크기(`table`)가 64 미만이면 리사이즈 작업을 수행한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반대로 Red-Black 트리에서 연결 리스트로 구조가 변경되는 경우도 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;버킷에 저장된 노드 수가 6개 이하(`UNTREEIFY_THRESHOLD = 6`)일 경우&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;왜 버킷의 구조를 변경하는 걸까?&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 키에 해당하는 버킷의 노드가 하나뿐이라면 O(1)의 성능을 가진다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만, 해시 충돌이 발생하여 버킷에 여러 개의 노드가 저장되어 있다면?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;버킷에 노드가 많아질수록 연결 노드를 다 순회하면서 탐색해야 하기 때문에 O(n)의 성능을 가진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;노드가 많아지면 많아질수록 탐색 시간이 늘어나는 것이다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;`spread()`로 해시 충돌을 줄여 성능 최적화를 하더라도 한계가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 성능 최적화를 위해 &lt;b&gt;균형 이진 탐색 트리인 Red-Black 트리&lt;/b&gt; 구조로 변경한 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Red-Black 트리는 충돌이 아무리 많아도 탐색, 삽입, 삭제 모두 &lt;b&gt;O(log n)&lt;/b&gt;으로 유지가 가능하다.&lt;br /&gt;&lt;br /&gt;Red-Black 트리 구조가 항상 좋은 것은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 &lt;b&gt;버킷에 저장된 노드 수가 6개 이하일 경우&lt;/b&gt; Red-Black 트리에서 연결 리스트로 구조로 변경된다고 했다. 이 경우 적은 수의 노드를 트리 구조로 구성하면 오히려 불필요한 오버헤드가 발생하기 때문에 일정 노드 수(6개)로 줄어든다면 연결 리스트로 변경하는 것이다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div&gt;
&lt;pre id=&quot;code_1761660830429&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;addCount(1L, binCount);&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로 노드 개수를 증가시킨다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해시테이블의 전체 노드 수를 증가시키고, 특정 조건이 만족되면 해시 테이블을 리사이징하는 역할을 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Constructor&lt;/h3&gt;
&lt;pre id=&quot;code_1761660853940&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Creates a new, empty map with the default initial table size (16).
public ConcurrentHashMap() {
}

// Creates a new, empty map with an initial table size accommodating
// the specified number of elements without the need to dynamically resize.
public ConcurrentHashMap(int initialCapacity) {
    this(initialCapacity, LOAD_FACTOR, 1);
}

// Creates a new map with the same mappings as the given map.
public ConcurrentHashMap(Map&amp;lt;? extends K, ? extends V&amp;gt; m) {
    this.sizeCtl = DEFAULT_CAPACITY;
    putAll(m);
}

// Creates a new, empty map with an initial table size based on the given
// number of elements (initialCapacity) and initial table density (loadFactor)
public ConcurrentHashMap(int initialCapacity, float loadFactor) {
    this(initialCapacity, loadFactor, 1);
}

// Creates a new, empty map with an initial table size based on the given
// number of elements (initialCapacity), initial table density (loadFactor),
// and number of concurrently updating threads (concurrencyLevel).
public ConcurrentHashMap(int initialCapacity, float loadFactor, int concurrencyLevel) {
    if (!(loadFactor &amp;gt; 0.0f) || initialCapacity &amp;lt; 0 || concurrencyLevel &amp;lt;= 0)
        throw new IllegalArgumentException();
    if (initialCapacity &amp;lt; concurrencyLevel)   // Use at least as many bins
        initialCapacity = concurrencyLevel;   // as estimated threads
    long size = (long)(1.0 + (long)initialCapacity / loadFactor);
    int cap = (size &amp;gt;= (long)MAXIMUM_CAPACITY) ?
        MAXIMUM_CAPACITY : tableSizeFor((int)size);
    this.sizeCtl = cap;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ConcurrentHashMap의 생성자는 총 5개다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫 번째 기본 생성자의 주석을 보면 해시 테이블의 기본 사이즈는 16이 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 모든 생성자의 코드를 보면 해시 테이블(`table`)에 대한 초기화 코드가 없는 것을 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이것은 &lt;b&gt;지연 초기화&lt;/b&gt; 방식을 적용했기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제로 `put()`으로 데이터를 삽입할 때 `initTable()`를 호출하여 해시테이블을 초기화한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt; &lt;span data-token-index=&quot;0&quot;&gt;생성자의 주요 파라미터&lt;/span&gt; &lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;initialCapacity
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;초기 용량을 결정한다. ConcurrentHashMap의 구현체들은 loadFactor를 고려하여 용량을 수용하기 위해 리사이징 작업을 수행한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;loadFactor
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;초기 해시 테이블의 크기를 설정하기 위해 사용되며, 해시테이블의 밀도를 나타낸다.&lt;/li&gt;
&lt;li&gt;해시 테이블의 버킷 점유율 임계치를 나타낸다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;(노드 수 / 버킷 수)가 loadFactor를 넘으면 리사이징 작업이 수행된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;concurrencyLevel
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;동시에 업데이트되는 스레드의 예상 수&lt;/li&gt;
&lt;li&gt;JDK 8 이상부터 `deprecated`&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;참고&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;a href=&quot;https://curiousjinan.tistory.com/entry/java-concurrent-hash-map-cas&quot;&gt;https://curiousjinan.tistory.com/entry/java-concurrent-hash-map-cas&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;a href=&quot;https://velog.io/@alsgus92/ConcurrentHashMap의-Thread-safe-원리#4-concurrenthashmap-생성자&quot;&gt;https://velog.io/@alsgus92/ConcurrentHashMap의-Thread-safe-원리#4-concurrenthashmap-생성자&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;a href=&quot;https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/concurrent/ConcurrentHashMap.html&quot;&gt;https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/concurrent/ConcurrentHashMap.html&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;color: #353638; text-align: left;&quot;&gt;
&lt;p style=&quot;color: #353638;&quot; data-ke-size=&quot;size16&quot;&gt;잘못된 정보나 내용에 대해 피드백해주시면 감사하겠습니다!&lt;/p&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Java</category>
      <author>말차쟁이</author>
      <guid isPermaLink="true">https://shine-17.tistory.com/9</guid>
      <comments>https://shine-17.tistory.com/9#entry9comment</comments>
      <pubDate>Tue, 28 Oct 2025 22:47:31 +0900</pubDate>
    </item>
    <item>
      <title>제네릭 타입 소거 (Type Erasure)</title>
      <link>https://shine-17.tistory.com/8</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;제네릭은 타입 안정성을 보장하기 위해 JDK 1.5부터 도입됐으며, 이전 자바 버전의 코드와의 호환성을 위해 컴파일 타임에 제네릭 타입은 사라진다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;제네릭의 작동 방식&lt;/h3&gt;
&lt;pre class=&quot;kotlin&quot;&gt;&lt;code&gt;public class Node&amp;lt;T&amp;gt; {
    private T data;
    private Node&amp;lt;T&amp;gt; next;

    public Node(T data, Node&amp;lt;T&amp;gt; next) {
        this.data = data;
        this.next = next;
    }

    public T getData() { return data; }
    // ...
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제네릭 타입인 `Node` 클래스는 컴파일되면 아래 코드로 변환된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;kotlin&quot;&gt;&lt;code&gt;public class Node {

    private Object data;
    private Node next;

    public Node(Object data, Node next) {
        this.data = data;
        this.next = next;
    }

    public Object getData() { return data; }
    // ...
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;`T` 타입 매개변수가 제거되고 `Object`로 치환되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;타입 소거&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 컴파일 타임에 제네릭 타입이 제거되어 런타임에는 제네릭 타입이 사라지는 것(다른 타입으로 치환되는 것)을 &lt;b&gt;타입 소거&lt;/b&gt;라고 한다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;제네릭 타입을 해당 범위로 바꾸거나,&amp;nbsp;제네릭 타입에 제한이 없는 경우 Object로 치환한다. 따라서 생성된 바이트코드에는 &lt;b&gt;제네릭 타입이 존재하지 않는다&lt;/b&gt;.&lt;/li&gt;
&lt;li&gt;필요한 경우 컴파일러가 타입 안전성을 유지하기 위해 &lt;b&gt;형변환&lt;/b&gt;을 추가한다.&lt;/li&gt;
&lt;li&gt;상한 경계에서 컴파일러가 다형성을 유지하기 위해 &lt;b&gt;브릿지 메서드&lt;/b&gt;를 생성한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;타입 소거는 매개변수화된 타입에 대해 새로운 클래스가 생성되지 않도록 보장하여 제네릭은 런타임 오버헤드를 발생시키지 않는다.&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;제네릭 타입 치환 규칙&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;기본 타입 : `&amp;lt;T&amp;gt;` -&amp;gt; `Object`&lt;br /&gt;상한 경계 (upper bounded) : `&amp;lt;T extends Number&amp;gt;` -&amp;gt; `Number`&lt;br /&gt;하한 경계 (lower bounded) : `&amp;lt;T super Integer&amp;gt;` -&amp;gt;&amp;nbsp; `Object`&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;실체화 타입(Reifiable Types) / 비실체화 타입(Non-Reifiable Types)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;실체화 타입&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;런타임에 타입 정보가 있는 타입이다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;primitive type : int, long 등 원시 타입&lt;/li&gt;
&lt;li&gt;non-generic types : String, Integer 등 제네릭을 사용하지 않는 비제네릭 타입&lt;/li&gt;
&lt;li&gt;raw type : List, Map 등 타입 매개변수가 없는 제네릭 타입
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;List&amp;lt;E&amp;gt;의 raw type은 List&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;비실체화 타입&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비실체화 타입은 타입 소거로 인해 런타임에 타입 정보가 없는 타입이다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;List&amp;lt;String&amp;gt;, List&amp;lt;Number&amp;gt; 등 제네릭 타입&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;타입 소거 과정&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 제네릭 타입을 제거한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제네릭 타입 변환 규칙에 따라 제네릭 타입에 해당하는 타입으로 치환한다.&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;public class Queue&amp;lt;T extends Node&amp;gt; {
	private List&amp;lt;T&amp;gt; list = new ArrayLiat();
	
	public T add(T node) {
		list.add(node);
	}
	
	public T get(int index) {
		return list.get(index);
	}
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;public class Queue {
	private List list = new ArrayLiat();
	
	public Node add(Node node) {
		list.add(node);
	}
	
	public Node get(int index) {
		return list.get(index);
	}
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 타입 안정성을 위해 형변환을 추가한다.&lt;/p&gt;
&lt;pre class=&quot;crmsh&quot;&gt;&lt;code&gt;public class Queue {
	private List list = new ArrayLiat();
	
	public Node add(Node node) {
		list.add(node);
	}
	
	public Node get(int index) {
		return (Node) list.get(index);
	}
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;list는 제네릭 타입이 없는 `Object` 타입이다. 하지만 get 메서드는 반환 타입이 `Node` 타입인데 list의 타입은 `Object` 라서 형변환 불일치가 발생한다. 컴파일러가 형변환 불일치를 해결하기 위해서 형변환을 추가한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;브릿지 메서드 (Bridge Method)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컴파일러는 상한 경계 제네릭 타입에 대해서 타입 소거를 해도 다형성을 보존하게 하기위해 별도의 bridge method를 생성한다.&lt;/p&gt;
&lt;pre class=&quot;kotlin&quot;&gt;&lt;code&gt;public class Node&amp;lt;T&amp;gt; {
    public T data;
    
    public Node(T data) { this.data = data; }

    public void setData(T data) {
        System.out.println(&quot;Node.setData&quot;);
        this.data = data;
    }
}

public class MyNode extends Node&amp;lt;Integer&amp;gt; {
    public MyNode(Integer data) { super(data); }

    public void setData(Integer data) {
        System.out.println(&quot;MyNode.setData&quot;);
        super.setData(data);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;public class Main {
	public static void main(String[] args) {
		MyNode mn = new MyNode(5);
		Node n = mn;            // A raw type - compiler throws an unchecked warning
		n.setData(&quot;Hello&quot;);     // Causes a ClassCastException to be thrown.
		Integer x = mn.data;    
	}
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드에서 에러가 발생한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;왜?&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;kotlin&quot;&gt;&lt;code&gt;public class Node {
    public Object data;

    public Node(Object data) {
	    this.data = data;
    }

    public void setData(Object data) {
        System.out.println(&quot;Node.setData&quot;);
        this.data = data;
    }
}

public class MyNode extends Node {
    public MyNode(Integer data) {
	    super(data);
    }

    public void setData(Integer data) {
        System.out.println(&quot;MyNode.setData&quot;);
        super.setData(data);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;`Node` `MyNode` 클래스는 타입 소거 후 이런 코드가 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;`MyNode` 클래스는 `Node` 클래스의 `setData` 메서드를 오버라이딩했지만 타입 소거로 인해 타입 매개변수(`T`)가 `Object` 타입으로 치환되었고, 메서드 시그니처가 일치하지 않아 &lt;b&gt;오버라이딩이 아닌 오버로딩 처리&lt;/b&gt;가 되어버린 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 `ClassCastException`이 발생한 것이다.&lt;/p&gt;
&lt;pre class=&quot;scala&quot;&gt;&lt;code&gt;class MyNode extends Node {
    // Bridge method generated by the compiler
    public void setData(Object data) {
        setData((Integer) data);
    }

    public void setData(Integer data) {
        System.out.println(&quot;MyNode.setData&quot;);
        super.setData(data);
    }

    // ...
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 컴파일러는 위와 같은 문제를 해결하고 타입 소거 후에도 타입의 다형성을 보존하기 위해 &lt;b&gt;브릿지 메서드&lt;/b&gt;를 생성한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;참고&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.oracle.com/javase/tutorial/java/generics/genTypes.html&quot;&gt;https://docs.oracle.com/javase/tutorial/java/generics/genTypes.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.oracle.com/javase/tutorial/java/generics/genMethods.html&quot;&gt;https://docs.oracle.com/javase/tutorial/java/generics/genMethods.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.oracle.com/javase/tutorial/java/generics/nonReifiableVarargsType.html&quot;&gt;https://docs.oracle.com/javase/tutorial/java/generics/nonReifiableVarargsType.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.oracle.com/javase/tutorial/java/generics/restrictions.html&quot;&gt;https://docs.oracle.com/javase/tutorial/java/generics/restrictions.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://inpa.tistory.com/entry/JAVA-&quot;&gt;https://inpa.tistory.com/entry/JAVA-&lt;/a&gt;&lt;a href=&quot;https://inpa.tistory.com/entry/JAVA-%E2%98%95-%EC%A0%9C%EB%84%A4%EB%A6%AD-%ED%83%80%EC%9E%85-%EC%86%8C%EA%B1%B0-%EC%BB%B4%ED%8C%8C%EC%9D%BC-%EA%B3%BC%EC%A0%95-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0#bridge_%EB%A9%94%EC%84%9C%EB%93%9C&quot;&gt;☕-제네릭-타입-소거-컴파일-과정-알아보기#bridge_메서드&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Java</category>
      <author>말차쟁이</author>
      <guid isPermaLink="true">https://shine-17.tistory.com/8</guid>
      <comments>https://shine-17.tistory.com/8#entry8comment</comments>
      <pubDate>Thu, 3 Jul 2025 00:21:44 +0900</pubDate>
    </item>
    <item>
      <title>ArrayList는 왜 제네릭 타입 E[]가 아닌 Object[]으로 요소를 관리할까?</title>
      <link>https://shine-17.tistory.com/7</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;제네릭을 공부하다가 제네릭을 사용하는 클래스 중 대표적으로 자주 사용되는 &lt;b&gt;ArrayList&lt;/b&gt;에 대해 파헤치기 시작했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 이상한 점을 하나 발견했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내부 요소들을 관리하는 자료구조가 &lt;code&gt;Object[]&lt;/code&gt; 이었던 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ArrayList가 제네릭을 사용하니 &lt;code&gt;E[]&lt;/code&gt; 를 사용해도 될 것 같은데, 왜 &lt;code&gt;Object[]&lt;/code&gt; 를 사용하고 있을까?&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;693&quot; data-origin-height=&quot;639&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/RorIN/btsOyHLs80V/mpDzN1xwBJsT6PdZRA5SHK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/RorIN/btsOyHLs80V/mpDzN1xwBJsT6PdZRA5SHK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/RorIN/btsOyHLs80V/mpDzN1xwBJsT6PdZRA5SHK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FRorIN%2FbtsOyHLs80V%2FmpDzN1xwBJsT6PdZRA5SHK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;693&quot; height=&quot;639&quot; data-origin-width=&quot;693&quot; data-origin-height=&quot;639&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;ArrayList&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;제네릭을 이용해 구현한 ArrayList&lt;/h3&gt;
&lt;pre class=&quot;aspectj&quot;&gt;&lt;code&gt;private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

public ArrayList(int initialCapacity) {
    if (initialCapacity &amp;gt; 0) {
        this.elementData = new Object[initialCapacity];
    }
    ...
}

public ArrayList() {
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ArrayList&lt;/b&gt;의 생성자들을 보면 &lt;code&gt;elementData&lt;/code&gt;에 인스턴스를 할당하는 것을 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;elementData&lt;/code&gt;는 &lt;b&gt;ArrayList&lt;/b&gt;의 요소들을 관리하는 필드다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;public E get(int index) {
    Objects.checkIndex(index, size);
    return elementData(index);
}

@SuppressWarnings(&quot;unchecked&quot;)
E elementData(int index) {
    return (E) elementData[index];
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ArrayList&lt;/b&gt;의 요소들을 가져오는 &lt;code&gt;get()&lt;/code&gt; 메서드를 호출했을 때 &lt;b&gt;elementData&lt;/b&gt;의 값을 &lt;code&gt;E&lt;/code&gt; 제네릭 타입으로 반환해주는 것을 볼 수 있다. 실제 요소들은 &lt;code&gt;Object[]&lt;/code&gt; 타입으로 관리하고 외부로 반환할 때는 &lt;code&gt;E&lt;/code&gt; 제네릭 타입으로 형변환 후 반환해주는 것이다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;code&gt;elementData&lt;/code&gt;를 &lt;code&gt;Object[]&lt;/code&gt; 타입으로 선언했을 때 단점&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&amp;nbsp;요소를 반환할 때 E 타입으로 형변환 후 반환해야 한다.&lt;/li&gt;
&lt;li&gt;`Object` -&amp;gt; `E` 로의 형변환은 ClassCastException 예외를 발생시킬 수 있다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;게다가 unchecked 경고를 무시하기 위해 `@SuppressWarnings`를 사용했다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;그런데 굳이 &lt;code&gt;Object[]&lt;/code&gt; 타입으로 사용하는 이유는 뭘까?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이유는 제네릭의 &lt;b&gt;타입 소거&lt;/b&gt; 때문이다.&lt;/p&gt;
&lt;figure id=&quot;og_1751471238165&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;제네릭 타입 소거 (Type Erasure)&quot; data-og-description=&quot;제네릭은 타입 안정성을 보장하기 위해 JDK 1.5부터 도입됐으며, 이전 자바 버전의 코드와의 호환성을 위해 컴파일 타임에 제네릭 타입은 사라진다.제네릭의 작동 방식public class Node { private T data; &quot; data-og-host=&quot;shine-17.tistory.com&quot; data-og-source-url=&quot;https://shine-17.tistory.com/8&quot; data-og-url=&quot;https://shine-17.tistory.com/8&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/beLxyH/hyZf5AyZIP/wRZzuNbnjVoJndX0sbgfg1/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/oqzJN/hyZf4n5RdH/BoixvKg2ADNCF9rkSWyzD1/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800&quot;&gt;&lt;a href=&quot;https://shine-17.tistory.com/8&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://shine-17.tistory.com/8&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/beLxyH/hyZf5AyZIP/wRZzuNbnjVoJndX0sbgfg1/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/oqzJN/hyZf4n5RdH/BoixvKg2ADNCF9rkSWyzD1/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;제네릭 타입 소거 (Type Erasure)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;제네릭은 타입 안정성을 보장하기 위해 JDK 1.5부터 도입됐으며, 이전 자바 버전의 코드와의 호환성을 위해 컴파일 타임에 제네릭 타입은 사라진다.제네릭의 작동 방식public class Node { private T data;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;shine-17.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;타입 소거&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제네릭 타입은 컴파일 타임에만 존재하여 타입 안정성을 제공하고 런타임에는 타입 정보가 소거된다.&lt;/p&gt;
&lt;pre class=&quot;n1ql&quot;&gt;&lt;code&gt;E elementData(int index) {
    return (E) elementData[index];
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드가 컴파일 과정에서 타입 소거되어&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;Object elementData(int index) {
    return elementData[index];
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 코드가 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;그럼 만약 &lt;code&gt;elementData&lt;/code&gt;를 &lt;code&gt;E[]&lt;/code&gt; 타입으로 사용한다면 어떻게 될까?&lt;/h4&gt;
&lt;pre class=&quot;haxe&quot;&gt;&lt;code&gt;E element = new E();         // 컴파일 에러
E[] elementData = new E[10]; // 컴파일 에러&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드는 컴파일 에러가 발생한다.&lt;br /&gt;&lt;b&gt;왜?&lt;/b&gt;&lt;br /&gt;자바는 구체적인 타입이 있어야 인스턴스를 생성할 수 있다. 하지만 위 코드는 컴파일 타임에 타입 소거되어 런타임에서는 &lt;code&gt;E&lt;/code&gt; 타입에 대한 정보를 가지고 있지 않다. 그래서 런타임에 JVM이 &lt;code&gt;E&lt;/code&gt;의 구체적인 타입 정보를 알 수 없어 컴파일 에러가 발생하는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;padding: 5px;&quot;&gt;&lt;b&gt;그래서 애초에 제네릭 타입의 인스턴스를 생성할 수 없다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 한 가지 방법은 있다.&lt;/p&gt;
&lt;pre class=&quot;inform7&quot;&gt;&lt;code&gt;E[] elementData = (E[]) new Object[10];&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 하면 컴파일 에러는 발생하지 않지만, 문제점이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;타입 안정성이 보장되지 않는다.&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;public class ArrayList&amp;lt;E&amp;gt; {
    public E[] elementData = (E[]) new Object();
    ...
}

public class Main {
	public static void main(String[] args) {
        // Integer[] elementData = (Integer[]) new Object();
        List&amp;lt;Integer&amp;gt; list = new ArrayList&amp;lt;&amp;gt;();
        list.add(1);
        list.add(2);

        // ClassCastException 발생
        Integer[] intList = list.elementData;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;Object[]&lt;/code&gt;를 &lt;code&gt;E[]&lt;/code&gt;로 강제 형변환하기 때문에 컴파일 에러는 발생하지 않지만 &lt;code&gt;Object[]&lt;/code&gt; 타입의 인스턴스가 &lt;code&gt;Integer[]&lt;/code&gt; 타입을 참조하게 되면서 &lt;b&gt;ClassCastException&lt;/b&gt;가 발생하게 된다.&lt;br /&gt;&lt;code&gt;Object[]&lt;/code&gt;를 &lt;code&gt;E[]&lt;/code&gt;로 강제 형변환하기 때문에 &lt;b&gt;컴파일러가 제대로 타입 체크를 못할 가능성이 있다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;왜 제네릭 타입의 인스턴스를 생성하지 못할까?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제네릭은 &lt;b&gt;비실체화&lt;/b&gt; &lt;b&gt;타입&lt;/b&gt;이기 때문이다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;비실체화 타입 : 컴파일 타임에 타입 소거에 의해 타입 정보가 제거된 타입&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컴파일 타임에 타입 소거로 인해 런타임에는 타입 정보를 가지고 있지 않다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 제네릭 타입의 객체 또는 배열 인스턴스를 생성할 수 없는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;그렇다면 제네릭은 왜 비실체화 타입일까?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제네릭은 JDK 1.5에 처음 도입되었고, 기존 코드와의 호환성을 위해 타입 소거를 통해 타입 정보를 유지하지 않기로 했다. 그로 인해 제네릭은 런타임에 타입 정보가 없는 비실체화 타입이 된 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;결론&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;애초에 제네릭 타입의 배열 `E[]`을 생성할 수 없어서 `Object[]`을 사용한 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;강제 형변환을 통해 제네릭 타입의 배열을 사용할 수는 있지만 타입 안정성을 보장할 수 없어 권장하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;참고&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://velog.io/@bedshanty/자바의-ArrayList가-제네릭-타입-E가-아닌-Object를-저장하는-이유#자바-제네릭의-작동-방식&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://velog.io/@bedshanty/자바의-ArrayList가-제네릭-타입-E가-아닌-Object를-저장하는-이유#자바-제네릭의-작동-방식&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://docs.oracle.com/javase/tutorial/java/generics/genTypes.html&quot;&gt;https://docs.oracle.com/javase/tutorial/java/generics/genTypes.html&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://docs.oracle.com/javase/tutorial/java/generics/genMethods.html&quot;&gt;https://docs.oracle.com/javase/tutorial/java/generics/genMethods.html&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://docs.oracle.com/javase/tutorial/java/generics/nonReifiableVarargsType.html&quot;&gt;https://docs.oracle.com/javase/tutorial/java/generics/nonReifiableVarargsType.html&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://docs.oracle.com/javase/tutorial/java/generics/restrictions.html&quot;&gt;https://docs.oracle.com/javase/tutorial/java/generics/restrictions.html&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://docs.oracle.com/javase/tutorial/java/generics/restrictions.html&quot;&gt;https://docs.oracle.com/javase/tutorial/java/generics/restrictions.html&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://pompitzz.github.io/blog/Java/whyCantCreateGenericsArray.html#&quot;&gt;https://pompitzz.github.io/blog/Java/whyCantCreateGenericsArray.html#&lt;/a&gt;&lt;a style=&quot;color: #0070d1; text-align: start;&quot; href=&quot;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%87%E1%85%A2%E1%84%8B%E1%85%A7%E1%86%AF-%E1%84%89%E1%85%A1%E1%84%8B%E1%85%AD%E1%86%BC%E1%84%92%E1%85%A1%E1%84%80%E1%85%B5&quot;&gt;제네릭-배열-사용하기&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;잘못된 정보나 내용에 대해 피드백해주시면 감사하겠습니다!&lt;/p&gt;</description>
      <category>Java</category>
      <author>말차쟁이</author>
      <guid isPermaLink="true">https://shine-17.tistory.com/7</guid>
      <comments>https://shine-17.tistory.com/7#entry7comment</comments>
      <pubDate>Thu, 12 Jun 2025 19:44:08 +0900</pubDate>
    </item>
    <item>
      <title>[백준] [자바/JAVA] 11723번 - 집합</title>
      <link>https://shine-17.tistory.com/6</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/11723&quot;&gt;https://www.acmicpc.net/problem/11723&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;901&quot; data-origin-height=&quot;584&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cYAdLR/btsJZ7odk7t/H1whaA8QkcpltOqRbhRBOK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cYAdLR/btsJZ7odk7t/H1whaA8QkcpltOqRbhRBOK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cYAdLR/btsJZ7odk7t/H1whaA8QkcpltOqRbhRBOK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcYAdLR%2FbtsJZ7odk7t%2FH1whaA8QkcpltOqRbhRBOK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;901&quot; height=&quot;584&quot; data-origin-width=&quot;901&quot; data-origin-height=&quot;584&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;문제&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;문제 그대로 공집합 S에 아래 연산을 수행한다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;add x : S에 x를 &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;추가&lt;/b&gt;&lt;/span&gt; (&lt;span style=&quot;color: #000000;&quot;&gt;S에 x가 이미 있는 경우에는 연산을 무시&lt;/span&gt;)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;remove x : S에서 x를 &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;제거&lt;/b&gt;&lt;/span&gt; (S에 x가 없는 경우에는 연산을 무시)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;check x : &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;S에&lt;/b&gt;&lt;/span&gt; &lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;x가 있으면&lt;/span&gt;&lt;/b&gt; 1을, &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;없으면&lt;/b&gt;&lt;/span&gt; 0을 출력&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;toggle x : &lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;S에 x가 있으면&lt;/span&gt;&lt;/b&gt; x를 &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;제거&lt;/b&gt;&lt;/span&gt;, &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;없으면&lt;/b&gt;&lt;/span&gt; x를 &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;추가&lt;/b&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;all : S를 {1, 2, ..., 20} 으로 변경&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;empty : S를 &lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;공집합으로 변경&lt;/span&gt;&lt;/b&gt; (S를 비운다)&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;조건&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;1 &amp;le; x &amp;le; 20&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;빨간색 마킹표시를 정리하면 아래 내용과 같다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;S에 x를 추가&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;S에서 x를 제거&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;S에 x가 있는지 없는지 유무 확인&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;위 기능들을 보면 자바에서 떠오르는 자료구조들이 있을 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Collection의 List, Set 이 있고 Map도 위 기능들을 수행할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;주어진 값들을 키 - 값 구조로 넣을 필요는 없으니 Map은 제외하고, 주어진 연산의 수의 최댓값이 300만이니 List보다 속도가 빠른 Set으로 구현해보겠다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;사실 처음에 Set으로 구현하면 풀겠다 라고 생각했지만 로직을 잘못 구현해 자꾸 시간초과가 났었다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;그래서 문제의 힌트인 비트마스킹으로 풀었고, Set으로도 다시 풀어서 성공했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;총 2가지 방법으로 풀이했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt; &lt;b&gt;문제풀이&lt;/b&gt; &lt;br /&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #666666; font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;방법 1 - 비트마스킹&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;비트마스킹&lt;/b&gt;은 정수의 이진수 표현을 자료구조로 쓰는 기법이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 비트연산자를 알아보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 13.3333%;&quot;&gt;비트 연산&lt;/td&gt;
&lt;td style=&quot;width: 15.4263%;&quot;&gt;자바 비트 연산&lt;/td&gt;
&lt;td style=&quot;width: 71.2403%;&quot;&gt;설명&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 13.3333%;&quot;&gt;AND&lt;/td&gt;
&lt;td style=&quot;width: 15.4263%;&quot;&gt;&amp;amp;&lt;/td&gt;
&lt;td style=&quot;width: 71.2403%;&quot;&gt;대응하는 두 비트가 모두 1이면 1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 13.3333%;&quot;&gt;OR&lt;/td&gt;
&lt;td style=&quot;width: 15.4263%;&quot;&gt;|&lt;/td&gt;
&lt;td style=&quot;width: 71.2403%;&quot;&gt;대응하는 두 비트 중 하나라도 1이면 1, 아니면 0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 13.3333%;&quot;&gt;XOR&lt;/td&gt;
&lt;td style=&quot;width: 15.4263%;&quot;&gt;^&lt;/td&gt;
&lt;td style=&quot;width: 71.2403%;&quot;&gt;대응하는 두 비트가 다르면 1, 같으면 0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 13.3333%;&quot;&gt;NOT&lt;/td&gt;
&lt;td style=&quot;width: 15.4263%;&quot;&gt;~&lt;/td&gt;
&lt;td style=&quot;width: 71.2403%;&quot;&gt;비트의 값을 반전&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 13.3333%;&quot;&gt;SHIFT&lt;/td&gt;
&lt;td style=&quot;width: 15.4263%;&quot;&gt;&amp;lt;&amp;lt;. &amp;gt;&amp;gt;&lt;/td&gt;
&lt;td style=&quot;width: 71.2403%;&quot;&gt;비트를 왼쪽 또는 오른쪽으로 이동&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 이진수 표현을 자료구조로 어떻게 사용하는지 알아보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;int는 4바이트 자료형이다. 4바이트 = 32비트 = 2&amp;sup3;&amp;sup2;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 x는 &lt;span style=&quot;color: #555555;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;1 ~ 20 범위의 정수다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #555555;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;int로 표현할 수 있는 값은 2&amp;sup3;&amp;sup2; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #555555;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;0000 0000 &lt;span style=&quot;background-color: #ffffff; color: #555555; text-align: start;&quot;&gt;0000 &lt;span style=&quot;background-color: #ffffff; color: #555555; text-align: start;&quot;&gt;&lt;b&gt;0000 &lt;span style=&quot;background-color: #ffffff; color: #555555; text-align: start;&quot;&gt;0000&lt;/span&gt; &lt;span style=&quot;background-color: #ffffff; color: #555555; text-align: start;&quot;&gt;0000&lt;/span&gt; &lt;span style=&quot;background-color: #ffffff; color: #555555; text-align: start;&quot;&gt;0000&lt;/span&gt; &lt;/b&gt;&lt;span style=&quot;background-color: #ffffff; color: #555555; text-align: start;&quot;&gt;&lt;b&gt;0000&lt;/b&gt; 으로 표현할 수 있다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #555555;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;여기서 1 ~ 20 범위인 x를 위의 2진수에서 각 자리수를 x와 매칭시켜 아래 그림처럼 표현할 수 있다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;773&quot; data-origin-height=&quot;100&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bmcNUw/btsKfwG6DJp/zLCeqDsEWDBxNMQvmgHxtK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bmcNUw/btsKfwG6DJp/zLCeqDsEWDBxNMQvmgHxtK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bmcNUw/btsKfwG6DJp/zLCeqDsEWDBxNMQvmgHxtK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbmcNUw%2FbtsKfwG6DJp%2FzLCeqDsEWDBxNMQvmgHxtK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;78&quot; data-origin-width=&quot;773&quot; data-origin-height=&quot;100&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 2진수인 형태에서 연산을 수행하면 아래와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;x = 1 이라고 가정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;add x : S에 x를&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;추가&lt;/b&gt;&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;(&lt;span style=&quot;color: #000000;&quot;&gt;S에 x가 이미 있는 경우에는 연산을 무시&lt;/span&gt;)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;자바 비트 연산 : S |= (1 &amp;lt;&amp;lt; x)&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;1을 x만큼 왼쪽으로 이동 후 S의 x자리와 OR 연산&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;520&quot; data-origin-height=&quot;88&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/8qnGs/btsKgLQFR3y/3rtKakexlbWd2dk2DQQ1E0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/8qnGs/btsKgLQFR3y/3rtKakexlbWd2dk2DQQ1E0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/8qnGs/btsKgLQFR3y/3rtKakexlbWd2dk2DQQ1E0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F8qnGs%2FbtsKgLQFR3y%2F3rtKakexlbWd2dk2DQQ1E0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;400&quot; height=&quot;88&quot; data-origin-width=&quot;520&quot; data-origin-height=&quot;88&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;remove x : S에서 x를&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;제거&lt;/b&gt;&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;(S에 x가 없는 경우에는 연산을 무시)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;자바 비트 연산 : S &amp;amp;= ~(1 &amp;lt;&amp;lt; x)&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;1을 x만큼 왼쪽으로 이동 후 반전시키면 0이 됨. 0과 S의 x자리와 AND 연산&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;514&quot; data-origin-height=&quot;87&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Dqu8h/btsKeFZkBSl/foKpRkkjofsTqXxTNPs0jK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Dqu8h/btsKeFZkBSl/foKpRkkjofsTqXxTNPs0jK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Dqu8h/btsKeFZkBSl/foKpRkkjofsTqXxTNPs0jK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDqu8h%2FbtsKeFZkBSl%2FfoKpRkkjofsTqXxTNPs0jK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;400&quot; height=&quot;87&quot; data-origin-width=&quot;514&quot; data-origin-height=&quot;87&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;check x :&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;S에&lt;/b&gt;&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;x가 있으면&lt;/span&gt;&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;1을,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;없으면&lt;/b&gt;&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;0을 출력&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;자바 비트 연산 : (S &amp;amp; (1 &amp;lt;&amp;lt; x)) != 0&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;1을 x만큼 왼쪽으로 이동 후 S의 x자리와 AND 연산 후 값이 1이면 있는 것이고 0이면 없는 것&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;remove 하여 1이 제거되었으니 &lt;b&gt;0을 출력&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;toggle x :&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;S에 x가 있으면&lt;/span&gt;&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;x를&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;제거&lt;/b&gt;&lt;/span&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;없으면&lt;/b&gt;&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;x를&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;추가&lt;/b&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #333333;&quot;&gt;자바 비트 연산 : S ^= (1 &amp;lt;&amp;lt; x)&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #333333;&quot;&gt;1을 x만큼 왼쪽으로 이동 후 S의 x자리와 XOR 연산 (S의 x자리에 값이 있다면 0으로 변환(제거), 없다면 1로 변환(추가))&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;520&quot; data-origin-height=&quot;88&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/8qnGs/btsKgLQFR3y/3rtKakexlbWd2dk2DQQ1E0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/8qnGs/btsKgLQFR3y/3rtKakexlbWd2dk2DQQ1E0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/8qnGs/btsKgLQFR3y/3rtKakexlbWd2dk2DQQ1E0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F8qnGs%2FbtsKgLQFR3y%2F3rtKakexlbWd2dk2DQQ1E0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;400&quot; height=&quot;88&quot; data-origin-width=&quot;520&quot; data-origin-height=&quot;88&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;all : S를 {1, 2, ..., 20} 으로 변경&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;자바 비트 연산 : S = (1 &amp;lt;&amp;lt; 21) - 1;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;1을 21만큼 왼쪽으로 이동 후 1을 빼게되면, 10 0000 0000 0000 0000 에서 1을 빼는 것이니 1부터 20자리까지 1로 변환됨&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;522&quot; data-origin-height=&quot;96&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Hwmsj/btsKe9yH0Ax/kE83lRv6QDWEjLQEyUk6ik/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Hwmsj/btsKe9yH0Ax/kE83lRv6QDWEjLQEyUk6ik/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Hwmsj/btsKe9yH0Ax/kE83lRv6QDWEjLQEyUk6ik/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHwmsj%2FbtsKe9yH0Ax%2FkE83lRv6QDWEjLQEyUk6ik%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;400&quot; height=&quot;96&quot; data-origin-width=&quot;522&quot; data-origin-height=&quot;96&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;empty : S를&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;공집합으로 변경&lt;/span&gt;&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;(S를 비운다)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;자바 비트 연산 : S = 0&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;S에 0을 대입하면 모든 자리가 0으로 변환됨&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;518&quot; data-origin-height=&quot;91&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/VePVP/btsKfcPDUwJ/r6Rgzz3sPsrDcI1RSsgEzk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/VePVP/btsKfcPDUwJ/r6Rgzz3sPsrDcI1RSsgEzk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/VePVP/btsKfcPDUwJ/r6Rgzz3sPsrDcI1RSsgEzk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FVePVP%2FbtsKfcPDUwJ%2Fr6Rgzz3sPsrDcI1RSsgEzk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;400&quot; height=&quot;91&quot; data-origin-width=&quot;518&quot; data-origin-height=&quot;91&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비트 연산만 이해되면 바로 풀 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1729609421395&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.StringTokenizer;

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

        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

        try {
            int m = Integer.parseInt(br.readLine());
            StringTokenizer st;
            StringBuilder sb = new StringBuilder();

            // 공집합 S
            int set = 0;

            for(int i=0; i&amp;lt;m; i++) {
                st = new StringTokenizer(br.readLine());
                String op = st.nextToken();

                if(op == null || op.isBlank()) continue;

                int value = 0;
                if(!op.equals(&quot;all&quot;) &amp;amp;&amp;amp; !op.equals(&quot;empty&quot;)) {
                    value = Integer.parseInt(st.nextToken());
                }

                switch (op) {
                    case &quot;add&quot;:
                        set |= (1 &amp;lt;&amp;lt; value);
                        break;
                    case &quot;remove&quot;:
                        set &amp;amp;= ~(1 &amp;lt;&amp;lt; value);
                        break;
                    case &quot;check&quot;:
                        sb.append((set &amp;amp; (1 &amp;lt;&amp;lt; value)) != 0 ? 1 : 0).append(&quot;\n&quot;);
                        break;
                    case &quot;toggle&quot;:
                        set ^= (1 &amp;lt;&amp;lt; value);
                        break;
                    case &quot;all&quot;:
                        // 1~20 의 수를 표현해야 되기때문에 21자리로 시트프 후 -1을 해서 20자리를 맞춤
                        set = (1 &amp;lt;&amp;lt; 21) - 1;
                        break;
                    case &quot;empty&quot;:
                        set = 0;
                        break;
                }
            }

            System.out.println(sb);

        } catch(Exception e) {}
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt; &lt;b&gt;방법 1 - Set 이용&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;span&gt;문제에서 주어진 연산들을 모두 Set이 가지고 있기 때문에 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;Set을 이용하면 간단하다.&lt;/span&gt; &lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;혹시 Set에 대해 아직 잘 모른다면 Set에 대해 조금 더 학습을 한 후 풀어보면 좋을 것 같다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;풀이는 간단하니 주석으로 대체하겠다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1728572535492&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.HashSet;
import java.util.Set;
import java.util.StringTokenizer;

public class Main {
    public static void main(String[] args) {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

        try {
            int m = Integer.parseInt(br.readLine());
            StringTokenizer st;
            StringBuilder sb = new StringBuilder();

            // 공집합 S
            Set&amp;lt;Integer&amp;gt; set = new HashSet&amp;lt;&amp;gt;();

            for(int i=0; i&amp;lt;m; i++) {
                st = new StringTokenizer(br.readLine());
                String op = st.nextToken();

                // all이나 empty 연산은 값이 안 들어오기 때문에 예외처리 필요
                int value = st.hasMoreTokens() ? Integer.parseInt(st.nextToken()) : 0;

                switch (op) {
                    case &quot;add&quot;:
                        // S에 x 추가
                        set.add(value);
                        break;
                    case &quot;remove&quot;:
                        // S에서 x 제거
                        set.remove(value);
                        break;
                    case &quot;check&quot;:
                        // S에 x가 있으면 1을, 없으면 0을 출력 (삼항 연산자)
                        sb.append(set.contains(value) ? 1 : 0).append(&quot;\n&quot;);
                        break;
                    case &quot;toggle&quot;:
                        // S에 x가 있으면 x를 제거
                        if(set.contains(value)) set.remove(value);
                        // S에 x가 없으면 x를 추가
                        else set.add(value);
                        break;
                    case &quot;all&quot;:
                        // S를 초기화
                        set.clear();
                        // 반복문을 통해 S에 {1, 2, ..., 20} 추가
                        for(int j=1; j&amp;lt;=20; j++) {
                            set.add(j);
                        }
                        break;
                    case &quot;empty&quot;:
                        // S를 초기화
                        set.clear();
                        break;
                }
            }

            System.out.println(sb);

        } catch(Exception e) {}
    }
}&lt;/code&gt;&lt;/pre&gt;</description>
      <author>말차쟁이</author>
      <guid isPermaLink="true">https://shine-17.tistory.com/6</guid>
      <comments>https://shine-17.tistory.com/6#entry6comment</comments>
      <pubDate>Wed, 23 Oct 2024 00:12:49 +0900</pubDate>
    </item>
    <item>
      <title>객체 지향 프로그래밍 (Object-Oriented Programming, OOP)</title>
      <link>https://shine-17.tistory.com/5</link>
      <description>&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;객체 지향 프로그래밍 (Object-Oriented Programming, OOP)&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #333333; background-color: #e9ecef;&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;컴퓨터 프로그래밍 패러다임 중 하나로, 현실 세계의 사물이나 개념을 객체(Object)로 보고 그 객체들 간의 상호작용으로 프로그래밍하는 방법이다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;객체&amp;nbsp;지향&amp;nbsp;프로그래밍은&amp;nbsp;프로그램을&amp;nbsp;유연하고&amp;nbsp;변경이&amp;nbsp;쉽게&amp;nbsp;만들기&amp;nbsp;때문에&amp;nbsp;대규모&amp;nbsp;소프트웨어&amp;nbsp;개발에&amp;nbsp;많이&amp;nbsp;사용된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;구성 요소&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #202122; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;클래스(Class) : 객체를 정의해놓은 것으로 객체를 생성하는데 사용된다. &lt;span style=&quot;background-color: #e9ecef; color: #333333;&quot;&gt;(객체를&lt;span style=&quot;text-align: left;&quot;&gt;&amp;nbsp;생성하기&amp;nbsp;&lt;/span&gt;위한 설계도&lt;span style=&quot;text-align: left;&quot;&gt; 또는 틀)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;span style=&quot;background-color: #e9ecef; color: #333333;&quot;&gt;&lt;span style=&quot;text-align: left;&quot;&gt; &lt;span style=&quot;background-color: #ffffff; color: #202122; text-align: left;&quot;&gt;객체(Object) :&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #f8f9fa; color: #212529; text-align: start;&quot;&gt;현실 세계의 사물이나 개념&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #202122; text-align: left;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #e9ecef; color: #333333;&quot;&gt;(클래스에서 정의한 내용을 기반으로 생성된 실체)&lt;/span&gt; &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;메시지(Message) : 객체들 간의 상호작용에 사용되는 수단으로, &lt;span style=&quot;background-color: #e9ecef;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #202122; text-align: left;&quot;&gt;객체에 어떠한 행위를 하도록 지시하는 명령 또는 요구사항&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;인스턴스(Instance) : 클래스를 사용하여 실제로 메모리상에 할당된 객체&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;메서드(Method) : 다른 객체로부터 메시지를 수신할 때 메시지를 처리하는 로직 &lt;span style=&quot;background-color: #e9ecef; color: #202122; text-align: left;&quot;&gt;(객체가 수행하는 행위)&lt;/span&gt;&lt;span style=&quot;background-color: #e9ecef; color: #202122; text-align: left;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #e9ecef; color: #202122; text-align: left;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1161&quot; data-origin-height=&quot;205&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/N9mo8/btsISNdfx2f/ADcAkUkC70kcZe5zba9snk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/N9mo8/btsISNdfx2f/ADcAkUkC70kcZe5zba9snk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/N9mo8/btsISNdfx2f/ADcAkUkC70kcZe5zba9snk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FN9mo8%2FbtsISNdfx2f%2FADcAkUkC70kcZe5zba9snk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;820&quot; height=&quot;145&quot; data-origin-width=&quot;1161&quot; data-origin-height=&quot;205&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #333333;&quot;&gt;&lt;b&gt;클래스&lt;/b&gt;는 &lt;b&gt;객체&lt;/b&gt;를 생성하기 위한 설계도 역할을 하며, &lt;b&gt;클래스&lt;/b&gt;를 사용하여 &lt;b&gt;객체&lt;/b&gt;를 생성한다.&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt;이 때 생성된&amp;nbsp;&lt;/span&gt;&lt;b&gt;객체&lt;/b&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt;가 실제 메모리에 할당되면&amp;nbsp;&lt;/span&gt;&lt;b&gt;인스턴스&lt;/b&gt;&lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt;라고 한다.&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;운전자가 브레이크를 밟아 차를 멈출 때 &lt;b&gt;&quot;차를 멈추다&quot;라는 메시지&lt;/b&gt;를 자동차(객체)에게 전달한다.&lt;br /&gt;&quot;차를 멈추다&quot;라는 &lt;b&gt;메시지&lt;/b&gt;를 수신한 자동차(객체)는 차를 멈추는 것을 수행하는 로직을 &lt;b&gt;메서드&lt;/b&gt;라고 한다.&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;특징&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;추상화 (Abstraction)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;캡슐화 (Encapsulation)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;상속 (Inheritance)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;다형성 (Polymorphism)&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;추상화 (Abstraction)&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;690&quot; data-origin-height=&quot;270&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bCBR4A/btsIKe9d7Ck/4JoltPxoSh3Av8UoTdcrl1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bCBR4A/btsIKe9d7Ck/4JoltPxoSh3Av8UoTdcrl1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bCBR4A/btsIKe9d7Ck/4JoltPxoSh3Av8UoTdcrl1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbCBR4A%2FbtsIKe9d7Ck%2F4JoltPxoSh3Av8UoTdcrl1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;439&quot; height=&quot;172&quot; data-origin-width=&quot;690&quot; data-origin-height=&quot;270&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;예를 들어 포르쉐, 테슬라, 페라리가 있다고 하자. (모두 자동차의 종류)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이들은 모두 전진하다, 멈추다(기능), 핸들(속성)을 공통으로 가지고 있다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이를 자동차라는 클래스(인터페이스)로 &lt;span style=&quot;background-color: #e9ecef; color: #333333;&quot;&gt;객체의 공통적인 속성과 기능을 추출하여 정의&lt;/span&gt;하는 것을 &lt;b&gt;추상화&lt;/b&gt;라고 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이처럼 공통 기능을 자동차에서 미리 정의를 하고 포르쉐, 테슬라, 페라리는 자동차라는 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;클래스(인터페이스)를 상속(구현)을 받아&amp;nbsp; 미리 정의된 공통 기능을 각각 구현할 수 있다. 이렇게되면 공통 기능을 한 곳에서 정의를 했기 때문에 &lt;b&gt;코드의 중복이 줄어든다&lt;/b&gt;.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 id=&quot;캡슐화encapsulation&quot; style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;캡슐화 (Encapsulation)&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;캡슐은 내용물을 외부에서 보이지 않게 포장한 형태를 말한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;span style=&quot;background-color: #e9ecef;&quot;&gt;객체의 속성과 기능을 캡슐처럼 외부에서 직접 접근하지 못하도록&lt;/span&gt;&amp;nbsp;제한하는 &lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;정보은닉&lt;/b&gt;&lt;/span&gt;의 개념 중 하나이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;자바로 예를 들면, Car 라는 클래스에&amp;nbsp; price 라는 변수가 있다고 하자.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Car는 price가 10,000 값으로 입력되어있는데 외부에서 임의로 다른 값으로 변경할 가능성이 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;그렇기 때문에 price는 외부에서 직접 접근하지 못하게 &lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;접근제어자&lt;/b&gt;&lt;/span&gt;로 제한하고 접근 가능한 메서드를 제공한다. &lt;span style=&quot;background-color: #e9ecef; color: #000000;&quot;&gt;getter/setter&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;또한 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;유효성 체크 로직을 통해 &lt;/span&gt;유효한 값만 넣을 수 있도록 제한할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1721726547381&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class Car {

    // private으로 접근 제한 (private: Car 클래스 내에서만 접근 가능)
    private int price = 10000;

    // price에 접근해서 조회할 수 있는 getter 메서드 (public: 모든 클래스에서 접근 가능)
    public int getPrice() {
        return price;
    }

    // price에 접근해서 값을 변경할 수 있는 setter 메서드 (public: 모든 클래스에서 접근 가능)
    public void setPrice(int price) {

        // price에 대한 유효성 검사
        if(price &amp;lt; 0) {
            return;
        }

        this.price = price;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;정보은닉&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #575757; text-align: left; font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt; 객체 지향에서 정보 은닉(data hiding)이란 사용자가 굳이 알 필요가 없는 정보는 사용자로부터 숨겨야 한다는 개념이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #575757; text-align: left; font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;객체에 대한 구체적인 정보를 노출시키지 않도록 하는 기법&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;접근제어자&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;자바(JAVA)는 4가지의 접근제어자를 통해 객체의 캡슐화, 정보은닉을 구현한다&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;public &lt;/b&gt;: 모든 클래스에서 접근 가능&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;default &lt;/b&gt;: 같은 패키지에서만 접근 가능&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;protected &lt;/b&gt;: 같은 패키지이거나 해당 클래스를 상속받은 클래스에서만 접근 가능&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;private &lt;/b&gt;: 같은 클래스 내에서만 접근 가능&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 id=&quot;상속_inheritance&quot; style=&quot;background-color: #ffffff; color: #262d40; text-align: left;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;상속 (Inheritance)&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;객체들 간의 관계를 구축하는 개념이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;span style=&quot;background-color: #e9ecef; color: #333333;&quot;&gt;상위 클래스(부모 클래스)로부터 속성(변수)과 기능(메서드)을 하위 클래스(자식 클래스)가 물려받는 것&lt;/span&gt;을 &lt;b&gt;상속&lt;/b&gt;이라 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #006dd7; font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;&lt;span style=&quot;background-color: #ffffff; text-align: left;&quot;&gt; &lt;b&gt;&lt;span style=&quot;background-color: #ffffff; text-align: left;&quot;&gt;상위 클래스 &lt;/span&gt;&lt;/b&gt;= 부모 클래스&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #006dd7; font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;&lt;span style=&quot;background-color: #ffffff; text-align: left;&quot;&gt;하위 클래스 = 자식 클래스&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1187&quot; data-origin-height=&quot;789&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bxy3SP/btsIJHxhPua/rM4Tt9J609vXUhKEsfPUvK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bxy3SP/btsIJHxhPua/rM4Tt9J609vXUhKEsfPUvK/img.png&quot; data-alt=&quot;상속 구성도 (화살표를 받는 쪽이 부모클래스이다)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bxy3SP/btsIJHxhPua/rM4Tt9J609vXUhKEsfPUvK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbxy3SP%2FbtsIJHxhPua%2FrM4Tt9J609vXUhKEsfPUvK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;578&quot; height=&quot;384&quot; data-origin-width=&quot;1187&quot; data-origin-height=&quot;789&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;상속 구성도 (화살표를 받는 쪽이 부모클래스이다)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;위 그림으로 예시를 들어보자.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt; &lt;b&gt;교통수단&lt;/b&gt;은 &lt;b&gt;자동차&lt;/b&gt;의 상위 클래스이고,&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;자동차&lt;/b&gt;는 &lt;b&gt;교통수단&lt;/b&gt;의 하위 클래스이자 &lt;b&gt;포르쉐, 테슬라, 페라리&lt;/b&gt;의 상위 클래스이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;포르쉐, 테슬라, 페라리&lt;/b&gt; 또한 &lt;b&gt;자동차&lt;/b&gt;의 하위 클래스이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;자동차&lt;/b&gt;는 상위 클래스인 &lt;b&gt;교통수단&lt;/b&gt;으로부터 &lt;span style=&quot;color: #006dd7;&quot;&gt;수송하다&lt;/span&gt;를 물려받았고,&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;포르쉐, 테슬라, 페라리&lt;/b&gt;는 상위 클래스인 자동차로부터 &lt;span style=&quot;color: #006dd7;&quot;&gt;수송하다&lt;/span&gt;, &lt;span style=&quot;color: #409d00;&quot;&gt;전진하다&lt;/span&gt;, &lt;span style=&quot;color: #409d00;&quot;&gt;멈추다&lt;/span&gt;, &lt;span style=&quot;color: #409d00;&quot;&gt;핸들&lt;/span&gt;을 물려받았다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;또한 &lt;b&gt;오버라이딩&lt;/b&gt;을 통해 상위 클래스로부터 물려받은 기능(메서드)를 하위 클래스의 고유 기능으로 재정의할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이렇게 상속을 통해서 상위 클래스의 속성(변수)과 기능(메서드)을 하위 클래스에서 상속받아 &lt;b&gt;코드 중복 제거&lt;/b&gt;, &lt;b&gt;코드 재사용&lt;/b&gt;의 장점을 얻을 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;다형성 (Polymorphism)&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #e9ecef; font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;하나의 객체가 여러 가지 타입을 가질 수 있는 것&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;다형성은 객체 지향 프로그래밍의 상속과 인터페이스 개념을 통해 구현되며 코드 재사용성과 유연성을 향상시킨다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;691&quot; data-origin-height=&quot;354&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bFN9Bg/btsIR2HkDb6/HUFup4JdmUDY7EJRouls01/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bFN9Bg/btsIR2HkDb6/HUFup4JdmUDY7EJRouls01/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bFN9Bg/btsIR2HkDb6/HUFup4JdmUDY7EJRouls01/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbFN9Bg%2FbtsIR2HkDb6%2FHUFup4JdmUDY7EJRouls01%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;383&quot; height=&quot;196&quot; data-origin-width=&quot;691&quot; data-origin-height=&quot;354&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;위 그림에서 운전자는 자동차를 운전을 할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;만약 운전자가 자동차의 한 종류인 포르쉐를 운전하다 사고가 났다. 다른 차로 변경해서 운전을 해야하는 상황에서 운전자는 자동차를 운전할 수 있기 때문에 포르쉐가 아닌 자동차의 다른 종류인 테슬라나 페라리로 변경해도 운전할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;이렇게 &lt;span style=&quot;background-color: #e9ecef;&quot;&gt;자동차(객체)가 포르쉐, 테슬라, 페라리 등 여러 가지 자동차 종류(타입)를 가질 수 있는 것&lt;/span&gt;을 &lt;b&gt;다형성&lt;/b&gt;이라고 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;다형성의 필수 조건&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;&lt;/b&gt;1. 상속관계&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp; &amp;nbsp; 부모(상위) 클래스와 자식(하위) 클래스는 상속 관계여야 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;2. 오버라이딩 (Overriding)&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp; &amp;nbsp; 다형성이 보장되기 위해 오버라이딩(자식 클래스 메서드의 재정의)이 반드시 필요하다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;3. 업캐스팅 (Upcasting)&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&amp;nbsp; &amp;nbsp; 자식 클래스의 객체가 부모 클래스의 타입으로 형변환(업캐스팅)해야 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;다형성이 하나의 객체가 여러 가지 타입을 가질 수 있는 것이라고 했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;하지만 모든 타입을 가질 수 있는 것은 아니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;아래 자바코드로 예시를 들어보겠다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1722350579551&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Porsche extends Car {
    void heatedSeat() {}
}

public class Car {
    void drive() {}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;1. &lt;span style=&quot;background-color: #fcfcfc; text-align: left;&quot;&gt;부모 타입의 참조변수로 자식 타입의 인스턴스를 참조할 수 있다.&lt;/span&gt; &lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1722350597022&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Car car = new Porsche(); // O&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt; &lt;span style=&quot;background-color: #ffffff; color: #575757; text-align: left;&quot;&gt;자바에서는 다형성을 통해 &lt;/span&gt;부모 타입(Car)의 참조 변수로 자식 타입(Porsche)의 인스턴스를 참조할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt; &lt;span style=&quot;background-color: #fcfcfc; text-align: left;&quot;&gt;2. 자식 타입의 참조변수로 부모 타입의 인스턴스를 참조할 수 없다.&lt;/span&gt; &lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1722331429563&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Porsche porsche = new Car(); // X (컴파일 에러)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;실제 인스턴스인 Car의 멤버 개수보다 Porsche 타입의 참조 변수 porsche가 사용할 수 있는 멤버 개수가 더 많기 때문이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Porsche 클래스가 Car 클래스를 상속받으면 Car 클래스의 모든 멤버(필드, 메서드)를 가지고 있기 때문에 Porsche 타입인 참조변수 porsche는 인스턴스 Car보다 사용할 수 있는 멤버 개수가 더 많다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;위 코드에서 참조변수 porsche는 Porsche 타입이므로 drive(), heatedSeat()을 사용할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;(drive는 Car 클래스를 상속받았기 때문에 사용가능)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;하지만 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;참조변수 porsche가 참조하고 있는 인스턴스는 Car 타입이고, Car 타입의 인스턴스에는 heatedSeat()가 존재하지 않기 때문에 문제가 발생한다.&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;u&gt;&lt;span style=&quot;color: #333333;&quot;&gt;이렇게 존재하지 않는 멤버를 사용할 가능성이 있으므로 자식 타입의 참조변수로 부모 타입의 인스턴스를 참조할 수 없다.&lt;/span&gt;&lt;/u&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;3. &lt;span style=&quot;background-color: #fcfcfc; text-align: left;&quot;&gt;참조변수가 사용할 수 있는 멤버의 개수는 인스턴스의 멤버 개수보다 같거나 적어야 한다.&lt;/span&gt; &lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1722332015964&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Car car = new Porsche();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;2번의 내용에 나왔던 것처럼&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;인스턴스 Porsche의 멤버 개수보다 참조 변수 car의 멤버 개수가 많으면, 존재하지 않는 멤버를 사용하는 문제가 발생할 수 있기 때문이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;따라서 자식 타입의 참조변수로 부모 타입의 인스턴스를 참조할 수 없고,&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;u&gt;참조변수가 사용할 수 있는 멤버의 개수는 인스턴스의 멤버 개수보다 같거나 적어야 한다.&lt;/u&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;409&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/5f4Sc/btsISU3CcPq/DtqGNnEwj3qOvP2XW7mrcK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/5f4Sc/btsISU3CcPq/DtqGNnEwj3qOvP2XW7mrcK/img.png&quot; data-alt=&quot;참조변수의 타입과 인스턴스 타입에 따른 접근범위&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/5f4Sc/btsISU3CcPq/DtqGNnEwj3qOvP2XW7mrcK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F5f4Sc%2FbtsISU3CcPq%2FDtqGNnEwj3qOvP2XW7mrcK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1200&quot; height=&quot;409&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;409&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;참조변수의 타입과 인스턴스 타입에 따른 접근범위&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;Car 클래스와 Porsche 클래스의 상속관계를 도식화한 그림이다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;참조변수의 타입은 참조변수가 참조하는 인스턴스의 사용범위라고 이해하면 될 것 같다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;다형성을 활용한 자바코드&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1721806752891&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Car extends Transportation {
    void accelerate() {}
    void stop() {}
}
class Porsche extends Car {
    void heatedSeat() {}
}
class Tesla extends Car {
    void selfDrive() {}
}
class Ferrari extends Car {
    void coolSeat() {}
}

public class Transportation {

    public void transport() {
        System.out.println(&quot;수송하다&quot;);
    }

    public static void main(String[] args) {
    	// 1. 참조변수의 타입과 인스턴스의 타입이 같음
        Transportation transportation = new Transportation();
        Car car = new Car();
        Porsche porsche = new Porsche();
        Tesla tesla = new Tesla();
        Ferrari ferrari = new Ferrari();

	// 2. 부모 타입의 참조변수로 자식 타입의 인스턴스 생성
        Transportation transportation1 = new Car();
        Transportation transportation2 = new Porsche();

	// 3. 부모 타입의 참조변수로 자식 타입의 인스턴스 생성
        Car car1 = new Porsche();
        Car car2 = new Tesla();
		
        // 4. 자식 타입의 참조변수로 부모 타입의 인스턴스 생성
        Car car3 = new Transportation(); // 컴파일 에러
        Porsche porsche1 = new Car(); // 컴파일 에러
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;코드 중복 제거, 코드 재사용 : 상속을 기반으로 하기 때문에 상속이 가지고 있는 코드 중복 제거, 코드 재사용의 장점이 있다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;유지보수 용이 : &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;여러 객체를 하나의 타입으로 관리가 가능하기 때문에 유지보수가 용이하다.&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;유연성 : 다형성을 사용하면 객체를 추상화하고, 해당 객체를 사용하는 다른 객체에서 동일한 클래스 또는 인터페이스를 사용할 수 있다. 이를 통해 객체 간의 결합도를 낮추고 코드의 유연성과 확장성을 높일 수 있다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;  정리&lt;/b&gt; &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;객체지향 프로그래밍&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;컴퓨터 프로그래밍 패러다임 중 하나로 현실 세계의 사물이나 개념을 객체로 보고 그 객체들 간의 상호작용으로 프로그래밍하는 방법&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;구성 요소&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;클래스 : 객체를 정의해놓은 것으로, 객체를 생성하는데 사용된다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;메시지 : 객체들 간의 상호작용에 사용되는 수단으로, 객체에 어떠한 행위를 하도록 지시하는 명령 또는 요구사항&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;객체 : 현실 세계의 사물이나 개념&lt;/span&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;특징&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;추상화 : 객체의 공통적인 속성과 기능을 추출하여 정의&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;캡슐화 : 객체의 속성과 기능을 캡슐처럼 외부에서 직접 접근하지 못하도록 제한하는 정보은닉의 개념 중 하나&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;상속 : 상위 클래스(부모 클래스)로부터 속성(변수)과 기능(메서드)을 하위 클래스(자식 클래스)가 물려받는 것&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;다형성 : 하나의 객체가 여러 가지 타입을 가질 수 있는 것&lt;/span&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;b&gt;  참고&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;자바의정석 [남궁성]&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR';&quot;&gt;&lt;a href=&quot;https://ko.wikipedia.org/wiki/%EA%B0%9D%EC%B2%B4_%EC%A7%80%ED%96%A5_%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://ko.wikipedia.org/wiki/%EA%B0%9D%EC%B2%B4_%EC%A7%80%ED%96%A5_%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Object-oriented programming</category>
      <author>말차쟁이</author>
      <guid isPermaLink="true">https://shine-17.tistory.com/5</guid>
      <comments>https://shine-17.tistory.com/5#entry5comment</comments>
      <pubDate>Sun, 4 Aug 2024 17:24:28 +0900</pubDate>
    </item>
  </channel>
</rss>