Jay·2023년 8월 27일

Java

목록 보기
6/6
Java

body {
line-height: 1.5;
white-space: pre-wrap;
}

a,
a.visited {
color: inherit;
text-decoration: underline;
}

.pdf-relative-link-path {
font-size: 80%;
color: #444;
}

h1,
h2,
h3 {
letter-spacing: -0.01em;
line-height: 1.2;
font-weight: 600;
margin-bottom: 0;
}

.page-title {
font-size: 2.5rem;
font-weight: 700;
margin-top: 0;
margin-bottom: 0.75em;
}

h1 {
font-size: 1.875rem;
margin-top: 1.875rem;
}

h2 {
font-size: 1.5rem;
margin-top: 1.5rem;
}

h3 {
font-size: 1.25rem;
margin-top: 1.25rem;
}

.source {
border: 1px solid #ddd;
border-radius: 3px;
padding: 1.5em;
word-break: break-all;
}

.callout {
border-radius: 3px;
padding: 1rem;
}

figure {
margin: 1.25em 0;
page-break-inside: avoid;
}

figcaption {
opacity: 0.5;
font-size: 85%;
margin-top: 0.5em;
}

mark {
background-color: transparent;
}

.indented {
padding-left: 1.5em;
}

hr {
background: transparent;
display: block;
width: 100%;
height: 1px;
visibility: visible;
border: none;
border-bottom: 1px solid rgba(55, 53, 47, 0.09);
}

img {
max-width: 100%;
}

@media only print {
img {
max-height: 100vh;
object-fit: contain;
}
}

@page {
margin: 1in;
}

.collection-content {
font-size: 0.875rem;
}

.column-list {
display: flex;
justify-content: space-between;
}

.column {
padding: 0 1em;
}

.column:first-child {
padding-left: 0;
}

.column:last-child {
padding-right: 0;
}

.table_of_contents-item {
display: block;
font-size: 0.875rem;
line-height: 1.3;
padding: 0.125rem;
}

.table_of_contents-indent-1 {
margin-left: 1.5rem;
}

.table_of_contents-indent-2 {
margin-left: 3rem;
}

.table_of_contents-indent-3 {
margin-left: 4.5rem;
}

.table_of_contents-link {
text-decoration: none;
opacity: 0.7;
border-bottom: 1px solid rgba(55, 53, 47, 0.18);
}

table,
th,
td {
border: 1px solid rgba(55, 53, 47, 0.09);
border-collapse: collapse;
}

table {
border-left: none;
border-right: none;
}

th,
td {
font-weight: normal;
padding: 0.25em 0.5em;
line-height: 1.5;
min-height: 1.5em;
text-align: left;
}

th {
color: rgba(55, 53, 47, 0.6);
}

ol,
ul {
margin: 0;
margin-block-start: 0.6em;
margin-block-end: 0.6em;
}

li > ol:first-child,
li > ul:first-child {
margin-block-start: 0.6em;
}

ul > li {
list-style: disc;
}

ul.to-do-list {
padding-inline-start: 0;
}

ul.to-do-list > li {
list-style: none;
}

.to-do-children-checked {
text-decoration: line-through;
opacity: 0.375;
}

ul.toggle > li {
list-style: none;
}

ul {
padding-inline-start: 1.7em;
}

ul > li {
padding-left: 0.1em;
}

ol {
padding-inline-start: 1.6em;
}

ol > li {
padding-left: 0.2em;
}

.mono ol {
padding-inline-start: 2em;
}

.mono ol > li {
text-indent: -0.4em;
}

.toggle {
padding-inline-start: 0em;
list-style-type: none;
}

/ Indent toggle children /
.toggle > li > details {
padding-left: 1.7em;
}

.toggle > li > details > summary {
margin-left: -1.1em;
}

.selected-value {
display: inline-block;
padding: 0 0.5em;
background: rgba(206, 205, 202, 0.5);
border-radius: 3px;
margin-right: 0.5em;
margin-top: 0.3em;
margin-bottom: 0.3em;
white-space: nowrap;
}

.collection-title {
display: inline-block;
margin-right: 1em;
}

.page-description {
margin-bottom: 2em;
}

.simple-table {
margin-top: 1em;
font-size: 0.875rem;
empty-cells: show;
}
.simple-table td {
height: 29px;
min-width: 120px;
}

.simple-table th {
height: 29px;
min-width: 120px;
}

.simple-table-header-color {
background: rgb(247, 246, 243);
color: black;
}
.simple-table-header {
font-weight: 500;
}

time {
opacity: 0.5;
}

.icon {
display: inline-block;
max-width: 1.2em;
max-height: 1.2em;
text-decoration: none;
vertical-align: text-bottom;
margin-right: 0.5em;
}

img.icon {
border-radius: 3px;
}

.user-icon {
width: 1.5em;
height: 1.5em;
border-radius: 100%;
margin-right: 0.5rem;
}

.user-icon-inner {
font-size: 0.8em;
}

.text-icon {
border: 1px solid #000;
text-align: center;
}

.page-cover-image {
display: block;
object-fit: cover;
width: 100%;
max-height: 30vh;
}

.page-header-icon {
font-size: 3rem;
margin-bottom: 1rem;
}

.page-header-icon-with-cover {
margin-top: -0.72em;
margin-left: 0.07em;
}

.page-header-icon img {
border-radius: 3px;
}

.link-to-page {
margin: 1em 0;
padding: 0;
border: none;
font-weight: 500;
}

p > .user {
opacity: 0.5;
}

td > .user,
td > time {
white-space: nowrap;
}

input[type="checkbox"] {
transform: scale(1.5);
margin-right: 0.6em;
vertical-align: middle;
}

p {
margin-top: 0.5em;
margin-bottom: 0.5em;
}

.image {
border: none;
margin: 1.5em 0;
padding: 0;
border-radius: 0;
text-align: center;
}

.code,
code {
background: rgba(135, 131, 120, 0.15);
border-radius: 3px;
padding: 0.2em 0.4em;
border-radius: 3px;
font-size: 85%;
tab-size: 2;
}

code {
color: #eb5757;
}

.code {
padding: 1.5em 1em;
}

.code-wrap {
white-space: pre-wrap;
word-break: break-all;
}

.code > code {
background: none;
padding: 0;
font-size: 100%;
color: inherit;
}

blockquote {
font-size: 1.25em;
margin: 1em 0;
padding-left: 1em;
border-left: 3px solid rgb(55, 53, 47);
}

.bookmark {
text-decoration: none;
max-height: 8em;
padding: 0;
display: flex;
width: 100%;
align-items: stretch;
}

.bookmark-title {
font-size: 0.85em;
overflow: hidden;
text-overflow: ellipsis;
height: 1.75em;
white-space: nowrap;
}

.bookmark-text {
display: flex;
flex-direction: column;
}

.bookmark-info {
flex: 4 1 180px;
padding: 12px 14px 14px;
display: flex;
flex-direction: column;
justify-content: space-between;
}

.bookmark-image {
width: 33%;
flex: 1 1 180px;
display: block;
position: relative;
object-fit: cover;
border-radius: 1px;
}

.bookmark-description {
color: rgba(55, 53, 47, 0.6);
font-size: 0.75em;
overflow: hidden;
max-height: 4.5em;
word-break: break-word;
}

.bookmark-href {
font-size: 0.75em;
margin-top: 0.25em;
}

.sans { font-family: ui-sans-serif, -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, "Apple Color Emoji", Arial, sans-serif, "Segoe UI Emoji", "Segoe UI Symbol"; }
.code { font-family: "SFMono-Regular", Menlo, Consolas, "PT Mono", "Liberation Mono", Courier, monospace; }
.serif { font-family: Lyon-Text, Georgia, ui-serif, serif; }
.mono { font-family: iawriter-mono, Nitti, Menlo, Courier, monospace; }
.pdf .sans { font-family: Inter, ui-sans-serif, -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, "Apple Color Emoji", Arial, sans-serif, "Segoe UI Emoji", "Segoe UI Symbol", 'Twemoji', 'Noto Color Emoji', 'Noto Sans CJK JP'; }
.pdf:lang(zh-CN) .sans { font-family: Inter, ui-sans-serif, -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, "Apple Color Emoji", Arial, sans-serif, "Segoe UI Emoji", "Segoe UI Symbol", 'Twemoji', 'Noto Color Emoji', 'Noto Sans CJK SC'; }
.pdf:lang(zh-TW) .sans { font-family: Inter, ui-sans-serif, -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, "Apple Color Emoji", Arial, sans-serif, "Segoe UI Emoji", "Segoe UI Symbol", 'Twemoji', 'Noto Color Emoji', 'Noto Sans CJK TC'; }
.pdf:lang(ko-KR) .sans { font-family: Inter, ui-sans-serif, -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, "Apple Color Emoji", Arial, sans-serif, "Segoe UI Emoji", "Segoe UI Symbol", 'Twemoji', 'Noto Color Emoji', 'Noto Sans CJK KR'; }
.pdf .code { font-family: Source Code Pro, "SFMono-Regular", Menlo, Consolas, "PT Mono", "Liberation Mono", Courier, monospace, 'Twemoji', 'Noto Color Emoji', 'Noto Sans Mono CJK JP'; }
.pdf:lang(zh-CN) .code { font-family: Source Code Pro, "SFMono-Regular", Menlo, Consolas, "PT Mono", "Liberation Mono", Courier, monospace, 'Twemoji', 'Noto Color Emoji', 'Noto Sans Mono CJK SC'; }
.pdf:lang(zh-TW) .code { font-family: Source Code Pro, "SFMono-Regular", Menlo, Consolas, "PT Mono", "Liberation Mono", Courier, monospace, 'Twemoji', 'Noto Color Emoji', 'Noto Sans Mono CJK TC'; }
.pdf:lang(ko-KR) .code { font-family: Source Code Pro, "SFMono-Regular", Menlo, Consolas, "PT Mono", "Liberation Mono", Courier, monospace, 'Twemoji', 'Noto Color Emoji', 'Noto Sans Mono CJK KR'; }
.pdf .serif { font-family: PT Serif, Lyon-Text, Georgia, ui-serif, serif, 'Twemoji', 'Noto Color Emoji', 'Noto Serif CJK JP'; }
.pdf:lang(zh-CN) .serif { font-family: PT Serif, Lyon-Text, Georgia, ui-serif, serif, 'Twemoji', 'Noto Color Emoji', 'Noto Serif CJK SC'; }
.pdf:lang(zh-TW) .serif { font-family: PT Serif, Lyon-Text, Georgia, ui-serif, serif, 'Twemoji', 'Noto Color Emoji', 'Noto Serif CJK TC'; }
.pdf:lang(ko-KR) .serif { font-family: PT Serif, Lyon-Text, Georgia, ui-serif, serif, 'Twemoji', 'Noto Color Emoji', 'Noto Serif CJK KR'; }
.pdf .mono { font-family: PT Mono, iawriter-mono, Nitti, Menlo, Courier, monospace, 'Twemoji', 'Noto Color Emoji', 'Noto Sans Mono CJK JP'; }
.pdf:lang(zh-CN) .mono { font-family: PT Mono, iawriter-mono, Nitti, Menlo, Courier, monospace, 'Twemoji', 'Noto Color Emoji', 'Noto Sans Mono CJK SC'; }
.pdf:lang(zh-TW) .mono { font-family: PT Mono, iawriter-mono, Nitti, Menlo, Courier, monospace, 'Twemoji', 'Noto Color Emoji', 'Noto Sans Mono CJK TC'; }
.pdf:lang(ko-KR) .mono { font-family: PT Mono, iawriter-mono, Nitti, Menlo, Courier, monospace, 'Twemoji', 'Noto Color Emoji', 'Noto Sans Mono CJK KR'; }
.highlight-default {
color: rgba(55, 53, 47, 1);
}
.highlight-gray {
color: rgba(120, 119, 116, 1);
fill: rgba(120, 119, 116, 1);
}
.highlight-brown {
color: rgba(159, 107, 83, 1);
fill: rgba(159, 107, 83, 1);
}
.highlight-orange {
color: rgba(217, 115, 13, 1);
fill: rgba(217, 115, 13, 1);
}
.highlight-yellow {
color: rgba(203, 145, 47, 1);
fill: rgba(203, 145, 47, 1);
}
.highlight-teal {
color: rgba(68, 131, 97, 1);
fill: rgba(68, 131, 97, 1);
}
.highlight-blue {
color: rgba(51, 126, 169, 1);
fill: rgba(51, 126, 169, 1);
}
.highlight-purple {
color: rgba(144, 101, 176, 1);
fill: rgba(144, 101, 176, 1);
}
.highlight-pink {
color: rgba(193, 76, 138, 1);
fill: rgba(193, 76, 138, 1);
}
.highlight-red {
color: rgba(212, 76, 71, 1);
fill: rgba(212, 76, 71, 1);
}
.highlight-gray_background {
background: rgba(241, 241, 239, 1);
}
.highlight-brown_background {
background: rgba(244, 238, 238, 1);
}
.highlight-orange_background {
background: rgba(251, 236, 221, 1);
}
.highlight-yellow_background {
background: rgba(251, 243, 219, 1);
}
.highlight-teal_background {
background: rgba(237, 243, 236, 1);
}
.highlight-blue_background {
background: rgba(231, 243, 248, 1);
}
.highlight-purple_background {
background: rgba(244, 240, 247, 0.8);
}
.highlight-pink_background {
background: rgba(249, 238, 243, 0.8);
}
.highlight-red_background {
background: rgba(253, 235, 236, 1);
}
.block-color-default {
color: inherit;
fill: inherit;
}
.block-color-gray {
color: rgba(120, 119, 116, 1);
fill: rgba(120, 119, 116, 1);
}
.block-color-brown {
color: rgba(159, 107, 83, 1);
fill: rgba(159, 107, 83, 1);
}
.block-color-orange {
color: rgba(217, 115, 13, 1);
fill: rgba(217, 115, 13, 1);
}
.block-color-yellow {
color: rgba(203, 145, 47, 1);
fill: rgba(203, 145, 47, 1);
}
.block-color-teal {
color: rgba(68, 131, 97, 1);
fill: rgba(68, 131, 97, 1);
}
.block-color-blue {
color: rgba(51, 126, 169, 1);
fill: rgba(51, 126, 169, 1);
}
.block-color-purple {
color: rgba(144, 101, 176, 1);
fill: rgba(144, 101, 176, 1);
}
.block-color-pink {
color: rgba(193, 76, 138, 1);
fill: rgba(193, 76, 138, 1);
}
.block-color-red {
color: rgba(212, 76, 71, 1);
fill: rgba(212, 76, 71, 1);
}
.block-color-gray_background {
background: rgba(241, 241, 239, 1);
}
.block-color-brown_background {
background: rgba(244, 238, 238, 1);
}
.block-color-orange_background {
background: rgba(251, 236, 221, 1);
}
.block-color-yellow_background {
background: rgba(251, 243, 219, 1);
}
.block-color-teal_background {
background: rgba(237, 243, 236, 1);
}
.block-color-blue_background {
background: rgba(231, 243, 248, 1);
}
.block-color-purple_background {
background: rgba(244, 240, 247, 0.8);
}
.block-color-pink_background {
background: rgba(249, 238, 243, 0.8);
}
.block-color-red_background {
background: rgba(253, 235, 236, 1);
}
.select-value-color-interactiveBlue { background-color: rgba(35, 131, 226, .07); }
.select-value-color-pink { background-color: rgba(245, 224, 233, 1); }
.select-value-color-purple { background-color: rgba(232, 222, 238, 1); }
.select-value-color-green { background-color: rgba(219, 237, 219, 1); }
.select-value-color-gray { background-color: rgba(227, 226, 224, 1); }
.select-value-color-translucentGray { background-color: rgba(255, 255, 255, 0.0375); }
.select-value-color-orange { background-color: rgba(250, 222, 201, 1); }
.select-value-color-brown { background-color: rgba(238, 224, 218, 1); }
.select-value-color-red { background-color: rgba(255, 226, 221, 1); }
.select-value-color-yellow { background-color: rgba(253, 236, 200, 1); }
.select-value-color-blue { background-color: rgba(211, 229, 239, 1); }
.select-value-color-pageGlass { background-color: undefined; }
.select-value-color-washGlass { background-color: undefined; }

.checkbox {
display: inline-flex;
vertical-align: text-bottom;
width: 16;
height: 16;
background-size: 16px;
margin-left: 2px;
margin-right: 5px;
}

.checkbox-on {
background-image: url("data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%2216%22%20height%3D%2216%22%20viewBox%3D%220%200%2016%2016%22%20fill%3D%22none%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%3Crect%20width%3D%2216%22%20height%3D%2216%22%20fill%3D%22%2358A9D7%22%2F%3E%0A%3Cpath%20d%3D%22M6.71429%2012.2852L14%204.9995L12.7143%203.71436L6.71429%209.71378L3.28571%206.2831L2%207.57092L6.71429%2012.2852Z%22%20fill%3D%22white%22%2F%3E%0A%3C%2Fsvg%3E");
}

.checkbox-off {
background-image: url("data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%2216%22%20height%3D%2216%22%20viewBox%3D%220%200%2016%2016%22%20fill%3D%22none%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%3Crect%20x%3D%220.75%22%20y%3D%220.75%22%20width%3D%2214.5%22%20height%3D%2214.5%22%20fill%3D%22white%22%20stroke%3D%22%2336352F%22%20stroke-width%3D%221.5%22%2F%3E%0A%3C%2Fsvg%3E");
}

Java

  • Java용어정리
    💡
    이번 챕터에서 나오는 용어 정리
    1. 데이터🔢 : 컴퓨터가 정보를 담는 자료값
    1. 데이터 타입🔢(자료형) : 데이터의 유형 ex. 정수 / 문자 / 부동소수(소숫점)
    1. 자료구조🗳️ : 데이터 여러개를 분류해서 담을 수 있는 분류통🗳️
    1. 문맥💌  : 문맥이라 함은 개발언어로 표현한 프로그램의 동작 흐름을 의미합니다. 이케아 가구 조립 설명서, 밀키트 요리 레시피에 조건에 따른 문맥이 있듯이 프로그램도 문맥을 가지고 있습니다.
    1. 메서드🎁 : 데이터🔢와 문맥💌을 감싸는 포장지🎁 입니다.
    1. 공통 실행환경🗺️ : 여러 기기의 운영환경에서 실행 할 수 있도록 판을 깔아주는 실행환경
    1. 운영 체제📱: 기기가 프로그램을 실행시키는 운영환경 (ex. Window, Android, iOS, Linux …)
  • Mac 에 JDK 설치하기

    1. https://brew.sh/index_ko 에 접속하여 Homebrew 설치하기 명령어 복사하기 버튼을 클릭합니다.

    1. 터미널을 열어서 복사한 명령어를 붙여넣기하고 엔터를 쳐서 실행해줍니다. (Password 를 요청하면 잠금 비밀번호를 입력해줍니다.)

    1. 이렇게 brew 설치가 완료되면, JDK 를 설치할 저장소를 brew 등록해주는 아래 명령어를 실행합니다.
    brew tap homebrew/cask-versions
    1. 저장소 등록이 완료되면 아래 명령어를 통해 JDK 를 설치해줍니다.
    brew install --cask temurin17
    1. 설치가 완료되면 java -version 명령어를 실행했을때 버전이 나오면 잘 설치된것 입니다.
    java -version

  • Mac 에 IntelliJ 설치하기
    1. 터미널을 켜고 아래 명령어 실행
    brew install --cask intellij-idea-ce

    1. 설치가 모두 완료되면 배경화면에서 IntelliJ 앱 실행 후 New Project 클릭합니다.
    1. New Project 팝업에서 Name 입력해주고 Location~₩Desktop 으로 설정 후 Create 클릭 (배경화면이 아닌 다른 경로에 프로젝트를 저장하고 싶으면 폴더 아이콘을 눌러서 지정하면 됩니다.)
    1. -1.생성된 프로젝트의 Main 파일을 열어서 -2.main 의 중괄호({}) 사이에 아래 코드 입력 후 -3.오른쪽 위 초록색 재생버튼 클릭하여 Java 프로그램을 실행한뒤 -4.아래 콘솔에서 Java 프로그램이 출력한 값을 확인합니다.
    System.out.println("Hello world!");
    1. Hello World! 가 정상적으로 출력되었으면 IntelliJ 설치가 완료된것 입니다.

  • 기본지식
    C언어기능 + 공통 실행환경,클래스,객체
    자바 장점 5가지
    1. 고통 실행환경이 있어서 여러 기기에서 실행이 가능
    2. 객체를 사용하여 이해및 유지보수가 쉬움
    3. compiler가 먼저 오류를 체크하여 안정성이 높음
    // 대신 다른 언어보다 무거움
    4. 네트워크및 데이터베이스 연결 및 통신등의 작업을 처리하는 API를 제공
    5. 다양한 개발 도구와 라이브러리

    JVM

    Java Virtual Machine 자바 가상 머신
    가상의 기기를 만들어 주는것
    => Java프로그램을 실행시킬 수 있는 가상의 기기를 만들어 주는것

    바이트 코드

    내가 작성한 코드가 운영체제가 읽을 수 있는 코드(바이트 코드💬)로 Java 컴파일러가 변환한 코드
    💡
    Java 컴파일러란?

    내가 작성한 Java 코드들(.java 파일)을 운영체제가 읽을 수 있는 바이트 코드💬(.class 파일)로 변환하는 변환기

    인터프리터📇

    Java .class 코드 해석기

    • 운영체제가 읽은 바이트 코드💬를 기기(기계)가 실행할 수 있는 기계어로 번역

    JIT 컴파일러📇

    빠른 Java .class 코드 해석기

    • 인터프리터의 효율을 높여주는 서포터 해석기

    메모리 영역🗂️

    Java 데이터를 저장하는 영역

    • 운영체제로 부터 JVM이 할당받은 메모리 영역

    클래스 로더🚚

    Java .class 바이트 코드💬를 메모리 영역에 담는 운반기

    • JVM으로 class(바이트 코드💬)를 불러와서 메모리🗂️에 저장함

    가비지 컬렉터🧹

    Java 쓰레기 청소기

    • 메모리 영역🗂️에서 안쓰는 데이터를 주기적으로 흡수해가는 청소기

    💡
    JVM 놀이터에서 프로그램이 동작하는 흐름
    • 놀이터 그림으로 표현하자면 흐름은 아래와 같다.
      • 여기서, Runtime 은 “프로그램이 실행중인 상태”를 말합니다.
      • 따라서, Runtime 시스템은 “프로그램이 실행중인 상태를 관리하는 시스템” 입니다.
    • 좀더 개발자🧑‍💻스럽게 그룹지어서 정리하자면 아래 그림과 같습니다.
    JRE
    - Java Runtime Environment 자바 실행 환경
    - Java프로그램을 실행만 시킬 수 있음
    - .class파일만 실행 가능하다
    우리가 작성하는것은 .java파일로 JRE만으로는 기능을 할 수 없다
    

    JDK

    • Java Development Kit 자바 개발 키트
    • javac명령을 통해 .java파일을 .class파일로 변환
    • JRE(JVM)기능을 포함
    • 코드를 디버깅하는 jdb등의 기능
    💡
    디버깅 = 중단점 일시정지 + 코드 라인단위 수행
  • 자바 기본 구조
    import java.util.Arrays;
    import java.util.Scanner;
    // import = im(in) + port(항구) -> 바깥쪽에서 안쪽을 가져오기

// 클래스
// public : (접근)제어자 -> 어디까지 접근하게 해줄껀지
public class Main { // 클래스의 명은 .java파일과 동일하여야 한다

// JDK 역할
// 1. .java 파일을 .class 파일로 변환하는 compiler

// 2. JRE

// 3. JDB : 디버깅(버그를 없애기위해 코드를 살피는 과정)
// -> 웹브라우저처럼 해당 위치의 순번을 클릭하고 벌레모양의 디버그 마크 클릭 이어서 재생하고싶으면 하단왼쪽의 재생버튼 누르기

// () : 소괄호
// {} : 중괄호
// [] : 대괄호

// main 메소드 부분
// 무조건 main메소드가 있어야한다
// 자바 프로젝트(앱)은 제일먼저 클래스의 main메소드를 실행
// = JVM의 약속
// static : 이 프로그램이 시작될 때 무조건 실행된다는 표현
// input output 타입을 제한

// output
// static다음에 오는곳이 메소드의 출력 데이터 타입을 정함
// void : return이 없다

// input
// String[] args : 매개변수 자리
// 매개변수 타입과 변수명을 지정
// 변수명의 제한은 없음

public static void main(String[] args) {

    // 객체 : 특징(속성,변수), 행동(메소드)
    // 자바에서 하위 요소를 표현할때 .으로 들어감
    // println -> 줄바꿈
    // print -> 줄바꿈x
    // ln : line
    System.out.println("야야양 왜이리 느려");
    System.out.print("여기는 뭐니");
    System.out.println("여기는 뭐니222");


    System.out.println(7);
    System.out.println(3);
    System.out.println(3.14);
    System.out.println("수비JAVA");

}

}

  • 변수 상수
    // 변수 : 변하는 값
    // 상수 : 변하지 않는 값
    // 변수를 사용하는 이유 : 유지보수를 위해
    // 코드를 수정할때 굳이 하나하나씩 수정하지않고 변수 하나만 바꾸면 아래가 변경되는등의 편리성도 있다

        // 변수
        int number = 10;
        System.out.println(number);
        number = 222;
        System.out.println(number);
    
        // 상수
        final int num = 2222;
        System.out.println(num);</code></pre></details></li></ul><ul id="e5aabdd5-9967-4bfe-a3a1-d023aa85513b" class="toggle"><li><details open=""><summary>변수 타입(stack heap)</summary><figure id="4ffce2ca-7232-4b6f-8d1f-f7388de74201" class="image"><a href="Java%20bd4a58d5f12642da8418ff1b8949afe5/Untitled_(2).png"><img style="width:1860px" src="Java%20bd4a58d5f12642da8418ff1b8949afe5/Untitled_(2).png"/></a></figure><pre id="734f36be-d0cc-477a-96e3-5803704dbe17" class="code"><code>기본형 변수 : 값을 저장하는 저장공간으로 실제값(리터럴)을저장

    참조형 변수 : 실제값이 아닌 주소값을 저장

    메모리는 공간마다 주소값을 가지고 있다

    • 기본형 변수는 Stack영역에 선언한 변수만큼의 공간이 생성이 되고 그공간 안에 값이 들어가게 된다
    • 참조형 변수의 경우 해당 변수의 크기만큼 고정된 크기가 Stack에 생성되고 주소값이 저장된다
    • 그리고 new를 통해 Heap영역에 새로운 저장공간을 생성하게 된다
    • 공간의 크기는 우리가 넣으려는 값의 크기만큼 생성한다
    • 이 공간은 메모리 주소값을 할당받게된다 ## Heap영역뿐만 아니라 Stack영역에서 생성된 공간도 주소는 할당받는다

    Stack의 경우에는 정적으로 할당된 메모리 영역
    -> 크기가 정해져있는 값을 저장
    Heap의 경우에는 동적으로 할당된 메모리 영역
    -> 크기가 정해져있지 않은 값을 저장

    • 기본형 변수
      				// boolean
      boolean flag = true; // 1. 논리형 변수 boolean 으로 선언 및 True 값으로 초기화

          flag = false; // 2. False 값으로도 저장할 수 있습니다.
      
          // 문자형
          char alphabet = &#x27;A&#x27;; // 문자 하나를 저장합니다. &quot;&quot;로 입력하면 문자열로 인식
      
          // 정수형
          // 카멜케이스 -&gt; 변수명중 중간에 대문자가 있는형태
          byte byteNumber = 127; // byte 는 -128 ~ 127 범위의 숫자만 저장 가능합니다. 1byte만 표현 가능
          // 1byte = 8bit
          // bit = 0과1을 표현하는 최소단위
          // 비트마다 리소스를 확인하는게 비효율적이니까 byte를 만듬
          // 2의8제곱 -&gt; 256 -&gt; 표현할수있는 범위 -128 ~ 127
      
          short shortNumber = 32767; // short 는 -32,768~32,767 범위의 숫자만 저장 가능합니다.
      
          int intNumber = 2147483647; // int 는 -21억~21억 범위의 숫자만 저장 가능합니다.
      
          long longNumber = 2147483647L; // long 은 숫자뒤에 알파벳 L 을 붙여서 표기하며 매우 큰수를 저장 가능합니다.
      
      			// 데이터값(리터럴) 뒤에 붙이는 구분값을 “접미사”라고 부릅니다.
      			// byte -&gt; short(2byte) -&gt; int(4byte) -&gt; long(8bite)
      
          // 실수형
          float floatNumber = 0.123F; // float 는 4byte 로 3.4 * 10^38 범위를 표현하는 실수값
          double doubleNumber = 0.123123123; // double 은 8byte 로 1.7 * 10^308 범위를 표현하는 실수값</code></pre></details></li></ul><ul id="12ff8f4c-5c78-4b42-9b4c-93be51dbc258" class="toggle"><li><details open=""><summary>참조형 변수</summary><pre id="a4402144-3f2e-4acc-b3d7-60e651579652" class="code"><code>// 기본형을 제외한 나머지들 이외에도 많다
      
      			// 문자열
          String hello = &quot;야야야야야야ㅑ야양&quot;;
          System.out.println(hello);
      
          // 배열
          int[] arr = {1,2,3,4,5};
          System.out.println(arr[0]);
          System.out.println(Arrays.toString(arr));</code></pre></details></li></ul><ul id="24fa79bf-57e1-49f9-8b66-ed23ac2cb1d7" class="toggle"><li><details open=""><summary>레퍼클래스 변수</summary><pre id="47eb42b6-3202-472a-99e4-59cdf999c6d3" class="code"><code>기본형 변수를 클래스로 랩핑하는 변수
      • 해당하는 메소드를 사용할 수 있는 장점
      기본 타입 래퍼 클래스
      byteByte
      shortShort
      intInteger
      longLong
      floatFloat
      doubleDouble
      charCharacter
      booleanBoolean
  • 아스키 코드
    // 숫자 -> 문자
    import java.util.Scanner;

public class Main {

public static void main(String[] args) {
	Scanner sc = new Scanner(System.in);

	int asciiNumber = sc.nextInt();
	char ch = (char)asciiNumber; // 문자로 형변환을 해주면 숫자에 맞는 문자로 표현됩니다.

	System.out.println(ch);
}

}

// 입력
97

// 출력
a

// 문자 -> 숫자

import java.util.Scanner;

public class Main {

public static void main(String[] args) {
	Scanner sc = new Scanner(System.in);

	char letter = sc.nextLine().charAt(0); // 첫번째 글자만 받아오기위해 charAt(0) 메서드를 사용합니다.
	int asciiNumber = (int)letter; // 숫자로 형변환을 해주면 저장되어있던 아스키 숫자값으로 표현됩니다.

	System.out.println(asciiNumber);
}

}

// 입력
a

// 출력
97

  • 문자와 문자열
    문자는 선언할때 ''를 사용해야한다
    문자열의 경우 ""를 사용한다

    문자 뒤에는 \0(널문자)가 없다 -> 1byte만 쓰기 때문에 끝을 알 수 있어서
    문자열의 경우 널문자와 함께저장되는데 몇개의 byte를 사용하게 될지 모르기때문이다

  • Scanner 클래스
    • java.util 패키지 내에서 존재하는 클래스
    • 정수,실수등의 기본적인 데이터 타입 입력을 받기위한 클래스
    • 문자열,한줄(엔터키 기준)을 모두 읽기 위해서는 nextLine()함수를 사용
    • 단일 문자(char)를 읽기 위해서 next()와 charAt()함수를 함께 사용 가능
    import java.util.Scanner;

    public class myScanner
    {
    public static void main(String[] args)
    {
    Scanner scanner = new Scanner(System.in);

        int i = scanner.nextInt();
        char c = scanner.next().charAt(0);
        long l = scanner.nextLong();
        double d = scanner.nextDouble();
        String s = scanner.nextLine();
    
        System.out.println(&quot;int : &quot; + i);
        System.out.println(&quot;char : &quot; + c);
        System.out.println(&quot;long : &quot; + l);
        System.out.println(&quot;double : &quot; + d);
        System.out.println(&quot;string : &quot; + s);
    }

    }

    • hasNext~()함수를 사용하여, 원하는 만큼 입력을 받을수도 있다.
    • boolean타입으로 다음에 가져올값이 있으면 true,아니면 false
    import java.util.Scanner;

    public class myScanner
    {
    public static void main(String[] args)
    {
    Scanner scanner = new Scanner(System.in);

        int i = 0;
    
        while(scanner.hasNextInt())
        {
            int num = scanner.nextInt();
            i += num;
        }
    
        System.out.println(&quot;total : &quot; + i);
    }

    }

  • bit byte
    • Bit
      • Bit(비트)는 0,1 형태의 2진수 데이터로써 컴퓨터가 저장(표현)할 수 있는 최소 단위 입니다.
      • 정수형 값은 10진수 숫자(0~10범위의 숫자)이며 2진수(0~1범위)Bit 로 저장(표현) 합니다.
      • 4개의 Bit로 16진수 숫자(0~F(16)범위의 숫자)를 2진수(0~1범위)Bit 로 저장(표현) 합니다.
      • 2진수(0~1)를 10진수, 16진수로 변환된 값 예시 표
    • Byte = 8 Bit
      • Byte(바이트)는 8개의 Bit(비트)로 구성되어 있습니다.
      • 1 Byte 내에서 숫자 변수는 Bit 2진수를 10진수로 저장(표현)을 합니다.
        • 10진수로는 0~255(2의8승)까지 저장(표현) 합니다.
      • 1 Byte 내에서 문자 변수의 경우만 Bit 2진수를 16진수로 저장(표현)합니다
  • 형변환
    // 형변화 : 변수의 타입을 바꾸는 방법
    // 문자열 -> 숫자
    // 정수 -> 실수
    // 실수 -> 정수

    double doubleNum = 10.1010101;
    float floatNum = 10.10101f;

        int intNum;
    
        intNum = (int)doubleNum;
        System.out.println(intNum);
        intNum = (int)floatNum;
        System.out.println(intNum);
    
        // 정수 -&gt; 실수
        int intNumber2 = 10;
    
        double doubletest = (double) intNumber2;
        System.out.println(doubletest);
    
       float floattest = (float) intNumber2;
        System.out.println(floattest);</code></pre><pre id="2fe552b4-1a46-447d-947b-4ef78631be37" class="code"><code>// 암시적인형변환 : 자동으로 형변환
        // 변수 타입별 크기 순서
        // byte(1) -&gt; short(2) -&gt; int(4) -&gt; long(8) -&gt; float(4) -&gt; double(8)
        // 큰타입에 작은타입을 넣어주면 자동현변환가능
    
        // byte -&gt; int
        byte bytenum = 10;
        int intnum = bytenum;
        System.out.println(&quot;여기요&quot;+intnum);
    
        // char -&gt; int
        char charalp = &#x27;A&#x27;;
        intnum = charalp; //
        System.out.println(&quot;여기요&quot;+intnum); // 65 -&gt; 아스키코드값출력
    
        // int -&gt; long
        intnum = 100;
        long longnum = intnum;
        System.out.println(&quot;여기요&quot;+intnum);
    
        // int -&gt; double
        intnum = 200;
       double doublenum = intnum;
        System.out.println(&quot;여기요&quot;+doublenum);
    
        // 작은 크기의 타입이 큰 크기의 타입과 계산될때
        // 자동으로 큰 크기의 타입으로 형변환이 됨
        intnum = 10;
        doublenum = 5.5;
        // double이 더커서 나오는 값의 데이터 타입은 double로 나옴
        System.out.println(intnum + doublenum);
    
        int iresult = intnum/4;
        System.out.println(iresult);
    
        double dresult = intnum/4.0;
        System.out.println(dresult);</code></pre></details></li></ul><ul id="08992f00-22a8-4467-84fc-9545ef61acd2" class="toggle"><li><details open=""><summary>연산자</summary><ul id="0f581b30-3cf7-4085-8f9b-90ae73ce2b87" class="bulleted-list"><li style="list-style-type:disc">연산자 : 덧셈, 뺄셈 처럼 계산할 기호 ➕</li></ul><ul id="5a30ae62-9118-4999-81f8-56d411eb272e" class="bulleted-list"><li style="list-style-type:disc">피연산자 : 연산자로 인해 계산되는 숫자 🔢</li></ul><ul id="2062389d-9f3d-4153-828f-a034d7cfde6a" class="toggle"><li><details open=""><summary>산술 연산자</summary><ul id="ff0b61ad-f735-4b80-bb98-99bb6e1b0edd" class="bulleted-list"><li style="list-style-type:disc">사칙 연산 및 비트연산을 포함합니다.  (비트연산은 다른 챕터에서 다뤄보아요.)</li></ul><ul id="a188f4ce-824f-4298-b635-5eb3b55ed6ee" class="bulleted-list"><li style="list-style-type:disc">사칙 연산을 계산한 결과값을 응답합니다.</li></ul><ul id="528032d1-c34d-4e29-9f9e-f7047be5edc1" class="bulleted-list"><li style="list-style-type:disc"><code>+</code>(덧셈),  <code>-</code> (뺄셈),  <code>*</code>(곱셈),  <code>/</code>(나눗셈/몫),  <code>%</code> (나눗셈/나머지)</li></ul><pre id="e179f186-8528-4bc2-921e-6bd887dd554b" class="code"><code>// 사칙 연산

    System.out.println(4 + 2); // 6
    System.out.println(4 - 2); // 2
    System.out.println(4 * 2); // 8
    System.out.println(4 / 2); // 2
    System.out.println(5 / 2); // 2
    System.out.println(2 / 4); // 0
    System.out.println(4 % 2); // 0
    System.out.println(5 % 2); // 1

    // 우선 순위 연산
    System.out.println(2 + 2 2); // 6
    System.out.println((2 + 2)
    2); // 8
    System.out.println(2 + (2 * 2)); // 6

    // 변수를 이용한 연산
    int a = 20;
    int b = 10;
    int c;

    // 덧셈
    c = a + b;
    System.out.println(c); // 30

    // 뺄셈
    c = a - b;
    System.out.println(c); // 10

    // 곱셈
    c = a * b;
    System.out.println(c); // 200

    // 나눗셈 (몫)
    c = a / b;
    System.out.println(c); // 2

    // 나눗셈 (나머지) = 나머지 연산
    c = a % b;
    System.out.println(c); // 0

  • 비교 연산자
    • 값의 크고/작음을 비교하거나 같고/다름 을 비교하여 참(true)/거짓(false) 값인 boolean 값을 출력합니다.
    • > (크다) , < (작다), >= (크거나 같다), <= (작거나 같다), == (같다), != (다르다)
    // 비교 연산자 (참이면 true, 거짓이면 false)
    System.out.println(10 > 9); // 10 는 9 보다 크다 (참이면 true, 거짓이면 false)
    System.out.println(10 >= 9); // 10 는 9 보다 크거나 같다 (true)
    System.out.println(10 < 9); // 10 는 9 보다 작다 (false)
    System.out.println(10 <= 9); // 10 는 9 보다 작거나 같다 (false)
    System.out.println(10 == 10); // 10 는 10 와 같다 (true)
    System.out.println(10 == 9); // 10 는 9 과 같다 (false)
    System.out.println(10 != 10); // 10 는 10 와 같지 않다 (false)
    System.out.println(10 != 9); // 10 는 9 과 같지 않다 (true)
  • 논리 연산자
    • 비교 연산의 결과값으로 받을 수 있는 boolean 값을 연결하는 연산자 입니다.
    • 조건을 연결 하였을때의 boolean 값들을 조합하여 참(true)/거짓(false) 값인 boolean 값을 출력합니다.
    • && (AND = 피연산자 모두 참), ||(OR = 피연산자 둘중 하나라도 참), !(피연산자의 반대 boolean 값)
    // 논리 연산자
    boolean flag1 = true;
    boolean flag2 = true;
    boolean flag3 = false;

    System.out.println(flag1); // true
    System.out.println(flag2); // true
    System.out.println(flag3); // false

    // 피연산자 중 하나라도 true 이면 true
    System.out.println(flag1 || flag2); // true
    System.out.println(flag1 || flag2 || flag3); // true
    // 피연산자 모두 true 이면 true
    System.out.println(flag1 && flag2); // true (flag1, flag2 모두 true 라서)
    System.out.println(flag1 && flag2 && flag3); // false (flag3은 false 라서)

    // And 연산
    System.out.println((5 > 3) && (3 > 1)); // 5 는 3 보다 크고, 3 은 1 보다 크다 (true)
    System.out.println((5 > 3) && (3 < 1)); // 5 는 3 보다 크고, 3 은 1 보다 작다 (false)

    // Or 연산
    System.out.println((5 > 3) || (3 > 1)); // 5 는 3 보다 크거나, 3 은 1 보다 크다 (true)
    System.out.println((5 > 3) || (3 < 1)); // 5 는 3 보다 크거나, 3 은 1 보다 작다 (true)
    System.out.println((5 < 3) || (3 < 1)); // 5 는 3 보다 작거나, 3 은 1 보다 작다 (false)

    // System.out.println(1 < 3 < 5); // 불가능한 코드

    // 논리 부정 연산자
    System.out.println(!flag1); // false (flag1 값의 반대)
    System.out.println(!flag3); // true (flag3 값의 반대)
    System.out.println(!(5 == 5)); // false
    System.out.println(!(5 == 3)); // true

  • 대입 연산자
    • 변수를 바로 연산해서 그자리에서 저장하는 연산자
    • 기본 대입 연산자= 와 다른 연산을 함께쓰는 복합 대입 연산자가 있습니다. (+=, -=, *= …)
    • 복합 대입 연산자기본 대입 연산자를 심플하게 작성하게 해주는것이지 기능은 같습니다.
    • 추가로, += 1++ 과 동일합니다. (피연산자에 1 더해주기)
    • 똑같이 -= 1—-와 동일합니다. (피연산자에 1 빼주기)
    // 대입 연산자
    int number = 10;
    number = number + 2;
    System.out.println(number); // 12

    number = number - 2;
    System.out.println(number); // 10

    number = number * 2;
    System.out.println(number); // 20

    number = number / 2;
    System.out.println(number); // 10

    number = number % 2;
    System.out.println(number); // 0

    number = number++;
    System.out.println(number); // 2

    number = number--;
    System.out.println(number); // 0

    // 복합 대입 연산자
    number = 10;

    number += 2;
    System.out.println(number); // 12

    number -= 2;
    System.out.println(number); // 10

    number *= 2;
    System.out.println(number); // 20

    number /= 2;
    System.out.println(number); // 10

    number %= 2;
    System.out.println(number); // 0

    🔥
    대입 연산자 중에 증감 연산자 쓸때 주의할점!
    • ++ 또는 —- 를 붙이면 피연산자가 1 더해지거나 1 빼기가 된다고 말씀드렸는데요.
    • 주의할 점은, 피연산자 뒤에 붙이냐, 앞에 붙이냐에 따라서 연산순서가 달라집니다!!
    연산자연산자 위치기능연산 예
    ++++{피연산자}연산 전에 피연산자에 1 더해줍니다.val = ++num;
    num값+1 후에 val변수에 저장
    ++{피연산자}++연산 후에 피연산자에 1 더해줍니다.val = num++;
    num값을 val변수에 저장 후 num+1
    —{피연산자}연산 전에 피연산자에 1 빼줍니다.val = —num;
    num값-1 후에 val변수에 저장
    {피연산자}—연산 후에 피연산자에 1 빼줍니다.val = num—;
    num값을 val변수에 저장 후 num-1
    • 이처럼 대입연산할때 뿐만아니라 연산을 직접할때도 마찬가지로 선/후적용이 나뉩니다.
    // 대입 증감 연산자

    public class Main {

    public static void main(String[] args) {
    	int a = 10;
    	int b = 10;
    	int val = ++a + b--; // a 는 연산전에 +1, b 는 연산후에 -1
    
    	System.out.println(a); // 11
    	System.out.println(b); // 9
    	System.out.println(val); // 21
    
    	// 11 + 9 가 왜 21이 나오는거죠??
    	// 그 이유는 
    	// a 는 val 연산전에 ++ 가 수행되어서 11로 연산되었지만
    	// b 는 val 연산후에 -- 가 수행되어서 기존값이 10으로 연산된 후 -- 가 수행되었습니다.
    	// 따라서 연산된 a값인 11과 연산되기전 b값인 10이 더해져서 21이 된겁니다!
    
    
    	int c = a--; // c 에 a 값 대입 후 a-1  (즉, a=10, c=11)
    	int d = ++b; // b 값 +1 후에 d 에 대입 (즉, b=10, d=10)
    	int val = c-- * ++d; // c 는 연산후에 -1, d 는 연산전에 +1
    
    	System.out.println(a); // 10
    	System.out.println(b); // 10
    	System.out.println(c); // 11
    	System.out.println(d); // 10
    	System.out.println(val); // 11 * 11 = 121
    
    	// 11 * 10 이 왜 121이 나오는거죠??
    	// 그 이유는 
    	// c 는 val 연산후에 -- 가 수행되어서 기존값이 11로 연산된 후 -- 가 수행되었지만
    	// d 는 val 연산전에 ++ 가 수행되어서 11로 연산되었습니다.
    	// 따라서 연산전 a값인 11과 연산된 b값인 11이 곱해져서 121이 된겁니다!
    }

    }

  • 기타 연산자
    • 형변환 연산자
      • 1일차 강의에서 배운내용으로 괄호 안에 변환할 타입을 넣으면 피연산자의 타입이 변경 됩니다.
      // 형변환 연산자
      int intNumber = 93 + (int) 98.8; // 93 + 98

      double doubleNumber = (double) 93 + 98.8; // 93.0 + 98.8

    • 삼항 연산자
      • 비교 연산의 결과값에 따라 응답할 값을 직접 지정할 수 있는 연산자 입니다.
      • 삼항 연산자는 3가지 피연산자가 존재하여 삼항 연산자 라고 합니다. (조건/참결과/거짓결과)
      • (조건) ? (참결과) : (거짓결과)
      // 삼항 연산자
      int x = 1;
      int y = 9;

      boolean b = (x == y) ? true : false;
      System.out.println(b); // false

      String s = (x != y) ? "정답" : "땡";
      System.out.println(s); // 정답

      int max = (x > y) ? x : y;
      System.out.println(max); // 9

      int min = (x < y) ? x : y;
      System.out.println(min); // 1

    • instance of 연산자
      • (객체명) instance of (클래스명)
      • 객체 타입을 확인하는 연산자
      • 형변환 가능 여부를 확인하여 true/false 결과 반환
      • 주로 상속 관계에서 부모객체인지 자식 객체인지 확인하는 데 사용
      class Parent{}
      class Child extends Parent{}

      public class InstanceofTest {

      public static void main(String[] args){
      
          Parent parent = new Parent();
          Child child = new Child();
      
          System.out.println( parent instanceof Parent );  // true
          System.out.println( child instanceof Parent );   // true
          System.out.println( parent instanceof Child );   // false
          System.out.println( child instanceof Child );   // true
      }

      }

      // 1번은 같은거니까 true
      // 2번은 child가 Parent를 상속했으니까 true
      // 3번은 parent는 부모 이니까 Child가 대처불가능 false
      // 4번은 같으니까 true

  • 연산자 우선순위
    📌
    연산자 우선순위 : 산술 > 비교 > 논리 > 대입
    • 연산자 여러개가 함께 있는 연산을 계산할때는 우선순위가 있습니다.
    • 위 우선순위에 따라서 최종적인 응답값이 결정됩니다.
    • 단, 괄호로 감싸주면 괄호안의 연산이 최우선순위로 계산됩니다.
    // 연산자 우선순위
    int x = 2;
    int y = 9;
    int z = 10;

    boolean result = x < y && y < z; // <,> 비교연산자 계산 후 && 논리 연산자 계산
    System.out.println(result); // true

    result = x + 10 < y && y < z; // +10 산술연산자 계산 후 <,> 비교연산자 계산 후 && 논리 연산자 계산
    System.out.println(result); // false

    result = x + 2 * 3 > y; // 산술연산자 곱센 > 덧셈 순으로 계산 후 > 비교연산자 계산
    System.out.println(result); // false (8>9)

    result = (x + 2) * 3 > y; // 괄호안 덧셈 연산 후 괄호 밖 곱셈 계산 후 > 비교연산자 계산
    System.out.println(result); // true (12>9)

  • 산술변환
    📌
    연산 전에 피연산자의 타입을 일치시키는 것
    • 두 피연산자의 타입을 같게 일치시킨다. (둘중에 저장공간 크기가 더 큰 타입으로 일치
    • 피연산자의 타입이 int 보다 작은 short 타입이면 int 로 변환
    • 피연산자의 타입이 long 보다 작은 int, short 타입이면 Long 으로 변환
    • 피연산자의 타입이 float보다 작은 long, int, short 타입이면 float 으로 변환
    • 피연산자의 타입이 double 보다 작은 float, long, int, short 타입이면 double 으로 변환
    • 이처럼, 변수여러개를 연산했을때 결과값은 피연산자 중 표현 범위가 가장 큰 변수 타입을 가지게 됩니다.
    // 산술변환
    public class Main {

    public static void main(String[] args) {
    
    	short x = 10;
    	int y = 20;
    
    	int z = x + y; // 결과값은 더 큰 표현타입인 int 타입의 변수로만 저장할 수 있습니다.
    
    	long lx = 30L;
    	long lz = z + lx; // 결과값은 더 큰 표현타입인 long 타입의 변수로만 저장할 수 있습니다.
    
    	float fx = x; // 결과값은 더 큰 표현타입인 float 타입의 변수로만 저장할 수 있습니다.
    	float fy = y; // 결과값은 더 큰 표현타입인 float 타입의 변수로만 저장할 수 있습니다.
    	float fz = z; // 결과값은 더 큰 표현타입인 float 타입의 변수로만 저장할 수 있습니다.
    	System.out.println(lz);
    	System.out.println(fx);
    	System.out.println(fy);
    	System.out.println(fz);
    }

    }

  • 비트 연산
    • <<(왼쪽으로 자리수 옮기기), >>(오른쪽으로 자리수 옮기기)
    • 0,1 은 2진수 값이기 때문에,
      • 자리수를 왼쪽으로 옮기는 횟수만큼 2의 배수로 곱셈이 연산되는것과 동일합니다.
      • 자리수를 오른쪽으로 옮기는 횟수만큼 2의 배수로 나눗셈이 연산되는것과 동일합니다.
    // 비트 연산
    System.out.println(3 << 2);
    // 3의 이진수는 11 11을 왼쪽으로 2번이동 1100
    // 2의 영승은 0 2의 1승은 0 2의2승은 1 2의3승은 1
    // 8 + 4 = 12
    System.out.println(3 << 1);
    // 3을 왼쪽으로 1만큼 비트연산 해달라는 의미
    // 110
    // 4 + 2 = 6
  • 조건문
    // if
    if(flag){
    System.out.println("정답입니다");
    } else if (4 > 3) {
    System.out.println("여기도 실행해주세요");
    }else {
    System.out.println("여기가 마지막입니다!!");
    }

    // switch / case
    int num = 11;
    String result;

        switch (num){
            case 1: 
    									result = &quot;1이정답이래&quot;;
                    break;
            case 2: 
    									result = &quot;2가정답이래&quot;;
                    break;
            default:result = &quot;결국 11이래&quot;;
        }
    
        System.out.println(result);

    // 개선된 switch문

    switch (operationInput){
    case "+" -> this.calculator.setOperation(new AddOperation());
    case "-" -> this.calculator.setOperation(new SubstractOperation());
    case "*" -> this.calculator.setOperation(new MultiplyOperation());
    case "/" -> this.calculator.setOperation(new DivideOperation());
    }

  • 반복문
    // for

        int[] arr = {1,2,3,4,5};
        for(int i = 0; i &lt; arr.length; i++){
            System.out.println(arr[i]);
        }

    // 최신 for
    for(int i : arr){
    System.out.println(i);
    // 초기값이 0으로 설정되고
    // 배열끝까지 for문이 돌아간다
    // for문이 돌아갈 때마다 설정한 변수에 배열값이 할당된다
    }

    // while
    boolean answer = true;
    int i = 1;
    while (answer){
    if(i < arr.length){
    System.out.println("i값이 아직작아 : "+i);
    i++;
    }
    }

    // do-while
    // 연산을 한번 수행 후 조건문 체크
    int num1 = 4;
    do{
    System.out.println(num1 + "출력" );
    }while (num1 < 3);

    // Objects.equals(좌,우) : 좌우가 같은경우 true ,다른경우 false
    // Scanner sc = new Scanner(System.in);
    //
    // System.out.print("A 입력 : ");
    // String aHand = sc.nextLine(); // A 입력
    //
    // System.out.print("B 입력 : ");
    // String bHand = sc.nextLine(); // B 입력
    //
    // if (Objects.equals(aHand, "가위")) { // 값을 비교하는 Obects.equals() 메서드 사용
    // if (Objects.equals(bHand, "가위")) {
    // System.out.println("A 와 B 는 비겼습니다."); // A 와 B 의 입력값을 비교해서 결과 출력
    // } else if (Objects.equals(bHand, "바위")) {
    // System.out.println("B 가 이겼습니다.");
    // } else if (Objects.equals(bHand, "보")) {
    // System.out.println("A 가 이겼습니다.");
    // } else {
    // System.out.println(" B 유저 값을 잘못 입력하셨습니다.");
    // }
    // } else if (Objects.equals(aHand, "바위")) {
    // if (Objects.equals(bHand, "가위")) {
    // System.out.println("A 가 이겼습니다.");
    // } else if (Objects.equals(bHand, "바위")) {
    // System.out.println("A 와 B 는 비겼습니다.");
    // } else if (Objects.equals(bHand, "보")) {
    // System.out.println("B 가 이겼습니다.");
    // }
    // } else if (Objects.equals(aHand, "보")) {
    // if (Objects.equals(bHand, "가위")) {
    // System.out.println("B 가 이겼습니다.");
    // } else if (Objects.equals(bHand, "바위")) {
    // System.out.println("A 가 이겼습니다.");
    // } else if (Objects.equals(bHand, "보")) {
    // System.out.println("A 와 B 는 비겼습니다.");
    // }
    // }
  • break continue
    // break 명령 범위

        for (int i = 0; i &lt; 10; i++) {
            System.out.println(&quot;i: &quot; + i);
            if (i == 2) {
                break; // i 가 2일때 가장 바깥 반복문이 종료됩니다.
            }
            for (int j = 0; j &lt; 10; j++) {
                System.out.println(&quot;j: &quot; + j);
                if (j == 2) {
                    break; // j 가 2일때 가장 안쪽 반복문이 종료됩니다.
                }
            }
        }

    // 출력
    // i: 0 // 바깥 반복문 부터 수행 시작
    // j: 0 // 안쪽 반복문 1회차 수행
    // j: 1
    // j: 2 // j 가 2일때 안쪽 반복문 break;
    // i: 1 // 바깥 반복문은 아직 break; 호출이 안됬으므로 다음 반복수행
    // j: 0 // 안쪽 반복문 2회차 수행
    // j: 1
    // j: 2 // j 가 2일때 안쪽 반복문 두번째 break;
    // i: 2 // i 가 2일때 바깥 반복문도 break; 호출되어 종료

    // continue 명령

        int number = 0;
        while(number &lt; 3) {
            number++;
            if (number == 2) {
                continue;  // 2일때 반복 패스
                // 바깥쪽 부분이 패스됨
                // 바로 다음 반복문으로 넘어감
                // 해당 부분은 스킵된다는 의미
            }
            System.out.println(number + &quot;출력&quot;);
        }

    // 출력
    // 1출력
    // 3출력

  • 배열
    • 1차원
      // 선언 방식 2가지
      int [] num;
      int num2 [];

          // 배열위치값은 0부터시작
          // Arrays.fill -&gt; 초기화
      
          num = new int[5]; // 배열의 크기 결정 초기값으로 0이들어감
          for(int i =0; i &lt; num.length; i++){
              num[i] = i;
              System.out.println(num[i]);
          }
      
          for (int i : num){
              // map함수 사용법이라고 생각하면 편함
              System.out.println(i);
          }
      
      
          int [] num3 = {1, 2, 3, 4, 5}; // 선언과 동시에 값집어넣음
          Arrays.fill(num3,22); // 22로 전부 변환
          for(int i : num3){
              System.out.println(i);
          }</code></pre></details></li></ul><ul id="dc45e4a6-37b2-4f96-9ce2-d7b489c178aa" class="toggle"><li><details open=""><summary>2차원 / 3차원</summary><pre id="790b751f-2bcc-45ba-a47b-d54de55a30eb" class="code"><code>// 2차원 배열 선언 방식
          // - int[][] array
          // - int array[][]
          // - int[] array[]
      
          // 중괄호를 사용해 초기화
      
          int[][] array = {
                  {1, 2, 3},
                  {4, 5, 6}
          };
      
          // 반복문을 통한 초기화
      
          int[][] array2 = new int[2][3]; // 최초 선언
      
          for (int i = 0; i &lt; array2.length; i++) {
              for (int j = 0; j &lt; array2[i].length; j++) {
                  System.out.println(&quot;확인 : &quot; + i + &quot;//&quot; + j);
      
                  array2[i][j] = 0;  // i, j 는 위 노란색 네모박스 안에있는 숫자를 의미하며 인덱스 라고 부릅니다.
              }
          }

      // 3차원 배열의 이해

          // 중괄호 3개를 써서 3차원 배열 초기화를 할 수 있습니다.
          int[][][] MultiArray = {{{1, 2}, {3, 4}}, {{5, 6}, {7, 8}}};</code></pre></details></li></ul><ul id="aabc6078-2ccd-47f1-b302-88a7285972e4" class="toggle"><li><details open=""><summary>가변배열</summary><pre id="b547c95b-f4d1-4a10-8228-60cc7e53c6ad" class="code"><code>// 가변배열
          // 2차원 배열 생성시 행마다 다른 길이를 지정해줄수 있다
      
      
          // 선언 및 초기화
      
          int[][] array = new int[3][]; // 다른 길이를 주고싶을때는 이렇게 그공간의 크기는 작성해주지 않는다
      
          // 배열 원소마다 각기다른 크기로 지정 가능
          array[0] = new int[2];
          array[1] = new int[4];
          array[2] = new int[1];
      
          // 중괄호 초기화할때도 원소배열들의 크기를 각기 다르게 생성 가능
          int[][] array2 = {
                  {10, 20},
                  {10, 20, 30, 40},
                  {10}
      
          };
      
          // 가변 2차원 배열 조회
          int[][] array3 = {
                  {10, 20, 30},
                  {10, 20, 30, 40},
                  {10, 20}
          };
      
          for (int i = 0; i &lt; array3.length; i++) { // 1차원 길이
              for (int j = 0; j &lt; array3[i].length; j++) { // 2차원 길이
                  System.out.println(array3[i][j]); // 2중 반복문으로 i, j 인덱스 순회
              }
          }
      
          // 출력

      // 10
      // 20
      // 30
      // 10
      // 20
      // 30
      // 40
      // 10
      // 20

    • 최대 최소값 예제
      int [] arr = {21,33,555,2,43};

          int max = arr[0];
      
          for(int i : arr){
              if(max &lt; i){
                  max = i;
              }
          }
          System.out.println(max);
      
          int min = arr[0];
      
          for(int i : arr){
              if(min &gt; i){
                  min = i;
              }
          }
          System.out.println(min);</code></pre></details></li></ul></details></li></ul><ul id="29952dc6-6d06-4816-92d5-a9c0a17b1c35" class="toggle"><li><details open=""><summary>깊은 복사 얕은 복사</summary><pre id="2ba0fbf5-141c-470a-b506-38ccb178bd37" class="code"><code>// 바로 할당하면 해당 부분의 주소를 복사하는것이기때문에
          // 할당해준 변수가 변하면 값이 같이 변한다
          // 똑같은 값을 가지고 있는게 아니라 주소값을 가진다
          int a [] = {1,2,3,4};
          int b [] = a; // 얕은 복사
      
          b[0] = 55;
      
          System.out.println(a[0]);
      			System.out.println(b[0]);
      			// 두개 다 같은값을 가진다 
      			// 동일한 주소를 가지기때문에 값역시 같이 변하는것을 확인할 수 있다
      
          // 깊은 복사
          // 할당해준 변수와 별개의 주소를 가지게 된다.
      
          int[] a1 = { 1, 2, 3, 4 };
          int[] b1 = new int[a.length];
      
          for (int i = 0; i &lt; a1.length; i++) {
              b1[i] = a1[i]; // 깊은 복사
          }
      
          b1[0] = 3; // b 배열의 0번째 순번값을 3으로 수정했습니다. (1 -&gt; 3)
      
          System.out.println(a1[0]); // 출력 1 &lt;- 깊은 복사를 했기때문에 a 배열은 그대로 입니다.
      
          // 깊은 복사 메서드
      
          // 1. clone() 메서드
          int[] a2 = { 1, 2, 3, 4 };
          int[] b2 = a2.clone(); // 가장 간단한 방법입니다.
          // 하지만, clone() 메서드는 2차원이상 배열에서는 얕은 복사로 동작합니다!!
      
      
          // 2. Arrays.copyOf() 메서드
          int[] a3 = { 1, 2, 3, 4 };
          int[] b3 = Arrays.copyOf(a3, a3.length); // 배열과 함께 length값도 같이 넣어줍니다.
          for(int i:b3){
              System.out.println(i);
          }</code></pre></details></li></ul><ul id="1a99071d-4564-426f-9364-0ef8a7024730" class="toggle"><li><details open=""><summary>문자 문자열</summary><table id="47d8ff08-51bc-4d56-bd30-3492afdc0bbc" class="simple-table"><tbody><tr id="49a6e1fa-0ca9-49fd-8f47-e657379a6218"><td id="bjre" class="">메서드</td><td id="M_Cj" class="">응답값 타입</td><td id="Rpih" class="" style="width:323px">설명</td></tr><tr id="07253789-1099-4a74-9d1c-d1b58cdcfb23"><td id="bjre" class="">length()</td><td id="M_Cj" class="">int</td><td id="Rpih" class="" style="width:323px">문자열의 길이를 반환한다.</td></tr><tr id="b75212a8-cb77-41d8-a254-85ca4936ea57"><td id="bjre" class="">charAt(int index)</td><td id="M_Cj" class="">char</td><td id="Rpih" class="" style="width:323px">문자열에서 해당 index의 문자를 반환한다.</td></tr><tr id="d5152f12-10d6-423a-bd12-3e53907d3a3e"><td id="bjre" class="">substring(int from, int to)</td><td id="M_Cj" class="">String</td><td id="Rpih" class="" style="width:323px">문자열에서 해당 범위(from~to)에 있는 문자열을 반환한다. (to는 범위에 포함되지 않음)</td></tr><tr id="a0fd74b0-6f66-496c-939c-64a0b50ae0b0"><td id="bjre" class="">equals(String str)</td><td id="M_Cj" class="">boolean</td><td id="Rpih" class="" style="width:323px">문자열의 내용이 같은지 확인한다. 같으면 결과는 true, 다르면 false가 된다.</td></tr><tr id="5269d040-a1f1-4434-ae55-060986417a97"><td id="bjre" class="">toCharArray()</td><td id="M_Cj" class="">char[]</td><td id="Rpih" class="" style="width:323px">문자열을 문자배열(char[])로 변환해서 반환한다.</td></tr><tr id="d5980ae5-60d0-4a8f-bad5-76c27e9194be"><td id="bjre" class="">new String(char[] charArr)</td><td id="M_Cj" class="">String</td><td id="Rpih" class="" style="width:323px">문자배열(char[]) 을 받아서 String으로 복사해서 반환한다.</td></tr></tbody></table><pre id="dd83c706-e169-4482-992c-359e63d7ede4" class="code"><code>// 문자(char / 1byte), 문자열(string)
      
          // 기본형 변수 vs 참조형 변수
          // 1. 기본형 변수는 &#x27;소문자로 시작함&#x27; / 참조형은 &#x27;대문자로 시작함&#x27;
          //  - Wrapper class에서 기본형 변수를 감싸줄 때(boxing), int -&gt; Integer
          // 2. 기본형 변수는 값 자체를 저장, 참조형 변수는 별도의 공간에 값을 저장 후 그 주소를 저장함(=주소형 변수)
      
          // char &lt; String
          // String 가지고 있는 기능이 너무 많아서
          // Wrapper class와도 비슷함 -&gt; 기본형 변수가 가지고 있는 기능이 제한 -&gt; 다양한 기능을 제공하는 Wrpper을 감쌈으로써, 추가기능을 더함
      
          String str =&quot;abcd&quot;;
      
          // length
          int strLength = str.length();
          System.out.println(strLength); // 4
      
          // charAt(int index)
          char strChar = str.charAt(1);
          System.out.println(strChar); // b
      
          // substring (int fromIndex, int toIndex) -&gt; 문자열을 잘라서 새로운 str을 생성
          String strsub = str.substring(0,3);
          System.out.println(strsub); // abc
      
          // equals(String str) 입력받은 문자열과 같니 문자열의 경우의 비교연산자
          String newstr = &quot;abcd&quot;;
          boolean strEqual = newstr.equals(str);
          System.out.println(strEqual); // true
      
          // toCharArray() : String을 char[]로 변환
          char [] strCharArray = str.toCharArray();
      
          // char[] -&gt; String변환 -&gt; char
          char [] charArray = {&#x27;a&#x27;,&#x27;b&#x27;,&#x27;c&#x27;};
          String charArrayString = new String(charArray);
          System.out.println(charArrayString);</code></pre></details></li></ul><ul id="b16e029e-c7b3-4dec-962b-19a6564658ea" class="toggle"><li><details open=""><summary>동기화</summary><pre id="4106af5b-7d17-42f8-917c-2d922fd508c2" class="code"><code>- 쓰레드가 하나의 자원을 놓고 싸우지 말고 락을 가진 쓰레드부터 차례대로 그자원을 쓰고 다음 쓰레드에게 락과 함께 자원을 반남하라는 의미
      • 동기화를 안해도 되는 부분을 괜히 동기화하면 쓰레드가 하나밖에 수행되지 못하는 이슈가 있을수있음
      방법설명
      임계영역(critical section)공유 자원에 대해 단 하나의 스레드만 접근하도록 한다.
      (하나의 프로세스에 속한 스레드만 가능하다)
      뮤텍스 (mutex)공유 자원에 대해 단 하나의 스레드만 접근하도록 한다.
      (서로 다른 프로세스에 속한 스레드도 가능)
      이벤트 (event)특정한 사건의 발생을 다른 스레드에게 알린다.
      세마포어 (Semaphore)한정된 개수의 자원을 여러 스레드가 사용하려고 할 때 접근을 제한한다.
      대기 가능 타이머 (waitable timer)특정 시간이 되면 대기중이던 스레드를 깨운다.
    • 컬렉션(Collection)
      JCF(Java Collections Framework)
      데이터, 자료구조인 컬렉션과 이를 구현하는 클래스를 정의하는 인터페이스를 제공
      사용할때는 기본형 변수가 아닌 참조형 변수를 사용해야한다

    아래 사진은 상속구조를 보여주는 사진
    map의 경우에는 collection 인터페이스를 상속받고 있지 않지만 collection으로 분류

    // Collection을 사용하는 이유
    1. Collection의 일관된 API를 사용하여 모든 클래스가 Collection에서 상속받아 통일된 메서드를 사용
    2. 객체지향프로그래밍의 추상화가 성공적으로 구현

    ■ Collection 인터페이스의 특징

    인터페이스구현클래스특징
    SetHashSet
    TreeSet
    순서를 유지하지 않는 데이터의 집합으로 데이터의 중복을 허용하지 않는다.
    ListLinkedList
    Vector
    ArrayList
    순서가 있는 데이터의 집합으로 데이터의 중복을 허용한다.
    QueueLinkedList
    PriorityQueue
    FIFO(First in First out) 들어간 순서대로 나온다.
    MapHashtable
    HashMap
    TreeMap
    키(Key), 값(Value)의 쌍으로 이루어진 데이터 집합으로,
    순서는 유지되지 않으며 키(Key)의 중복을 허용하지 않으나 값(Value)의 중복은 허용한다.
    • List
      // Vector는 동기화가 이미되어있는 클래스로 한번에 하나의 쓰레드만 접근 가능
      // Arraylist는 동기화처리가 안된 클래스로 동시에 여러쓰레드에 접근이 가능
      // ArrayList
      // List
      // array와 다른점 -> 처음에 길이를 몰라도 만들수있다
      // Array -> 정적배열
      // List(ArrayList) -> 동적배열(크기가 가변적으로 늘어남)
      // - 생성 시점에 작은 연속된 공간을 요청해서 참조형 변수들을 담아놓는다
      // - 값이 추가될 때 더 큰 공간이 필요한면 더 큰 공간을 받아서 저장함
      // 표준 배열보다 느릴수도있지만 움직이이 많은 배열에서 유용
      // ArrayList import를 해주어야함

          ArrayList&lt;Integer&gt; intList = new ArrayList&lt;Integer&gt;(); // 선언과 생성
      
          intList.add(1); // 값 추가
          intList.add(12); // 값 추가
          intList.add(123); // 값 추가
      
          System.out.println(intList.get(0)); // 첫번째로 집어넣은 값 가져오기
      
          intList.set(1,22); // 1번째 index위치에있는값을 22로 변환
      
          System.out.println(intList.get(1)); // 22
      
          intList.remove(1); // index번호의 값을 삭제
      
          System.out.println(intList.get(1)); // 123 -&gt; 22번값이 삭제되어 123번이 1번째 index위치로 땅겨져옴
      
          System.out.println(intList.toString()); // list안의값 전부 가져오기
          intList.clear(); // 해당 list 초기화
          System.out.println(intList.toString()); // list안의값 전부 가져오기</code></pre><pre id="483ffd8d-d6d4-42e9-a8cf-ca7105f68f97" class="code"><code>// linked list
          // 메모리에 남는 공간을 요청해서 여기 저기 나누어서 실제 값을 담아둠
          // 실제 값이 있는 주소값으로 목록을 구성하고 저장하는 자료구조
      
          // 기본적 기능 -&gt; ArrayList와 동일
          // LinkedList는 값 -&gt; 여기 저기 나누어서 값을 넣었기때문에 조회하는 속도가 느리다
          // 값을 추가 하거나 삭제할때는 빠르다
          // 이유로는 ArrayList처럼 순서가 있는 경우에는 순서하나하나를 확인하지마
          // Linked List의 경우에는 비어있는 값에가서 그냥 저장 삭제를 하기때문에
      
          LinkedList&lt;Integer&gt; linkedList = new LinkedList&lt;Integer&gt;(); 
      			// LinkedList&lt;Integer&gt; linkedList = new LinkedList&lt;&gt;(); // 생성시 타입생략가능
          linkedList.add(1);
          linkedList.add(122);
          linkedList.add(1333);
      
          System.out.println(linkedList.get(0));
          System.out.println(linkedList.get(1));
          System.out.println(linkedList.get(2));
      
          System.out.println(linkedList.toString()); // arraylist보다 속도가 느림
      
          // arraylist와 다른점
          // add를 할때 index위치를 지정할수있다
      
          linkedList.add(1,4444); // 1번째 index에 4444값을 추가
          System.out.println(linkedList.toString()); // 추가 삭제는 빠르다
      
          linkedList.set(2,5555);
          System.out.println(linkedList.toString());
      
          linkedList.remove(2);
          System.out.println(linkedList.toString());
      
          linkedList.clear();
          System.out.println(linkedList.toString());</code></pre><pre id="ea734050-afad-4567-8ace-3b218d083331" class="code"><code>// Vector

      // ArrayList와 유사
      // 차이점으로는 Vector는 동기화가되고, ArrayList는 안됨
      // Vector는 동기화가 이미되어있는 클래스로 한번에 하나의 쓰레드만 접근 가능
      // Arraylist는 동기화처리가 안된 클래스로 동시에 여러쓰레드에 접근이 가능

      mport java.io.;
      import java.util.
      ;

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

          // Vector 선언
          Vector&lt;Integer&gt; v
              = new Vector&lt;Integer&gt;();
      
          // 데이터 입력
          for (int i = 1; i &lt;= 5; i++)
              v.add(i);
      
          // 결과 출력
          System.out.println(v);
      
          // 3번 데이터 삭제
          v.remove(3);
      
          // 결과 출력
          System.out.println(v);
      
          // 하나씩 결과 출력
          for (int i = 0; i &lt; v.size(); i++)
              System.out.print(v.get(i) + &quot; &quot;);
      }

      }

    • Stack
      // Stack
      // 수직을 값을 쌓아놓고, 넣었다가 뺀다 filo
      // push(넣기),peek(조회),pop(삭제)
      // 최근 저장된 데이터를 나열하거나 데이터 중복 처리를 막고 싶을때

          Stack&lt;Integer&gt; intStack = new Stack&lt;Integer&gt;();
      
          intStack.push(12);
          intStack.push(1222);
          intStack.push(124444);
      
          // 다 지워질때까지 출력
          while(!intStack.isEmpty()){
              // isEmpty() -&gt; 공백일때 true
              // 위의 조건은 !이 있으니까 값이 있을때 true반환
              System.out.println(intStack.pop());
              // 제일 위에있는 값이 조회되면서 빠져나옴
              // 지금같이 3개가 있는경우 3번돌아감
      
          }
      
          intStack.push(12333);
          intStack.push(12);
          intStack.push(1);
      
          System.out.println(intStack.peek());
          // 가장 마지막에 들어감 값인 1이들어감 / 맨위값을조회
          System.out.println(intStack.size());
          // 길이

      // 스택 인덱스의 값 확인

      // 스택.elementAt(인덱스);

      // 스택 특정 값이 어느 인덱스에 들었나 확인

      // 스택.indexOf("값");

    • Queue
      // Queue : fifo
      // add, peek, poll
      // Queue : 생성자가 없는 인터페이스

          Queue&lt;Integer&gt; intQueue = new LinkedList&lt;Integer&gt;();
      
          intQueue.add(1);
          intQueue.add(12);
          intQueue.add(31);
      
          while (!intQueue.isEmpty()){
              System.out.println(intQueue.poll());
              // stack과 다르게 들어간순서대로 나옴
          }
      
          intQueue.add(1);
          intQueue.add(12);
          intQueue.add(31);
      
          System.out.println(intQueue.peek());
          System.out.println(intQueue.size());
      
          intQueue.add(3133);
          System.out.println(intQueue.size());</code></pre></details></li></ul><ul id="3b2d3347-8d66-4ecd-8dcf-99d9412d17b4" class="toggle"><li><details open=""><summary>Set</summary><ul id="8d7a507d-ca20-49b0-9364-39025e657838" class="bulleted-list"><li style="list-style-type:disc"><strong>HashSet</strong><ul id="77a23984-fe73-409b-932c-a269398141eb" class="bulleted-list"><li style="list-style-type:circle">가장빠른 임의 접근 속도순서를 예측할 수 없음</li></ul><ul id="da3145af-d5a9-4240-b01d-02da27b78659" class="bulleted-list"><li style="list-style-type:circle">순서를 예측할 수 없음</li></ul><ul id="210a3722-690c-4b77-90ff-1eb9df860248" class="toggle"><li><details open=""><summary>HashSet사용법</summary><pre id="464ba4da-e6bd-4740-8d82-1ac843e11353" class="code"><code>// set : 집합 순서 없고, 중복 없음
          // set -&gt; 그냥 사용할수도있고, HashSet,TreeSet등으로 응용해서도 사용 가능
          // set은 생성자가 없는 인터페이스
          // 바로 생성 불가능
          // HashSet은 생성자가 존재
      
          Set&lt;Integer&gt; intSet = new HashSet&lt;Integer&gt;();
      
          intSet.add(11);
          intSet.add(112);
          intSet.add(1133);
          intSet.add(11);
      
          for(Integer value : intSet){
              System.out.println(value);
              // 11이 중복되어서 세번만 반복됨
          }
      
          // contains
          // 해당값을 포함 하고있는지 여부확인 reture값 true/false
          System.out.println(intSet.contains(2));
          System.out.println(intSet.contains(233));</code></pre></details></li></ul></li></ul><ul id="c830386d-b8e7-4e0c-9d0b-aed6937cd022" class="bulleted-list"><li style="list-style-type:disc"><strong>TreeSet</strong><ul id="dc25d88b-7c6f-42a6-8dde-fd86083baac4" class="bulleted-list"><li style="list-style-type:circle">정렬방법을 지정할 수 있음</li></ul><ul id="48647af3-1f02-415b-b869-4d18ddf4d800" class="toggle"><li><details open=""><summary>TreeSet사용법</summary><pre id="15e9d8d0-44d7-4bf2-acb3-c62e438a97d4" class="code"><code>// 처음설정은 오름차순

      TreeSet<자료형> 변수명 = new TreeSet<>();

      // 내림차순
      TreeSet<자료형> 변수명 = new TreeSet<>(Collections.reverseOrder);

      메서드 (Method)

      TreeSet<Integer> tSet = new TreeSet<>();
      연산코드반환 값
      삽입tSet.add(삽입할 value);트리에 value가 저장되어있던 경우 = False트리에 value가 저장되어있지 않던 경우 = True
      삭제tSet.remove(삭제할 value);트리에 value가 저장되어있던 경우 = True트리에 value가 저장되어있지 않던 경우 = False
      모든 값 삭제tSet.clear();-
      크기tSet.size();트리의 사이즈
      전체 원소 집합 출력System.out.println(tSet);[원소1, 원소2, ..., 원소n]
      정렬 순서 기준 맨 앞 원소tSet.first();정렬 순서 기준 맨 앞 원소
      정렬 순서 기준 맨 뒤 원소tSet.last();정렬 순서 기준 맨 뒤 원소
      value보다 큰 원소들 중 최솟값tSet.higher(value);value보다 큰 원소들 중 최솟값
      value보다 작은 원소들 중 최댓값tSet.lower(value);value보다 작은 원소들 중 최댓값
      트리가 비어있는지 확인tSet.isEmpty();트리의 사이즈가 0이면 = True트리의 사이즈가 0보다 크면 = False
      트리에 value가 저장되어 있는지tSet.contains(value);트리에 value가 저장되어있는 경우 = True트리에 value가 저장되어있지 않은 경우 = False
  • SortedSet
    • 엘리멘트들을 정렬된 순서로 저장
    • 저장되는 엘리먼트가 Comparable 인터페이스를 구현하고 있다면 compare()메서드의 로직을 이용
    • 구현체를 생성할 때 Comparator 클래스를 넘겨 엘리먼트들의 대소 비교에 사용가능
    SortedSet<String> sortedSet = new TreeSet<>();

    // sortedSet.add("Elem5");
    // sortedSet.add("Elem3");
    // sortedSet.add("Elem1");
    // sortedSet.add("Elem3");
    // sortedSet.add("Elem1");
    //
    // for (String elem : sortedSet) {
    // System.out.println(elem);
    // // 중복값은 제거되고 1,3,5순으로 정렬되서 나옴
    // }

        sortedSet.add(&quot;Elem5&quot;);
        sortedSet.add(&quot;Elem2&quot;);
        sortedSet.add(&quot;Elem4&quot;);
        sortedSet.add(&quot;Elem3&quot;);
        sortedSet.add(&quot;Elem1&quot;);

    // SortedSet<String> headSet = sortedSet.headSet("Elem3");
    // System.out.println("Head Set");
    // for (String elem : headSet) {
    // System.out.println(elem);
    // // Elem3값을 넣어주었기때문에
    // // 끝값인 3전까지 출력
    // // 1,2가 나옴
    // }

    // SortedSet<String> tailSet = sortedSet.tailSet("Elem4");
    // System.out.println("Tail Set");
    // for (String elem : tailSet) {
    // System.out.println(elem);
    // // Elem4부터 이후로의 값이 나옴
    // }
    //
    // SortedSet<String> subSet = sortedSet.subSet("Elem2", "Elem5");
    // System.out.println("Sub Set");
    // for (String elem : subSet) {
    // System.out.println(elem);
    // // 2부터 5사이의 값이 나옴
    // // 마지막 값은 포함안됨
    // }

        System.out.println(sortedSet.first()); // 첫번째 값만
        System.out.println(sortedSet.last()); // 마지막 값만</code></pre></li></ul></details></li></ul><ul id="21281538-e114-4a02-8f17-776745c2b32c" class="toggle"><li><details open=""><summary>Map</summary><ul id="ff125c4f-dff3-4e5b-b228-a700e79ff4c3" class="bulleted-list"><li style="list-style-type:disc"><strong>Hashtable</strong><ul id="19dacc8f-b0ac-4200-935a-d5c40ef45377" class="bulleted-list"><li style="list-style-type:circle">HashMap보다는 느리지만 동기화 지원</li></ul><ul id="684ca327-c1af-466d-bd3d-38544e9e916c" class="bulleted-list"><li style="list-style-type:circle">null불가</li></ul></li></ul><ul id="839b1d57-9096-47cb-900c-aca2f5fab2e4" class="bulleted-list"><li style="list-style-type:disc"><strong>HashMap</strong><ul id="6ff0274e-bad2-4382-925d-90485bcc0821" class="bulleted-list"><li style="list-style-type:circle">중복과 순서가 허용되지 않으며 null값이 올 수 있다.</li></ul></li></ul><ul id="2fb9ca01-e60e-41e9-89fd-f4c530d9327e" class="bulleted-list"><li style="list-style-type:disc"><strong>TreeMap</strong><ul id="9147ab4d-f402-41f3-95ae-80abbe866002" class="bulleted-list"><li style="list-style-type:circle">정렬된 순서대로 키(Key)와 값(Value)을 저장하여 검색이 빠름</li></ul></li></ul><pre id="431660e3-ed58-4fce-aa83-3853119dc69b" class="code"><code>// Map : key - value pair -&gt; 중요
        // key라는 값으로  unique하게 보장이 되어야함
        // Map -&gt; HashMap,TreeMap으로 응용
    
        Map&lt;String,Integer&gt; intmap = new HashMap&lt;&gt;();
    
        // 키 값
        intmap.put(&quot;&quot;,1);
        intmap.put(&quot;&quot;,2);
        intmap.put(&quot;&quot;,3);
        intmap.put(&quot;&quot;,4);
        intmap.put(&quot;&quot;,5);
        intmap.put(&quot;&quot;,6);
    
        for(String key : intmap.keySet()){
            // .keySet() : key값만 가져옴
            System.out.println(key); // 중복된 key는 생략되어서 하나만 나옴
        }
    
        for(Integer value : intmap.values()){
            // .values() : values값만 가져옴
            System.out.println(value); // 중복된 값중에서 가장 마지막으로 들어간 값이 나옴
        }
    
        System.out.println(intmap.get(&quot;&quot;)); // key값으로 value값 찾아오기</code></pre><ul id="0a1c64ec-1387-4a37-b95d-7e1d609f701d" class="toggle"><li><details open=""><summary>Map출력방법</summary><p id="03b520dc-d939-437f-b43f-5b765da82f7c" class="">Map에 값을 전체 출력하기 위해서는 entrySet(), keySet() 메소드를 사용하면 되는데 entrySet() 메서드는 key와 value의 값이 모두 필요한 경우 사용하고, keySet() 메서드는 key의 값만 필요한 경우 사용</p><p id="02a8e03d-2178-446b-9de5-40928b9bf3ea" class=""><strong>방법 01. entrySet()</strong></p><pre id="7937cc90-1b9d-4c70-bc2d-915e97962fc3" class="code code-wrap"><code>Map&lt;String, String&gt; map = new HashMap&lt;&gt;();

    map.put("key01", "value01");
    map.put("key02", "value02");
    map.put("key03", "value03");
    map.put("key04", "value04");
    map.put("key05", "value05");

    // 방법 01 : entrySet()
    for (Map.Entry<String, String> entry : map.entrySet()) {
    System.out.println("[key]:" + entry.getKey() + ", [value]:" + entry.getValue());
    }

    방법 02. keySet()

    Map<String, String> map = new HashMap<>();
    map.put("key01", "value01");
    map.put("key02", "value02");
    map.put("key03", "value03");
    map.put("key04", "value04");
    map.put("key05", "value05");

    // 방법 02 : keySet()
    for (String key : map.keySet()) {
    String value = map.get(key);
    System.out.println("[key]:" + key + ", [value]:" + value);
    }

    방법 03. entrySet().iterator()

    Iterator 인터페이스를 사용할 수 없는 컬렉션인 Map에서 Iterator 인터페이스를 사용하기 위해서는 Map에 entrySet(), keySet() 메소드를 사용하여 Set 객체를 반환받은 후 Iterator 인터페이스를 사용

    Map<String, String> map = new HashMap<>();
    map.put("key01", "value01");
    map.put("key02", "value02");
    map.put("key03", "value03");
    map.put("key04", "value04");
    map.put("key05", "value05");

    // 방법 03 : entrySet().iterator()
    Iterator<Map.Entry<String, String>> iteratorE = map.entrySet().iterator();
    while (iteratorE.hasNext()) {
    Map.Entry<String, String> entry = (Map.Entry<String, String>) iteratorE.next();
    String key = entry.getKey();
    String value = entry.getValue();
    System.out.println("[key]:" + key + ", [value]:" + value);
    }

    방법 04. keySet().iterator()

    Map<String, String> map = new HashMap<>();
    map.put("key01", "value01");
    map.put("key02", "value02");
    map.put("key03", "value03");
    map.put("key04", "value04");
    map.put("key05", "value05");

    // 방법 04 : keySet().iterator()
    Iterator<String> iteratorK = map.keySet().iterator();
    while (iteratorK.hasNext()) {
    String key = iteratorK.next();
    String value = map.get(key);
    System.out.println("[key]:" + key + ", [value]:" + value);
    }

    방법 05. Lambda 사용

    Map<String, String> map = new HashMap<>();
    map.put("key01", "value01");
    map.put("key02", "value02");
    map.put("key03", "value03");
    map.put("key04", "value04");
    map.put("key05", "value05");

    // 방법 05 : Lambda 사용
    map.forEach((key, value) -> {
    System.out.println("[key]:" + key + ", [value]:" + value);
    });

    방법 06. Stream 사용

    Map<String, String> map = new HashMap<>();
    map.put("key01", "value01");
    map.put("key02", "value02");
    map.put("key03", "value03");
    map.put("key04", "value04");
    map.put("key05", "value05");

    // 방법 06 : Stream 사용
    map.entrySet().stream().forEach(entry-> {
    System.out.println("[key]:" + entry.getKey() + ", [value]:"+entry.getValue());
    });

    // Stream 사용 - 내림차순
    map.entrySet().stream().sorted(Map.Entry.comparingByKey()).forEach(entry-> {
    System.out.println("[key]:" + entry.getKey() + ", [value]:"+entry.getValue());
    });

    // Stream 사용 - 오름차순
    map.entrySet().stream().sorted(Map.Entry.comparingByKey(Comparator.reverseOrder())).forEach(entry-> {
    System.out.println("[key]:" + entry.getKey() + ", [value]:"+entry.getValue());
    });

  • Vector
    // ArrayList와 동일한 내부구조
    // ArrayList와 다르게 동기화된 메소드로 구성
    // 멀티 스레드가 동시에 이 메서드를 실행할 수 없고,하나의 스레드가 실행을 완료해야만 다른 스레드들이 실행할 수 있다
    // 멀티 스레드 환경에서 안전하게 객체를 추가 삭제가 가능

    // 항상 동기화 되기때문에 스레드가 하나여도 동기화를 한다
    // 그렇기 때문에 ArrayList보다 성능이 떨어진다
    // ArrayList는 자동 동기화기능은 없고 옵션으로 존재한다
    // 그리고 벡터보다 속도가 빠르기 때문에 ArrayList를 더많이 사용한다

    // 선언
    // 타입을 지정하지 않고 임의의 타입의 값을 넣고 사용할 수도 있지만
    // 벡터 내부의 값을 사용하려면 캐스팅(Casting)연산이 필요하며 잘못된 타입으로 캐스팅을 한 경우에는 에러가 발생할 수 도 있다
    // 그렇기 때문에 사용시 타입명시를 하는게 좋다

    Vector v = new Vector();//타입 미설정 Object로 선언된다.
    Vector<Student> student = new Vector<Student>(); //타입설정 Student객체만 사용가능
    Vector<Integer> num2 = new Vector<Integer>(); //타입설정 int타입만 사용가능
    Vector<Integer> num3 = new Vector<>(); //new에서 타입 파라미터 생략가능
    Vector<String> v2 = new Vector<String>(10);//초기 용량(capacity)지정
    Vector<Integer> v3 = new Vector<Integer>(Arrays.asList(1,2,3)); //초기값 지정

    // 값 추가
    // 구조는 ArrayList와 LinkedList와 같다
    // add(index,value)
    // index를 사용하지않으면 맨뒤에 값이 추가된다

    Vector<Integer> v = new Vector<Integer>();
    v.add(3); //값 추가
    v.add(null); //null값도 add가능
    v.add(1,10); //index 1뒤에 10 삽입
    Vector v = new Vector();
    Student student = new Student(name,age);
    v.add(student);
    v.add(new Member("홍길동",15));

    // 값 삭제

    Vector<Integer> v = new Vector<Integer>(Arrays.asList(1,2,3));
    v.remove(1); //index 1 제거
    v.removeAllElements(); //모든 값 제거
    v.clear(); //모든 값 제거

    // 크기 구하기

    Vector<Integer> v = new Vector<Integer>(10);//초기용량 10
    v.add(1); //값 추가
    System.out.println(v.size()); //Vector 자료 개수 : 1
    System.out.println(v.capacity()); //Vector 물리적크기 : 10

    // 값 출력

    Vector<Integer> list = new Vector<Integer>(Arrays.asList(1,2,3));

    System.out.println(list.get(0));//0번째 index 출력

    for(Integer i : list) { //for문을 통한 전체출력
    System.out.println(i);
    }

    Iterator iter = list.iterator(); //Iterator 선언
    while(iter.hasNext()){//다음값이 있는지 체크
    System.out.println(iter.next()); //값 출력
    }

  • Iterator
    // 컬렉션 프레임워크에서 값을 가져오거나 삭제할 때 사용
    // 컬렌션 프레임워크를 생성한 뒤
    // Iterator<데이터타입> 변수명 = 컬렉션.iterator();

    // 장점
    // 1. Iterator는 모든 컬렉션 프레임워크에 공통으로 사용가능
    // 2. 컬렉션 프레임워크에서 쉽게 값을 가져오고 제거할 수 있음
    // 3. 3개의 메소드만 알면 되어서 사용하기 쉬움

    // 단점
    // 1. 처음부터 끝까지의 단방향 반복만 가능
    // 2. 값을 변경하거나 추가하는게 안됨
    // 3. 대량의 데이터를 제어할 때 속도가 느림

    Iterator.hasNext()

    • Iterator 안에 다음 값이 들어있는지 확인
    • 들었으면 true, 안들었음 false

    Iterator.next()

    • iterator의 다음 값 가져오기

    Iterator.remove()

    • iterator에서 next() 시에 가져왔던 값을
    • 컬렉션(List, Set, Map) 등에서 삭제
    • 반드시 next() 후에 사용해야 함
  • Collections(Collection과다름!!!)
    // 이 클래스는 컬렉션에서 작동하거나 반환하는 정적 메서드로만 구성됨
    // 컬렉션에서 작동하는 다형성 알고리즘이 포함됨
    // Collection과 Colletions는 다른것이다
    // 정적(static)메서드이므로 인스턴스화 불필요
    // Collections는 클래스이다!!
    // Collection인터페이스를 구현한 클래스에 대한 객체생성,정렬,병합등의 기능을 안정적으로 수행하도록 도와주는 역할

    주요 method()

    메소드설명쓰는법
    max지정된 컬렉션에서 최대 요소를 반환한다. (인덱스 X)
    min지정된 컬렉션에서 초소 요소를 반환한다. (인덱스 X)
    sort지정된 컬렉션을 정렬시킨다.오버로드 메소드들이 존재하며 가장 기본적인 메소드는 자연순서에 따라 내림차순으로 정렬된다.Collections.sort(list)
    shuffle지정된 컬렉션의 요소들의 순서를 무작위로 섞는다.Collections.shuffle(list)
    synchronizedCollection지정된 컬렉션에 의해 지원되는 동기화 된 컬렉션을 재생성해 반환한다.
    binarySearch지정된 컬렉션에서 이진 탐색 알고리즘을 사용해 지정된 객체를 검색해 인덱스를 반환한다.Collections.binarySearch(list,element(탐색할원소))
    disjoint2개의 지정된 컬렉션들에서 공통된 요소가 하나도 없는 경우 true 를 반환한다.
    copy지정된 켈렉션의 모든 요소를 새로운 컬렉션으로 복사해 반환한다.
    reverse지정된 컬렉션에 있는 순서를 역으로 변경한다.
    reverseOrderComparable인터페이스를 구현하는 객체 컬렉션에 자연순서(natural ordering)의 역순을 적용하는 comparator를 반환

    Sort()

    • List인터페이스를 구현하는 컬렉션에 대하여 정렬을 수행
    • Collections.sort()는 list / Arrays.sort()는 array에 사용된다
    • 들어가는 값이 Date타입이면 시간적인 순서로 정렬
      • String클래스와 Date클래스 모두 Comparable 인터페이스를 구현하기 때문에
      • 정렬은 Comparable 인터페이스를 이용하여 이루어진다
      • List인터페이스는 Comparable인터페이스를 상속하므로 정렬이 가능하다
      • comparaTo()메서드는 매개변수 객체를 현재의 객체와 비교하여 음수,같으면0,크면 양수를 반환

    shuffle()

    • list에 존재하는 정렬을 파괴하여서 원소들의 순서를 랜덤하게 만든다
    • 정렬과 반대 동작을 한다

    binarySearch()

    • Collections클래스는 정렬된 리스트에서 지정된 원소를 이진 탐색
    • “정렬된 리스트” 에 한해서 탐색을 진행한다는 점을 명심!!!
      • 선형 탐색 : 처음부터 모든 원소를 방문하는 탐색 방법으로, 리스트가 정렬되어 있지않은 경우 사용됨
      • 이진 탐색 :
        • 리스트가 정렬되어 있는 경우 중간에 있는 원소(n)와 먼저 비교하여, 크면 그 다음부터(n+1)끝까지 비교하고,
        • 작으면 처음부터 그 전(n-1)까지의 원소들과 비교하여 방식을 반복하여 하나의 리스트를 계속해서 두 개의 리스트로 분할
        • 리스트에 하나의 원소가 남을 때까지 반복된다
        • 이진탐색은 문제의 크기를 반으로 줄일 수 있기 때문에 선형 탐색보다 효율적
      • 반환값이 양수이면 찾고자 하는 원소의 인덱스값을 출력
      • 음수이면 탐색이 실패하여 원소를 찾지 못했음을 의미
      • 단, 실패하더라도 도움이 되는 정보를 반환한다
      • 반환값에는 현재의 데이터가 삽입될 수 있는 위치정보가 들어있다
      • 반환값이 pos라고 한다면, (-pos-1)이 해당 데이터를 삽입할 수 있는 위치이다.
      // -pos-1이 데이터 삽입 가능 index이다.
      int pos = Collections.binarySearch(list, key);
      if(pos < 0) list.add(-pos-1);
  • length vs length() vs size()
    💡
    length vs length() vs size() - 길이값 가져오기

    1. length

    • arrays(int[], double[], String[])
    • length는 배열의 길이를 조회해줍니다.

    2. length()

    • String related Object(String, StringBuilder etc)
    • length()는 문자열의 길이를 조회해줍니다. (ex. “ABCD”.length() == 4)

    3. size()

    • Collection Object(ArrayList, Set etc)
    • size()는 컬렉션 타입목록의 길이를 조회해줍니다.
  • 랜덤값 만들기
    System.out.println("0.0 ~ 1.0 사이의 난수 1개 발생 : " + Math.random());
    System.out.println("0 ~ 10 사이의 난수 1개 발생 : " + (int)((Math.random()10000)%10));
    System.out.println("0 ~ 100 사이의 난수 1개 발생 : " + (int)(Math.random()
    100));
    Random random = new Random(); //랜덤 객체 생성(디폴트 시드값 : 현재시간)
    random.setSeed(System.currentTimeMillis()); //시드값 설정을 따로 할수도 있음

    System.out.println("n 미만의 랜덤 정수 리턴 : " + random.nextInt(10));
    System.out.println("무작위 boolean 값 : " + random.nextBoolean());
    System.out.println("무작위 long 값 : " + random.nextLong());
    System.out.println("무작위 float 값 : " + random.nextFloat());
    System.out.println("무작위 double 값 : " + random.nextDouble());
    System.out.println("무작위 정규 분포의 난수 값 :" + random.nextGaussian());

  • 숫자야구만들기
    // 이슈

    int 타입의 숫자값을 scanner로 입력한 값을 받아오고 그다음 잘라서 배열에 넣어주어야하는 작업이 있었다
    String으로 변환하는 과정에서 첫번째 숫자값이 0이면 인식을 못하고 나중에 배열에 값을 넣어줄때
    가장 마지막 index로 값이 들어가는 이슈가 발생
    타입변환으로 사용한 메소드는
    1. String.valueof()
    2. Integer.toString()
    3. int [] answer = Stream.of(String.valueOf(result).split("")).mapToInt(Integer::parseInt).toArray();

    해결법으로는 받아오는 값을 String으로 하여 나중에 배열로 집어넣을때 Integer로 형변환 해주었다.
    하지만 왜 첫번째자리가 0이면 인식을 못하는지의 이슈는 찾아보면서 해결해야한다.

    // 해결

    int typing = sc.nextInt();
    System.out.print("받아온 숫자값 : ");
    System.out.println(typing);
    String str = String.valueOf(typing);
    System.out.print("문자열로 형변환 : ");
    System.out.println(str);
    String [] strarr = str.split("");
    System.out.print("split진행 : ");
    System.out.println(Arrays.toString(strarr));

    위의식으로 테스트해본결과 입력할때 숫자를 023으로 하였지만
    출력했을때는 전부다23으로나왔다 여기서 유추해볼수있는것은 숫자는 첫숫자가 0이면 생략하고
    023과 23이 같다고 인식하여 23으로 나오게한다.
    그래서 데이터를 변환하는 과정에서 0이계속해서 사라지는것을 확인할수있었다.

    그리고 마지막에 0이들어간 이유로는 이미 배열은 3개의공간을 생성했는데
    받아와지는 값이 두개 밖에없으니까 마지막값은 생성할때의 0인상태로 남아있었던 것이다.

    import java.util.Random;
    import java.util.Scanner;

    public class Main {
    public static void main(String[] args) {
    Random rd = new Random();
    Scanner sc = new Scanner(System.in);
    int [] arr = new int[3];
    for(int i = 0; i < arr.length; i++){
    arr[i] = rd.nextInt(10);
    for(int j = 0; j < i; j++){
    if(arr[i] == arr[j]){
    arr[i] = rd.nextInt(10);
    i--;
    }
    }
    }
    // System.out.println(Arrays.toString(arr));

      int cnt = 0;
      while (true){
          cnt++;
          System.out.print(cnt+&quot;번째 시도 : &quot;);
          String result = sc.nextLine();
    
          String [] result2 = result.split(&quot;&quot;);
          int [] arr2 = new int[3];
          for(int i =0; i &lt; result2.length; i++){
              arr2[i] = Integer.parseInt(result2[i]); // (int)로는 기본형변수타입이기때문에 참조형변수인 string이 변환이 안되어 한번 wrapper class를 사용하여 변환
          }
    
          int strikes = 0;
          int balls = 0;
          for(int i = 0; i &lt; arr.length; i++){
              if(arr[i] == arr2[i]){
                  strikes++;
              }
              for(int j = 0; j &lt; arr.length; j++){
                  if(arr[i] == arr2[j] &amp;&amp; j != i){
                      balls++;
                  }
              }
          }
    
          String strikeresult = strikes+&quot;S&quot;;
          String ballsresult = balls+&quot;B&quot;;
          if(strikes == 0){
              strikeresult =&quot;&quot;;
          }
          if(balls == 0){
              ballsresult = &quot;&quot;;
          }
    
          if(strikes == 3){
              System.out.println(strikeresult);
              System.out.println(cnt+&quot;번만에 맞히셨습니다.&quot;);
              break;
          }
    
          System.out.println(ballsresult + strikeresult);
    
    
    
    
      }
    }

    }

  • 객체
    // 속성 = 필드
    // 메소드 = 행위
    // 객체 = 인스턴스(클래스를 기반으로) 인스턴스화
    // 클래스 : 객체를 만들기 위한 설계도
    // 캡슐화
    // 필드와 메서드를 하나로 묶어 객체로 만든후 외부에서 알 수 없게 감춘것
    // 외부에서는 내부를 알 수 없기때문에 접근할 수 있는 필드나 메서드를 통해 접근
    // 외부에서 잘못사용하여 객체가 변화하지않는 것을 막기위해
    // 접근제어자를 통해서 허용범위를 정함

    // 상속
    // 부모객체의 필드와 메서드를 자식객체에게 물려줘서 사용할 수 있게함
    // 객체간의 구조파악이 쉬워짐
    // 필드 메서드 변경시 부모객체에 있는 것만은 부모객체에서 수정하면 자식객체에서 전부에 반영되므로 일관성이 좋음
    // 코드 중복이줄고 재사용성이 좋다

    // 다형성
    // 객체가 연산을 수행할 때 하나의 행위에 대해 각 객체가 가지고있는 고유한 특성에 따라 다른 여러가지 형태로 재구성되는것

    // 추상화
    // 객체에서 공통되는 부분들을 모아 상위 개념으로 새롭게 선언하는것
    // 공통적이고 중요한 것들을 모아 객체를 모델링

    • 전반적인 틀만들기 및 사용법 예시
      Car car1 = new Car(); // new 키워드를 사용해서 생성자 호출
      Car car2 = new Car(); // 인스턴스 생성

          System.out.println(car1); // 주소값 : week03.chap07.Car@7a81197d
      
          car1.horn();
      
          Car [] carArray = new Car[3];
          char p = car1.changeGear(&#x27;P&#x27;);
          System.out.println(p);
      
          carArray[0] = car1;
      
          car2.changeGear(&#x27;N&#x27;);
      
          carArray[1] = car2;
      
          Car car3 = new Car();
          car3.changeGear(&#x27;D&#x27;);
          carArray[2] = car3;
      
          for(Car car : carArray){
              System.out.println(&quot;car.gear = &quot; + car.gear); // .(도트)연산자로 접근
               위에서 changeGear()메소드를 통해서 값을 변경하고
               해당값이 들어간 필드를 불러와서 보여줌
          }</code></pre><pre id="f1a3ec79-6f5a-49b6-bebb-b11701a73fd8" class="code"><code>Car car = new Car();
      
           초기값 기본 값 확인 : 초기값을 준 것은 그 값이 들어가고, 아닌값은 default value가 들어있음
          System.out.println(&quot;car.model = &quot;+car.model);
          System.out.println(&quot;car.color = &quot;+car.color);
      
          System.out.println(&quot;car.speed = &quot;+car.speed);
          System.out.println(&quot;car.gear = &quot; + car.gear);
          System.out.println(&quot;car.lights = &quot;+car.lights);
      
          System.out.println(&quot;car.tire = &quot;+car.tire);
          System.out.println(&quot;car.door = &quot;+car.door);
      
          // 필드 사용
          car.color = &quot;blue&quot;;
          car.speed = 100;
          car.lights = false;
      
          System.out.println(&quot;car.color = &quot;+car.color);
          System.out.println(&quot;car.speed = &quot;+car.speed);
          System.out.println(&quot;car.lights = &quot;+car.lights);</code></pre><pre id="d0ef6683-9c63-4f90-ad00-0eed62896e1b" class="code"><code>System.out.println(&quot;car.gear = &quot;+ car.gear);
      
          double speed = car.gasPedal(100,&#x27;D&#x27;);
          System.out.println(&quot;speed : &quot; + speed);
      
          boolean lights = car.onOffLight();
          System.out.println(&quot;lights : &quot; + lights);
      
          System.out.println();
          System.out.println(&quot;car.gear = &quot;+ car.gear);
      
          System.out.println(&quot;&quot;);
          car.carSpeed(100,80,22);
      
          System.out.println(&quot;&quot;);
          car.carSpeed(100,80,22,2222,44);</code></pre><hr id="25e8146e-4baf-4ca5-960a-b4eeac3fc2c9"/><pre id="7bbd36cb-cca0-479f-bfeb-19e9509c2fa6" class="code"><code>package week03.chap07;

      // 클래스 만들때의 4가지 스텝
      // 1. 클래스 선언
      // 2. 속성 정의
      // 3. 생성자 -> constructor(정의하는방식)
      // 4. 메서드 정의
      public class Car {// 1. 클래스 선언

      // 2. 속성 정의
      // 필드 영역
      // 선언만한 상태
      
      // 고유 데이터 영역
      String company;
      String model = &quot;Gv80&quot;;
      String color;
      double price;
      
      // 상태 데이터 영역
      double speed;
      char gear = &#x27;P&#x27;;
      boolean lights = true;
      
      // 객체 데이터 영역
      Tire tire = new Tire();
      Door door;
      Handle handle;
      
      
      // 3. 생성자(클래스 이름과 동일해야함) : 처음 객체가 생성될때(instance화) 어떤 로직을 수행해야하며, 어떤값이 필수로 들어와야 하는지 정의
      public Car (){
          // 기본생성자 : 생략이 가능출
          System.out.println(&quot;Car 생성자 호출&quot;);
      }
      
      // 4. 메소드
      
      // gasPedal
      // input : kmh
      // output : spped
      double gasPedal(double kmh, char type){ // 리턴타입을 작성
          changeGear(type);
          speed = kmh;
          return speed;
      }
      
      // brakePedal
      // input : none
      // output : speed
      double brakePedal(){
          speed = 0;
          return speed;
      }
      
      // changeGear
      // input : gear(char type)
      // output : gear
      char changeGear(char type){
          gear = type;
          return gear;
      }
      
      // onOffLight
      // input : none
      // output : boolean
      boolean onOffLight(){
          lights = !lights;
          return lights;
      }
      
      // horn
      // input : x
      // output : x
      void horn(){
          // return값이 필수가 아니다
          System.out.println(&quot;나와!!!!!&quot;);
      }
      
      
      
      // 자동차의 속도 .. 가변길이 메소드
      void carSpeed(double ... speeds){   // ... : 스프레드 매개 변수 / 배열로 들어옴
          for(double v : speeds){
              System.out.println(&quot;v = &quot;+v);
          }
      }

      }

      package week03.chap07;

      public class Door {
      public Door(){
      System.out.println("문 객체 생성");
      }
      }

      package week03.chap07;

      public class Handle {
      public Handle (){
      System.out.println("핸드객체 만들어짐");
      }
      }

      package week03.chap07;

      public class Tire {
      public Tire(){
      System.out.println("타이어 생성");
      }
      }

    • 필드 타입별 기본값
      데이터 타입기본값
      byte0
      char\u0000 (공백)
      short0
      int0
      long0L
      float0.0F
      double0.0
      booleanfalse
      배열null
      클래스null
      인터페이스null
  • 오버로딩
    // 오버로딩
    // 메서드의 이름은 같지만 매개변수의 타입이나 순서나 개수가 다른것
    // 응답값만 다른것은 안된다
    // 접근 제어자만 다른것도 오버로딩이라고 할 수 없다
    // 매개변수의 차이로만 구현가능

        // 장점
        // 1. 메서드명을 절약할수있다 -&gt; 같은 이름에서 타입만 변경해서 쓸수있기때문에
        // 2. 같은이름으로 다른 동작을 개별 정의가 가능하다
    
        // 기본형 매개변수는 값자체가 복사되어 넘어가기때문에 매개값으로 지정된 변수의 원본값이 변경안됨
        // 참조형 매개변수는 지정한 값의 주소를 매개변수에 복사해서 전달하기 때문에 값을 읽거나 변경하는 것이 가능</code></pre><ul id="800b055a-a94e-4baf-8b57-b83a2dad9bd3" class="bulleted-list"><li style="list-style-type:disc">예시 </li></ul><pre id="4d7ec027-db4e-4cbf-aee4-d02e4f1afdc1" class="code"><code>public class PrintStream extends FilterOutputStream
    implements Appendable, Closeable

    {
    ...

    	public void println() {
        newLine();
    }
    
    public void println(boolean x) {
        if (getClass() == PrintStream.class) {
            writeln(String.valueOf(x));
        } else {
            synchronized (this) {
                print(x);
                newLine();
            }
        }
    }
    
    public void println(char x) {
        if (getClass() == PrintStream.class) {
            writeln(String.valueOf(x));
        } else {
            synchronized (this) {
                print(x);
                newLine();
            }
        }
    }
    
    public void println(int x) {
        if (getClass() == PrintStream.class) {
            writeln(String.valueOf(x));
        } else {
            synchronized (this) {
                print(x);
                newLine();
            }
        }
    }
    
    public void println(long x) {
        if (getClass() == PrintStream.class) {
            writeln(String.valueOf(x));
        } else {
            synchronized (this) {
                print(x);
                newLine();
            }
        }
    }
    
    public void println(float x) {
        if (getClass() == PrintStream.class) {
            writeln(String.valueOf(x));
        } else {
            synchronized (this) {
                print(x);
                newLine();
            }
        }
    }
    
    public void println(double x) {
        if (getClass() == PrintStream.class) {
            writeln(String.valueOf(x));
        } else {
            synchronized (this) {
                print(x);
                newLine();
            }
        }
    }
    
    public void println(char[] x) {
        if (getClass() == PrintStream.class) {
            writeln(x);
        } else {
            synchronized (this) {
                print(x);
                newLine();
            }
        }
    }
    
    public void println(String x) {
        if (getClass() == PrintStream.class) {
            writeln(String.valueOf(x));
        } else {
            synchronized (this) {
                print(x);
                newLine();
            }
        }
    }
    
    public void println(Object x) {
        String s = String.valueOf(x);
        if (getClass() == PrintStream.class) {
            // need to apply String.valueOf again since first invocation
            // might return null
            writeln(String.valueOf(s));
        } else {
            synchronized (this) {
                print(s);
                newLine();
            }
        }
    }
    
    
    	  ...

    }

  • 기본형과 참조형 매개변수의 차이
    // 기본형 매개변수는 매개값 자체가 복사되어 넘어가기때문에 원본에는 영향을 안미친다
    // 참조형 매개변수의 경우에는 주소값이 넘어가기 때문에 원본이 바뀌는등 영향을 미친다
    package week03.parameter;

    public class Main {
    public static void main(String[] args) {
    Car car = new Car(); // 객체 생성

        // 기본형 매개변수
        char type = &#x27;D&#x27;;
        car.brakePedal(type);
    
        // 메서드 실행 완료 후 전달할 매개값으로 지정된 type 값 확인
        System.out.println(&quot;type = &quot; + type); // 기존에 선언한 값 &#x27;D&#x27; 출력, 원본 값 변경되지 않음
        // 메서드 실행 완료 후 반환된 car 인스턴스의 gear 타입 확인
        System.out.println(&quot;gear = &quot; + car.gear); // 객체 내부에서 type을 변경하여 수정했기 때문에 &#x27;P&#x27; 출력
    
    
    
        System.out.println();
        // 참조형 매개변수
        Tire tire = new Tire();
        tire.company = &quot;금호&quot;; // 금호 타이어 객체 생성
    
        // 차 객체의 타이어를 등록하는 메서드 호출한 후 반환값으로 차 객체의 타이어 객체 반환
        Tire carInstanceTire = car.setTire(tire);
    
        // 메서드 실행 완료 후 전달할 매개값으로 지정된 참조형 변수 tire의 company 값 확인
        System.out.println(&quot;tire.company = &quot; + tire.company); // &quot;KIA&quot; 출력
        // 전달할 매개값으로 지정된 tire 인스턴스의 주소값이 전달되었기 때문에 호출된 메서드에 의해 값이 변경됨.
    
        // 메서드 실행 완료 후 반환된 car 인스턴스의 tire 객체 값이 반환되어 저장된 참조형 변수 carInstanceTire의 company 값 확인
        System.out.println(&quot;carInstanceTire.company = &quot; + carInstanceTire.company); // &quot;KIA&quot; 출력
    }

    }

    package week03.parameter;

    public class Tire {
    String company; // 타이어 회사
    public Tire() {}
    }

    package week03.parameter;

    public class Car {

    String company; // 자동차 회사
    String model; // 자동차 모델
    String color; // 자동차 색상
    double price; // 자동차 가격
    
    double speed;  // 자동차 속도 , km/h
    char gear; // 기어의 상태, P,R,N,D
    boolean lights; // 자동차 조명의 상태
    
    Tire tire;
    Door door = new Door();
    Handle handle = new Handle();
    
    public Car() {} // 기본 생성자
    
    double gasPedal(double kmh, char type) {
        changeGear(type);
        speed = kmh;
        return speed;
    }
    
    double brakePedal(char type) {
        speed = 0;
        type = &#x27;P&#x27;; // 정지 후 매개변수 type을 어떤 타입으로 전달 받았는지 상관없이 &#x27;P&#x27;로 고정시키기
        changeGear(type);
        return speed;
    }
    
    char changeGear(char type) {
        gear = type;
        return gear;
    }
    
    boolean onOffLights() {
        lights = !lights;
        return lights;
    }
    
    void horn() {
        System.out.println(&quot;빵빵&quot;);
    }
    
    Tire setTire(Tire tireCompany) {
        tireCompany.company = &quot;KIA&quot;; // 금호 타이어를 전달 받았지만 강제로 KIA 타이어로 교체
        tire = tireCompany;
        return tire;
    }

    }

  • 인스턴스 멤버와 클래스 멤버
    💡
    멤버 = 필드 + 메서드
    • 인스턴스 멤버 = 인스턴스 필드 + 인스턴스 메서드
    • 클래스 멤버(정적 멤버) = 클래스 필드 + 클래스 메서드

    선언하는 방법에 따라서 인스턴스 멤버와 클래스 멤버로 구분할 수 있다

    인스턴스 멤버는 객체 생성 후에만 사용할 수 있고, 클래스 멤버의 경우에는 그럴 필요가 없다

    인스턴스 멤버의 경우 지금까지 new를 사용해서 객체생성해준것들

    클래스는 Java의 클래스 로더에 의해 메서드 영역에 저장되고 사용됨

    클래스 멤버란 메서드 영역의 클래스와 같은 위치에 고정적으로 위치하는 멤버

    객체생성이 불필요

    // 클래스 멤버 선언
    필드와 메서드에서 static키워드를 사용하면 가능
    static은 정적인 메소드를 의미한다
    Static영역에 할당되며, Static영역에 할당된 메모리는 모든 객체가 공유하여 하나의 멤버를 어디서든지 참조할 수 있는 장점을 가지지만
    Garbage Collector의 관리 영역 밖에 존재하므로 Static영역에 있는 멤버들은 프로그램의 종료시까지 메모리가 할당된 채로 존재
    그러므로 너무 남발하면 시스템 성능에 악영향을 줄 수 있다.

    // 사용하는 경우
    1. 인스턴스마다 모두 가지고 있을 필요가 없는 공용적인 데이터를 저장하는 필드의 경우
    2. 인스턴스 필드를 사용하지 않고 실행되는 메서드의 경우

    클래스 멤버로 선언된 메서드는 인스턴스 멤버를 사용할 수 없다
    인스턴스 멤버로 선언된 메서드는 클래스 멤버를 사용할 수 있다

    • 클래스 멤버는 객체 생성없이 바로 사용 가능하기 때문에 객체가 생성되어야 존재할 수 있는 인스턴스 멤버를 사용할 수 없다

    클래스 필드로 공유하게 만든다면 메모리를 효율적으로 사용할 수 있다

    // 인스턴스 변수는 인스턴스가 생성될때마다 생성되므로 인스턴스마다 각기 다른 값을 가지지만
    // 정적변수(클래스 필드)는 모든 인스턴스가 하나의 저장공간을 공유하기에 항상 같은 값을 가진다
    // 아래식에서 첫번째 println은 num이 정적 변수이기에 new로 객체생성을 해도 number1과number2와 동일한 저장공간을 공유하기때문에 값이1로 올라간것을 확인할수있다.
    // 하지만 두번째 println의 경우 인스턴스 필드로 객체가 생성될때 마다 별도의 값을 생성해서 사용하기때문에 number2.num2는 0인상태이고 number1.num2만 1로올라간것을 확인할수있다.

    class Number{
    static int num = 0; //클래스 필드
    int num2 = 0; //인스턴스 필드
    }

    public class Static_ex {

    public static void main(String[] args) {
    	Number number1 = new Number(); //첫번째 number
    	Number number2 = new Number(); //두번쨰 number
    	
    	number1.num++; //클래스 필드 num을 1증가시킴
    	number1.num2++; //인스턴스 필드 num을 1증가시킴
    	System.out.println(number2.num); //두번째 number의 클래스 필드 출력
    	System.out.println(number2.num2); //두번째 number의 인스턴스 필드 출력
    }

    }

    • 예제코드
    public class Car {

    static String company = &quot;GENESIS&quot;; // 자동차 회사 : GENESIS
    String model; // 자동차 모델
    String color; // 자동차 색상
    double price; // 자동차 가격
    
    double speed;  // 자동차 속도 , km/h
    char gear; // 기어의 상태, P,R,N,D
    boolean lights; // 자동차 조명의 상태
    
    
    public Car() {} // 기본 생성자
    
    double gasPedal(double kmh, char type) {
        changeGear(type);
        speed = kmh;
        return speed;
    }
    
    double brakePedal() {
        speed = 0;
        return speed;
    }
    
    char changeGear(char type) {
        gear = type;
        return gear;
    }
    
    boolean onOffLights() {
        lights = !lights;
        return lights;
    }
    
    void horn() {
        System.out.println(&quot;빵빵&quot;);
    }
    
    String getCompany() {
        return &quot;(주)&quot; + company;
    }
    
    static String setCompany(String companyName) {
        // System.out.println(&quot;자동차 모델 확인: &quot; + model); // 인스턴스 필드 사용 불가
        company = companyName;
        return company;
    }

    }

    public class Main {
    public static void main(String[] args) {
    // 클래스 필드 company 확인
    System.out.println(Car.company + "\n");
    // 클래스 필드 변경 및 확인
    Car.company = "Audi";
    System.out.println(Car.company + "\n");

        // 클래스 메서드 호출
        String companyName = Car.setCompany(&quot;Benz&quot;);
        System.out.println(&quot;companyName = &quot; + companyName);
    
        System.out.println();
        // 참조형 변수 사용
        Car car = new Car(); // 객체 생성
    
        car.company = &quot;Ferrari&quot;;
        System.out.println(car.company + &quot;\n&quot;);
    
        String companyName2 = car.setCompany(&quot;Lamborghini&quot;);
        System.out.println(&quot;companyName2 = &quot; + companyName2);
    }

    }

  • 지역 변수 final필드 상수
    // 지역 변수 <-> 전역 변수
    // 해당 메서드가 실행될 때 마다 독립적인 값을 저장하고 관리
    // 이 지역변수가 메서드 내부에서 정의될 때 생성
    // 이 메서드가 종료될 때 소멸

    // final
    // final 필드는 초기값이 저장되면 해당값을 프로그램이 실행하는 도중에는 절대로 수정할 수 없습니다.
    // 또한 final 필드는 반드시 초기값을 지정해야 합니다.

    // 상수
    // 전체가 대문자인 변수는 상수
    // 상수의 특징은 값이 반드시 한개이며 불변의 값
    // 스턴스마다 상수를 저장할 필요가 없다
    // final 앞에 static 키워드를 추가하여 모든 인스턴스가 공유할 수 있는 값이 한개이며 불변인 상수를 선언

    static final String COMPANY = "GENESIS";

    ...

    System.out.println(Car.COMPANY);

  • 생성자
    // 기본 생성자는 매개변수가 없는것을 의미한다
    // 모든 클래스에는 매개변수가 하나이상 존재해야지만 매개변수는 생략이 가능하기때문에
    // 따로 만들지 않아도 오류가 발생하지 않는것이다
    // 하지만 생성자가 있는경우에는 자동으로 기본 생성자는 생성되지않는다
    

    // 생성자는 객체를 초기화 하는 역할을 수행
    // 생성자의 매개변수에 맞추어서 인스턴스화해야한다.

    // 생성자 오버로딩도 가능하다
    // 개수,타입,순서가 동일한데 매개변수명만 다른경우에는 불가능

    package week03.constructor;

    import java.util.stream.StreamSupport;

    public class Car {

    static final String COMPANY = &quot;GENESIS&quot;; // 자동차 회사 : GENESIS
    String model; // 자동차 모델
    String color; // 자동차 색상
    double price; // 자동차 가격
    
    double speed;  // 자동차 속도 , km/h
    char gear = &#x27;P&#x27;; // 기어의 상태, P,R,N,D
    boolean lights; // 자동차 조명의 상태
    
    
    // constructor
    // 오버로딩 생성자
    public Car(String modelName) {
        model = modelName;
        System.out.println(&quot;첫번째 생성자&quot;);
    }
    
    public Car(String modelName, String colorName) {
        model = modelName;
        color = colorName;
        System.out.println(&quot;두번째 생성자&quot;);
    }
    
    public Car(String modelName, String colorName, double priceValue) {
        model = modelName;
        color = colorName;
        price = priceValue;
        System.out.println(&quot;세번째 생성자&quot;);
    }
    
    double gasPedal(double kmh, char type) {
        changeGear(type);
        speed = kmh;
        return speed;
    }
    
    double brakePedal() {
        speed = 0;
        return speed;
    }
    
    char changeGear(char type) {
        gear = type;
        return gear;
    }
    
    boolean onOffLights() {
        lights = !lights;
        return lights;
    }
    
    void horn() {
        System.out.println(&quot;빵빵&quot;);
    }

    }

    package week03.constructor;

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

        // 기본 생성자 호출 오류 확인
        // Car car1 = new Car(); // 오류 발생
    
        // 생성자 오버로딩을 통해 여러 상황에서 자동차 생산
        // 제네시스 자동차를 생산 : static final String COMPANY = &quot;GENESIS&quot;; 상수 고정
        // 모든 자동차는 생산시 기어의 최초 상태 &#x27;P&#x27; 로 고정 : char gear = &#x27;P&#x27;; 직접 대입하여 초기화
    
        // GV60 모델만 기본으로 선택
        Car car2 = new Car(&quot;GV60&quot;);
        System.out.println(&quot;car2.model = &quot; + car2.model);
        System.out.println(&quot;car2.gear = &quot; + car2.gear + &quot;\n&quot;);
    
        // GV70 모델, 색상 Blue 만 기본으로 선택
        Car car3 = new Car(&quot;GV70&quot;, &quot;Blue&quot;);
        System.out.println(&quot;car3.model = &quot; + car3.model);
        System.out.println(&quot;car3.color = &quot; + car3.color);
        System.out.println(&quot;car3.gear = &quot; + car3.gear + &quot;\n&quot;);
    
        // GV80 모델, 색상 Black, 가격 50000000 으로 완전하게 고정된 경우
        Car car4 = new Car(&quot;GV80&quot;, &quot;Black&quot;, 50000000);
        System.out.println(&quot;car4.model = &quot; + car4.model);
        System.out.println(&quot;car4.color = &quot; + car4.color);
        System.out.println(&quot;car4.price = &quot; + car4.price);
        System.out.println(&quot;car4.gear = &quot; + car4.gear + &quot;\n&quot;);
    
    }

    }

  • this this()
    // this는 객체(인스턴스) 자신을 표현
    // 매개변수명과 사용하는 필드이름이 같은 경우
    // 오류는 발생하지않지만 필드가 아닌 가장 가까운 매개변수명을 가리키게 됨으로 자기 자신에게 값을 대입하는 상황이 된다.
    // 그래서 this 키워드를 사용해서 해결해줄수있다

    // this는 인스턴스 자신을 뜻하기도하니까 객체의 메서드에서 리턴타입이 인스턴스 자신의 클래스 타입이라면
    // this르 사용하여 인스턴스 자신의 주소 반환 가능
    Car returnInstance() {
    return this;
    }

    // this()
    // 인스턴스 자신의 생성자를 호출
    // this()키워드를 사용해서 다른 생성자를 호출할때는 반드시 첫번째줄에 작성해야한다
    public class Car {

    static final String COMPANY = &quot;GENESIS&quot;; // 자동차 회사 : GENESIS
    String model; // 자동차 모델
    String color; // 자동차 색상
    double price; // 자동차 가격
    
    double speed;  // 자동차 속도 , km/h
    char gear = &#x27;P&#x27;; // 기어의 상태, P,R,N,D
    boolean lights; // 자동차 조명의 상태
    
    
    	// 결론은 세번째의 생성자로 만들어지게 된다.
    public Car(String model) {
        this(model, &quot;Blue&quot;, 50000000);
    }
    
    public Car(String model, String color) {
        this(model, color, 100000000);
    }
    
    public Car(String model, String color, double price) {
        this.model = model;
        this.color = color;
        this.price = price;
    }
    
    double gasPedal(double kmh, char type) {
        changeGear(type);
        speed = kmh;
        return speed;
    }
    
    double brakePedal() {
        speed = 0;
        return speed;
    }
    
    char changeGear(char type) {
        gear = type;
        return gear;
    }
    
    boolean onOffLights() {
        lights = !lights;
        return lights;
    }
    
    void horn() {
        System.out.println(&quot;빵빵&quot;);
    }
    
    Car returnInstance() {
        return this;
    }

    }

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

        // 생성자 오버로딩을 통해 여러 상황에서 자동차 생산
        // 제네시스 자동차를 생산 : static final String COMPANY = &quot;GENESIS&quot;; 상수 고정
        // 모든 자동차는 생산시 기어의 최초 상태 &#x27;P&#x27; 로 고정 : char gear = &#x27;P&#x27;; 직접 대입하여 초기화
    
        // 모델을 변경하면서 만들 수 있고 색상 : Blue, 가격 50000000 고정
        Car car1 = new Car(&quot;GV60&quot;);
        System.out.println(&quot;car1.model = &quot; + car1.model);
        System.out.println(&quot;car1.color = &quot; + car1.color);
        System.out.println(&quot;car1.price = &quot; + car1.price);
        System.out.println(&quot;car1.gear = &quot; + car1.gear + &quot;\n&quot;);
    
        // 모델, 색상을 변경하면서 만들 수 있고 가격 100000000 고정
        Car car2 = new Car(&quot;GV70&quot;, &quot;Red&quot;);
        System.out.println(&quot;car2.model = &quot; + car2.model);
        System.out.println(&quot;car2.color = &quot; + car2.color);
        System.out.println(&quot;car2.price = &quot; + car2.price);
        System.out.println(&quot;car2.gear = &quot; + car2.gear + &quot;\n&quot;);
    
        // GV80 모델, 색상 Black, 가격 120000000 으로 완전하게 고정된 경우
        Car car3 = new Car(&quot;GV80&quot;, &quot;Black&quot;, 120000000);
        System.out.println(&quot;car3.model = &quot; + car3.model);
        System.out.println(&quot;car3.color = &quot; + car3.color);
        System.out.println(&quot;car3.price = &quot; + car3.price);
        System.out.println(&quot;car3.gear = &quot; + car3.gear + &quot;\n&quot;);
    
        // this 키워드를 통해 car3 인스턴스 자신을 반환 : car3.returnInstance() = 인스턴스의 주소
        System.out.println(car3.returnInstance().model); // car3의 model
        System.out.println(car3.returnInstance().color); // car3의 color
        System.out.println(car3.returnInstance().price); // car3의 price
    
    }

    }

  • super super()
    // super -> 부모 클래스의 멤버를 참조 / 자식클래스의 위치에서 사용
    // 자식클래스의 내부와 부모클래스에서 상속받은 멤버와 이름이 같은경우에 구분하기위해서
    // super.필드 -> 부모클래스의 필드값에 들어감
    public class Car {

    String company; // 자동차 회사
    String model; // 자동차 모델
    String color; // 자동차 색상
    double price; // 자동차 가격
    
    double speed;  // 자동차 속도 , km/h
    char gear = &#x27;P&#x27;; // 기어의 상태, P,R,N,D
    boolean lights; // 자동차 조명의 상태
    
    public String getModel() {
        return model;
    }
    
    public String getColor() {
        return color;
    }
    
    public double gasPedal(double kmh, char type) {
        changeGear(type);
        speed = kmh;
        return speed;
    }
    
    public double brakePedal() {
        speed = 0;
        return speed;
    }
    
    public char changeGear(char type) {
        gear = type;
        return gear;
    }
    
    public boolean onOffLights() {
        lights = !lights;
        return lights;
    }
    
    public void horn() {
        System.out.println(&quot;빵빵&quot;);
    }

    }

    public class SportsCar extends Car{
    String engine;

    String model = &quot;Ferrari&quot;; // 자동차 모델
    String color = &quot;Red&quot;; // 자동차 색상
    double price = 300000000; // 자동차 가격
    
    public SportsCar(String engine) {
        this.engine = engine;
    }
    
    public void booster() {
        System.out.println(&quot;엔진 &quot; + engine + &quot; 부앙~\n&quot;);
    }
    
    public void setCarInfo(String model, String color, double price) {
        super.model = model; // model은 부모 필드에 set
        super.color = color; // color는 부모 필드에 set
        this.price = price; // price는 자식 필드에 set
    }
    
    @Override
    public double brakePedal() {
        speed = 100;
        System.out.println(&quot;스포츠카에 브레이크란 없다&quot;);
        return speed;
    }
    
    @Override
    public void horn() {
        booster();
    }

    }

    public class Main {
    public static void main(String[] args) {
    // 자식 클래스 스포츠카 객체 생성
    SportsCar sportsCar = new SportsCar("Orion");

        // 자식 객체의 model, color, price 초기값 확인
        System.out.println(&quot;sportsCar.model = &quot; + sportsCar.model); // Ferrari
        System.out.println(&quot;sportsCar.color = &quot; + sportsCar.color); // Red
        System.out.println(&quot;sportsCar.price = &quot; + sportsCar.price); // 3.0E8
        System.out.println();
    
        // setCarInfo 메서드 호출해서 부모 및 자식 필드 값 저장
        sportsCar.setCarInfo(&quot;GV80&quot;, &quot;Black&quot;, 50000000);
    
        // this.price = price; 결과 확인
        System.out.println(&quot;sportsCar.price = &quot; + sportsCar.price); // 5.0E7
        System.out.println();
    
        // super.model = model; super.color = color;
        // 결과 확인을 위해 자식 클래스 필드 model, color 확인 &amp; 부모 클래스 메서드인 getModel(), getColor() 호출
        // 자식 클래스 필드 값은 변화 없음.
        System.out.println(&quot;sportsCar.model = &quot; + sportsCar.model); // Ferrari
        System.out.println(&quot;sportsCar.color = &quot; + sportsCar.color); // Red
        System.out.println();
    
        // 부모 클래스 필드 값 저장됨.
        System.out.println(&quot;sportsCar.getModel() = &quot; + sportsCar.getModel()); // GV80
        System.out.println(&quot;sportsCar.getColor() = &quot; + sportsCar.getColor()); // Black
    
    }

    }


    // super() -> 부모클래스의 생성자를 호출
    // 기본 생성자의 부모클래스는 자동적으로 자식생성자에서도 생성
    // 부모 클래스 Car 생성자
    public Car(String model, String color, double price) {
    this.model = model;
    this.color = color;
    this.price = price;
    }

    // 자식 클래스 SportsCar 생성자
    public SportsCar(String model, String color, double price, String engine) {
    // this.engine = engine; // 오류 발생
    super(model, color, price);
    this.engine = engine;
    }

  • 접근제어자
    💡
    제어자는 클래스, 변수, 메서드의 선언부에 사용되어 부가적인 의미를 부여해 줍니다.
    • 접근 제어자 : public, protected, default, private
    • 그 외 제어자 : static, final, abstract

    ❗️ 하나의 대상에 여러 개의 제어자를 조합해서 사용할 수 있으나, 접근 제어자는 단 하나만 사용할 수 있습니다.

    private < default < protected < public 순으로 많은 접근을 허용

    1. private
    • 해당 클래스 안에서만 접근이 가능
    • 다른 클래스에서는 접근이 불가능
    1. default
    • 따로 설정하지않으면 default로 자동설정됨
    • 동일한 패키지 안에서만 접근이 가능
    1. protected
    • 동일 패키지의 클래스 또는 해당 클래스를 상속받은 클래스에서만 접근 가능
    1. public
    • 어떤 클래스에서도 접근 가능
    • 사용가능한 접근 제어자
      • 클래스 : public, default
      • 메서드 & 멤버변수 : public, protected, default, private
      • 지역변수 : 없음
    • 접근 제어자를 이용한 캡슐화 (은닉성)
      • 접근제어자는 클래스 내부에 선언된 데이터를 보호하기 위해서 사용합니다.
      • 유효한 값을 유지하도록, 함부로 변경하지 못하도록 접근을 제한하는 것이 필요합니다.
    • 생성자의 접근 제어자
      • 생성자에 접근 제어자를 사용함으로 인스턴스의 생성을 제한할 수 있습니다.
      • 일반적으로 생성자의 접근 제어자는 클래스의 접근 제어자와 일치합니다.
  • Getter Setter
    객체의 무결성(변경이 없는 상태를 유지하기 위해 접근제어자를 사용)
  • 외부에서 필드에 직접 접근을 막기위해 private default등 사용
    • Getter
      📌
      외부에서 객체의 private 한 필드를 읽을 필요가 있을 때 Getter 메서드를 사용합니다.
      private double speed;  // 자동차 속도 , km/h
      private char gear = 'P'; // 기어의 상태, P,R,N,D
      private boolean lights; // 자동차 조명의 상태
      • 자동차 클래스의 필드에 이처럼 private 접근 제어자로 지정한 필드가 있을 때 Getter 메서드를 통해 값을 가져올 수 있습니다.
      public String getModel() {
      return model;
      }

    public String getColor() {
    return color;
    }

    public double getPrice() {
    return price;
    }

    • 메서드 이름의 규칙은 : get + 필드이름(첫 글자 대문자) 입니다.
    • 사용하는 방법은 인스턴스 메서드 호출과 동일합니다.
  • Setter
    💡
    외부에서 객체의 private 한 필드를 읽을 필요가 있을 때 Getter 메서드를 사용합니다.
    private double speed;  // 자동차 속도 , km/h
    private char gear = 'P'; // 기어의 상태, P,R,N,D
    private boolean lights; // 자동차 조명의 상태
    • 자동차 클래스의 필드에 이처럼 private 접근 제어자로 지정한 필드가 있을 때 Getter 메서드를 통해 값을 가져올 수 있습니다.
    public String getModel() {
    return model;
    }

    public String getColor() {
    return color;
    }

    public double getPrice() {
    return price;
    }

    • 메서드 이름의 규칙은 : get + 필드이름(첫 글자 대문자) 입니다.
    • 사용하는 방법은 인스턴스 메서드 호출과 동일합니다.
  • 제어자의 조합
    • 사용가능한 제어자
      • 클래스 : public, default, final, abstract
      • 메서드 : public, protected, default, private, final, abstract, static
      • 멤버변수 : public, protected, default, private, final, static
      • 지역변수 : final
    ⚠️
    제어자 사용시 주의 사항
    • 메서드에 staticabstract를 함께 사용할 수 없다.
    • 클래스에 abstractfinal을 동시에 사용할 수 없다.
    • abstract메서드의 접근 제어자가 private일 수 없다.
    • 메서드에 privatefinal을 같이 사용할 필요는 없다.
  • 패키지
    - 자바 클래스를 모아놓은 디렉토리(폴더)

    • 클래스 또는 인터페이스를 포함 시킬 수 있다.
    • 서로 관련된 클래스들끼리 그룹 단위로 묶어 놓아 효율적으로 관리할 수 있다
    • 서로 관련된 클래스의 묶음
    • rt.jar -> java9이후로는 무거와서 module로 쪼개서 넣음
    • 클래스패스(classpath) : 클래스파일의 위치를 알려주는 경로
    • classpath라는 환경변수에서 관리하고, 경로간의 구분자은 ;으로 구분
    • classpath(환경변수)에 패키지의 루트를 등록해주어야함
    // 선언 방식
    // 소스파일의 첫번째 문장으로 단 한번 선언
    // 같은 소소 파일의 클래스들은 모두 같은 패키지에 속하게 된다
    // 패키지 선언이 없음녀 이름없는(unnamed)패키지 = default packge에 속하게된다
    package {패키지, 즉 해당 소스가 속한 폴더 이름};

// 굳이 이 키워드를 선언해야하는 이유
// 자바에서 클래스를 선언할 때 package를 지정해주지 않으면 default unnamed package로 여겨진다
// 이렇게 생성된 클래스는 다른 package에 있는 자바파일에서 import 할 수 없다
// 같은 default package에 존재하면 가능하다

// import
import {패키지명}[.{하위 패키지명}].클래스(또는 *);

// ./java/lang/Function 클래스를 불러오고 싶다면
import java.lang.Function;

// 해당 패키지 내 모든 클래스를 불러옴
// 특정 클래스만 import 윗 구문과 성능상 차이는 전혀 없다
import java.lang.*;

// 패키지는 다중선언이 불가능 하다
// 하나의 소스 파일엔 오직 하나의 packge만 선언이 가능하다
// 패키지는 컴파일 시점의 계층 구성을 정의
// 그러므로 패키지를 다중 선언하게되면 해당 소스 파일은 다수의 폴더에 존재한다는 의미
// 같은 내용과 이름은 존재할 수 있지만 그파일 자체가 여럿 존재할 수 없다

// 패키지와 경로를 일치시키는것이 좋음
// 패키지 계층 구성과 실제 폴더 경로를 다르게 하면 결과가 기존 구성과 다르게 나옴

// 클래스 이름이 같아도 존재할수있는 이유
// 자바는 이름이 같더라도 클래스의 실제이름은 패키지 명도 포함되어 구분된다
// 그래서 클래스 이름이 같아도 패키지명이 다르면 가능하다

package특성

  • 계층 구조로 클래스 라이브러리를 관리하기 위한 컴파일러 단에서 정의하기 위한 계층 구조 정의 키워드
  • 소스 당 하나만 선언 가능 이유로는 파일은 오직 하나의 계층구조를 가지기 때문에
  • 유지보수를 위해서라도 packge선언 시 실제 폴더구성경로와 일치시키는것이 좋다
  • 클래스의 실제 이름은 {package 구성}.{클래스이름}
  • import
    // 같은 패키지면 그냥 가져올수있다
    // import를 하지않으면 이렇게 호출 해온다

    	    // 클래스의 일부분이면서, 하위 패키지를 도트(.)로 구분한다
        week03.packgeExam.pk1.Car car = new week03.packgeExam.pk1.Car();
        week03.packgeExam.pk2.Car car2 = new week03.packgeExam.pk2.Car();
    
        car.horn();
        car2.horn();
    
        // 이름이 같기때문에 동시에 import가 불가능하다
        Car car = new Car();
        car.horn();
    
        week03.packgeExam.pk2.Car car2 = new week03.packgeExam.pk2.Car();
        car2.horn();

    // 클래스를 사용할 때 import를 사용하면 패키지이름을 생략할 수 있다
    // 컴파일러에게 클래스가 속한 패키지를 알려준다
    // java.lang패키지의 클래스는 import하지 않고도 사용할 수 있다
    // import 패키지명.클래스명; or import 패키지명.;
    // import문은 패키지문과 클래스선언의 사이에 선언한다
    // import 패키지.
    을 사용해도 성능에 영향은 없다
    // import문은 컴파일 시에 처리되므로 성능에 영향은 없다
    // *은 모든클래스만을 의미하지 패키지는 포함이 안된다
    // 클래스이름이 같을경우에는 패키지이름을 앞에적어서 구분해야한다

    // static import
    // static멤버를 사용할 때 클래스 이름을 생략할수 있다
    // static멤버는(static변수 + static메서드)

  • 제어자(modifier)
    - 클래스와 클래스의 멤버(멤버변수,메서드)에 부가적인 의미 부여

    • 접근제어자 : public,protected,(default 아무것도 안붙일때),private => 중복해서 사용불가

    • 그외 : static,final,abstract,native,transient,synchronized,volatile,strictfp

    • 하나의 대상에 여러 제어자를 같이 사용가능(접근 제어자는 하나만 가능)

    • 순서는 상관없다 대부분 접근 제어자를 맨앞에 쓴다

    • static : 클래스의,공통적인

    final - 마지막의,변경될 수 없는
    상속을 시킬 수가 없다는 의미 = 상속계층도의 마지막 계층
    String,Math가 대표적인 final클래스
    abstract - 추상의,미완성의
    상속받아서 구현화하지않으면 사용할 수가 없다 = 인스터스화 불가
  • 상속
    // 중복되는 값들은 부모클래스에 작성하고 나머지 추가하고 싶은 경우에 extends를 사용하여 자식클래스에
    // 상속할 수 있다
    // 이럴 경우 코드중복을 절약할 수 있고 재상용성이 높아져서 유지보수및 생산성이좋다
    public class 자식클래스 extends 부모클래스 {
    

}

// 다중상속은 클래스간의 관계가 복잡해짐으로 불가능하다
// final클래스로 정의되면 상속이 불가능하다 -> 오버라이딩과 연관됨
// 상속에서는 기본적으로 받아오면 재정의하는것으로 사용되기때문에 수정이 불가능한 final은 안된다
// object는 최상위 객체로 최상위 부모 클래스
// -> 모든클래스에서 사용이 가능 / 부모클래스가 없더라도 자동으로 compiler에 의해 상속받음
public class Car {

String company; // 자동차 회사
private String model; // 자동차 모델
private String color; // 자동차 색상
private double price; // 자동차 가격

double speed;  // 자동차 속도 , km/h
char gear = &#x27;P&#x27;; // 기어의 상태, P,R,N,D
boolean lights; // 자동차 조명의 상태

public String getModel() {
    return model;
}

public void setModel(String model) {
    this.model = model;
}

public double gasPedal(double kmh, char type) {
    changeGear(type);
    speed = kmh;
    return speed;
}

public double brakePedal() {
    speed = 0;
    return speed;
}

public char changeGear(char type) {
    gear = type;
    return gear;
}

public boolean onOffLights() {
    lights = !lights;
    return lights;
}

public void horn() {
    System.out.println(&quot;빵빵&quot;);
}

}

public class SportsCar extends Car{
String engine;
public void booster() {
System.out.println("엔진 " + engine + " 부앙~\n");
}
}
public class Main {
public static void main(String[] args) {
// 부모 클래스 객체에서 자식 클래스 멤버 사용
Car car = new Car();
// car.engine = "Orion"; // 오류
// car.booster(); // 오류

    // 자식 클래스 객체 생성
    SportsCar sportsCar = new SportsCar();
    sportsCar.engine = &quot;Orion&quot;;
    sportsCar.booster();

    // 자식 클래스 객체에서 부모 클래스 멤버 사용
			// 자식 클래스에 없어도 상속받았기 때문에 가져와서 사용이 가능
    // 자식 클래스에 작성되어있지 않지만 별도로 사용이 가능
    sportsCar.company = &quot;GENESIS&quot;;
    sportsCar.setModel(&quot;GV80&quot;);
    System.out.println(&quot;sportsCar.company = &quot; + sportsCar.company);
    System.out.println(&quot;sportsCar.getModel() = &quot; + sportsCar.getModel());
    System.out.println();

    sportsCar.horn();
    System.out.println(sportsCar.changeGear(&#x27;D&#x27;));
}

}

  • 포함관계
    // 상속과 다르게 공통된 클래스에서 물려받아 확장하는게 아니라
    // 여려가지의 파트 클래스를 포함시켜서 하나의 큰클래스를 만들어주는 방식
    public class Tire {
    String company; // 타이어 회사
    double price; // 타이어 가격

    public Tire(String company, double price) {
        this.company = company;
        this.price = price;
    }

    }

    public class Door {
    String company; // 차문 회사
    String location; // 차문 위치, FL, FR, BL, BR

    public Door(String company, String location) {
        this.company = company;
        this.location = location;
    }

    }

    public class Handle {
    String company; // 핸들 회사
    String type; // 핸들 타입

    public Handle(String company, String type) {
        this.company = company;
        this.type = type;
    }
    
    public void turnHandle(String direction) {
        System.out.println(direction + &quot; 방향으로 핸들을 돌립니다.&quot;);
    }

    }

    public class Car {

    static final String company = &quot;GENESIS&quot;; // 자동차 회사
    String model; // 자동차 모델
    String color; // 자동차 색상
    double price; // 자동차 가격
    
    double speed;  // 자동차 속도 , km/h
    char gear = &#x27;P&#x27;; // 기어의 상태, P,R,N,D
    boolean lights; // 자동차 조명의 상태
    
    Tire[] tire;
    Door[] door;
    Handle handle;
    
    public Car(String model, String color, double price) {
        this.model = model;
        this.color = color;
        this.price = price;
    }
    
    public void setTire(Tire ... tire) {
        this.tire = tire;
    }
    
    public void setDoor(Door ... door) {
        this.door = door;
    }
    
    public void setHandle(Handle handle) {
        this.handle = handle;
    }
    
    public double gasPedal(double kmh, char type) {
        changeGear(type);
        speed = kmh;
        return speed;
    }
    
    public double brakePedal() {
        speed = 0;
        return speed;
    }
    
    public char changeGear(char type) {
        gear = type;
        return gear;
    }
    
    public boolean onOffLights() {
        lights = !lights;
        return lights;
    }
    
    public void horn() {
        System.out.println(&quot;빵빵&quot;);
    }

    }

    public class Main {
    public static void main(String[] args) {
    // 자동차 객체 생성
    Car car = new Car("GV80", "Black", 50000000);

        // 자동차 부품 : 타이어, 차문, 핸들 선언
        Tire[] tires = new Tire[]{
                new Tire(&quot;KIA&quot;, 150000), new Tire(&quot;금호&quot;, 150000),
                new Tire(&quot;Samsung&quot;, 150000), new Tire(&quot;LG&quot;, 150000)
        };
        Door[] doors = new Door[]{
                new Door(&quot;LG&quot;, &quot;FL&quot;), new Door(&quot;KIA&quot;, &quot;FR&quot;),
                new Door(&quot;Samsung&quot;, &quot;BL&quot;), new Door(&quot;LG&quot;, &quot;BR&quot;)
        };
        Handle handle = new Handle(&quot;Samsung&quot;, &quot;S&quot;);
    
    
        // 자동차 객체에 부품 등록
        car.setTire(tires);
        car.setDoor(doors);
        car.setHandle(handle);
    
        // 등록된 부품 확인하기
        for (Tire tire : car.tire) {
            System.out.println(&quot;tire.company = &quot; + tire.company);
        }
        System.out.println();
    
        for (Door door : car.door) {
            System.out.println(&quot;door.company = &quot; + door.company);
            System.out.println(&quot;door.location = &quot; + door.location);
            System.out.println();
        }
        System.out.println();
    
        // 자동차 핸들 인스턴스 참조형 변수에 저장
        Handle carHandle = car.handle;
        System.out.println(&quot;carHandle.company = &quot; + carHandle.company);
        System.out.println(&quot;carHandle.type = &quot; + carHandle.type + &quot;\n&quot;);
    
        // 자동차 핸들 조작해보기
        carHandle.turnHandle(&quot;Right&quot;);
        carHandle.turnHandle(&quot;Left&quot;);
    }

    }

  • Overriding(오버라이딩)
    // 부모클래스로부터 상속받은 메서드의 내용을 재정의 하는것
    // 상속받은 메서드를 상황에 맞게 수정하고싶을 때 사용
    // 조건
    // 1. 선언부가 부모 클래스의 메서드와 일치
    // 2. 접근 제어자가 부모클래스의 메서드보다 좁은 범위로 변경 불가능
    // 3. 부모클래스의 메서드 보다 많이 선언할 수 없다
    public class Car {

    String company; // 자동차 회사
    private String model; // 자동차 모델
    private String color; // 자동차 색상
    private double price; // 자동차 가격
    
    double speed;  // 자동차 속도 , km/h
    char gear = &#x27;P&#x27;; // 기어의 상태, P,R,N,D
    boolean lights; // 자동차 조명의 상태
    
    public String getModel() {
        return model;
    }
    
    public void setModel(String model) {
        this.model = model;
    }
    
    public double gasPedal(double kmh, char type) {
        changeGear(type);
        speed = kmh;
        return speed;
    }
    
    public double brakePedal() {
        speed = 0;
        return speed;
    }
    
    public char changeGear(char type) {
        gear = type;
        return gear;
    }
    
    public boolean onOffLights() {
        lights = !lights;
        return lights;
    }
    
    public void horn() {
        System.out.println(&quot;빵빵&quot;);
    }

    }

    public class SportsCar extends Car{
    String engine;
    public void booster() {
    System.out.println("엔진 " + engine + " 부앙~\n");
    }

    public SportsCar(String engine) {
        this.engine = engine;
    }
    
    	// @ : 에노테이션(annotaion)
    @Override
    public double brakePedal() {
        speed = 100;
        System.out.println(&quot;스포츠카에 브레이크란 없다&quot;);
        return speed;
    }
    
    @Override
    public void horn() {
        booster();
    }

    }

    public class Main {
    public static void main(String[] args) {
    // 부모 클래스 자동차 객체 생성
    Car car = new Car();
    car.horn(); // 경적

        System.out.println();
        // 자식 클래스 스포츠카 객체 생성
        SportsCar sportsCar = new SportsCar(&quot;Orion&quot;);
    
        // 오버라이딩한 brakePedal(), horn() 메서드 호출
        sportsCar.brakePedal();
        sportsCar.horn();
    
    }

    }

  • 다형성(polymorphism)
    - 여러 가지 형태를 가질 수 있는 능력

    • 부모(조상) 타입 참조 변수로 자손 타입 객체르 다루는 것

    - 객체와 참조변수의 타입이 일치할 때와 일치하지 않을때의 차이

  • 참조변수와 인스턴스의 타입이 일치할때는 모든 멤버를 사용할 수 있다

  • 다를경우에는 타입클래스의 멤버만 사용할 수 있다

    - 자손 타입의 참조변수로 조상 타입의 객체를 가리킬 수 없다
    • 참조변수 타입변환(형변환)
      - 사용할 수 있는 멤버의 갯수를 조절

    • 값이나 주소가 바뀌는 것이 아니다

    • 부모 자식 관계의 참조변수일 경우 서로 형변환이 가능

    • 형제관계는 없다

    • 부모 자식으로 변환하는게 업캐스팅 , 다운캐스팅

    • f의 경우 5개전부다 가능하지만 c는 부모객체에 있는 메서드만 가능하고

    • f2는 다시 자식객체로 변환했으니 5개를 다시 다쓸수있다

    • 사용할 수 있는 개수를 줄이는 것은 안전 증가할경우에는 안전하지 않다고 말한다

    • 실제 참조변수가 가르키고 있는게 뭔지잘모르니까 증가할때는 위험한 변환이라 형변환생략 불가

    • 참조변수를 변경함으로써 사용할 수 있는 멤버의 갯수를 조절하기위해서 사용

      • 자동 타입 변환
        // 부모타입 변수 = 자식타입객체;는 자동으로 부모타입으로 변환이 일어난다
        // 자식 객체는 부모 객체의 멤버를 상속받기 때문에 부모와 동일하게 취급될 수 있다
        // 부모타입 변수로 자식객체의 멤버에 접급할때 상속받은 멤버만 접근할수있다
        class Mammal {
        // 포유류는 새끼를 낳고 모유수유를 한다.
        public void feeding() {
        System.out.println("모유수유를 합니다.");
        }
        }
        class Whale extends Mammal {
        // 고래는 포유류 이면서 바다에 살며 수영이 가능하다.
        public void swimming() {
        System.out.println("수영하다.");
        }

        @Override
        public void feeding() {
            System.out.println(&quot;고래는 모유수유를 합니다.&quot;);
        }

        }

        public class Main {
        public static void main(String[] args) {
        // Mammal을 상속받았기 때문에 자동형변환 가능
        Mammal mammal = new Whale();

            // 부모 클래스에 swimming이 선언되어있지 않아서 사용 불가능합니다.
            // mammal.swimming(); // 오류 발생
        
            // 부모타입의 객체는 자식타입의 변수로 변환될 수 없습니다.
            // Whale whale = new Mammal(); // 오류 발생
        
            mammal.feeding();
        }

        }

      • 강제 타입 변환
        // 부모타입객체는 자식타입변수로 자동으로 타입변환되지 않는다
        // 자식타입 변수 = (자식타입) 부모타입객체;

      // 조건
      // 1. 자식타입객체가 부모타입으로 자동 타입변환이된 후 다시 자식타입으로 변환될때만 가능

      // 자식타입객체가 자동 타입변환된 부모타입의 변수
      Mammal mammal = new Whale();
      mammal.feeding();

      // 자식객체 고래의 수영 기능을 사용하고 싶다면
      // 다시 자식타입으로 강제 타입변환을 하면된다.
      Whale whale = (Whale) mammal;
      whale.swimming();

      // 2. 부모타입 변수로는 자식타입객체의 고유한 멤버를 사용할 수 없기 때문에 사용이 필요한 경우가 생겼을 때 강제타입변환

      class Mammal {
      // 포유류는 새끼를 낳고 모유수유를 한다.
      public void feeding() {
      System.out.println("모유수유를 합니다.");
      }
      }
      class Whale extends Mammal {
      // 고래는 포유류 이면서 바다에 살며 수영이 가능하다.
      public void swimming() {
      System.out.println("수영하다.");
      }

      @Override
      public void feeding() {
          System.out.println(&quot;고래는 모유수유를 합니다.&quot;);
      }

      }

      public class Main {
      public static void main(String[] args) {
      // 자동 타입변환된 부모타입의 변수의 자식 객체
      Mammal mammal = new Whale();
      mammal.feeding();

          // 자식객체 고래의 수영 기능을 사용하고 싶다면
          // 다시 자식타입으로 강제 타입변환을 하면된다.
          Whale whale = (Whale) mammal;
          whale.swimming();
      
          Mammal newMammal = new Mammal();
          // Whale newWhale = (Whale) newMammal;
      }

      }

  • 예제
    - 여려가지 형태를 가질 수 있는 능력
    = 하나의 타입에 여러 객체를 대입할 수 있는 성질
    -> 기능확장, 타입변경없이 객체주입만으로 객체 타입변경가능
    // 매개변수 반환타입에도 다형성이 적용될 수 있다
    // 부모 클래스

    public class Tire {
    String company; // 타이어 회사

    public Tire(String company) {
        this.company = company;
    }
    
    public void rideComfort() {
        System.out.println(company + &quot; 타이어 승차감은?&quot;);
    }

    }

    // 자식 클래스

    public class KiaTire extends Tire{

    public KiaTire(String company) {
        super(company);
    }
    
    @Override
    public void rideComfort() {
        System.out.println(super.company + &quot; 타이어 승차감은 &quot; + 60);
    }

    }

    // 자식 클래스

    public class HankookTire extends Tire {

    public HankookTire(String company) {
        super(company);
    }
    
    @Override
    public void rideComfort() {
        System.out.println(super.company + &quot; 타이어 승차감은 &quot; + 100);
    }

    }

    public class Car {
    Tire tire;

    public Car(Tire tire) {
        this.tire = tire;
    }
    
    Tire getHankookTire() {
        return new HankookTire(&quot;HANKOOK&quot;);
    }
    
    Tire getKiaTire() {
        return new KiaTire(&quot;KIA&quot;);
    }

    }

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

        // 부모클래스로 강제 형변환
        Tire KiaSampleTire = new KiaTire(&quot;KIA&quot;);
        Tire HankookSampleTire = new HankookTire(&quot;HANKOOK&quot;);
    
        // 매개변수 다형성 확인!
        Car car1 = new Car(KiaSampleTire);
        Car car2 = new Car(HankookSampleTire);
    
        // 반환타입 다형성 확인!
        Tire hankookTire = car1.getHankookTire();
        KiaTire kiaTire = (KiaTire) car2.getKiaTire();
    
        // 오버라이딩된 메서드 호출
        car1.tire.rideComfort(); // KIA 타이어 승차감은 60
        car2.tire.rideComfort(); // HANKOOK 타이어 승차감은 100
    }

    }

  • instanceof 연산자
    대상객체 instance of 클래스 이름

    • 참조변수의 형변환 가능여부 확인에 사용, 가능하면 true 반환(부모자식관계만가능)
      1. 연산자로 형변환 해도 되는지 확인(이경우에 사용)
      1. 형변환
    • 형변환 전에 반드시 instanceof로 확인해야한다
    // 다형성

class Parent { }
class Child extends Parent { }
class Brother extends Parent { }

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

			Parent pc = new Child();  // 다형성 허용 (자식 -&gt; 부모)

    Parent p = new Parent();

    System.out.println(p instanceof Object); // true 출력
    System.out.println(p instanceof Parent); // true 출력
    System.out.println(p instanceof Child);  // false 출력

    Parent c = new Child();

    System.out.println(c instanceof Object); // true 출력
    System.out.println(c instanceof Parent); // true 출력
    System.out.println(c instanceof Child);  // true 출력 자동형변환을 했지만 생성시에는 Child였기때문에

}

}

  • 매개변수의 다형성(장점)
    - 장점

      1. 다형적 매개변수
        - 참조형 매개변수는 메서드 호출시, 자신과 같은 타입 또는 자손타입의 인스턴스를 넘겨줄 수 있다
        - 오버로딩 갯수가 늘어나면 관리나 코드중복오류등 유지보수가 어려워진다
        - 타입을 조상 타입으로 바꾸는걸로 오버로딩을 하지 않아도 된다

    - 2. 하나의 배열로 여러종류 객체 다루기
    - 조상타입의 배열에 자손들의 객체르 담을 수 있다
  • 추상 클래스
    // 그냥 override할때는 부모클래스에서 정의하고 실행되는것에서 확대하다보니 실행되는것에서의 확대만 가능했다
    // 한마디로 어느정도 일정한 틀이 존재한다
    // 추상클래스의 경우 정의만 되어있어 실행내용이 없기때문에 상속받아서 자유롭게 재정의 할 수 있다
    // 본인 스스로는 객체를 완성할 수 없고 자식클래스가 가능하다
    // 1. 상속 받은 클래스는 추상 메서드를 무조건 오버라이딩해야한다.
    // 2. 정의만 했을 뿐 실행 내용이 없기 때문에 {}가 없다
    // 3. 추상메서드는 추상클래스에서만 정의가 가능하기 때문에 해당 클래스에도 abstract를 붙여줘야한다
    // 4. 추상 메서드는 미완성 메서드이기 때문에 상속받아 override를 해야 실행할 수 있게된다.

    • 예시
    public class BenzCar extends Car {

    @Override
    public void horn() {
        System.out.println(&quot;Benz 빵빵&quot;);
    }

    }

    public class AudiCar extends Car {

    @Override
    public void horn() {
        System.out.println(&quot;Audi 빵빵&quot;);
    }

    }

    public class ZenesisCar extends Car {

    @Override
    public void horn() {
        System.out.println(&quot;Zenesis 빵빵&quot;);
    }

    }

    public abstract class Car {
    String company; // 자동차 회사
    String color; // 자동차 색상
    double speed; // 자동차 속도 , km/h

    public double gasPedal(double kmh) {
        speed = kmh;
        return speed;
    }
    
    public double brakePedal() {
        speed = 0;
        return speed;
    }
    
    public abstract void horn();

    }

    public class Main {
    public static void main(String[] args) {
    Car car1 = new BenzCar();
    car1.horn();
    System.out.println();

        Car car2 = new AudiCar();
        car2.horn();
        System.out.println();
    
        Car car3 = new ZenesisCar();
        car3.horn();
    }

    }

  • 인터페이스
    // 인터페이스의 경우 내부가 추상메소드로만 이루어져있다(자바8이후로는 일반메서드도 가능)
    // 그러므로 implement한경우에는 해당 인터페이스를 전부 오바라이딩해주어야한다

// 선언
public interface 인터페이스명 { // 클래스와 동일 public, default 접근 제어자 지정 가능

}

// 구성

// 모든 멤버 변수는 public static final이어야한다
// - 인터페이스는 클래스 멤버변수밖에 생성하지 못한다
// - 인터페이스는 객체를 생성하지않고 객체 멤버 변수가없다(객체를 생성하지 않기때문에 생성자도 없다)
// 모든 메서드는 public abstract이어야한다 (생략가능 static메서드와 default메서드 예외)
// - 모든 메서드가 추상메서드이기 때문에
// - 오버라이딩을 해야하기때문에 private메서드는 의미가 없다
// 생략되는 제어자는 컴파일러가 자동 추가

public interface 인터페이스명 {
public static final char A = 'A';
static char B = 'B';
final char C = 'C';
char D = 'D';

void turnOn(); // public abstract void turnOn();

}

// 구현

// 추상클래스와 마찬가지로 직접 인스턴스 생성을 할수 없기 때문에 클래스에 구현되어 생성
// implements 키워드 사용
// 추상메서드를 일부만 구현 해야하는경우에는 추상클래스로 변경해서 사용
public class 클래스명 implements 인터페이스명 {
// 추상 메서드 오버라이딩
@Override
public 리턴타입 메서드이름(매개변수, ...) {
// 실행문
}
}

// 인터페이스간의 상속

// 인터페이스간의 상속시에는 extends사용
// 인터페이스간의 상속시에는 다중 상속이 가능

// 아래코드 해석을 해보면
// Main클래스는 먼저 D라는 클래스를 상속받았다.
// 그러므로 인스턴스화시 D의 메서드를 사용할 수 있다.
// 그리고 implements를 보아 C라는 interface도 상속받았다.
// C라는 interface는 A,B라는 interface를 다중상속받았기때문에 C는 A,B를 합친 interface로 해석할 수 있다.
// 그러므로 Main은 A,B의 오버라이딩을 진행해주어야한다.
// 그러므로 main생성자에서 실행할때 Main타입으로 정의생성된main은 a,b,c의 메서드를 전부사용하는것을 볼 수 있다.
public class Main extends D implements C {

@Override
public void a() {
    System.out.println(&quot;A&quot;);
}

@Override
public void b() {
    System.out.println(&quot;B&quot;);
}

@Override
void d() {
    super.d();
}

public static void main(String[] args) {
    Main main = new Main();
    main.a();
    main.b();
    main.d();
}

}

interface A {
void a();
}

interface B {
void b();
}

interface C extends A, B {
}

class D {
void d() {
System.out.println("D");
}
}

  • 디폴트 메서드 static메서드
    • 디폴트 메서드
      // 추상메서드의 기본적인 구현을 제공
      // 메서드앞에 default를 붙이고 {}가 존재
      // default 메서드 역시 접근제어자가 public이며 생략 가능
      // 추상메서드가 아니므로 재정의할 필요가 없음
      

      public class Main implements A {

      @Override
      public void a() {
          System.out.println(&quot;A&quot;);
      }
      
      
      public static void main(String[] args) {
          Main main = new Main();
          main.a();
      
          // 디폴트 메서드 재정의 없이 바로 사용가능합니다.
          main.aa();
      }

      }

      interface A {
      void a();
      default void aa() {
      System.out.println("AA");
      }
      }

    • static 메서드
      // 인터페이스에서 static 메서드 선언가능
      // static의 특성 그대로 인터페이스의 static메서드 또한 객체없이 호출 가능
      // 선언 호출 방식은 클래스의 static메서드와 동일
      // - 접근 제어자를 생략하면 컴파일러가 public 추가

      public class Main implements A {

      @Override
      public void a() {
          System.out.println(&quot;A&quot;);
      }
      
      public static void main(String[] args) {
          Main main = new Main();
          main.a();
          main.aa();
          System.out.println();
      
          // static 메서드 aaa() 호출
          A.aaa();
      }

      }

      interface A {
      void a();
      default void aa() {
      System.out.println("AA");
      }
      static void aaa() {
      System.out.println("static method");
      }
      }

  • 타입변환 다형성 인터페이스 다형성
    • 타입변환
      // 자동 타입변환
      // 인터페이스 변수 = 구현객체는 자동으로 타입변환
      // 인터페이스의 경우가 상위이기 때문에

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

          // A 인터페이스에 구현체 B 대입
          A a1 = new B();
          
          // A 인터페이스에 구편체 B를 상속받은 C 대입
          A a2 = new C();
          
      }

      }

      interface A { }
      class B implements A {}
      class C extends B {}

      // 강제 타입변환
      // 구현객체타입 변수 = (구현객체타입) 인터페이스변수

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

          // 자동형변환
          // A 인터페이스에 구현체 B 대입
          A a1 = new B();
          a1.a(); // B.a()가 출력
          // a1.b(); // 불가능 -&gt; A인터페이스가 가지고있는 메서드가 아님
          // 상위 인터페이스가 가지고있는 메서드의 경우에만 가능
      
      
          // 강제형변환
          System.out.println(&quot;\nB 강제 타입변환&quot;);
          B b = (B) a1;
          b.a();
          b.b(); // 강제 타입변환으로 사용 가능
          System.out.println();
      
          // A 인터페이스에 구편체 B를 상속받은 C 대입
          A a2 = new C();
          a2.a();
          //a2.b(); // 불가능
          //a2.c(); // 불가능
      
          System.out.println(&quot;\nC 강제 타입변환&quot;);
          C c = (C) a2;
          c.a();
          c.b(); // 강제 타입변환으로 사용 가능
          c.c(); // 강제 타입변환으로 사용 가능
      
      
      }

      }

      interface A {
      void a();
      }
      class B implements A {
      @Override
      public void a() {
      System.out.println("B.a()");
      }

      public void b() {
          System.out.println(&quot;B.b()&quot;);
      }

      }
      class C extends B {
      public void c() {
      System.out.println("C.c()");
      }
      }

    • 인터페이스의 다형성
      public abstract class Tv {

      private String company; // 티비 회사
      private int channel = 1; // 현재 채널 상태
      private int volume = 0;  // 현재 볼륨 상태
      private boolean power = false; // 현재 전원 상태
      
      public Tv(String company) {
          this.company = company;
      }
      
      public void displayPower(String company, boolean power) {
          if(power) {
              System.out.println(company + &quot; Tv 전원이 켜졌습니다.&quot;);
          } else {
              System.out.println(company + &quot; Tv 전원이 종료되었습니다.&quot;);
          }
      }
      
      public void displayChannel(int channel) {
          System.out.println(&quot;현재 채널은 &quot; + channel);
      }
      
      public void displayVolume(int volume) {
          System.out.println(&quot;현재 볼륨은 &quot; + volume);
      }
      
      public String getCompany() {
          return company;
      }
      
      public int getChannel() {
          return channel;
      }
      
      public int getVolume() {
          return volume;
      }
      
      public boolean isPower() {
          return power;
      }
      
      public void setChannel(int channel) {
          this.channel = Math.max(channel, 0);
      }
      
      public void setVolume(int volume) {
          this.volume = Math.max(volume, 0);
      }
      
      public void setPower(boolean power) {
          this.power = power;
      }

      }

      public class SamsungTv extends Tv implements MultiRemoteController{

      public SamsungTv(String company) {
          super(company);
      }
      
      @Override
      public void turnOnOff() {
          setPower(!isPower());
          displayPower(getCompany(), isPower());
      }
      
      @Override
      public void channelUp() {
          setChannel(getChannel() + 1);
          displayChannel(getChannel());
      }
      
      @Override
      public void channelDown() {
          setChannel(getChannel() - 1);
          displayChannel(getChannel());
      }
      
      @Override
      public void volumeUp() {
          setVolume(getVolume() + 1);
          displayVolume(getVolume());
      }
      
      @Override
      public void volumeDown() {
          setVolume(getVolume() - 1);
          displayVolume(getVolume());
      }

      }

      public class LgTv extends Tv implements MultiRemoteController {

      public LgTv(String company) {
          super(company);
      }
      
      @Override
      public void turnOnOff() {
          setPower(!isPower());
          displayPower(getCompany(), isPower());
      }
      
      @Override
      public void channelUp() {
          setChannel(getChannel() + 1);
          displayChannel(getChannel());
      }
      
      @Override
      public void channelDown() {
          setChannel(getChannel() - 1);
          displayChannel(getChannel());
      }
      
      @Override
      public void volumeUp() {
          setVolume(getVolume() + 1);
          displayVolume(getVolume());
      }
      
      @Override
      public void volumeDown() {
          setVolume(getVolume() - 1);
          displayVolume(getVolume());
      }

      }

      public interface MultiRemoteController {

      void turnOnOff();
      void channelUp();
      void channelDown();
      void volumeUp();
      void volumeDown();
      
      // 매개변수와 반환타입 다형성 확인 메서드
      default MultiRemoteController getTV(Tv tv) {
          if(tv instanceof SamsungTv) {
              return (SamsungTv) tv;
          } else if(tv instanceof LgTv){
              return (LgTv) tv;
          } else {
              throw new NullPointerException(&quot;일치하는 Tv 없음&quot;);
          }
      }

      }


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

          // LG TV 구현체를 조작
          // 자동 형변환
          // LgTv클래스를 인스턴스하여도 작동은 한다
          // 하지만 MultiRemoteController 인터페이스를 인스턴스한 이유는
          // 아래mrc에서 삼성타입으로도 변경해서 생성하기 위해서이다
          // LgTv도 SamsungTv이도 MultiRemoteController를 implements했기때문에
          // 아래에서 자동현변환하고 변경도 가능한것이다
          MultiRemoteController mrc = new LgTv(&quot;LG&quot;);
          mrc.turnOnOff();
          mrc.volumeUp();
          mrc.channelDown();
          mrc.channelUp();
          mrc.turnOnOff();
      
          // 조작 대상을 Samsung TV로 교체
      			// 같은 mrc에서 생성만 다르게 해준것이기때문에 계속해서 값은 이어져오고있다.
          System.out.println(&quot;\n&lt;Samsung TV로 교체&gt;&quot;);
          mrc = new SamsungTv(&quot;Samsung&quot;);
          mrc.turnOnOff();
          mrc.channelUp();
          mrc.volumeDown();
          mrc.volumeUp();
          mrc.turnOnOff();
      
          // 매개변수, 반환타입 다형성 체크
          System.out.println(&quot;\n&lt;매개변수, 반환타입 다형성 체크&gt;&quot;);
      
          MultiRemoteController samsung = mrc.getTV(new SamsungTv(&quot;Samsung&quot;)); // 자동으로 Tv형변환이루어져서 매개변수로 들어감
          samsung.turnOnOff();
      
          SamsungTv samsungTv = (SamsungTv) samsung;
          samsungTv.turnOnOff();
      
      
          System.out.println();
          MultiRemoteController lg = mrc.getTV(new LgTv(&quot;LG&quot;));
          lg.turnOnOff();
      
          LgTv lgTv = (LgTv) lg;
          lgTv.turnOnOff();
      
      }

      }

  • 오류와 예외
    // 오류
    // - 일반적으로 회복이 불가능한 문제

    // 예외
    // - 일반적으로 회복이 가능한 문제

    // 컴파일
    // - .java파일을 .class로 변환(컴퓨터가 이해할수있는 코드로 번역)
    // - 컴파일 에러의 경우 사용방법의 문제가 대부분

    // 런타임 에러
    // 실행도중에 이러나는 예측할 수 없는 에러
    // Runtime -> 프로그램이 돌고있는 시간

    // 예외처리의 종류

    // 1. 확인된 예외(Checked Exception)
    // 컴파일 시점에서 확인되는 예외
    // 반드시 예외처리르 해주어야하는 예외

    // 2. 미확인된 예외(Unchecked Exception)
    // 런타임 시점에서 확인되는 예외
    // 예외처리가 반드시 필요하지 않다

    try - catch finally

    // 예외 클래스를 만들어서 예외를 정의
    public class OurBadException extends Exception{
    public OurBadException(){
    super("위험한 행동을 하면 예외처리를 꼭 해야함!!!");
    // Exception에 값을 집어넣음
    }
    }

    public class OurClass {
    private final boolean just = true;

    // throws : 예외를 던지다
    public void thisMethodIsDangerous () throws OurBadException{ // Exception이어도 가능
        // custom logic
        if(just){
            throw new OurBadException(); // throws를 작성했으니까 미리 인지하고있는 상황
        }
    
    }

    }

    public class StudyException {
    public static void main(String[] args) {
    OurClass ourClass = new OurClass();
    // ourClass.thisMethodIsDangerous();
    // 이미 throw로 예외발생을 예견하고 표시했기때문에 그냥 호출하면 오류가 발생한다
    // try ~ catch ~ finally
    // try : 일단 시도하는부분
    // catch : 예외발생시
    // finally : 예외든 정상작동이든 수행해야되는 로직 수행

        try {
            // 일단 실행
            ourClass.thisMethodIsDangerous();
        }catch (OurBadException e){ // 무슨종류의 예외상황을 catch할것인지 명시해야함
                // 인스턴스화 진행해주어야함
            System.out.println(e.getMessage()); // 해당 오류메시지 출력
        }finally {
            // 무조건 마지막에 실행됨
            System.out.println(&quot;예외 handling시도. 무슨상황이든 실행되는 finally부분&quot;);
        }
    
    
    }

    }

    throwsthrow
    메서드 이름 뒤에 붙어 이 메서드가 어떠한 예외사항을 던질 수 있는지 알려주는 예약어 입니다.메서드 안에서, 실제로 예외 객체를 던질 때 사용하는 예약어 입니다.
    여러 종류의 예외사항을 적을 수 있습니다.실제로 던지는 예외 객체 하나와 같이 써야 합니다.
    일반 메서드의 return 키워드처럼 throw 아래의 구문들은 실행되지 않고, throw문과 함께 메서드가 종료됩니다.

    자바의 Thrwoable 클래스

    • Checked Exception은 IOException을 상속받고
    • Unchecked Exception은 RuntimeException을 상속받는다
    // 출처 : https://programming.guide/java/list-of-java-exceptions.html

    java.io
    IOException
    CharConversionException
    EOFException
    FileNotFoundException
    InterruptedIOException
    ObjectStreamException
    InvalidClassException
    InvalidObjectException
    NotActiveException
    NotSerializableException
    OptionalDataException
    StreamCorruptedException
    WriteAbortedException
    SyncFailedException
    UnsupportedEncodingException
    UTFDataFormatException
    UncheckedIOException

    java.lang

    ReflectiveOperationException
    ClassNotFoundException
    InstantiationException
    IllegalAccessException
    InvocationTargetException
    NoSuchFieldException
    NoSuchMethodException
    CloneNotSupportedException
    InterruptedException

    산술 예외
    IndexOutOfBoundsException
    ArrayIndexOutOfBoundsException
    StringIndexOutOfBoundsException
    ArrayStoreException
    ClassCastException
    EnumConstantNotPresentException
    IllegalArgumentException
    IllegalThreadStateException
    NumberFormatException
    IllegalMonitorStateException
    IllegalStateException
    NegativeArraySizeException
    NullPointerException
    SecurityException
    TypeNotPresentException
    UnsupportedOperationException

    java.net
    HttpRetryException
    SocketTimeoutException
    MalformedURLException
    ProtocolException
    SocketException
    BindException
    ConnectException
    NoRouteToHostException
    PortUnreachableException
    UnknownHostException
    UnknownServiceException
    URISyntaxException

    java.text
    ParseException

    java.time
    DateTimeException

    java.time.zone
    ZoneRulesException

    Chained Exception(연결된 예외)

    • 예외가 꼭 하나일꺼라는 보장은 없다
    • 여러가지 예외를 하나의 큰 분류의 예외로 묶어서 다루기 위해
    • 원인 예외를 다루기 위한 메소드
      • initCause()
        • 지정한 예외를 원인 예외로 등록
      • getCause()
        • 원인 예외를 반환
      // 연결된 예외
      public class main {

      public static void main(String[] args) {
          try {
              // 예외 생성
              NumberFormatException ex = new NumberFormatException(&quot;가짜 예외이유&quot;);
      
              // 원인 예외 설정(지정한 예외를 원인 예외로 등록)
              ex.initCause(new NullPointerException(&quot;진짜 예외이유&quot;));
      
              // 예외를 직접 던집니다.
              throw ex;
          } catch (NumberFormatException ex) {
              // 예외 로그 출력
              ex.printStackTrace();
              // 예외 원인 조회 후 출력
              ex.getCause().printStackTrace();
          }
      
          // checked exception 을 감싸서 unchecked exception 안에 넣습니다.
          throw new RuntimeException(new Exception(&quot;이것이 진짜 예외 이유 입니다.&quot;));
      }

      }

      // 출력
      Caused by: java.lang.NullPointerException: 진짜 예외이유

      실제예외 처리

      • 예외복구
      • 예외처리회피
      • 예외전환
      1. 예외 복구
      public String getDataFromAnotherServer(String dataPath) {
      try {
      return anotherServerClient.getData(dataPath).toString();
      } catch (GetDataException e) {
      return defaultData;
      }
      }

      // try catch로 예외를 처리하고 프로그램을 정상상태로 복구하는 방법
      // try문에서 에러발생시 기본값반환
      // 가장기본적이지만 많이사용안됨

      1. 예외 처리 회피
      public void someMethod() throws Exception { ... }

      public void someIrresponsibleMethod() throws Exception {
      this.someMethod();
      }

      // throws를 통하여 미리 예외 인지
      // someMethod에서 발생한 에러를 someIrresponsibleMethod에서도 throws해서 회피
      // 같은 객체내에서는 사용하지 않음 단순예시

      1. 예외 전환
      public void someMethod() throws IOException { ... }

      public void someResponsibleMethod() throws MoreSpecificException {
      try {
      this.someMethod();
      } catch (IOException e) {
      throw new MoreSpecificException(e.getMessage());
      }
      }

      // MoreSpecificException 부분에서 조금더 상세하게 에러확인

  • 제네릭 <>
    // object로 데이터 타입을 설정하면 해당로직에서 다양한 경우의 수를 대비해야하는 이슈발생
    // 사용 이유
    // 1. 중복및 필요없는 코드를 줄여준다
    // 2. 타입 안정성을 해치지않는다

    // 1. 제네릭은 클래스 또는 메서드에 사용 가능
    // <>안에 들어가야할 타입 명시

    public class Generic<T> { // 제네릭 클래스 = 원시타입 / T -> 타입변수
    // static에서는 제네릭 사용불가 -> 타입변수가 인스턴스로 간주되기때문에
    // static은 인스턴스화 하지않아도 사용할수있기때문에
    // 제네릭 배열생성 불가

    // 2. 내부 필드에 String
    private T t;
    // 3. method의 return 타입도 String
    public T get() {
        return this.t;
    }
    
    public void set(T t) {
        this.t = t;
    }
    
    public static void main(String[] args) {
        // 4. 이 구문에서 String을 집어넣었으니까 T가 String으로 됨
        Generic&lt;String&gt; stringGeneric = new Generic&lt;&gt;();
        // 5. 그렇기때문에 String타입실행 가능
        stringGeneric.set(&quot;Hello World&quot;);
    
        String tValueTurnOutWithString = stringGeneric.get();
    
        System.out.println(tValueTurnOutWithString);
    }

    }

    // 다수의 타입변수로 사용 가능
    public class Generic<T, U, E> {
    public E multiTypeMethod(T t, U u) { ... }
    }

    Generic<Long, Integer, String> instance = new Generic();
    instance.multiTypeMethod(longVal, intVal);
    // 상속과 타입관계는 그대로 적용
    // -> 부모클래스로 제네릭타입변수를 지정하고, 자식클래스로 넘기는것이 가능

    // 와일드 카드를 통해 제네릭의 제한을 구체적으로 정할수 있다
    // 1. <? extends T> : T와 그 자손들만 사용 가능
    // 2. <? super T> : T와 그 조상들만 가능
    // 3. <?> : 제한 없음

    public class ParkingLot<T extends Car> { ... }

    ParkingLot<BMW> bmwParkingLot = new ParkingLot();
    ParkingLot<Iphone> iphoneParkingLot = new ParkingLog(); // error!

    // 메서드를 스코프로 제네릭을 별도 선언 가능
    // 스코프 -> 변수를 사용할 수 있는 범위({}안에서 변수를 선언했을때 영역이 끝나기전까지는 어디서든 사용가능)
    // 또는 ..
    static <T> void sort(List<T> list, Comparator<? super T> c) { ... }

    // 반환 타입 앞에 제네릭을 사용한 경우, 해당 메서드에만 적용되는 제네릭 타입변수를 선언 가능

    // 제네릭 클래스의 타입변수를 static메서드에는 사용할 수 없지만 제네릭 메서드의 제네릭 타입변수는 가능
    // 이유로는 타입변수를 클래스 내부의 인스턴스 변수로 취급하기 때문에 제네릭 클래스는 안된다
    // 하지만 제네릭 메서드의 제네릭 타입변수는 해당 메서드에만 적용되기 때문에 메서드 하나를 기준으로 선언하고 사용 가능

    // 같은 이름의 변수를 사용했다고 해도 제네릭 메서드의 타입변수는 제네릭 클래스의 타입변수와 다르다
    public class Generic<T, U, E> {
    // Generic<T,U,E> 의 T와 아래의 T는 이름만 같을뿐 다른 변수
    static <T> void sort(List<T> list, Comparator<? super T> c) { ... }
    }

  • Wrapper 객체 심화
    1. 원시타입은 값이상의 의미를 가지지않는 경우가 많기때문에 객체로 사용하는 대신에 원시타입 값 그래도 사용
    2. 하지만 그외의 경우가 있을수도있기때문에 추상화해서 Wrapper Class로 인스턴스화함
    3. 하지만 성능적이면에서는 원시타입값 그대로 사용하는것이 좋기때문에 값이상의 의미를 가지고있지 않으면 원시타입을 사용함

    Integer num = new Integer(17); // Boxing
    int n = num.intValue(); // UnBoxing

    Character ch = 'X'; // AutoBoxing
    char c = ch; // AutoUnBoxing

    // AutoUnBoxing의 경우는 JDK 1.5이상부터 지원

  • 정규표현식(Regular Expression)
    • 특정한 규칙을 가진 문자열의 집합을 표현하는데 사용

    자주사용되는 정규 표현식

    정규 표현식설명
    ^[0-9]$숫자
    ^[a-zA-Z]</td><td id="Aq{v" class="">영문자</td></tr><tr id="c88cb9f2-6e23-4614-bd14-7b1191b53494"><td id="Kmia" class="">^[가-힣]*한글
    \w+@\w+\.\w+(\.\w+)?E-Mail
    ^\d{2,3}-\d{3,4}-\d{4}</td><td id="Aq{v" class="">전화번호</td></tr><tr id="ab2d3ceb-d0b2-4fcb-af2f-ec0b8e68ee0a"><td id="Kmia" class="">^01(?:0|1|[6-9])-(?:\d{3}|\d{4})-\d{4}휴대전화번호
    \d{6} - [1-4]\d{6}주민등록번호
    ^\d{3}-\d{2}$우편번호

    Pattern 클래스

    import java.util.regex.Pattern;

    Pattern.matches(검증할 패턴형식, 검증할값) // true ,false로 반환

    // Pattern 클래스 주요 메서드
    compile(String regex) : 주어진 정규표현식으로부터 패턴을 만듭니다.
    matcher(CharSequence input) : 대상 문자열이 패턴과 일치할 경우 true를 반환합니다.
    asPredicate() : 문자열을 일치시키는 데 사용할 수있는 술어를 작성합니다.
    pattern() : 컴파일된 정규표현식을 String 형태로 반환합니다.
    split(CharSequence input) : 문자열을 주어진 인자값 CharSequence 패턴에 따라 분리합니다.

    // Parttern 플래그 값 사용(상수)
    Pattern.CANON_EQ : None표준화된 매칭 모드를 활성화합니다.
    Pattern.CASE_INSENSITIVE : 대소문자를 구분하지 않습니다.
    Pattern.COMMENTS : 공백과 #으로 시작하는 주석이 무시됩니다. (라인의 끝까지).
    Pattern.MULTILINE : 수식 ‘^’ 는 라인의 시작과, ‘’는라인의끝과match됩니다.Pattern.DOTALL:수식‘.’과모든문자와match되고‘\n’도match에포함됩니다.Pattern.UNICODECASE:유니코드를기준으로대소문자구분없이match시킵니다.Pattert.UNIXLINES:수식‘.’과및‘’ 는 라인의 끝과 match 됩니다. Pattern.DOTALL : 수식 ‘.’과 모든 문자와 match 되고 ‘\n’ 도 match 에 포함됩니다. Pattern.UNICODE_CASE : 유니코드를 기준으로 대소문자 구분 없이 match 시킵니다. Pattert.UNIX_LINES : 수식 ‘.’ 과 ‘^’ 및 ‘’의 match시에 한 라인의 끝을 의미하는 ‘\n’만 인식됩니다.

    Matcher 클래스

    // 대상 문자열의 패턴을 해석하고 주어진 패턴과 일치하는지 판별할 때 주로 사용됨
    // Matcher 클래스의 입력값으로는 CharSequence라는 인터페이스가 사용됨
    // 이를 통해 다양한 형태의 입력데이터로부터 문자 단위의 매칭기능을 지원 받는다
    // Matcher객체는 Pattern객체의 matcher()메서드를 호출하여 받아올 수 있다

    import java.util.regex.Matcher;
    import java.util.regex.Pattern;

    public class RegexExample {
    public static void main(String[] args) {
    Pattern pattern = Pattern.compile("^[a-zA-Z]*$"); //영문자만
    String val = "abcdef"; //대상문자열

            Matcher matcher = pattern.matcher(val);
            System.out.println(matcher.find());
    }

    }

    Matcher 클래스 주요 메서드
    matches() : 대상 문자열과 패턴이 일치할 경우 true 반환합니다.
    find() : 대상 문자열과 패턴이 일치하는 경우 true를 반환하고, 그 위치로 이동합니다.
    find(int start) : start위치 이후부터 매칭검색을 수행합니다.
    start() : 매칭되는 문자열 시작위치 반환합니다.
    start(int group) : 지정된 그룹이 매칭되는 시작위치 반환합니다.
    end() : 매칭되는 문자열 끝 다음 문자위치 반환합니다.
    end(int group) : 지정되 그룹이 매칭되는 끝 다음 문자위치 반환합니다.
    group() : 매칭된 부분을 반환합니다.
    group(int group) : 매칭된 부분중 group번 그룹핑 매칭부분 반환합니다.
    groupCount() : 패턴내 그룹핑한(괄호지정) 전체 갯수를 반환합니다.

    정규표현식 문법

    정규 표현식설명
    ^문자열 시작
    $문자열 종료
    .임의의 한 문자(단 \은 넣을 수 없음)
    *앞 문자가 없을 수도 무한정 많을 수도 있음
    +앞 문자가 하나 이상
    ?앞 문자가 없거나 하나 있음
    [ ]문자의 집합이나 범위를 나타내며 두 문자 사이는 - 기호로 범위를 나타냅니다. [] 내에서 ^ 가 선행하여 존재하면 not을 나타낸다.
    { }횟수 또는 범위를 나타냅니다.
    ( )소괄호 안의 문자를 하나의 문자로 인식
    |패턴 안에서 or 연산을 수행할 때 사용
    \정규 표현식 역슬래시()는 확장문자 (역슬래시 다음에 일반 문자가 오면 특수문자로 취급하고 역슬래시 다음에 특수문자가 오면 그 문자 자체를 의미)
    \b단어의 경계
    \B단어가 아닌것에 대한 경계
    \A입력의 시작 부분
    \G이전 매치의 끝
    \Z입력의 끝이지만 종결자가 있는 경우
    \z입력의 끝
    \s공백 문자
    \S공백 문자가 아닌 나머지 문자
    \w알파벳이나 숫자
    \W알파벳이나 숫자를 제외한 문자
    \d숫자 [0-9]와 동일
    \D숫자를 제외한 모든 문자
    (?i)앞 부분에 (?!)라는 옵션을 넣어주게 되면 대소문자는 구분하지 않습니다.

    유효성 검사

    정규 표현식은 유효성 검사 코드 작성 시 가장 효율적인 방법입니다.

    import java.util.regex.Pattern;

    public class RegexExample {
    public static void main(String[] args) {
    String name = "홍길동";
    String tel = "010-1234-5678";
    String email = "test@naver.com";

          //유효성 검사
          boolean name_check = Pattern.matches(&quot;^[가-힣]*$&quot;, name);
          boolean tel_check = Pattern.matches(&quot;^01(?:0|1|[6-9])-(?:\\d{3}|\\d{4})-\\d{4}$&quot;, tel);
          boolean email_check = Pattern.matches(&quot;\\w+@\\w+\\.\\w+(\\.\\w+)?&quot;, email);
    
          //출력
          System.out.println(&quot;이름 : &quot; + name_check);
          System.out.println(&quot;전화번호 : &quot; + tel_check);
          System.out.println(&quot;이메일 : &quot; + email_check);
    }

    }

  • 문자열 ↔ 숫자 타입변환
    // String -> int
    Integer.parseInt() // 리턴타입이 int
    Integer.valueOf() // 리턴타입이 Integer
    // Integer.valueOf(str1).intValue(); -> int로 데이터타입변환 // 굳이 필요없이 자동형변환이 된다

    // int -> String
    Integer.toString()
    String.valueOf()
    +문자열

  • long ↔ String
    // Wrapper class로 변환할때
    String.valueOf()
    Long.toString()
    // 기본형으로 변환할때
    Long.parseLong()
  • 예외처리 예제

    public class Calculator {
    private int firstNumber;
    private int secondNumber;

    private AbstractOperation operation;
    
    public Calculator(AbstractOperation operation) {
        this.operation = operation;
    }
    
    public Calculator() {
    }
    
    public void setOperation(AbstractOperation operation) {
        this.operation = operation;
    }
    
    public void setFirstNumber(int firstNumber) {
        this.firstNumber = firstNumber;
    }
    
    public void setSecondNumber(int secondNumber) {
        this.secondNumber = secondNumber;
    }
    
    public double calculate() {
        double answer = 0;
        answer = operation.operate(this.firstNumber, this.secondNumber);
        return answer;
    }

    }

    import java.util.regex.Pattern;

    public class Parser {
    // parsing한다는의미는 타입변환을한다고 알고있으면된다.
    private static final String OPERATION_REG = "[+\-*/]";
    private static final String NUMBER_REG = "^[0-9]*$";

    private final Calculator calculator = new Calculator();
    
    public Parser parseFirstNum(String firstInput) throws BadInputException{
        // 구현 1.
    
        boolean check = Pattern.matches(NUMBER_REG,firstInput);
        if(!check){
            throw new BadInputException(&quot;정수값&quot;);
        }
    
        this.calculator.setFirstNumber(Integer.parseInt(firstInput));
        return this;
        // Parser의 생성자를 리턴
        // 위에식인 this.calculator.setFirstNumber(Integer.parseInt(firstInput)); 이부분에서 인스턴스에 대한 설정을 바꾸어 주었기때문에?
    
    }
    
    public Parser parseSecondNum(String secondInput) throws BadInputException{
        // 구현 1.
        boolean check = Pattern.matches(NUMBER_REG,secondInput);
        if(!check){
            throw new BadInputException(&quot;정수값&quot;);
        }
        this.calculator.setSecondNumber(Integer.parseInt(secondInput));
    
        return this;
    
    }
    
    public Parser parseOperator(String operationInput) throws BadInputException{
        // 구현 1.
        boolean check = Pattern.matches(OPERATION_REG,operationInput);
        if(!check){
            throw new BadInputException(&quot;사칙연사자의 연산자&quot;);
        }
        switch (operationInput){
            case &quot;+&quot; -&gt; this.calculator.setOperation(new AddOperation());
            case &quot;-&quot; -&gt; this.calculator.setOperation(new SubstractOperation());
            case &quot;*&quot; -&gt; this.calculator.setOperation(new MultiplyOperation());
            case &quot;/&quot; -&gt; this.calculator.setOperation(new DivideOperation());
        }
        return this;
    }
    
    public double executeCalculator() {
        return calculator.calculate();
    }

    }

    import java.util.Scanner;

    public class CalculatorApp {

    public static boolean start() throws Exception{ // static이므로 별도의 인스턴스화 필요없음
        Parser parser = new Parser();
        Scanner scanner = new Scanner(System.in);
    
        System.out.println(&quot;첫번째 숫자를 입력해주세요!&quot;);
        String firstInput = scanner.nextLine();
        parser.parseFirstNum(firstInput);
    
        System.out.println(&quot;연산자를 입력해주세요!&quot;);
        String operator = scanner.nextLine();
        parser.parseOperator(operator);
    
        System.out.println(&quot;두번째 숫자를 입력해주세요!&quot;);
        String secondInput = scanner.nextLine();
        parser.parseSecondNum(secondInput);
    
        System.out.println(&quot;연산 결과 : &quot; + parser.executeCalculator());
        return true;
    }

    }


    public abstract class AbstractOperation {
    public abstract double operate(int num1,int num2);
    }

    public class AddOperation extends AbstractOperation{
    @Override
    public double operate(int num1,int num2){
    return num1+num2;
    }
    }

    public class DivideOperation extends AbstractOperation{
    @Override
    public double operate(int num1,int num2){
    return num1 / num2;
    }
    }

    public class MultiplyOperation extends AbstractOperation{
    @Override
    public double operate(int num1, int num2){
    return num1 * num2;
    }
    }

    public class SubstractOperation extends AbstractOperation {
    @Override
    public double operate(int num1,int num2){
    return num1 - num2;
    }
    }
    public class BadInputException extends Exception{
    public BadInputException(String type){
    super("잘못된 입력 : "+type+"을 입력해주세요");
    }
    }


    public class Main {
    public static void main(String[] args) {
    boolean calculateEnded = false;
    while (!calculateEnded){
    try {
    calculateEnded = CalculatorApp.start(); // throws Exception이 되어있으니까 try catch문을 사용하지않으면 에러발생
    }catch (Exception e){
    System.out.println(e.getMessage());
    }
    }
    }
    }
  • 프로세스와 쓰레드
    // 프로그램 -> 어떤 작업을 위해 실행할 수 있는 파일
    // 프로세스 -> 실행 중인 프로그램
    // 예) Java프로그램을 실행시키면 OS위에 프로세스에서 실행됨
    💡
    프로세스 상태
    • 프로세스는 실행중인 프로그램일 수도 있고 아닐수도있다
    • 프로세스는 기본적으로 프로그램이 메모리에 적재되어있는 상태
    1. 최초의 프로세스는 운영체제가 시스템 부팅 시 생성해주며, 새로운 프로세스는 응용프로그램을 실행시키거나 프로그램 안에서 시스템 호출을 통해 생성될 수 있다.
    1. 생성된 프로세스는 기본적으로 준비상태에 들어간다
    1. 준비 상태의 프로세스는 언제라도 CPU로부터 선택되어 실행될 수 있는 상태
    1. CPU가 해당 프로세스를 선택하면(해당 프로세스에 CPU가 할당이 되면) 프로세스는 실행 상태로 들어간다
    1. 실행도중 입출력처럼 CPU가 계속해서 일을 할 수 없는 사건을 요청받으면 대기 상태로 들어간다
    1. 그러다가 입출력 처리가 완료되면, 실행상태가 아닌 준비상태로 돌아가 다시CPU가 할당되는것을 기다린다
    1. 하나의 프로세스가 CPU를 독점하는것을 방지하기 위해 각 프로세스마다 CPU를 점유할 수 있는 시간이 정해져있다 = CPU할당시간(time quantum)
    1. 실행 상태의 프로세스가 할당된 시간내에 작업을 완료하지 못하면 준비상태로 돌아가 다음 차례를 기다린다. 시간내에 완수하면 해당 프로세스는 종료
    💡
    프로세스 구조
    • OS가 프로그램 실행을 위한 프로세스를 할당할 때 프로세스안에는 Code, Data , 메모리영역(Stack,Heap)을 포함해서 할당
      • Code : Java main 메소드와 같은 코드
      • Data : 프로그램 실행중 저장 할 수 있는 저장공간( 전역변수,정적변수,배열등 초기화된 데이터를 저장하는 공간)
      • 메모리 영역
        • Stack : 지역변수,매개변수 리턴변수를 저장하는 공간
        • Heap : 프로그램이 동적으로 필요한 변수를 저장하는 공간 ( new(), mallock() )

    쓰레드

    💡
    프로세스 내에서 실행되는 여러흐름의 단위

    프로세스의 특정한 수행 경로

    프로세스가 할당받은 자원을 이용하는 실행의 단위


    생성 : 프로세스가 작업중인 프로그램에서 실행요청이 들러오면 쓰레드를 만들어 명령을 처리하도록 함

    • 쓰레드는 프로세스 내에서 각각 Stack만 따로 할당받고 Code,Data,Heap영역은 공유한다
    • 쓰레드는 한 프로세스 내에서 동작되는 여러 실행의 흐름으로, 프로세스 내의 주소공간이나 자원들을 같은 프로세스 내에 쓰레드끼리 공유하면서 실행한다.
    • 프로세스는 다른 프로세스의 메모리에 직접 접근할 수 없다
    • 쓰레드는 별도의 레지스터와 스택을 갖고있지만, 힙메모리는 서로 읽고 쓸 수 있다
    • 한 쓰레드가 프로세스 자원을 변경하면, 다른 이웃 쓰레드(sibling thread)도 그 변경 결과를 즉시 볼 수 있다
      💡
      Java쓰레드도 일반 쓰레드와 동일하면JVM프로세스 안에서 실행된다
      • JVM이 운영체제의 역할을 한다
      • 자바에는 프로세스가 존재하지않고 쓰레드만 존재하며, 자바 쓰레드는 JVM에 의해 스케줄되는 실행 단위 코드 블록

  • 싱글 쓰레드와 멀티 쓰레드
    💡
    자바는 메인쓰레드가 main()메서드를 실행시키면 시작된다
    • 메인 쓰레드는 필요에 따라서 작업 쓰레드를 생성해서 병렬로 코드를 실행 시킬 수 있다
    • Java는 멀티 쓰레드를 지원한다
    • 싱글 쓰레드
      • 프로세스 안에서 하나의 쓰레드만이 실행되는 것
      • Java 프로그램의 경우 main() 메서드만 실행시켰을때를 말함
      • main()메서드의 쓰레드를 메인 쓰레드라고 부른다
      • JVM의 메인 쓰레드가 종료되면, JVM도 같이 종료된다
    public class Main {
        public static void main(String[] args) {
            // 싱글 쓰레드
            Runnable task = () -> {
                System.out.println("2번 : "+Thread.currentThread().getName()); // thread1 / setName값이 나옴
                for (int i = 0; i < 100; i++) {
                    System.out.print("$");
                }
            };
    
        System.out.println(&quot;1번 : &quot;+Thread.currentThread().getName()); // 현재실행중인 쓰레드 메서드 이름 확인 / main
        Thread thread1 = new Thread(task);
        thread1.setName(&quot;thread1&quot;);
    
        thread1.start();
    }

    }

    • 멀티 쓰레드
      • 프로세스안에서 여러개의 쓰레드가 실행되는 것
      • 하나의 프로세스 안에서 여러개의 실행단위(쓰레드)를 가질 수 있으며 자원을 공유
      • 메인쓰레드외에도 다른 작업쓰레드를 생성해서 여러흐름을 만들수있다
        • 장점
          • 여러개의 쓰레드를 통해 여러개의 작업을 동시에 할 수 있다
          • 스택을 제외한 모든 영역에서 메모리를 공유하기때문에 자원을 보다 효율적으로 사용할 수 있다
          • 응답 쓰레드와 작업 쓰레드를 분리하여 빠르게 응답할 수 있다(비동기)
        • 단점
          • 동기화 문제가 발생할 수 있다
            • 프로세스의 자원을 공유하다보니 자원을 서로 사용하려고 하는 충돌을 함
          • 교착 상태(데드락)이 발생할 수 있다
            • 둘 이상의 쓰레드가 서로의 자원을 원하는 상태가 되었을 때 서러 작업이 종료되기만을 기다리면 작업을 더 이상 진행하지 못하게 되는 상태
    public class Main {
    public static void main(String[] args) {

        // 순차적인 실행이 아닌 쓰레드를 사용한 병렬적인 실행을 위함
        // 한마디로 비동기로 동시?에 실행하기 위해서
        // 순서가 순차적이지 않으니까 순서가 보장되어있지않는다
        // 값이 섞여서 나올수도있다.
    
        // 걸리는 시간이나, 동작을 예측할 수가 없다.
        // 매번 실행할때마다 결과값이 다르게 나온다 = 운영체제의 상황에따라 가변적으로 변한다.
    
        // 1st
        Runnable task = () -&gt; {
            for (int i = 0; i &lt; 100; i++) {
                System.out.print(&quot;$&quot;);
            }
        };
    
        // 2st
        Runnable task2 = () -&gt; {
            for (int i = 0; i &lt; 100; i++) {
                System.out.print(&quot;*&quot;);
            }
        };
    
    
    
        Thread thread1 = new Thread(task);
        thread1.setName(&quot;thread1&quot;);
        Thread thread2 = new Thread(task2);
        thread2.setName(&quot;thread2&quot;);
    
        thread1.start();
        thread2.start();
    }

    }

  • Thread 와 Runnable
    💡
    아래 Main class에서 Thread와 Runnable과 람다식 실행

    Thread

    // Thread

    // 1. Thread Class를 이용(상속)
    public class testThread extends Thread{
    @Override
    public void run(){
    // 쓰레드 수행 작업
    System.out.println("테스트 쓰레드");
    for(int i = 0; i < 100; i++){
    System.out.print("*");
    }
    }
    }

    // TestThread thread = new TestThread(); // 쓰레드 생성
    // thread.start() // 쓰레드 실행

    Runnable

    // Runnable

    public class testRunnable implements Runnable{
    @Override
    public void run() {
    // 쓰레드에서 수행할 작업 정의
    for(int i = 0; i < 100; i++){
    System.out.print("*");
    }
    }
    }

    람다식

    public static void main(String[] args) {
    // testThread test = new testThread(); -> Thread class 상속받음
    // test.start();

        // Runnable interface로 구현하고 Thread에다 매개변수로 넣어서 실행하는 방식

    // Runnable run = new testRunnable();
    // Thread thread = new Thread(run);
    // thread.start();

        // 람다식
        Runnable task = () -&gt; {
            int sum = 0;
            for (int i = 0; i &lt; 50; i++) {
                sum += i;
                System.out.println(sum);
            }
            System.out.println(Thread.currentThread().getName() + &quot; 최종 합 : &quot; + sum);
        };
    
        // 쓰레드 두개가 병렬적을 실행중
        // 아래식을 실행하면 하나의 식이 먼저계산되는 과정에서 어느구간에서 값이 다시 처음부터 실행되는 것을 확인할 수 있다
        Thread thread1 = new Thread(task);
        thread1.setName(&quot;thread1&quot;);
        Thread thread2 = new Thread(task);
        thread2.setName(&quot;thread2&quot;);
    
        thread1.start();
        thread2.start();
    }</code></pre></details></li></ul><ul id="37c88022-bb7e-4015-bdc1-0102aac98f55" class="toggle"><li><details open=""><summary>데몬 쓰레드와 사용자 쓰레드</summary><ul id="61a85e96-ce2f-4815-854c-1a6ecf5ee941" class="bulleted-list"><li style="list-style-type:disc">데몬 쓰레드</li></ul><figure class="block-color-gray_background callout" style="white-space:pre-wrap;display:flex" id="6807a573-ebee-4f1a-b42d-0eb8bc888341"><div style="font-size:1.5em"><span class="icon">💡</span></div><div style="width:100%">보이지 않는곳(background)에서 실행되는 낮은 우선순위를 가진 쓰레드<ul id="9e804502-d209-4e93-86f1-6777231de10e" class="bulleted-list"><li style="list-style-type:disc">보조적인 역할을 담당하며 대표적인것은 메모리 영역을 정리해주는 가비지 컬렉터(GC)가 있다</li></ul></div></figure><pre id="ecd5109a-710b-48f5-9d96-0cf3df3a28e9" class="code"><code>public class Main {
    public static void main(String[] args) {
        Runnable demon = () -&gt; {
            for (int i = 0; i &lt; 1000000; i++) {
                System.out.println(i+&quot;번째 demon&quot;);
            }
        };
    
        Thread thread = new Thread(demon);
        thread.setDaemon(true); // true로 설정시 데몬스레드(우선순위가 낮음)로 실행됨
        // 우선순위가 낮다 -&gt; 상대적으로 다른 쓰레드에 비해 리소스를 적게 할당받는다 -&gt; 그러다보니까 상대적으로 느리게 실행될수있다.
    
        thread.start();
    
        // 이 for문이 실행되고 완료되면 deamon 쓰레드가 종료되는 것을 기다리지않고 main 메서드 종료
        // setDaemon을 하지않으면 기본적으로 사용자쓰레드이며, 데몬 쓰레드를 기달려주지않는다
        for (int i = 0; i &lt; 100; i++) {
            System.out.println(i+&quot;번째 task&quot;);
        }
    }

    }

    • 사용자 쓰레드
    💡
    보이는 곳(foreground)에서 실행되는 높은 우선 순위를 가진 쓰레드

    프로그램 기능을 담당하며, 대표적으로는 메인쓰레드가 있다

    기존에 만들어서 사용하고 있는 쓰레드가 사용자 쓰레드이다

    JVM은 사용자 쓰레드의 작업이 끝나면 데몬 쓰레드도 자동으로 종료 시킨다

  • 쓰레드 우선순위와 그룹
    • 작업의 중요도가 높을 때 우선순위를 높게 지정하여 더많은 작업시간과 리소스를 제공해줄수있다
    • 쓰레드가 생성시 우선순위가 정해지는데 JVM에 의해서도 가능하고 직접지정도 가능하다
    • 우선순위가 높다고 쓰레드가 무조건적으로 종료되는것이아닌 종료될 가능성이 높다는 것이다
    public class Main {
        public static void main(String[] args) {
            Runnable task1 = () -> {
                for (int i = 0; i < 100; i++) {
                    System.out.print("$");
                }
            };
    
        Runnable task2 = () -&gt; {
            for (int i = 0; i &lt; 100; i++) {
                System.out.print(&quot;*&quot;);
            }
        };
    
        Thread thread1 = new Thread(task1);
        thread1.setPriority(8);
        // 우선순위 지정 / 숫자가 높을수록 우선순위가 높다-&gt; 상대적으로 리소스를 많이받아서 빨리끝날가능성이 높다
        // 무거운 작업을 할때 차이를 알 수 있다
        int threadPriority = thread1.getPriority(); // 우선순위 값 가져오기
        System.out.println(&quot;threadPriority = &quot; + threadPriority);
    
        Thread thread2 = new Thread(task2);
        thread2.setPriority(2);
    
        thread1.start();
        thread2.start();
    }

    }

    • 그룹
      • 쓰레드는 기본적으로 그룹에 포함되어있다
        • JVM이 시작되면 system 그룹이 생성되고 쓰레드들은 기본적으로 system 그룹에 포함
      • 메인 쓰레드는 system 그룹 하위에 있는 main 그룹에 포함
      • 모든 쓰레드들은 반드시 하나의 그룹에 포함되어 있어야한다
        • 쓰레드 그룹을 지정받지 못한 쓰레드는 자신을 생성한 부모 쓰레드의 그룹과 우선순위를 상속받게되는데 우리가 생성하는 쓰레드들은 main 쓰레드 하위에 포함
        • 쓰레드 그룹을 지정하지않으면 해당 쓰레드는 자동으로 main그룹에 포함
    public class Main {
    public static void main(String[] args) {
    Runnable task = () -> {
    while (!Thread.currentThread().isInterrupted()) {
    System.out.println("여기 뭐나오니 : "+Thread.currentThread().isInterrupted());
    try {
    Thread.sleep(1000);
    System.out.println(Thread.currentThread().getName());
    } catch (InterruptedException e) {
    break;
    }
    }
    System.out.println(Thread.currentThread().getName() + " Interrupted");
    };

        // ThreadGroup 클래스로 객체를 만듭니다.
        ThreadGroup group1 = new ThreadGroup(&quot;Group1&quot;);
    
        // Thread 객체 생성시 첫번째 매개변수로 넣어줍니다.
        // Thread(ThreadGroup group, Runnable target, String name)
        // Thread에 생성자가 여러개이기때문에 가능
        Thread thread1 = new Thread(group1, task, &quot;Thread 1&quot;);
        Thread thread2 = new Thread(group1, task, &quot;Thread 2&quot;);
    
        // Thread에 ThreadGroup 이 할당된것을 확인할 수 있습니다.
        System.out.println(&quot;Group of thread1 : &quot; + thread1.getThreadGroup().getName());
        System.out.println(&quot;Group of thread2 : &quot; + thread2.getThreadGroup().getName());
    
        thread1.start();
        thread2.start();
    
        try {
            // 현재 쓰레드를 지정된 시간동안 멈추게 합니다.
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    
        // interrupt()는 일시정지 상태인 쓰레드를 실행대기 상태로 만듭니다.
        group1.interrupt();
    
    }

    }

  • 쓰레드 상태와 제어
    💡
    쓰레드 상태 정리 표
    상태Enum(상수)설명
    객체생성NEW쓰레드 객체 생성, 아직 start()메서드 호출 전의 상태
    실행대기 RUNNABLE실행 상태로 언제든지 갈 수 있는 상태
    일시정지WAITING다른 쓰레드가 통지(notify)할 때까지 기다리는 상태
    일시정지TIMEDWAITING주어진 시간 동안 기다리는 상태
    일시정지BLOCKED사용하고자 하는 객체의 Lock이 풀릴 때까지 기다리는 상태
    종료TERMINATED쓰레드의 작업이 종료된 상태
    • 쓰레드 제어<a href="Java%20bd4a58d5f12642da8418ff1b8949afe5/thread(2).png">
      • sleep(), interrupt()
        • sleep()
          💡
          쓰레드 자기자신에 대해서만 멈추게 할 수 있다

          예외처리를 반드시 해야 사용할수 있다

          sleep상태에서 interrupt()를 만나면 다시 실행되기 때문에 InterruptedException이 발생

          public class Main {
          public static void main(String[] args) {
          Runnable task = () -> {
          try {
          // throws로 InterruptedException 예외처리를 열려주고있어서 예외처리를 필수로 해야한다.
          // InterruptedException :
          // sleep상태에서 Interrupt이 호출되면 실행대기상태로 무조건 보내서 sleep이 깨지는 상황이 발생한다
          // 그경우의 에러를 알려줄때 사용

                      Thread.sleep(2000);
                      // Thread.sleep은 클래스.메서드로 static 메서드 라는것을 예측해볼수있다
                      // sleep : 어떤 객체를 지칭하는게 아니라 해당 클래스 자체를 정지시킨다
                      // 객체.메서드() -&gt; 일반적인경우
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
                  System.out.println(&quot;task : &quot; + Thread.currentThread().getName());
              };
          
              Thread thread = new Thread(task, &quot;Thread&quot;); // NEW
              thread.start(); // start하는 순간 NEW -&gt; RUNNABLE로 변경
          
              try {
          
                  thread.sleep(1000); // 객체로 만든 thread
                  // 노란줄이 나오는 이유는 static메서드이기 때문에 인스턴스화가 불필요해서
                  // Thread.sleep(1000)과 같기때문에
                  // 원래있던 메인쓰레드가 나옴
                  System.out.println(&quot;sleep(1000) : &quot; + Thread.currentThread().getName());
              } catch (InterruptedException e) {
                  e.printStackTrace();
              }
          }

          }

        • interrupt()
          💡
          일시정지 상태인 쓰레드를 실행대기 상태로 만듬
          public class Main {
              public static void main(String[] args) {
          // 오류 발생부분
                  Runnable task = () -> {
                      try {
                          Thread.sleep(1000);
                          System.out.println(Thread.currentThread().getName());
                      } catch (InterruptedException e) {
                          e.printStackTrace();
                      }
                      System.out.println("task : " + Thread.currentThread().getName());
                  };
          
              Thread thread = new Thread(task, &quot;Thread&quot;); // NEW
              thread.start(); // NEW -&gt; RUNNABLE
          
              thread.interrupt(); // sleep에서 interrupt를 실행하면 catch문쪽으로 넘어감
          
              System.out.println(&quot;thread.isInterrupted() = &quot; + thread.isInterrupted()); // 현재 Interrupt상태인지 boolean으로 알려줌
          
          }

          }

          // 오류 해결부분

          //public class Main {
          // public static void main(String[] args) {
          // Runnable task = () -> {
          // while (!Thread.currentThread().isInterrupted()) {
          // try {
          // Thread.sleep(1000);
          // System.out.println(Thread.currentThread().getName());
          // } catch (InterruptedException e) {
          // break;
          // }
          // }
          // System.out.println("task : " + Thread.currentThread().getName());
          // };
          //
          // Thread thread = new Thread(task, "Thread");
          // thread.start();
          //
          // thread.interrupt();
          //
          // System.out.println("thread.isInterrupted() = " + thread.isInterrupted());
          //
          // }
          //}

      • join(),yield()
        • join()
          💡
          정해진 시간동안 지정한 쓰레드가 작업하는것을 기다림
          • 시간을 지정하지 않을때에는 지정한 쓰레드의 작업이 끝날 때까지 기다림
          // 사용방법

          Thread thread = new Thread(task, "thread");

          thread.start();

          try {
          thread.join();
          } catch (InterruptedException e) {
          e.printStackTrace();
          }

          // interrupt()를 만나면 기다리는 것을 멈추기때문에 InterruptException 예외처리 해주어야함

          public class Main {
          public static void main(String[] args) {
          Runnable task = () -> {
          try {
          Thread.sleep(5000); // 5초
          } catch (InterruptedException e) {
          e.printStackTrace();
          }
          };

              Thread thread = new Thread(task, &quot;thread&quot;);
          
              thread.start();
          
              long start = System.currentTimeMillis();
          
              try {
                  thread.join();
          
              } catch (InterruptedException e) {
                  e.printStackTrace();
              }
          
              // thread 의 소요시간인 5000ms 동안 main 쓰레드가 기다렸기 때문에 5000이상이 출력됩니다.
              System.out.println(&quot;소요시간 = &quot; + (System.currentTimeMillis() - start));
              // join을 사용하지않을 경우 : 0
              // 사용시 : 5000
          }

          }

        • yield()
          💡
          남은 시간을 다음 쓰레드에게 양보하고 쓰레드 자신은 실행대기 상태
          public class Main {
          public static void main(String[] args) {
          Runnable task = () -> {
          try {
          for (int i = 0; i < 10; i++) {
          Thread.sleep(1000);
          System.out.println(Thread.currentThread().getName());
          }
          } catch (InterruptedException e) {
          Thread.yield(); // 자신의 남은 시간을 다음쓰레드에게 양보하고 쓰레드자신을 실행대기로 감
          // thread1에서 interrupt가 발생하여 Thread.yield()가 실행되고
          // thread1은 실행대기로 가고 남은시간은 thread2에게 리소스가 양보됨
          }
          };

              Thread thread1 = new Thread(task, &quot;thread1&quot;);
              Thread thread2 = new Thread(task, &quot;thread2&quot;);
          
              thread1.start();
              thread2.start();
          
              try {
                  Thread.sleep(5000);
              } catch (InterruptedException e) {
                  e.printStackTrace();
              }
          
              thread1.interrupt(); // 실행중인 상태인것은 실행대기상태로 변경한다
          
          }

          }

      • synchronized
        💡
        멀티 쓰레드일 경우 여러쓰레드가 자원을 공유하기때문에 장애나 버그가 발생할 수 있다

        이것을 방지하기위해 하나의 쓰레드가 진행중인 작업을 다른쓰레드가 침범하지 못하도록 막는것이

        쓰레드 동기화(Synchronization)이다

        • 동기화를 하려면 임계영역을 설정하여야한다
        • 임계영역은 Lock을 가진 단 하나의 쓰레드만 출입이 가능하다
          • 임계영역은 한번에 하나의 쓰레드만 사용이 가능
        임계영역 지정 방식
        1. 메서드 전체를 임계영역으로 지정
        public synchronized void asyncSum() {
        ...침범을 막아야하는 코드...
        }
        1. 특정 영역을 임계영역으로 지정
        synchronized(해당 객체의 참조변수) {
        ...침범을 막아야하는 코드...
        }
        1. 코드 실전 예시
        public class Main {
        public static void main(String[] args) {
        AppleStore appleStore = new AppleStore();

            Runnable task = () -&gt; {
                while (appleStore.getStoredApple() &gt; 0) {
                    // sync를 해주지않으니까 조건을 체크할때는 0보다 컸는데
                    // 아래에서 eatApple메서드를 실행할때
                    // 1초기달렸다고 남겨져있는 사과를 기달렸는데 동시에
                    // 3개의 쓰레드가 접근해서 1개남은 상황에서는
                    // 1개의 쓰레드는 먹고 나머지 두쓰레드는 0개에서 -1,-2가되는 상황이된다
        
                    // 하지만 sync를 해주면 하나의쓰레드가 접근해서 로직이끝날때까지
                    // 나머지 쓰레드는 접근하지못하게할 수 있다
                    appleStore.eatApple();
                    System.out.println(&quot;남은 사과의 수 = &quot; + appleStore.getStoredApple());
                }
        
            };
        
            // 3개의 thread를 한꺼번에 만들어서 start
            // 생성과 동시에 start(NEW -&gt; RUNNABLE)
            for (int i = 0; i &lt; 3; i++) {
                new Thread(task).start();
            }
        }

        }

        class AppleStore {
        private int storedApple = 10;

        public int getStoredApple() {
            return storedApple;
        }
        
            public void eatApple() {
            synchronized (this) {
                if(storedApple &gt; 0) {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    storedApple -= 1;
                }
            }
        }

        }

      • wait(),notify()
        📌
        침범을 막은 코드를 수행하다가 작업을 더 이상 진행할 상황이 아니면, wait()을 호출하여 쓰레드가 Lock을 반납하고 기다리게 할 수 있다
        • 그러면 다른 쓰레드가 Lock을 얻어 해당 객체에 대한 작업을 수행할 수 있게 된다
        • 추후에 작업을 진행할 수 있는 상황이 되면 notify()를 호출해서, 작업을 중단했던 쓰레드가 다시 Lock을 얻어 진행할 수 있게 된다.
        wait()
        • 실행중이던 쓰레드는 해당 객체의 대기실(waiting pool)에서 통지를 기다림
        • 가지고 있던 고유락을 해제하고, 쓰레드르 잠들게한다
        • 호출하는 쓰레드가 반드시 고유 락을 갖고있어야 하기때문에 synchronized 블록내에서 호출되어야한다

        notify()

        • 해당 객체의 대기실(waiting pool)에 있는 모든 쓰레드 중에서 임의의 쓰레드만 통지를 받음

        notifyAll

        • 호출로 잠들어 있던 쓰레드 모두 깨운다

        // 아래코드는 이슈가 있다
        // 원하는 재고가 없어서 소비자는 기달리고 점원은 랜덤에서 물건을 넣었는데 이미공간이 다찬상태이면
        // 둘다 대기상태에 들어가게되어 while문에 멈추는 조건이 없는데도 아무것도 안하는 상태가 발생하게된다
        // 한마디로 병목현상이 발생된다
        // 병목현상 : 쓰레드가 서로 기다려서 아무것도 안하고 대기하는 현상
        import java.util.*;
        

        public class Main {
        public static String[] itemList = {
        "MacBook", "IPhone", "AirPods", "iMac", "Mac mini"
        };
        public static AppleStore appleStore = new AppleStore();
        public static final int MAX_ITEM = 5;

        public static void main(String[] args) {
        
            // 가게 점원
            Runnable StoreClerk = () -&gt; {
                while (true) {
                    // 0 ~ 4사이의 정수중 랜덤한 값을 뽑아내기 위함
                    int randomItem = (int) (Math.random() * MAX_ITEM);
                    // restock : 재고를 넣는 메서드
                    appleStore.restock(itemList[randomItem]);
                    try {
                        Thread.sleep(50);
                    } catch (InterruptedException ignored) {
                    }
                }
            };
        
            // 고객
            Runnable Customer = () -&gt; {
                while (true) {
                    try {
                        Thread.sleep(77);
                    } catch (InterruptedException ignored) {
                    }
        
                    int randomItem = (int) (Math.random() * MAX_ITEM);
        
                    // sale : 판매하는 메서드
                    appleStore.sale(itemList[randomItem]);
                    System.out.println(Thread.currentThread().getName() + &quot; Purchase Item &quot; + itemList[randomItem]);
                }
            };
        
        
            new Thread(StoreClerk, &quot;StoreClerk&quot;).start();
            new Thread(Customer, &quot;Customer1&quot;).start();
            new Thread(Customer, &quot;Customer2&quot;).start();
        
        }

        }

        class AppleStore {
        private List<String> inventory = new ArrayList<>();

        public void restock(String item) {
            synchronized (this) {
                while (inventory.size() &gt;= Main.MAX_ITEM) {
                    System.out.println(Thread.currentThread().getName() + &quot; Waiting!&quot;);
                    try {
                        wait(); // 재고가 꽉 차있어서 재입고하지 않고 기다리는 중!
                        Thread.sleep(333);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                // 재입고
                inventory.add(item);
                notify(); // 재입고 되었음을 고객에게 알려주기
                System.out.println(&quot;Inventory 현황: &quot; + inventory.toString());
            }
        }
        
        public synchronized void sale(String itemName) {
            while (inventory.size() == 0) {
                System.out.println(Thread.currentThread().getName() + &quot; Waiting!&quot;);
                try {
                    wait(); // 재고가 없기 때문에 고객 대기중
                    Thread.sleep(333);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        
            while (true) {
                // 고객이 주문한 제품이 있는지 확인
                for (int i = 0; i &lt; inventory.size(); i++) {
                    if (itemName.equals(inventory.get(i))) {
                        inventory.remove(itemName);
                        notify(); // 제품 하나 팔렸으니 재입고 하라고 알려주기
                        return; // 메서드 종료
                    }
                }
        
                // 고객이 찾는 제품이 없을 경우
                try {
                    System.out.println(Thread.currentThread().getName() + &quot; Waiting!&quot;);
                    wait();
                    Thread.sleep(333);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        
        }

        }

      • lock 인터페이스
        📌
        JDK1.5부터 추가된 동기화 방법

        synchronized블럭을 이용한 동기화는 자동적으로 lock이 걸리고 풀리기 때문에 synchronized블럭 내에서 예외가 발생해도 lock이 자동을 불린다

        같은 메서드내에서만 lock을 걸 수 있다. 이런 제약을 없애기 위함

        java.util.concurrent.locks패키지에서 제공

        • 종류
          • ReentrantLock
            • 읽기,쓰기에 대해 동시에 lock을 제어하는 클래스
            • lock인터페이스를 구현하며 lock인터페이스의 추상메서드인 lock(),unlock(),tryLock()메서드를 구체적으로 정의
          • ReentrantReadWriteLock
            • 읽기,쓰기에 대해 별도로 lock을 제어하는 클래스
            • 클래스 기능 특성상, lock()메서드가 존재하지않고 readLock(). writeLock()으로 분화된 메서드가 존재
          • StampedLock
            • 읽기,쓰기 이외에도 낙관적 읽기(Optimistic Read)에 대한 lock을 제어하는 클래스
            • 읽기 Lock이 걸려있으면 다른 쓰레드들도 읽기 Lock을 중복으로 걸고 읽기를 수행할 수 있다(read-only)
            • 읽기 Lock이 걸려있는 상태에서 쓰기 Lock을 거는 것은 허용되지 않는다 (데이터 변경 방지)

        ReentrantLock 클래스

        • synchronized 제어자가에서 자동을 lock해제와 재진입을 진행해주는것과 달리, 수동으로 lock해제와 재진입이 가능
        • Thread에 lock을 거는 lock(), 현재 Thread의 lock을 해제하는 unlock(), 특정조건을 만족하는 경우에 lock을 획득하는 tryLock()메서드 등이 정의되어 있다.
      • Condition
        📌
        wait()와notify()에서 waiting pool내 쓰레드를 구분하지 못해서 생기는 병목현상을 해결하기 위함

        JDK 5에서 java.util.concurrent.locks 패키지에서 Condition 인터페이스 제공

        waiting pool내의 스레드를 분리하여 특정 조건이 만족될때만 깨우도록 할 수 있다

        ReentrantLock클래스와 함께 사용된다

        Condition에서는 await()&signal()이 사용됨

        import java.util.ArrayList;
        import java.util.concurrent.locks.*;

        public class Main {
        public static final int MAX_TASK = 5;
        private ReentrantLock lock = new ReentrantLock();

        // lock으로 condition 생성
        private Condition condition1 = lock.newCondition();
        private Condition condition2 = lock.newCondition();
        
        private ArrayList&lt;String&gt; tasks = new ArrayList&lt;&gt;();
        
        // 작업 메서드
        public void addMethod(String task) {
            lock.lock(); // 임계영역 시작
        
            try {
                while(tasks.size() &gt;= MAX_TASK) {
                    String name = Thread.currentThread().getName();
                    System.out.println(name+&quot; is waiting.&quot;);
                    try {
                        condition1.await(); // wait(); condition1 쓰레드를 기다리게 합니다.
                        Thread.sleep(500);
                    } catch(InterruptedException e) {}
                }
        
                tasks.add(task);
                condition2.signal(); // notify();  기다리고 있는 condition2를 깨워줍니다.
                System.out.println(&quot;Tasks:&quot; + tasks.toString());
            } finally {
                lock.unlock(); // 임계영역 끝
            }
        }

        }

  • 일급객체와 이급객체
    • 일급 객체
      • 객체의 인자로 넘길 수 있어야 한다
      • 객체의 리턴값으로 리턴을 할 수 있어야 한다
      • 변수나 데이터에 할당 할 수 있어야 한다
    📌
    자바에서는 위의 조건에 해당하지 않는다

    Lanbda는 메서드가 1개만 존재하는 인터페이스를 통해, 마치 함수를 전달하는 것처럼 여겨서, 함수를 1급객체로 취급하지 않는 자바의 단점을 어느정도 해결하고 있다

  • 람다(Lambda) 함수형 인터페이스 스트림(Stream)
    1. 람다
    • 익명함수(이름이 없고 일급 객체로 취급)
    • 함수를 값으로 사용 할 수도 있으며 파라미터에 전달
    • 변수에 대입하기와 같은 연산들이 가능
    • 장점
      • 코드의 간결성
      • 지연연산 수행 → 지연연상을 수행 함으로써 불필요한 연산을 최소화 할 수 있다
      • 병렬처리 가능
    • 단점
      • 호출이 까다롭다
      • 람다 stream 사용시 단순 for문 또는 while문 사용시 성능이 떨어짐
      • 불필요하게 많이 쓰면 오히려 가독성이 안좋을 수도 있다
    // 람다 함수 문법
    

    // 기본적으로 문법은 다음과 같습니다.
    (파라미터 값, ...) -> { 함수 몸체 }

    // 아래의 함수 두개는 같은 함수입니다.
    // 이름 반환타입, return문 여부에 따라 {}까지도 생략이 가능합니다.
    public int toLambdaMethod(int x, int y) {
    return x + y;
    }

    (x, y) -> x + y

    // 이런 함수도 가능하겠죠?
    public int toLambdaMethod2() {
    return 100;
    }

    () -> 100

    // 모든 유형의 함수에 가능합니다.
    public void toLambdaMethod3() {
    System.out.println("Hello World");
    }

    () -> System.out.println("Hello World")


    1. 함수형 인터페이스
      • 추상메서드를 하나만 가지고있는 인터페이스
      • @FunctionalInterface 어노테이션으로 검증 가능

    @FunctionalInterface

    • Functional Interface는 일반적으로 '구현해야 할 추상 메소드가 하나만 정의된 인터페이스'를 가리킨다
    • 자바 컴파일러는 이렇게 명시된 함수형 인터페이스에 두 개 이상의 메소드가 선언되면 오류를 발생시킨다
    //구현해야 할 메소드가 한개이므로 Functional Interface이다.
    @FunctionalInterface
    public interface Math {
    public int Calc(int first, int second);
    }

    //구현해야 할 메소드가 두개이므로 Functional Interface가 아니다. (오류 사항)
    @FunctionalInterface
    public interface Math {
    public int Calc(int first, int second);
    public int Calc2(int first, int second);
    }

    함수형 Interface 선언

    @FunctionalInterface
    interface Math {
    public int Calc(int first, int second);
    }

    추상 메소드 구현 및 함수형 인터페이스 사용

    public static void main(String[] args){

    Math plusLambda = (first, second) -> first + second;
    System.out.println(plusLambda.Calc(4, 2));

    Math minusLambda = (first, second) -> first - second;
    System.out.println(minusLambda.Calc(4, 2));

    }

    실행결과

    6
    2

    Java에서 지원하는 java.util.function 인터페이스

    • IntFunction<R>
      • int 값의 인수를 받아들이고 결과를 생성하는 함수를 나타냄

    사용 예제

    IntFunction intSum = (x) -> x+1;
    System.out.println(intSum.apply(1));

    실행결과

    2
    • BinaryOperator<T>
      • 동일한 유형의 두 피연산자에 대한 연산을 나타내며 피연산자와 동일한 유형의 결과를 생성합니다.

    사용 예제

    BinaryOperator stringSum=(x, y)->x+" "+y;
    System.out.println(stringSum.apply("Welcome","Heejin blog"));

    실행결과

    Welcome Heejin blog

    그외 다양한 Interface 목록

    https://docs.oracle.com/javase/8/docs/api/java/util/function/package-summary.html


    1. Stream
    • 다양한 데이터를 표준화된 방법으로 다루기 위한 라이브러리
    • 특징
      • 데이터를 변경하지 않는다 → 자바 컬렉션으로부터 스트림(해당 컬렉션의 흐름)을 받아서 한 번 사용
      • 1회성이다(휘발성)
      • 지연 연산을 수행한다
      • 병렬 실행이 가능하다
      • collection에 정의되어있기 때문에,모든 컬렉션을 상속하는 구현체들은 스트림을 반환 할 수 있다
      • 두개의 코드는 같은 역할을 하고있다
      List<Car> benzParkingLot =
      // carsWantToPark의 스트림값을 받아와서
      carsWantToPark.stream()
      // 거기 구현되어 있는 filter()메서드를 사용합니다.
      // filter메서드는 함수를 파라미터로 전달받습니다.
      // 여기서 함수는 제조사가 벤츠면 true를 반환하는 함수네요.
      // 필터 메서드는 이름처럼 false를 반환한 스트림의 원소들을 제거합니다.
      .filter((Car car) -> car.getCompany().equals("Benz"))
      // 이 결과도 반환을 받아서 다시 리스트로 묶어줍니다.
      // equals는 해당값이 같은지 여부판단
      // contains는 포함되어있는지 여부판단
      .toList();
      ArrayList<Car> benzParkingLotWithoutStream = new ArrayList<>();

      for (Car car : carsWantToPark) {
      if (car.getCompany().equals("Benz")) {
      benzParkingLotWithoutStream.add(car);
      }
      }

      • 스트림을 사용하는 방법
        1. 스트림을 받아오기 (.stream())
          carsWantToPark.stream()
        1. 스트림 가공하기
          .filter((Car car) -> car.getCompany().equals("Benz"))
        1. 스트림 결과 만들기
          .toList();
      • map(),forEach(),filter()
        • forEach()
          List<String> carNames = Arrays.asList("Series 6", "A9", "Ionic 6");

          carNames.stream()
          .forEach(System.out::println);

          // 결과
          // Series 6
          // A9
          // Ionic 6

          • 각각의 원소에 넘겨받은 함수를 실행해줍니다.
          • 하지만 넘겨받은 반환값을 가지고 뭘 하지는 않으며, 있다고 해도 무시됩니다.
        • map()
          carNames.stream()
          .map(name -> name.toUpperCase()).toList();

          // 결과
          // ["SERIES 6", "A9", "IONIC 6"]

          • forEach와는 반대로 넘겨받은 토대로 값을 변환시키는데 주로 사용됩니다.

    Stream의 종류

    Stream <T>범용 Stream
    IntStream값 타입이 Int인 Stream
    LongStream값 타입이 Long인 Stream
    DoubleStream값 타입이 Double인 Stream

    Stream의 중간 연산 명령어

    Stream < T > distinct()Stream의 요소 중복 제거
    Stream < T > sorted()Stream 요소 정렬
    Stream < T > filter(Predicate < T > predicate)조건에 충족하는 요소를 Stream으로 생성
    Stream < T > limit(long maxSize)maxSize 까지의 요소를 Stream으로 생성
    Stream < T > skip(ling n)처음 n개의 요소를 제외하는 stream 생성
    Stream < T > peek(Consumer< T > action)T타입 요소에 맞는 작업 수행
    Stream < R > flatMap(Function< T, stream<? extends R>> Tmapper)T타입 요소를 1:N의 R타입 요소로 변환하여 스트림 생성
    Stream < R > map(Function<? super T, ? extends R> mapper)입력 T타입을 R타입 요소로 변환한 스트림 생성
    Stream mapToInt(),mapToLong(),mapToDobule()만약 map Type이 숫자가 아닌 경우 변환하여 사용

    Stream의 최종 연산 명령어

    void forEach(Consumer <? super T> action)Stream 의 각 요소에 지정된 작업 수행
    long count()Stream 의 요소 개수
    Optional < T > sum (Comparator <? super T> comparator)Stream 의 요소 합
    Optional < T > max (Comparator <? super T> comparator)Stream 요소의 최대 값
    Optional < T > min (Comparator <? super T> comparator)Stream 요소의 최소 값
    Optional < T > findAny()Stream 요소의 랜덤 요소
    Optional < T > findFirst()Stream 의 첫 번째 요소
    boolean allMatch(Pradicate < T > p)Stream 의 값이 모두 만족하는지 boolean 반환
    boolean anyMatch(Pradicate < T > p)Stream 의 값이 하나라도 만족하는지 boolean 반환
    boolean noneMatch(Pradicate < T > p)Stream 의 값이 하나라도 만족하지않는지 boolean 반환
    Object[] toArray()Stream 의 모든 요소를 배열로 반환
    reduce 연산Stream 의 요소를 하나씩 줄여가며 계산한다.
    - Optional < T > reduce(Binary Operator<T> accumulator)
    - T reduce ( T identity, BinaryOperator<T> accumulator)
    - <U> U reduce (U indentity, BiFunction<U, ? super T, U> accumulator, BinaryOperator<U> combiner)
    - .reduce((x,y) -> x > y ? x : y );
    - .reduce(1, (x,y) -> x * y);
    - .reduce(0.0,
    (val1, val2) -> Double.valueOf(val1 + val2 / 10),
    (val1, val2) -> val1 + val2);
    collector 연산Stream의 요소를 수집하여 요소를 그룹화 하거나 결과를 담아 반환하는데 사용한다.
    - Collectors.toList()
    - Collectors.toSet()
    - Collectors.toMap()
    - Collectors.groupingBy
    - Collectors.partioningBy
    - Collectors.summarizingInt()

    그외의 스트림관련정보

    • 예제
      // 첫번째를 설명하자면
      // Car라는 클래스에서 자동차회사와 모델 티켓여부 그리고 금액여부를 매개변수로 받아와
      // 주차를 할 수 있는지를 확인해준다

      // 중간쯤에 보면 parkingCarWithTicket이랑 parkingCarWithMoney메서드에 자동차 주차여부를 확인하는 기능구현이
      // 비슷하게 되어있다
      // 이부분을 익명함수를 사용하여 간략하게 축소할 수 있다

      // 두번째 쪽을 보면 parkCars라는 메서드에 매개변수로 첫번째에는 자동차 객체를 두번째로는 Predicate라는 interface타입의 함수를
      // 매개변수로 받아온다
      // 그리고 해당 함수를 메서드 안에서 실행하면서 Car타입으로 만들어진 cars List를 매개변수로 집어 넣었다
      // 그러면 해당 결과값을 true false로 반환하는 기능을 구현하였다

      // 그 기능을 사용한게 위쪽의 addAll부분이다
      // parkingLot에 addAll로 전부추가하는데 parkCars 메서드를 사용하고 그안에 객체타입을 매개변수1번에 집어넣고,
      // 2번째 매개변수로 Car클래스안에있는 메서드인 hasTicket을 불러와 사용하였다
      // hasTicket메서드 내부를보면 해당값이있는지 여부를 true,false로 반환해주게 되어있다

      // 이렇게 되어있는 두개의 메서드에 새로운 기능을 추가할때
      // 새롭게 메서드를 만드는게 효율적이지 못한다고 느낄때 익명 함수를 사용해 볼 수 있다.

      import java.util.ArrayList;
      import java.util.List;

      public class LambdaAndStream {
      public static void main(String[] args) {
      ArrayList<Car> carsWantToPark = new ArrayList<>();
      ArrayList<Car> parkingLot = new ArrayList<>();

          Car car1 = new Car(&quot;Benz&quot;, &quot;Class E&quot;, true, 0);
          Car car2 = new Car(&quot;BMW&quot;, &quot;Series 7&quot;, false, 100);
          Car car3 = new Car(&quot;BMW&quot;, &quot;X9&quot;, false, 0);
          Car car4 = new Car(&quot;Audi&quot;, &quot;A7&quot;, true, 0);
          Car car5 = new Car(&quot;Hyundai&quot;, &quot;Ionic 6&quot;, false, 10000);
      
          carsWantToPark.add(car1);
          carsWantToPark.add(car2);
          carsWantToPark.add(car3);
          carsWantToPark.add(car4);
          carsWantToPark.add(car5);
      
          parkingLot.addAll(parkingCarWithTicket(carsWantToPark));
          parkingLot.addAll(parkingCarWithMoney(carsWantToPark));
      
      
          for (Car car : parkingLot) {
              System.out.println(&quot;Parked Car : &quot; + car.getCompany() + &quot;-&quot; + car.getModel());
          }
      
      
      }
      
      public static List&lt;Car&gt; parkingCarWithTicket(List&lt;Car&gt; carsWantToPark) {
          ArrayList&lt;Car&gt; cars = new ArrayList&lt;&gt;();
          
          for (Car car : carsWantToPark) {
              if (car.hasParkingTicket()) {
                  cars.add(car);
              }
          }
      
          return cars;
      }
      
      public static List&lt;Car&gt; parkingCarWithMoney(List&lt;Car&gt; carsWantToPark) {
          ArrayList&lt;Car&gt; cars = new ArrayList&lt;&gt;();
          
          for (Car car : carsWantToPark) {
              if (!car.hasParkingTicket() &amp;&amp; car.getParkingMoney() &gt; 1000) {
                  cars.add(car);
              }
          }
          
          return cars;
      }

      }

      class Car {
      private final String company; // 자동차 회사
      private final String model; // 자동차 모델

      private final boolean hasParkingTicket;
      private final int parkingMoney;
      
      public Car(String company, String model, boolean hasParkingTicket, int parkingMoney) {
          this.company = company;
          this.model = model;
          this.hasParkingTicket = hasParkingTicket;
          this.parkingMoney = parkingMoney;
      }
      
      public String getCompany() {
          return company;
      }
      
      public String getModel() {
          return model;
      }
      
      public boolean hasParkingTicket() {
          return hasParkingTicket;
      }
      
      public int getParkingMoney() {
          return parkingMoney;
      }

      }

      import java.util.ArrayList;
      import java.util.List;

      // 주차장 예제
      // 티켓, 파킹머니 -> 주차하게 하는
      public class LambdaAndStream {
      public static void main(String[] args) {

          // 주차대상 차량
          ArrayList&lt;Car&gt; carsWantToPark = new ArrayList&lt;&gt;();
      
          // 주차장
          ArrayList&lt;Car&gt; parkingLot = new ArrayList&lt;&gt;();
      
          // 주말 주차장
          ArrayList&lt;Car&gt; weekendParkingLot = new ArrayList&lt;&gt;();
      
          // 5개의 car instance
          Car car1 = new Car(&quot;Benz&quot;, &quot;Class E&quot;, true, 0);
          Car car2 = new Car(&quot;BMW&quot;, &quot;Series 7&quot;, false, 100);
          Car car3 = new Car(&quot;BMW&quot;, &quot;X9&quot;, false, 0);
          Car car4 = new Car(&quot;Audi&quot;, &quot;A7&quot;, true, 0);
          Car car5 = new Car(&quot;Hyundai&quot;, &quot;Ionic 6&quot;, false, 10000);
      
          carsWantToPark.add(car1);
          carsWantToPark.add(car2);
          carsWantToPark.add(car3);
          carsWantToPark.add(car4);
          carsWantToPark.add(car5);
      
          
          parkingLot.addAll(parkCars(carsWantToPark,Car::hasTicket)); // 매개변수로 메서드 넘겨주는 방법
      
      
          parkingLot.addAll(parkCars(carsWantToPark,Car::noTicketButMoney));
      
          // 익명함수 적용
          // 람다문법
          // 화살표 함수를 사용하고
          // return이 한줄일때는 {}와 return을 생략할수있다
          parkingLot.addAll(parkCars(carsWantToPark, (Car car)-&gt; car.hasParkingTicket() &amp;&amp; car.getParkingMoney() &gt; 1000));
      
          for (Car car : parkingLot) {
              System.out.println(&quot;Parked Car : &quot; + car.getCompany() + &quot;-&quot; + car.getModel());
          }
      
          // 매개변수로 넘겨주는 함수의 데이터타입
          // (함수형)인터페이스로 가능
          // 인터페이스가 타입역할이 가능하기 때문에
          // 함수형 인터페이스 -&gt; 추상메서드를 딱 하나만 가지고 있음
      
      }
      
      // 위의 두 메서드를 하나로 : 내부 주요 로직을 함수로 전달
      public static List&lt;Car&gt; parkCars(List&lt;Car&gt; carsWantToPark, Predicate&lt;Car&gt; function){
          List&lt;Car&gt; cars = new ArrayList&lt;&gt;();
      
          for (Car car : carsWantToPark){
              // 전달된 함수를 사용하여 구현
              if(function.test(car)){
                  cars.add(car);
              }
          }
          return cars;
      }

      }

      class Car {
      private final String company; // 자동차 회사
      private final String model; // 자동차 모델

      private final boolean hasParkingTicket;
      private final int parkingMoney;
      
      public Car(String company, String model, boolean hasParkingTicket, int parkingMoney) {
          this.company = company;
          this.model = model;
          this.hasParkingTicket = hasParkingTicket;
          this.parkingMoney = parkingMoney;
      }
      
      public String getCompany() {
          return company;
      }
      
      public String getModel() {
          return model;
      }
      
      public boolean hasParkingTicket() {
          return hasParkingTicket;
      }
      
      public int getParkingMoney() {
          return parkingMoney;
      }
      
      public static boolean hasTicket(Car car){
          return car.hasParkingTicket;
      }
      
      public static boolean noTicketButMoney(Car car){
          return !car.hasParkingTicket &amp;&amp; car.getParkingMoney() &gt; 1000;
      }

      }

      interface Predicate<T>{
      boolean test(T t);
      }

  • 스트림 사용 예제들
    int [] solution = {4,3,2,1};
    int min = Arrays.stream(solution).min().getAsInt();
    System.out.println(min);
    int[] result = Arrays.stream(solution).filter(list -> list != min).toArray();
    System.out.println(Arrays.toString(result));
    // stream()에 배열을 매개변수로 넣고 min()으로 최소값을 구한다음 .getAsInt()로 int타입으로 변환
    // filter을 통해 최소값이 아닌것들만 배열로 반환
    class Solution {
    public int solution(int[] a, int[] b) {
    return IntStream.range(0, a.length).map(index -> a[index] * b[index]).sum();
    }
    }

    // Int형의 스트림에 0부터 a배열의 길이사이의 숫자들
    // map에서 그 숫자들의 위치의 배열값들을 곱하고 합친다

    String s = "Zbcdefg";
    String[] strArr = s.split("");
    String sortedStr = Arrays.stream(strArr)
    .sorted(Comparator.reverseOrder())
    .collect(Collectors.joining());
    System.out.println("sortedStr = " + sortedStr);
    // 문자열을 내림차순으로 정렬
    // Comparator.reverseOrder로 내림차순 default는 오름차순
    // Collectors.joining()으로 문자열 하나로 조합
    Scanner sc = new Scanner(System.in);
    int a = sc.nextInt();
    int b = sc.nextInt();

        StringBuilder sb = new StringBuilder();
        IntStream.range(0, a).forEach(s -&gt; sb.append(&quot;*&quot;));
        IntStream.range(0, b).forEach(s -&gt; System.out.println(sb.toString()));

    // sb에 range범위만큼의 *을 넣은다음
    // 두번째에서는 뛰어쓰기를 더한다

    import java.util.stream.IntStream;

    class Solution {
    public int solution(int n) {
    return IntStream.range(2, n).filter(i -> n % i == 1).findFirst().orElse(0);
    // findFirst : 조건에 일치하는 요소들중에 stream에서 순서가 가장 앞에 있는 요소 리턴
    // findAny : Stream에서 가장 먼저 탐색되는 요소 리턴
    // 두개다 조건에 일치하는 값이 없으면 empty리턴
    // 조건에 일치하는 하나의 값만 리턴

    		// 차이점
    		// 병렬처리시 findFrist는 순서에 맞게 가장 앞의 값을 리턴
    		// 하지만 findAny는 Multi thread에서 Stream을 처리할때
    		// 가장 먼저 찾은 요소를 리턴하기때문에 상황에 따라 뒤쪽의 element가 리턴될 수도 있다
    
    }

    }

  • Optional
    • null이라는 “개념”이 존재하기 때문에, 거의 모든 상황에 null이 발생 할 수 있음을 경계해야 합니다.
    • 이상적이라면 모두가 메서드 이름을 “findWhateverAndIfNoExistReturnNull()” 처럼 작성하고
    • 해당메서드를 사용하는 모두는 null 체크를 해줘야겠죠
    • 하지만 그런일은 일어나지 않았고, 이것을 가리켜 10억 달러짜리 실수라고 밝혔습니다.

    https://zorba91.tistory.com/339

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

        SomeDBClient myDB = new SomeDBClient();
        
        String userId = myDB.findUserIdByUsername(&quot;HelloWorldMan&quot;);
    
        System.out.println(&quot;HelloWorldMan&#x27;s user Id is : &quot; + userId);
    }

    }

    class SomeDBClient {

    public String findUserIdByUsername(String username) {
        // ... db에서 찾아오는 로직
    			String data = &quot;DB Connection Result&quot;;
    
        if (data != null) {
            return data;
        } else {
            return null;
        } 
    }

    }

    • 개선 1 : 위 코드의 문제점
      1. 논리적으로도, 환경적으로도 null이 반환될 여지가 있음에도, null이 반환될 수 있음을 명시하지 않았습니다.
      1. 메인함수쪽에서, 사용 할 때 null체크를 하지 않아, 만약 null이 반환된다면, nullPointerException이 발생하게 됩니다.
      • 개선 1 : null이 반환될 여지를 명시하고, 그 메서드를 사용하는 사람이 조심하기
        public class NullIsDanger {
        public static void main(String[] args) {

            SomeDBClient myDB = new SomeDBClient();
        
            String userId = myDB.findUserIdByUsernameOrThrowNull(&quot;HelloWorldMan&quot;);
            // 개선 1: null이 반환 될 수 있음을 인지한 메서드 사용자는, null을 대비합니다.
            if (userId != null) {
                System.out.println(&quot;HelloWorldMan&#x27;s user Id is : &quot; + userId);
            }
        }

        }

        class SomeDBClient {
        // 개선 1: 이 메서드는 null이 반환 될 수 있음을 명시합니다.
        public String findUserIdByUsernameOrThrowNull(String username) {
        // ... db에서 찾아오는 로직
        String data = "DB Connection Result";

            if (data != null) {
                return data;
            } else {
                return null;
            }
        }

        }

    • 개선 2 : 개선 1의 문제점
      🤔
      약속은 깨지라고 있는 것이고, 사람은 실수하게 되어 있습니다.
      누군가는 바빠서, 혹은 익숙하지 않아서 null체크를 하지 않는다면 시스템은 위험에 빠집니다.
      • 개선 2 : 객체를 감싸서 반환하기
        // 개선 2: 결과값을 감싼 객체를 만듭니다.
        class SomeObjectForNullableReturn {
        private final String returnValue;
        private final Boolean isSuccess;

        SomeObjectForNullableReturn(String returnValue, Boolean isSuccess) {
            this.returnValue = returnValue;
            this.isSuccess = isSuccess;
        }
        
        public String getReturnValue() {
            return returnValue;
        }
        
        public Boolean isSuccess() {
            return isSuccess;
        }

        }

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

            SomeDBClient myDB = new SomeDBClient();
        
            // 개선 2 : 이제 해당 메서드를 사용하는 유저는, 객체를 리턴받기 때문에 더 자연스럽게 성공여부를 체크하게 됩니다.
            SomeObjectForNullableReturn getData = myDB.findUserIdByUsername(&quot;HelloWorldMan&quot;);
        
            if (getData.isSuccess()) {
                System.out.println(&quot;HelloWorldMan&#x27;s user Id is : &quot; + getData.getReturnValue());
            }
        }

        }

        class SomeDBClient {
        // 개선 2 : 결과값을 감싼 객체를 리턴합니다.
        public SomeObjectForNullableReturn findUserIdByUsername(String username) {
        // ... db에서 찾아오는 로직
        String data = "DB Connection Result";

            if (data != null) {
                return new SomeObjectForNullableReturn(data, true);
            } else {
                return new SomeObjectForNullableReturn(null, false);
            }
        }

        }

        • 이제 해당 메서드는 결과를 감싼 객체를 리턴하고
        • 감싼 객체를 리턴받은 유저는 이 메서드가 위험할 수 있다는 것을 더 쉽게 인지 할 수 있습니다.
        • 개선 3 : 개선 2의 아이디어 발전시키기
          class SomeObjectForNullableReturn<T> {
          private final T returnValue;
          private final Boolean isSuccess;

          SomeObjectForNullableReturn(T returnValue, Boolean isSuccess) {
              this.returnValue = returnValue;
              this.isSuccess = isSuccess;
          }
          
          public T getReturnValue() {
              return returnValue;
          }
          
          public Boolean isSuccess() {
              return isSuccess;
          }

          }

          • “감싸는 객체”를 조금 수정하면, 이제 위험 할 수 있는 모든 메서드에 사용 할 수 있게 됩니다.
          • 지금부터라도 잘하자! Optional<T>
            • Optional 기본 정리
              • Java8에서는 Optional<T> 클래스를 사용해 Null Pointer Exception을 방지할 수 있도록 도와줍니다.
              • Optional<T>는 null이 올 수 있는 값을 감싸는 Wrapper 클래스입니다.
              • Optional이 비어있더라도, 참조해도 Null Pointer Exception가 발생하지 않습니다.
            • Optional 간단 사용법
              • 값이 null 인 Optional 생성하기
                Optional<Car> emptyOptional = Optional.empty();
              • 값이 있는 Optional 생성하기
                Optional<Car> hasDataOptional = Optional.of(new Car());
              • 값이 있을수도 없을수도 있는 Optional 생성하기
                Optional<Car> hasDataOptional = Optional.ofNullable(getCarFromDB());
              • Optional 객체 사용하기 (값 받아오기)
                Optional<String> carName = getCarNameFromDB();
                // orElse() 를 통해 값을 받아옵니다, 파라미터로는 null인 경우 반환할 값을 적습니다.
                String realCarName = carName.orElse("NoCar");

                // 위는 예시코드고 실제는 보통 아래와 같이 사용하겠죠?
                String carName = getCarNameFromDB().orElse("NoCar");

                // orElseGet()이라는 메서드를 사용해서 값을 받아올 수 있습니다.
                // 파라미터로는 없는 경우 실행될 함수를 전달합니다.
                Car car = getCarNameFromDB().orElseGet(Car::new);

                // 값이 없으면, 그 아래 로직을 수행하는데 큰 장애가 되는경우 에러를 발생시킬수도 있습니다.
                Car car = getCarNameFromDB()
                .orElseThrow(() -> new CarNotFoundException("NO CAR!)

  • Optional추가
    📌
    Optional<T>클래스를 사용해 NPE(NullPointException)방지를 할 수 있도록 도와준다

    null이 올 수 있는 값을 감싸는 Wrapper클래스

    참조하더라도 NPE가 발생하지 않도록 도와준다

    아래와 같은 value에 값을 저장하기 때문에 값이 null이더라도 바로 NPE가 발생하지 않으며, 클래스이기 때문에 각종 메소드를 제공

    public final class Optional<T> {
    

    // If non-null, the value; if null, indicates no value is present
    private final T value;

    ...
    }

    • 생성 하기

    Optional.empty() - 값이 Null인 경우

    Optional은 Wrapper 클래스이기 때문에 값이 없을 수도 있는데, 이때는 Optional.empty()로 생성할 수 있다.

    Optional<String> optional = Optional.empty();

    System.out.println(optional);// Optional.empty
    System.out.println(optional.isPresent());// false

    Optional 클래스는 내부에서 static 변수로 EMPTY 객체를 미리 생성해서 가지고 있다. 이러한 이유로 빈 객체를 여러 번 생성해줘야 하는 경우에도 1개의 EMPTY 객체를 공유함으로써 메모리를 절약하고 있다.

    public final class Optional<T> {

    private static final Optional&lt;?&gt; EMPTY = new Optional&lt;&gt;();
    private final T value;
    
    private Optional() {
        this.value = null;
    }
    
    ...

    }

    Optional.of() - 값이 Null이 아닌 경우

    만약 어떤 데이터가 절대 null이 아니라면 Optional.of()로 생성할 수 있다. 만약 Optional.of()로 Null을 저장하려고 하면 NullPointerException이 발생한다.

    // Optional의 value는 절대 null이 아니다.
    Optional<String> optional = Optional.of("MyName");

    Optional.ofNullbale() - 값이 Null일수도, 아닐수도 있는 경우

    만약 어떤 데이터가 null이 올 수도 있고 아닐 수도 있는 경우에는 Optional.ofNullbale로 생성할 수 있다. 그리고 이후에 orElse 또는 orElseGet 메소드를 이용해서 값이 없는 경우라도 안전하게 값을 가져올 수 있다.

    // Optional의 value는 값이 있을 수도 있고 null 일 수도 있다.
    Optional<String> optional = Optional.ofNullable(getName());
    String name = optional.orElse("anonymous");// 값이 없다면 "anonymous" 를 리턴

    [ Optional 사용법 예시 코드 ]

    Optional 사용법 예시 (1)

    기존에는 아래와 같이 null 검사를 한 후에 null일 경우에는 새로운 객체를 생성해주어야 했다. 이러한 과정을 코드로 나타내면 다소 번잡해보이는데, Optional<T>와 Lambda를 이용하면 해당 과정을 보다 간단하게 표현할 수 있다.

    // Java8 이전
    List<String> names = getNames();
    List<String> tempNames = list != null
    ? list
    : new ArrayList<>();

    // Java8 이후
    List<String> nameList = Optional.ofNullable(getNames())
    .orElseGet(() -> new ArrayList<>());

    Optional 사용법 예시 (2)

    예를 들어 아래와 같은 우편번호를 꺼내는 null 검사 코드가 있다고 하자. 이 코드는 null 검사 때문에 상당히 복잡하다.

    public String findPostCode() {
    UserVO userVO = getUser();
    if (userVO != null) {
    Address address = user.getAddress();
    if (address != null) {
    String postCode = address.getPostCode();
    if (postCode != null) {
    return postCode;
    }
    }
    }
    return "우편번호 없음";
    }

    하지만 Optional을 사용하면 이러한 코드를 아래와 같이 표현할 수 있다.

    public String findPostCode() {
    // 위의 코드를 Optional로 펼쳐놓으면 아래와 같다.
    Optional<UserVO> userVO = Optional.ofNullable(getUser());
    Optional<Address> address = userVO.map(UserVO::getAddress);
    Optional<String> postCode = address.map(Address::getPostCode);
    String result = postCode.orElse("우편번호 없음");

    // 그리고 위의 코드를 다음과 같이 축약해서 쓸 수 있다.
    String result = user.map(UserVO::getAddress)
    .map(Address::getPostCode)
    .orElse("우편번호 없음");
    }

    Optional 사용법 예시 (3)

    예를 들어 아래와 같이 이름을 대문자로 변경하는 코드에서 NPE 처리를 해준다고 하자.

    String name = getName();
    String result = "";

    try {
    result = name.toUpperCase();
    } catch (NullPointerException e) {
    throw new CustomUpperCaseException();
    }

    위의 코드는 다소 번잡하고 가독성이 떨어지는데 이를 Optional를 활용하면 아래와 같이 표현할 수 있다.

    Optional<String> nameOpt = Optional.ofNullable(getName());
    String result = nameOpt.orElseThrow(CustomUpperCaseExcpetion::new)
    .toUpperCase();

    [ Optional 정리 ]

    Optional은 null 또는 값을 감싸서 NPE(NullPointerException)로부터 부담을 줄이기 위해 등장한 Wrapper 클래스이다. Optional은 값을 Wrapping하고 다시 풀고,
    null 일 경우에는 대체하는 함수를 호출하는 등의 오버헤드가 있으므로 잘못 사용하면 시스템 성능이 저하된다.
    그렇기 때문에 메소드의 반환 값이 절대 null이 아니라면 Optional을 사용하지 않는 것이 좋다.
    즉, Optional은 메소드의 결과가 null이 될 수 있으며, null에 의해 오류가 발생할 가능성이 매우 높을 때 반환값으로만 사용되어야 한다.
    또한 Optional은 파라미터로 넘어가는 등이 아니라 반환 타입으로써 제한적으로 사용되도록 설계되었는데, 이것은 이어지는 포스팅에서 살펴보도록 하자.

    3. Optional의 orElse와 orElseGet 차이 및 예시 코드


    [ Optional의 orElse와 orElseGet 차이 및 예시 코드 ]

    orElse와 orElseGet의 차이

    Optional API의 단말 연산에는 orElse와 orElseGet 함수가 있다. 비슷해 보이는 두 함수는 엄청난 차이가 있다.

    • orElse: 파라미터로 값을 받는다.
    • orElseGet: 파라미터로 함수형 인터페이스(함수)를 받는다.

    실제로 Optional 코드를 보면 다음과 orElse와 orElseGet이 각각 구현되어 있음을 확인할 수 있다.

    public final class Optional<T> {

    ...// 생략public T orElse(T other) {
        return value != null ? value : other;
    }
    
    public T orElseGet(Supplier&lt;? extends T&gt; other) {
        return value != null ? value : other.get();
    }

    }

    orElse로는 값이, orElseGet로는 함수가 넘어간다는 것은 상당히 큰 차이가 있다. 이로 인해 호출 결과가 달라질 수 있기 때문인데, 관련된 내용을 코드로 살펴보도록 하자.

    orElse와 orElseGet의 차이 예시 코드

    예를 들어 다음과 같은 예시 코드가 있다고 하자. 첫 번째 함수는 값이 비어있을 때 orElse를 호출하도록 되어있고, 두 번째 함수는 orElseGet을 호출하도록 되어있다. 바로 아래로 내려가지 않고, 각각에 의한 출력 결과를 예상해보도록 하자.

    public void findUserEmailOrElse() {
    String userEmail = "Empty";
    String result = Optional.ofNullable(userEmail)
    .orElse(getUserEmail());

    System.out.println(result);

    }

    public void findUserEmailOrElseGet() {
    String userEmail = "Empty";
    String result = Optional.ofNullable(userEmail)
    .orElseGet(this::getUserEmail);

    System.out.println(result);

    }

    private String getUserEmail() {
    System.out.println("getUserEmail() Called");
    return "mangkyu@tistory.com";
    }

    위의 함수를 각각 실행해보면 다음과 같은데, 이러한 결과가 발생한 이유를 자세히 살펴보도록 하자.

    // 1. orElse인 경우
    getUserEmail() Called
    Empty

    // 2. orElseGet인 경우
    Empty

    먼저 OrElse인 경우에는 다음과 같은 순서로 처리가 된다.

    1. Optional.ofNullable로 "EMPTY"를 갖는 Optional 객체 생성
    1. getUserEmail()가 실행되어 반환값을 orElse 파라미터로 전달
    1. orElse가 호출됨, "EMPTY"가 Null이 아니므로 "EMPTY"를 그대로 가짐

    위와 같이 동작하는 이유는 Optional.orElse()가 값을 파라미터로 받고, orElse 파라미터로 값을 넘겨주기 위해 getUserEmail()이 호출되었기 때문이다. 하지만 함수형 인터페이스(함수)를 파라미터로 받는 orElseGet에서는 동작이 달라진다.

    1. Optional.ofNullable로 "EMPTY"를 갖는 Optional 객체 생성
    1. getUserEmail() 함수 자체를 orElseGet 파라미터로 전달
    1. orElseGet이 호출됨, "EMPTY"가 Null이 아니므로 "EMPTY"를 그대로 가지며 getUserEmail()이 호출되지 않음

    orElseGet에서는 파라미터로 넘어간 값인 getUserEmail 함수가 Null이 아니므로 .get에 의해 함수가 호출되지 않는다. 만약 Optional의 값으로 null이 있다면, 다음과 같은 흐름에 의해 orElseGet의 파라미터로 넘어온 getUserEmail()이 실행될 것이다.

    public void findUserEmailOrElseGet() {
    String result = Optional.ofNullable(null)
    .orElseGet(this::getUserEmail);

    System.out.println(result);

    }

    private String getUserEmail() {
    System.out.println("getUserEmail() Called");
    return "mangkyu@tistory.com";
    }

    1. Optional.ofNullable로 null를 갖는 Optional 객체 생성
    1. getUserEmail() 자체를 orElseGet 파라미터로 전달
    1. orElseGet이 호출됨, 값이 Null이므로 other.get()이 호출되어 getUserEmail()가 호출됨

    orElse에 의한 발생가능한 장애 예시

    위에서 살펴보았듯 orElse와 orElseGet은 명확하고 중요한 차이가 있는데, 이를 정확히 인식하지 못하면 장애가 발생할 수 있다. 예를 들어 userEmail을 Unique한 값으로 갖는 시스템에서 아래와 같은 코드를 작성하였다고 하자.

    public void findByUserEmail(String userEmail) {
    // orElse에 의해 userEmail이 이미 존재해도 유저 생성 함수가 호출되어 에러 발생return userRepository.findByUserEmail(userEmail)
    .orElse(createUserWithEmail(userEmail));
    }

    private String createUserWithEmail(String userEmail) {
    User newUser = new User(userEmail);
    return userRepository.save(newUser);
    }

    위의 예제는 Optional의 단말 연산으로 orElse를 사용하고 있기 때문에, 조회 결과와 무관하게 createUserWithEmail 함수가 반드시 실행된다. 하지만 Database에서는 userEmail이 Unique로 설정되어 있기 때문에 오류가 발생할 것이다. 그렇기 때문에 위와 같은 경우에는 다음과 같이 해당 코드를 orElseGet으로 수정해야 한다. 이렇게 코드를 수정하였다면 파라미터로 createUserWithEmail 함수 자체가 넘어가므로, 조회 결과가 없을 경우에만 사용자를 생성하는 로직이 호출 될 것이다.

    public void findByUserEmail(String userEmail) {
    // orElseGet에 의해 파라미터로 함수를 넘겨주므로 Null이 아니면 유저 생성 함수가 호출되지 않음return userRepository.findByUserEmail(userEmail)
    .orElseGet(this::createUserWithEmail(userEmail));
    }

    private String createUserWithEmail(String userEmail) {
    User newUser = new User(userEmail);
    return userRepository.save(newUser);
    }

    실제 서비스에서 위와 같은 오류를 범한다면 큰 시스템 장애로 돌아오게 된다. 설령 문제가 없다고 하더라도 orElse는 값을 생성하여 orElseGet보다 비용이 크므로 최대한 사용을 피해야 한다. 그러므로 orElse와 orElseGet의 차이점을 정확히 이해하고 사용하도록 하자.

    orElse와 orElseGet의 차이점 및 사용법 정리

    • orElse
      • 파라미터로 값을 필요로한다.
      • 값이 미리 존재하는 경우에 사용한다.
    • orElseGet
      • 파라미터로 함수(함수형 인터페이스)를 필요로 한다.
      • 값이 미리 존재하지 않는 거의 대부분의 경우에 orElseGet을 사용하면 된다.

    Optional은 상당히 유연하고 Null로부터 안전을 보장받을 수 있어서 좋아 보입니다. 하지만 잘못된 Optional의 사용은 커다란 비용을 요구하며 많은 단점들을 야기할 수 있습니다. Java 언어의 아키텍트(설계자)인 Brian Goetz는 Optional에 대해 다음과 같이 정의하였습니다.

    Optional is intended to provide a limited mechanism for library method return types where there needed to be a clear way to represent “no result," and using null for such was overwhelmingly likely to cause errors.- Brian Goetz(Java Architect) -

    즉, 위의 내용을 매우 간추리면 Optional은 반환 타입으로 사용되도록 매우 제한적인 경우로 설계되었다는 것인데, 다음 포스팅에서는 언제 Optional을 사용해야 하는지, 어떻게 사용해야 하는지에 대해 알아보도록 하겠습니다.

  • Optional추가2

    1. 언제 Optional을 사용해야 하는가?


    [ Optional이 만들어진 이유와 의도 ]

    Java8부터 Null이나 Null이 아닌 값을 저장하는 컨테이너 클래스인 Optional이 추가되었다. Java 언어의 아키텍트(설계자)인 Brian Goetz는 Optional에 대해 다음과 같이 정의하였다.

    Optional is intended to provide a limited mechanism for library method return types where there needed to be a clear way to represent “no result," and using null for such was overwhelmingly likely to cause errors.- Brian Goetz(Java Architect) -

    위의 내용을 정리하면 Optional은 null을 반환하면 오류가 발생할 가능성이 매우 높은 경우에 '결과 없음'을 명확하게 드러내기 위해 메소드의 반환 타입으로 사용되도록 매우 제한적인 경우로 설계되었다는 것이다. 이러한 설계 목적에 부합하게 실제로 Optional을 잘못 사용하면 많은 부작용(Side Effect)이 발생하게 되는데, 어떠한 문제가 발생할 수 있는지 자세히 살펴보도록 하자.

    [ Optional이 위험한 이유, Optional을 올바르게 사용해야 하는 이유 ]

    Optional을 사용하면 코드가 Null-Safe해지고, 가독성이 좋아지며 애플리케이션이 안정적이 된다는 등과 같은 얘기들을 많이 접할 수 있다. 하지만 이는 Optional을 목적에 맞게 올바르게 사용했을 때의 이야기이고, Optional을 남발하는 코드는 오히려 다음과 같은 부작용(Side-Effect)를 유발할 수 있다.

    • NullPointerException 대신 NoSuchElementException가 발생함
    • 이전에는 없었던 새로운 문제들이 발생함
    • 코드의 가독성을 떨어뜨림
    • 시간적, 공간적 비용(또는 오버헤드)이 증가함

    NullPointerException 대신 NoSuchElementException가 발생함

    만약 Optional로 받은 변수를 값이 있는지 판단하지 않고 접근하려고 한다면 NoSuchElementException가 발생하게 된다.

    Optional<User> optionalUser = ... ;

    // optional이 갖는 value가 없으면 NoSuchElementException 발생
    User user = optionalUser.get();

    Null-Safe하기 위해 Optional을 사용하였는데, 값의 존재 여부를 판단하지 않고 접근한다면 NullPointerException는 피해도 NoSuchElementException가 발생할 수 있다.

    이전에는 없었던 새로운 문제들이 발생함

    Optional을 사용하면 문제가 되는 대표적인 경우가 직렬화(Serialize)를 할 때이다. 기본적으로 Optional은 직렬화를 지원하지 않아서 캐시나 메세지큐 등과 연동할 때 문제가 발생할 수 있다. Optional을 사용함으로써 오히려 새로운 문제가 발생할 수 있는 것이다.

    public class User implements Serializable {

    private Optional&lt;String&gt; name;

    }

    물론 Jackson처럼 라이브러리에서 Optional이 있을 경우 값을 wrap, unwrap 하도록 지원해주기도 하지만 해당 라이브러리의 스펙을 파악해야한다는 점 등에서 오히려 불편함을 느낄 수 있다.

    참고로 Jackson 라이브러리는 jackson-modules-java8 project 프로젝트부터 Optional을 지원하여, empty일 경우에는 null을 값이 있는 경우에는 값을 꺼내서 처리하도록 지원하고 있다. 또한 Cacheable, CacheEvict, CachePut과 같은 Spring의 캐시 추상화 기술들은 Spring4부터 이러한 wrap, unwrap을 지원하고 있다.

    코드의 가독성을 떨어뜨림

    예를 들어 다음과 같이 Optional을 받아서 값을 꺼낸다고 하자. 우리는 앞에서 살펴본대로 optionalUser의 값이 비어있으면 NoSuchElementException가 발생할 수 있으므로 다음과 같이 값의 유무를 검사하고 꺼내도록 코드를 작성하였다.

    public void temp(Optional<User> optionalUser) {
    User user = optionalUser.orElseThrow(IllegalStateException::new);

    // 이후의 후처리 작업 진행...
    }

    그런데 위와 같은 코드는 또 다시 NPE를 유발할 수 있다. 왜냐하면 optionalUser 객체 자체가 null일 수 있기 때문이다. 그러면 우리는 이러한 문제를 해결하기 위해 다음과 같이 코드를 수정해야 한다.

    public void temp(Optional<User> optionalUser) {
    if (optionalUser != null && optionalUser.isPresent()) {
    // 이후의 후처리 작업 진행...
    }

    throw new IllegalStateException();

    }

    이러한 코드는 오히려 값의 유무를 2번 검사하게 하여 단순히 null을 사용했을 때보다 코드가 복잡해졌다. 그 외에도 Optional의 제네릭 타입 때문에도 불필요하게 코드 글자수까지 늘어났다. 이렇듯 Optional을 올바르게 사용하지 못하고 남용하면 오히려 가독성이 떨어질 수 있다.

    시간적, 공간적 비용(또는 오버헤드)이 증가함

    • 공간적 비용: Optional은 객체를 감싸는 컨테이너이므로 Optional 객체 자체를 저장하기 위한 메모리가 추가로 필요하다.
    • 시간적 비용: Optional 안에 있는 객체를 얻기 위해서는 Optional 객체를 통해 접근해야 하므로 접근 비용이 증가한다.

    어떤 글에서는 Optional을 사용하면 단순 값을 사용했을 때보다 메모리 사용량이 4배 정도 증가한다고 한다. 그 외에도 Optional은 Serializable 하지 않는 등의 문제가 있으므로 이를 해결하기 위해 추가적인 개발 시간이 소요될 수 있다.

    하지만 위에서 설명한 예시들의 대부분은 Optional을 올바르게 사용하지 않았기 때문에 발생하는 것이다. Optional은 만들어진 의도가 상당히 명확한 만큼 목적에 맞게 올바르게 사용되어야 한다. 아래에서는 올바른 Optional 사용을 위한 가이드를 소개한다.

    2. 올바른 Optional 사용법 가이드


    [ 올바른 Optional 사용법 가이드 ]

    • Optional 변수에 Null을 할당하지 말아라
    • 값이 없을 때 Optional.orElseX()로 기본 값을 반환하라
    • 단순히 값을 얻으려는 목적으로만 Optional을 사용하지 마라
    • 생성자, 수정자, 메소드 파라미터 등으로 Optional을 넘기지 마라
    • Collection의 경우 Optional이 아닌 빈 Collection을 사용하라
    • 반환 타입으로만 사용하라

    Optional 변수에 Null을 할당하지 말아라

    Optional은 컨테이너/박싱 클래스일 뿐이며, Optional 변수에 null을 할당하는 것은 Optional 변수 자체가 null인지 또 검사해야 하는 문제를 야기한다. 그러므로 값이 없는 경우라면 Optional.empty()로 초기화하도록 하자.

    // AVOIDpublic Optional<Cart> fetchCart() {

    Optional&lt;Cart&gt; emptyCart = null;
    ...

    }

    값이 없을 때 Optional.orElseX()로 기본 값을 반환하라

    Optional의 장점 중 하나는 함수형 인터페이스를 통해 가독성좋고 유연한 코드를 작성할 수 있다는 것이다. 가급적이면 isPresent()로 검사하고 get()으로 값을 꺼내기 보다는 orElseGet 등을 활용해 처리하도록 하자.

    private String findDefaultName() {
    return ...;
    }

    // AVOIDpublic String findUserName(long id) {

    Optional&lt;String&gt; optionalName = ... ;
    
    if (optionalName.isPresent()) {
        return optionalName.get();
    } else {
        return findDefaultName();
    }

    }

    // PREFERpublic String findUserName(long id) {

    Optional&lt;String&gt; optionalName = ... ;
    return optionalName.orElseGet(this::findDefaultName);

    }

    orElseGet은 값이 준비되어 있지 않은 경우, orElse는 값이 준비되어 있는 경우에 사용하면 된다. 만약 null을 반환해야 하는 경우라면 orElse(null)을 활용하도록 하자. 만약 값이 없어서 throw해야하는 경우라면 orElseThrow를 사용하면 되고 그 외에도 다양한 메소드들이 있으니 적당히 활용하면 된다.

    추가적으로 Java9 부터는 ifPresentOrElse도 지원하고 있으며, Java 10부터는 orElseThrow()의 기본으로 NoSuchElementException()를 던질 수 있다. 만약 Java8이나 9를 사용중이라면 명시적으로 넘겨주면 된다.

    단순히 값을 얻으려는 목적으로만 Optional을 사용하지 마라

    단순히 값을 얻으려고 Optional을 사용하는 것은 Optional을 남용하는 대표적인 경우이다. 이러한 경우에는 굳이 Optional을 사용해 비용을 낭비하는 것 보다는 직접 값을 다루는 것이 좋다.

    // AVOIDpublic String findUserName(long id) {
    String name = ... ;

    return Optional.ofNullable(name).orElse(&quot;Default&quot;);

    }

    // PREFERpublic String findUserName(long id) {
    String name = ... ;

    return name == null
      ? &quot;Default&quot;
      : name;

    }

    생성자, 수정자, 메소드 파라미터 등으로 Optional을 넘기지 마라

    Optional을 파라미터로 넘기는 것은 상당히 의미없는 행동이다. 왜냐하면 넘겨온 파라미터를 위해 자체 null체크도 추가로 해주어야 하고, 코드도 복잡해지는 등 상당히 번거로워지기 때문이다. Optional은 반환 타입으로 대체 동작을 사용하기 위해 고안된 것임을 명심해야 하며, 앞서 설명한대로 Serializable을 구현하지 않으므로 필드 값으로 사용하지 않아야 한다.

    // AVOIDpublic class User {

    private final String name;
    private final Optional&lt;String&gt; postcode;
    
    public Customer(String name, Optional&lt;String&gt; postcode) {
        this.name = Objects.requireNonNull(name, () -&gt; &quot;Cannot be null&quot;);
        this.postcode = postcode;
    }
    
    public Optional&lt;String&gt; getName() {
        return Optional.ofNullable(name);
    }
    
    public Optional&lt;String&gt; getPostcode() {
        return postcode;
    }

    }

    Optional을 접근자에 적용하는 경우도 마찬가지이다. 위의 예시에서 name을 얻기 위해 Optional.ofNullable()로 반환하고 있는데, Brian Goetz는 Getter에 Optional을 얹어 반환하는 것을 두고 남용하는 것이라고 얘기하였다.

    I think routinely using it as a return value for getters would definitely be over-use.- Brian Goetz(Java Architect) -

    Collection의 경우 Optional이 아닌 빈 Collection을 사용하라

    Collection의 경우 굳이 Optional로 감쌀 필요가 없다. 오히려 빈 Collection을 사용하는 것이 깔끔하고, 처리가 가볍다.

    // AVOIDpublic Optional<List<User>> getUserList() {
    List<User> userList = ...;// null이 올 수 있음return Optional.ofNullable(items);
    }

    // PREFERpublic List<User> getUserList() {
    List<User> userList = ...;// null이 올 수 있음return items == null
    ? Collections.emptyList()
    : userList;
    }

    아래의 경우도 사용을 피해야 하는 케이스이다. Optional은 비싸기 때문에 최대한 사용을 지양해야 한다. 아래의 케이스라면 map에 getOrDefault 메소드가 있으니 이걸 활용하는 것이 훨씬 좋다.

    // AVOIDpublic Map<String, Optional<String>> getUserNameMap() {
    Map<String, Optional<String>> items = new HashMap<>();
    items.put("I1", Optional.ofNullable(...));
    items.put("I2", Optional.ofNullable(...));

    Optional&lt;String&gt; item = items.get(&quot;I1&quot;);
    
    if (item == null) {
        return &quot;Default Name&quot;
    } else {
        return item.orElse(&quot;Default Name&quot;);
    }

    }

    // PREFERpublic Map<String, String> getUserNameMap() {
    Map<String, String> items = new HashMap<>();
    items.put("I1", ...);
    items.put("I2", ...);

    return items.getOrDefault(&quot;I1&quot;, &quot;Default Name&quot;);

    }

    반환 타입으로만 사용하라

    Optional은 반환 타입으로써 에러가 발생할 수 있는 경우에 결과 없음을 명확히 드러내기 위해 만들어졌으며, Stream API와 결합되어 유연한 체이닝 api를 만들기 위해 탄생한 것이다. 예를 들어 Stream API의 findFirst()나 findAny()로 값을 찾는 경우에 어떤 것을 반환하는게 합리적일지 Java 언어를 설계하는 사람이 되어 고민해보자. 언어를 만드는 사람의 입장에서는 Null을 반환하는 것보다 값의 유무를 나타내는 객체를 반환하는 것이 합리적일 것이다. Java 언어 설계자들은 이러한 고민 끝에 Optional을 만든 것이다.

    그러므로 Optional이 설계된 목적에 맞게 반환 타입으로만 사용되어야 한다.

    Optional을 잘못 사용하는 것은 비용은 증가시키는 반면에 코드 품질은 오히려 악화시킨다. 그러므로 위에서 정리한 규칙을 준수하며 올바른 방식으로 Optional을 사용하도록 하자.

  • Arrays클래스
    • Arrays클래스는 항목정렬,항목검색,항목비교와같은 메서드 제공
    • 모든 메서드는 static메서드로,Arrays클래스로 바로 사용가능

    배열 복사

    배열 복사를 위해 단순하게

    System.arraycopy(int[] src, int srcPos, int[] dest, int destPos, int length);// (원본배열, 원본시작인덱스, 타겟배열, 타겟시작인덱스, 복사개수)

    메소드를 이용할 수 있다

    이 이외에도 Arrays 메소드를 사용하여 다음과 같이 복사할 수 있다

    char[] arr2 = Arrays.copyOf(arr1, arr1.length);// arr1 배열 arr1.length 만큼을 arr2로 복사char[] arr3 = Arrays.copyOfRange(arr1, 1, 3);// arr1[1] ~ arr1[2] 를 arr3[0] ~ arr3[1] 로 복사// 1~3에서 끝 인덱스는 포함되지 않는다

    배열 항목 정렬

    Arrays.sort(arr);

    기본 타입 또는 String 배열은

    위와 같이 arr 배열을 사전순(오름차순)으로 정렬할 수 있다

    만약 사용자 정의 클래스 타입이라면, Comparable 인터페이스를 구현하고 있어야 한다 (implements)

    다음과 같이 Member  클래스를 작성해보자

    // Member 타입만 비교하기 위해 제네릭 <> 사용class Member implements Comparable<Member> {    String name;    Member(String name)        this.name = name;    @Override    int compareTo(Memeber m)        return name.compareTo(m.name);// 내림차순일 때는 *(-1) 를 하여 -1, 0, 1를 구분한다}public class Example {    public static void main(String[] args) {        Member m1 = new Member("aa");        Member m2 = new Member("bb");        Member m3 = new Member("cc");        Member[] members = {m1, m2, m3};        Arrays.sort(members);    }}

    배열 항목 검색

    Arrays.sort() 메소드로 정렬을 한 후에,

    Arrays.binarySearch() 메소드로 원하는 항목의 인덱스 값을 찾을 수 있다

    ( 정렬을 하지 않은 후에 binarySearch() 메소드를 사용하거나, 없는 항목을 찾으면 음수값이 리턴된다

    나중에 기회가 되면 리턴되는 음수값을 좀 더 알아보자 )

    // Member[] members = {"aa", "bb", "cc"}; 일 때Arrays.sort(members);int idx = Arrays.binarySearch(members, "bb");// idx == 1

    배열 동일 항목 채우기

    fill() 메소드로 배열에 동일한 값을 저장할 수 있다

    void fill(배열, 값)void fill(배열, 시작인덱스, 끝인덱스, 값)

  • StringBuilder

    StringBuilder (java.lang.StringBuilder)

    • 생성자
      • StringBuilder sb = new StringBuilder(): 객체 선언
      • StringBuilder sb = new StringBuilder("aaa"): 문자열을 바로 넣을 수도 있다.
    • 주요 메소드
      • .append(): 문자열을 추가한다. (sb.append("bbb"), sb.append(4))
      • .insert(int offset, String str): offset 위치에 str을 추가한다. (sb.insert(2, "ccc"))
      • .replace(): 첫번째와 두번째 파라미터로 받는 숫자 인덱스에 위치한 문자열을 대체한다. (.replace(3, 6, "ye"))
      • .substring(int start, (int end)): 인덱싱. 파라미터가 하나라면 해당 인덱스부터 끝까지, 두개라면 시작점과 끝점-1 까지 인덱싱 (sb.substring(5), sb.substring(3, 7))
      • .deleteCharAt(int index): 인덱스에 위치한 문자 하나를 삭제한다. (sb.deleteCharAt(3))
      • .delete(int start, int end): start 부터 end-1 까지의 문자를 삭제한다. (sb.delete(3, sb.length()))
      • .toString(): String으로 변환한다. (sb.toString())
      • .reverse(): 해당 문자 전체를 뒤집는다. (sb.reverse())
      • .setCharAt(int index, String s): index 위치의 문자를 s로 변경
      • .setLength(int len): 문자열 길이 조정, 현재 문자열보다 길게 조정하면 공백으로 채워짐, 현재 문자열보다 짧게 조정하면 나머지 문자는 삭제
      • .trimToSize(): 문자열이 저장된 char[] 배열 사이즈를 현재 문자열 길이와 동일하게 조정, String 클래스의 trim()이 앞 뒤 공백을 제거하는 것과 같이 공백 사이즈를 제공하는 것, 배열의 남는 사이즈는 공백이므로, 문자열 뒷부분의 공백을 모두 제거해준다고 보면 됨
    import java.lang.StringBuilder;

    publicclasssb {
    publicstaticvoidmain(String[] args)throws IOException{
    StringBuilder sb =new StringBuilder("aaa");

        // 문자열 추가
        System.out.println(sb.append(&quot;bbb&quot;)); // aaabbb
        System.out.println(sb.append(4)); // aaabbb4
    
        // 문자열 삽입
        System.out.println(sb.insert(2, &quot;ccc&&quot;)); // aacccabbb4
    
        // 문자열 치환, 문자열 교체
        System.out.println(sb.replace(3, 6, &quot;ye&quot;)); // aacyebbb4
    
        // 인덱싱, 문자열 자르기
        System.out.println(sb.substring(5)); // bbb4
        System.out.println(sb.substring(3, 7)); // yebb
    
        // 문자 삭제
        System.out.println(sb.deleteCharAt(3)); // aacebbb4
    
        // 문자열 삭제
        System.out.println(sb.delete(3, sb.length())); // aac
    
        // 문자열 변환
        System.out.println(sb.toString()); // aac
    
        // 문자열 뒤집기
        System.out.println(sb.reverse()); // caa
    
        // 문자 대체, 문자 교체, 문자 치환
        sb.setCharAt(1, &#x27;b&#x27;);
        System.out.println(sb); // cba
    
        // 문자열 길이 조정
        sb.setLength(2);
        System.out.println(sb); // cb
    }

    }

  • repeat메서드
    // String 메서드로 문자열을 파라미터의 주어진 횟수만큼 반복
    // 파라미터값을 0으로 지정하면 빈문자열 반환
    // 음수로 지정하면 IllegalArgumentExceptionthrow 에러를 반환
    // 1로 지정하면 문자열 그래도 반환
    // 내부적으로 Arrays.fill()및 System.arraycopy()메서드 호출하여 새로운 문자열 생성

    // 문자열.repeat(몇번반복할지의 수)

  • toString 과 String.valueOf()차이점
    String.valueOf() - 파라미터가 null이면 문자열 "null"을 만들어서 반환한다.
    toString() - 대상 값이 null이면 NPE를 발생시키고 Object에 담긴 값이 String이 아니여도 출력한다.

    valueOf의 null체크 방법은 "null".equals(string) 형태로 체크를 해야한다.

  • Comparator Comparable
    // 두개다 인터페이스로 객체를 비교할 수 있돌고 만들게하고, 사용할려면 인터페이스나 선언된 메서드를 반드시 구현해야한다
    // 객체는 정렬 기준이 없다

    public interface Comparator<T> {
    int compare(T o1, T o2);
    // 첫번째 값이 크면 양수, 두번째 값이 크면 음수, 같으면 0 반환
    }

    public interface Comparable<T> {
    public int compareTo(T o);
    // 반환값은 int이지만 두 객체가 같으면 0, 비교하는 값보다 크면 양수 작으면 음수 반환
    }

    // Comparable: 기본 정렬기준을 구현하는데 사용
    // Comparator: 기본 정렬 기준 외에 다른 기준으로 정렬하고자 할 때 사용

    Comparable

    • 컬렉션의 정렬 조건을 정렬 하고자 하는 객체 내부에 정의 할 수 있게 해준다
    package t1;

    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.Collections;
    import java.util.List;

    public class Test {
    public static void main(String[] args) {
    List<Student1> list = Arrays.asList(
    new Student1("와라비노",80,70),
    new Student1("강다온",45,100),
    new Student1("김재우",90,90)
    );

            Collections.sort(list);
            // 아래에 정렬기준을 만들지 않고 그냥 sort하면
            // 정렬기준이 없어서 에러가 발생한다
    
            for(Student1 s : list){
                System.out.println(s.getName() + s.getMath());
            }
        }
    }
    
    class Student1 implements Comparable&lt;Student1&gt;{
        private int english;
        private int math;
        private String name;
        public Student1(String name, int english, int math){
            this.name = name;
            this.english = english;
            this.math = math;
        }
    
        public int getEnglish(){
            return english;
        }
    
        public void setEnglish(int english){
            this.english = english;
        }
    
        public int getMath(){
            return math;
        }
        public void setMath(int math){
            this.math = math;
        }
    
        public String getName(){
            return name;
        }
        public void setName(){
            this.name = name;
        }
        @Override
        public int compareTo(Student1 o) {

    // System.out.println("일단 this.math : " + this.math);
    // System.out.println("그리고 받아온 매개변수 수학점수 : "+o.getMath());
    // 윗부분의 Collections.sort()메서드의 기준점 만들기 위한 부분
    return this.math - o.getMath();
    }
    }

    Compartor

    • 두 매개변수 객체를 비교
    • 자기자신과 매개변수 객체를 비교한다
    package t1;

    import java.util.*;

    public class Test {
    public static void main(String[] args) {
    List<Student1> list = Arrays.asList(
    new Student1("와라비노",80,70),
    new Student1("강다온",45,100),
    new Student1("김재우",90,90)
    );
    Score sc = new Score();

            Collections.sort(list,sc);
            // 1. 비교할 값들이 들어있는 Collection list
            // 2. 해당값들을 비교해줄 기준으로 implements Comparator한 클래스
    
            for(Student1 s : list){
                System.out.println(s.getName() + s.getMath());
            }
        }
    }
    
    class Student1{
        private int english;
        private int math;
        private String name;
        public Student1(String name, int english, int math){
            this.name = name;
            this.english = english;
            this.math = math;
        }
    
        public int getEnglish(){
            return english;
        }
    
        public void setEnglish(int english){
            this.english = english;
        }
    
        public int getMath(){
            return math;
        }
        public void setMath(int math){
            this.math = math;
        }
    
        public String getName(){
            return name;
        }
        public void setName(){
            this.name = name;
        }
    }
    
    class Score implements Comparator&lt;Student1&gt;{
    @Override
        public int compare(Student1 s1, Student1 s2){
        return s1.getEnglish() - s2.getEnglish();
    }
    }</code></pre><pre id="17dcd42e-2048-49ea-ab92-e3d75912437e" class="code"><code>package t1;

    import java.util.*;

    public class Test {
    public static void main(String[] args) {
    List<Student1> list = Arrays.asList(
    new Student1("와라비노",80,70),
    new Student1("강다온",45,100),
    new Student1("김재우",90,90)
    );

            Collections.sort(list, new Comparator&lt;Student1&gt;() {
                @Override
                public int compare(Student1 o1, Student1 o2) {
                    return o1.getEnglish() - o2.getEnglish();
                }
            });
            // 1. 비교할 값들이 들어있는 Collection list
            // 2. 해당값들을 비교해줄 기준으로 implements Comparator한 클래스
    
            for(Student1 s : list){
                System.out.println(s.getName() + s.getMath());
            }
        }
    }
    
    class Student1{
        private int english;
        private int math;
        private String name;
        public Student1(String name, int english, int math){
            this.name = name;
            this.english = english;
            this.math = math;
        }
    
        public int getEnglish(){
            return english;
        }
    
        public void setEnglish(int english){
            this.english = english;
        }
    
        public int getMath(){
            return math;
        }
        public void setMath(int math){
            this.math = math;
        }
    
        public String getName(){
            return name;
        }
        public void setName(){
            this.name = name;
        }
    }</code></pre><ul id="5a7ca597-de1e-4499-9113-5186eb55385a" class="bulleted-list"><li style="list-style-type:disc">Comparable은 자기 자신과 파라미터로 들어오는 객체를 비교하는 것</li></ul><ul id="91917b49-0140-4c76-aafd-201be0658586" class="bulleted-list"><li style="list-style-type:disc">Comparator은 자기 자신의 상태가 어떻든 상관없이 파라미터로 들어오는 두 객체를 비교하는 것</li></ul></details></li></ul><ul id="0f871926-3cad-4564-9ed4-9a7341fcd18a" class="toggle"><li><details open=""><summary>익명 클래스(Anonymous Class)</summary><pre id="d0255d8c-81c0-41f3-8c0f-57ee2bb3edc9" class="code"><code>// Java에서 다른 클래스 내에서 클래스를 정의할 수 있으며, 정의된 클래스를 중첩 클래스라고 한다.

    // 중첩 클래스는 이름을 설정하지않고 생성할 수 있으며, 이름이 없는 중첩 클래스를 익명 클래스라고 한다
    // 익명 클래스는 다른 클래스 내부에 정의되므로 익명 내부 클래스라고 말하기도 한다

    // 일반적으로 interface를 사용할때
    // 구현해서 사용함

    interface Test1{
    public void t();
    }
    public class Test implements Test1 {
    @Override
    public void t(){
    System.out.println("인터페이스 테스트");
    }
    public static void main(String[] args) {
    Test1 tt = new Test();
    tt.t();
    }

    }

    // 익명클래스 사용
    // implements 없이 main에 선언시 new로 직접 구현해줌

    interface Test1{
    public void t();
    }
    public class Test{
    public static void main(String[] args) {
    Test1 tt = new Test1() {
    @Override
    public void t() {
    System.out.println("익명 클래스의 메서드 정의");
    }
    };
    tt.t();

    }}

    익명 클래스 유형

    1. 클래스를 확장하는 익명 내부 클래스
    1. 인터페이스를 구현하는 익명 내부 클래스
    1. 내부 메서드/생상자 인수를 정의하는 익명 내부 클래스
    클래스 확장
    • 클래스를 확장하는 익명 내부 클래스는 상속없이 메서드를 재정의하기 위해서
    package t1;

    class test1{
    public void tt(){
    System.out.println("여기는 test1에서의 메서드 호출");
    }
    }
    public class Test{
    public static void main(String[] args) {
    test1 t1 = new test1();
    t1.tt();

    test1 t2 = new test1(){
        @Override
        public void tt(){
            System.out.println(&quot;여기는 main에서 메서드 호출&quot;);
        }
    };
    t2.tt();
    
    }

    }

    // 여기는 test1에서의 메서드 호출
    // 여기는 main에서 메서드 호출

    인터페이스 구현
    • 인터페이스를 구현하는 익명 내부 클래스는 구현 클래스를 정의하지 않고 인터페이스를 참조하는 익명 내부 클래스의 객체를 생성하기 위함

    interface Test11 {
    public void t11();

    }
    
    
    public class Test {
        public static void main(String[] args) {
            Test11  tt = new Test11() {
                @Override
                public void t11() {
                    System.out.println(&quot;여기는 생성해서 바로실행&quot;);
                }
            };
            tt.t11();
        }
    }</code></pre><blockquote id="eaeaf217-7188-4ad9-835b-eebddfa4c665" class="">내부 메서드/생성자 인수 정의</blockquote><ul id="a7fb7848-9c92-42e4-a4ba-9f39040379e9" class="bulleted-list"><li style="list-style-type:disc">익명 내부 클래스를 인수로 하여 메서드 또는 생성자에 전달할 수 있다</li></ul><pre id="70460f6e-d0fc-40d2-9aa0-cd64cd535cd4" class="code"><code>package t1;
    
    
    interface Test11 {
        public void t11();
    
    }

    class TestClass {
    public void t11(Test11 t){
    t.t11();
    }
    }

    public class Test {
        public static void main(String[] args) {
            TestClass tc = new TestClass();
            tc.t11(new Test11() {
                @Override
                public void t11() {
                    System.out.println(&quot;이렇게도 정의하고 사용이 가능해요&quot;);
                }
            });
        }
    }</code></pre><ul id="d61a1cfe-1ea1-40ce-946f-217cbc87e532" class="bulleted-list"><li style="list-style-type:disc">익명 내부 클래스는 클래스를 정의하지 않고 특정 작업을 수행하기 위해 사용된다</li></ul><ul id="b0009a1c-5df7-437e-b7bf-258dbe5073a0" class="bulleted-list"><li style="list-style-type:disc">익명 내부 클래스는 일반적으로 하위 클래스를 확장하거나 인터페이스를 구현한다</li></ul></details></li></ul><ul id="fb4b17d4-a355-4c57-ba04-a413c113a0bb" class="toggle"><li><details open=""><summary>set ↔ 배열 변환</summary><pre id="4f5409c4-e664-428c-85ea-fdc06c3d77b2" class="code"><code>Integer [] arr = {1,2,3,4,5};
        // 원시타입으로 배열을 선언하면 오류발생
        // Wrapper클래스로 해주어야함
    
        Set&lt;Integer&gt; set = new HashSet&lt;Integer&gt;(Arrays.asList(arr));
    
        System.out.println(set);</code></pre><pre id="e945ed8d-e12b-4634-b08b-c9e7a9997594" class="code"><code>Set&lt;Integer&gt; set = new HashSet&lt;Integer&gt;(Arrays.asList(1,2,3,45,5,3));
    
        Integer [] arr = set.toArray(new Integer[0]); // 배열의 사이즈를 지정해주는데 0이면 size크기로 생성된다
    
        System.out.println(Arrays.toString(arr));</code></pre></details></li></ul><ul id="e98b446b-28b1-4571-90ac-c3423c91c73a" class="toggle"><li><details open=""><summary>Math클래스</summary><pre id="57756d4c-7d20-4d52-b172-445ad697fc8a" class="code"><code>// java.Lang패키지에 포함된 클래스

    // 메소드들은 전부 static으로 구현 -> 객체구현 필요없음

    자바 대표적인 Math 메소드

    • 이외의 것은 자바 docs에서 찾아보기
    메소드설명
    static double random()0.0 이상 1.0 미만의 범위에서 임의의 double형 값을 하나 생성하여 반환함.
    static double abs(double a)static double abs(float a)static double abs(int a)static double abs(long a)전달된 값이 음수이면 그 값의 절댓값을 반환하며, 전달된 값이 양수이면 인수를 그대로 반환함.
    static double ceil(double a)전달된 double형 값의 소수 부분이 존재하면 소수 부분을 무조건 올리고 반환함.
    static double floor(double a)전달된 double형 값의 소수 부분이 존재하면 소수 부분을 무조건 버리고 반환함.
    static long round(double a)static int round(float a)전달된 값을 소수점 첫째 자리에서 반올림한 정수를 반환함.
    static double rint(double a)전달된 double형 값과 가장 가까운 정수값을 double형으로 반환함.
    static double max(double a, double b)static float max(float a, float b)static long max(long a, long b)static int max(int a, int b)전달된 두 값을 비교하여 큰 값을 반환함.
    static double min(double a, double b)static float min(float a, float b)static long min(long a, long b)static int min(int a, int b)전달된 두 값을 비교하여 작은 값을 반환함.
    static double pow(double a, double b)전달된 두 개의 double형 값을 가지고 제곱 연산을 수행하여, ab을 반환함.
    static double sqrt(double a)전달된 double형 값의 제곱근 값을 반환함.
    static double sin(double a)static double cos(double a)static double tan(double a)전달된 double형 값에 해당하는 각각의 삼각 함숫값을 반환함.
    static double toDegrees(double angrad)호도법의 라디안 값을 대략적인 육십분법의 각도 값으로 변환함.
    static double toRaidans(double angdeg)육십분법의 각도 값을 대략적인 호도법의 라디안 값으로 변환함.

    1. random() 메소드 - 자바 랜덤 함수

    • java random() 예제
    System.out.println((int)(Math.random() * 100));// 0 ~ 99 Random
    ran = new Random();
    System.out.println(ran.nextInt(100));// 0 ~ 99

    2. abs() 메소드 - 자바 절대값 함수

    • java abs() 예제
    System.out.println(Math.abs(10));// 10
    System.out.println(Math.abs(-10));// 10
    System.out.println(Math.abs(-3.14));// 3.14

    3. floor() 메소드, ceil() 메소드와 round() 메소드 - 자바 내림, 올림, 반올림 함수

    • java floor(), ceil(), round() 예제
    System.out.println(Math.ceil(10.0));// 10.0
    System.out.println(Math.ceil(10.1));// 11.0
    System.out.println(Math.ceil(10.000001));// 11.0
    System.out.println(Math.floor(10.0));// 10.0
    System.out.println(Math.floor(10.9));// 10.0
    System.out.println(Math.round(10.0));// 10
    System.out.println(Math.round(10.4));// 10
    System.out.println(Math.round(10.5));// 11

    4. max() 메소드와 min() 메소드 - 자바 최대값, 최소값 함수

    • 주어진 두 수중 값 반환

    • java max(),min() 예제

    System.out.println(Math.max(3.14, 3.14159));// 3.14159
    System.out.println(Math.min(3.14, 3.14159));// 3.14
    System.out.println(Math.max(-10, -11));// -10
    System.out.println(Math.min(-10, -11));// -11

    5. pow() 메소드와 sqrt() 메소드 - 자바 제곱, 제곱근 함수

    • pow()는 제곱, sqrt()는 제곱근 반환

    • java pow(), sqrt() 예제
    System.out.println((int)Math.pow(5, 2));// 25
    System.out.println((int)Math.sqrt(25));// 5
  • 람다식 ::(이중 콜론 연산자,메소드 참조 표현식)의 용도와 사용법
    - 메소드 참조 표현식
    - 람다식에서 파라미터 중복해서 사용하기 싫을 때 사용
    - 람다 표현식에서만 가능
    - 사용법
    [인스턴스]::[메소드명(또는 new)] 
    * static 메소드의 경우 인스터스대신 클래스를 사용할 수 있다
    import java.util.Arrays;
    import java.util.List;
    public class DoubleColonTest {
        public static void main(String[] args) {
            List<String> names = Arrays.asList("김갑순", "김갑돌");
            // x를 건네고 받는 과정에서 x를 두 번 적게 된다.
            names.forEach(x -> System.out.println(x));
            // 아예 x들을 빼버리고 아래와 같이 작성할 수 있다.
            names.forEach(System.out::println);
        }
    }
    

    import java.util.Arrays;
    import java.util.List;

    public class DoubleColonTest {

    public String addNim(String s) {
        return s + &quot;&quot;;
    }
    
    public static void main(String[] args) {
        List&lt;String&gt; names = Arrays.asList(&quot;김갑순&quot;, &quot;김갑돌&quot;);;
    
        DoubleColonTest dct = new DoubleColonTest();
    
        names.stream().map(x -&gt; dct.addNim(x)).forEach(System.out::println); // 적용 전
        names.stream().map(dct::addNim).forEach(System.out::println); // 적용 후
    
    }

    }

    // 생성자가 파라미터 한 개로 이루어진 DTO의 배열을 생성시
    public class Dog {
    private String name;
    private String species;

    // ...setter
    // ...getter
    
    @Override
    public String toString() {
        return &quot;Dog{&quot; +
                &quot;name=&#x27;&quot; + name + &#x27;\&#x27;&#x27; +
                &quot;, species=&#x27;&quot; + species + &#x27;\&#x27;&#x27; +
                &#x27;}&#x27;;
    }

    }


    import java.util.Arrays;
    import java.util.List;
    import java.util.stream.Collectors;

    public class DoubleColonTest {

    public static String addNim(String s) {
        return s + &quot;&quot;;
    }
    
    public static void main(String[] args) {
        List&lt;String&gt; names = Arrays.asList(&quot;김갑순&quot;, &quot;김갑돌&quot;);
    
        List&lt;Dog&gt; dogs1 = names.stream()
                .map(x -&gt; new Dog(x)) // 적용 전
                .collect(Collectors.toList());
        List&lt;Dog&gt; dogs2 = names.stream()
                .map(Dog::new) // 적용 후
                .collect(Collectors.toList());
    
        dogs2.forEach(x -&gt; x.setSpecies(&quot;이탈리안 그레이 하운드&quot;));
    
        System.out.println(dogs1);
        System.out.println(dogs2);
    
    }

    }

    // 함수형 인터페이스를 구현할 때 파라미터의 종류와 개수가 같으면 사용 가능
    @FunctionalInterface
    public interface StringToDog {
    public String convert(String name, String species, int price);
    }

    public class Dog {
    private String name;
    private String species;
    private int price;

    public static String introduce(String name, String species, int price) {
        return name + &quot; : &quot; + species + &quot; : &quot; + price;
    }
    
    // ............

    }

    import java.util.Arrays;
    import java.util.List;
    import java.util.stream.Collectors;

    public class DoubleColonTest {

    public static void main(String[] args) {
    
        // 파라미터의 개수와 타입이 같다면 메소드 참고 표현식이 적용된다.
        StringToDog stringToDog1 = (name, species, price) -&gt; Dog.introduce(name, species, price);
        StringToDog stringToDog2 = Dog::introduce;
    
        System.out.println(stringToDog1.convert(&quot;개똥이&quot;, &quot;믹스&quot;, 100));
        System.out.println(stringToDog2.convert(&quot;누렁이&quot;, &quot;믹스&quot;, 1000));
    }

    }


Errors

  • local variable is redundant

    에러는 아니고 불필요하거나 해당 변수가 사용되고있지않다는 의미

  • possible lossy conversion from double to int

    실수값은 int로 타입지정하였기때문에

    1. 형변환
    1. 데이터 타입변경
  • 인텔리제이 Cannot resolve symbol 에러
    // jdk 설정문제이거나 인텔리제이가 말썽을 부리거나
    1. file -> project structure -> project sdk부분 설정해주기(jdk추가)
    2. build -> rebuild project 
    3. file -> invalidate caches -> clear VCS log caches and indexes -> invalidate and restart(캐시비우고 재실행)
    
  • int 타입의 한계에러
    class Solution {
        public long[] solution(int x, int n) {
            long[] answer = new long[n];
            long result = 0;
            System.out.println("여기 뭐나오니 : " + answer.length);
            for(int i = 0; i < n; i++){
                result += x;
                answer[i] = result;
                System.out.println("그러면 여기는 뭐나오니 : " + answer[i] );
            }
            return answer;
        }
    }
    • result 부분이 int였을때는 에러부분이 발생했다
    • 그이유로는 int범위인 -21억에서 21억의 숫자크기를 넘어가서이다
  • 소수점 평균구하기 주의점
    • return타입이 소수점일 경우에는 해당값을 int로 나누는 경우 소수점부분이 생략되는 이슈가있다
    • 그러므로 같은 double로 나누어주어야지 평균의 소수점이 생략되지않는다
  • ‘.class’ expected에러
    • 컴파일하는 과정에서 발생하는에러
    • syntax error이다 {}나 ;등이 없는지 잘작성되었는지 확인하기
  • ArrayIndexOutOfBoundsException
    • 배열의 크기 이상의 숫자가 들어갔거나 배열의 위치값에 음수가 들어가는 등의 오류
  • Cannot find symbol
    📌
    코드가 컴파일될 때 컴파일러는 소스코드의 식별자들이 각각 어떤 의미가 있는지 해석

    이 작업을 할 수 없는 경우( = 컴파일러가 소스코드를 해석할 수 없음)인 경우 출력

    2. Cannot find symbol 원인

    굉장히 다양한 원인이 때로는 복합적으로 있을 수 있다.

    1. 스펠링 오작성 : 예를들어 StringBuffer를 SpringBuffer로 작성. SpringBuffer 클래스가 없어 코드를 컴파일할 수 없다.

    StringBuffer sb = new SpringBuffer();
    Error:(5, 31) java: cannot find symbolsymbol: class SpringBufferlocation: class com.example.demo.cannotfindsymbol.Test

    2. 대소문자 오작성 : StringBuffer -> Stringbuffer로 작성.

    3. 변수 선언을 하지않고 사용

    System.out.println(str); // 선언하지 않은 str 변수 사용

    4. 다른 영역에 선언한 변수를 사용하는 오류 : for문 영역의 변수를 외부에서 접근.

    for(int i=0; i<2; i++){
        System.out.println(i);
    }
    

    System.out.println(i); // i를 찾을 수 없어 오류

    5. 객체가 가지고 있지 않은 변수나 함수에 접근하는 경우

    String str = "123";
    str.reverse(); // String 객체는 reverse() 메소드가 없다.

    6. new 키워드를 작성하지 않은 경우

    StringBuffer sb = StringBuffer();

    7. 같은 클래스명의 다른 패키지 클래스가 import 된 경우 : IDE의 자동 임포트 기능을 이용하다 보면 종종 발생한다. 사용하고자 하는 패키지의 클래스가 임포트 되었는지 확인한다.

    8. 세미콜론(;)이 잘못 작성된 경우 : 아래는 없어야 될 세미콜론이 중간에 작성되었다.

    세미콜론에 의해 for(~~~) 와 코드블럭{ }이 분리되며 코드블럭{ }에서 i는 선언되지 않았으므로 오류가 발생한다.

    for (int i = 0; i < 100; i++); {
    System.out.println("i : " + i);
    }

    9. 오퍼레이터가 오작성 된 경우 : 아래에서는 *를 생략함으로써 i라는 메소드를 찾으나 없으므로 오류가 발생한다.

    int a = 1;
    int b = 2;
    int i = 1;

    int result = i(a+b); // i(a+b) 에서 를 작성하지 않음

    cannot find symbol 해결

    발생할 수 있는 원인이 워낙 다양하고 복합적이다 보니, 오류가 발생한 부분을 중심으로 작성한 코드를 주의 깊게 디버깅하는 수밖에 없다.

    개발자가 코드를 잘못 작성한 것 외에도 의도한 것과 다른 버전의 라이브러리를 사용했다던가(의존성 에러), IDE에서 문제가 있다던가, 특정 소스를 컴파일하지 않았던가 등의 다양한 원인이 있으므로 단계별로 살펴봐야 한다.

    참고

    stackoverflow.com/questions/25706216/what-does-a-cannot-find-symbol-or-cannot-resolve-symbol-error-mean

  • might not have been initialized
    • 변수가 선언은 되었는데 값이없는 상태에서의 오류
    int num;
    num++;
    // 오류 발생
    // num이 선언만 되었고 안에는 값이 없는 증감연산자를 요구하면 숫자몇부터 증감을 해야하는지 모르기때문에 오류발생
  • error: incompatible types: possible lossy conversion from double to int
    • 소수점을 int로 변환하면 소수점 아래의 수를 잃게된다
    • double을 int로 저장하는 것을 lossy 할 수 있게 때문에 발생하는 오류
      • 강제 형변환을 하던 데이터 타입을 맞추어주면된다
  • public, should be declared in a file name
    public interface로 생성해서 발생한 에러
    클래스명이 public으로 선언되었는데 파일명과 다를 경우 public으로 선언된 클래스가 있다면 반드시 그 클래스명과 파일명이 같아야한다
    결국 public을 지우고 interface생성

0개의 댓글