# 구조체
- 데이터를 그룹으로 묶어 새로운 데이터형을 만드는 방법
- 이 그룹을 마치 하나의 '변수'처럼 사용
### 구조체의 한계
- 여전히 데이터와 동작이 분리되어 있음
- 어떤 구조체가 어떤 함수랑 연관 있는지 찾기 복잡함
## 사람이 세상을 인지하는 법
- 사람은 세상을 물체(object)의 집합으로 인지
- 일반적인 이야기 (모든 것을 물체로 보지는 않음)
- object를 프로그래밍에서는 객체 또는 개체라 번역한다
- 물체는 상태를 가질 뿐만 아니라 동작도 할 수 있다.
### 상태와 동작
- 프로그래밍에서 변수는 상태를 저장
- 동작은 함수를 의미
- 즉, 개체란 상태(변수, 데이터) 외에 동작(함수)까지 포함한다.
---
# 객체 지향 프로그래밍
- OOP = Objected Oriented Programming = 객체지향 프로그래밍
- 여러가지 프로그래밍 패러다임 중 하나
- 목적 : 사람이 생각하는 방식과 유사하게 프로그래밍 할 수 있도록 하자!
- 절차지향 프로그래밍이 너무 어렵고 힘들어서 나온 개념
- 프로그램을 구성하는 기본 요소를 객체로 보고 하는 객체들이 서로 상호작용을 하여 프로그램이 돌아가게 만들려는 **노력**
- 노력인 이유 : 결과적으로 프로그램은 기계에서 돌기 때문에 어떤 프로그램이라도 최종적으로는 절차적으로 돌 수 밖에 없다
- 주요 특징은 캡슐화, 상속, 다형성, 추상화이다.
> ### 절차지향 vs 객체지향
>
> 
> ---
> 
---
# 캡슐화

- 데이터 구조와 데이터를 다루는 방법들을 결합 시켜 묶는 것
- 변수와 함수를 하나로 묶는 것을 뜻함
- 내부의 데이터를 외부로부터 보호
- 접근제어자 (`private`, `default`, `protected`, `public`)
- 접근제어자를 통해 정보은닉을 활용 할 수도 있다
- 정보은닉 : 객체 안에 있는 데이터를 외부로부터 보호
- 보호된 정보에 대해서 `getter`, `setter` 메서드를 만들어 접근 가능하게 하곤 한다
- 사용자가 클래스 속을 알 필요가 없음
- 사용자가 함수 속을 알 필요가 없는 것과 마찬가지
- 이 개념은 추강화로 이어진다
- 함수를 분리할 때 적용했던 원칙을 클래스에서도 적용할 것
- 중복된 코드가 있다면 `private` 메서드로 분리
- 실용적인 용도 : 사용자측의 변경 없이 구현부를 변경할 수 있게 해준다
> **접근제어자**
> |접근 제어자|같은 클래스의 멤버|같은 패키지의 멤버|자식 클래스의 멤버| 그 외의 영역 |
> |---|---|---|---|---|
> |public|○|○|○| ○ |
> |protected|○|○|○| X |
> |default|○|○|X| X |
> |private|○|X|X| X |
> **getter/setter**
> 함수를 통한 데이터 접근의 객관적인 장범
> 1. 멤버 변수를 저장하지 않고 필요할 때마다 getter에서 계산 가능
> 2. setter에서 추가적인 로직을 실행할 수 있음
> 3. 상속을 통한 다형성 구현 가능
# 상속

- 이미 존재하는 객체를 기반으로 확장된 객체를 만드는 방법
- 엄밀히 말하면 객체가 아니라 클래스
- 거의 모든 사람이 OOP 핵심이라 여기는 특성
- 특히, 재사용성이 궁극의 목적이라 신봉하던 시대에 특ㅎ
- 현재에도 상속을 지원하지 않으면 OO 언어라고 안 보는게 보통
- OOP의 또 다른 매우 중요한 특성인 **다형성의 기반**
- 한 번에 모든 구체적인 사항을 만들지 말고 점진적으로 기능을 확장시켜 나갈 수 있게 해준다
- 사람에게는 점진적 학습이 가장 효율적이다
- 확장된 객체
- 기존의 객체에 속한 데이터와 동작을 모두 물려받음 (유전)
- 여기에 다른 데이터나 동작을 추가할 수 있음 (진화)
- 물론 새클래스를 상속해서 또 다른 새 클래스를 만들 수 있음
- 실용적인 용도 : 코드 중복을 막음
- 여러 객체에는 공통되는 데이터와 동작을 부모 객체로 만듦
- 여러 객체는 각각 그 부모 객체를 상속 받음
- 그 후 자기에게만 필요한 데이터나 동작을 추가
> **super 키워드**
> - super는 현 개체의 부모 부분을 가리킴
> - super() 라고 코드를 작성하면 부모의 생성자를 호출
> - 멤버 변수나 메서드를 호출할 때도 가능
# 다형성

- 많은 사람들이 OOP의 핵심이라고 여기는 특징
- 같은 지시를 내렸는데 다른 종류의 객체가 동작을 달리 하는 것
- 같은 지시 : 동일한 함수 시그내처 호출
- 달리 동작 : 객체의 종류에 따라 실제로 실행되는 함수 구현 코드가 다름
- 절차적 언어에서 이런 일을 하려면 `if`문을 사용해야 했음
- 어떤 함수 구현이 실행될지는 실행 중에 결정됨
- 이를 늦은 바인딩이라고 함 (late binding)
- 일반적인 함수 호출은 이른 바인딩 (컴파일 중에 결정됨)
- 다형성의 혜택을 받으려면 상속 관계가 필요
- 부모 객체에서 함수 시그내처를 선언
- 자식 객체에서 그 함수를 다르게 구현 (overriding)
- 실용적인 용도
- 다른 종류의 객체를 편하게 저장 및 처리 가능
> **오버라이딩(Overriding)**
> - 하위 클래스가 이미 상위 클래스에 정의된 메서드의 자체 구현을 제공할 수 있도록 하는 기능
> - 오버라이딩 된 메서드를 호출 시 부모 클래스 구현 대신 자식 클래스 구현이 실행된다
> - 하위 클래스 메서드에서 `@Override` 어노테이션을 명시적으로 사용하여 오버라이딩 메서드임을 명시할 수 있다
> **다형성의 장점**
>
> - 각 자료형의 코드가 클래스 안에 들어가서 캡슐화
> - 유지 보수성 높아짐
> - 새로운 클래스를 추가할 때 클래스 코드만 추가하면 됨
> - 클라이언트가 작성할 코드가 줄어듦
> 
> 다형성을 통해 조건문의 사용 없이 코드 구현이 가능하다. 그렇기 때문에 조건문을 사용하면 OO가 아니라고 주장하는 사람이 있다. (조건문 대신 모든 것을 다형성으로 바꿔야 한다고 한다) 하지만 다형성이 조건문을 모두 대체하기는 어렵고, 설사 가능하더라고 그렇게 해야할 이유가 없다.
# 추상화

- OOP에서 추상화란 어떤 구체적인 것에 직접 손대지 않겠다는 것을 의미
- 객체 속에 있는 실제 데이터나 함수 구현 방법에 종속되지 않겠다는 뜻
- 데이터 추상화
- 객체 사용 시 그 안에 정확히 어떤 데이터가 있는지 알 필요 없음
- 객체 안에 있는 데이터에 접근 불가
- 그 대신 객체의 함수를 통해 접근
- 즉, 캡슐화는 추상화를 이루는 방법 중 하나
- 실용적인 용도 : 설계와 구현의 분리
> #### 추상 자료형쪽 관점
>
> - 사용자는 클래스를 자료형으로 사용할 수 있음
> - 그 클래스 안에 들어있는 멤버 변수가 정확히 뭔지 몰라도 됨
>
> #### 절차적 데이터 추상화쪽 관점
>
> - 데이터를 직접 조작하는 대신 메서드를 호출
>
> #### 추상화의 단점
>
> - 동작 없이 데이터만 있는 클래스는 쓸데없는 코드만 늘어남
> - 어떻게 추상화를 해야할 지 뚜렷한 객관적 기준이 없음
---
## 예시 코드
**Person.java**
``` java
// 추상화: 일반 개념을 나타내는 추상 클래스 정의
// 캡슐화: 개인 필드와 공용 메서드를 사용하여 데이터에 대한 액세스 제어
abstract class Person {
protected String name;
protected int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
// 다형성/추상화: 하위 클래스에 의해 구현되는 추상 메서드
public abstract void speak();
}
```
---
**Korean.java**
```java
// 상속: Person 클래스를 확장하여 보다 구체적인 클래스 생성
class Korean extends Person {
public Korean(String name, int age) {
super(name, age);
}
// 상속: Korean 클래스에 대한 speak() 메소드 오버라이딩
@Override
public void speak() {
System.out.println("안녕하세요! 저는 한국인입니다.");
}
}
```
---
**American.java**
```java
// 상속: Person 클래스를 확장하여 보다 구체적인 클래스 생성
class American extends Person {
public American(String name, int age) {
super(name, age);
}
// 상속: American 클래스에 대한 speak() 메서드 재정의
@Override
public void speak() {
System.out.println("Hello! I am an American.");
}
}
```
---
**Main.java**
```java
public class Main {
public static void main(String[] args) {
Person koreanPerson = new Korean("홍길동", 30);
Person americanPerson = new American("John til", 25);
List<Person> people = new ArrayList<>();
people.add(koreanPerson);
people.add(americanPerson);
for (Person person : people) {
System.out.println(person.getName() + " is " + person.getAge() + " years old.");
person.speak();
}
}
}
```
---
### 그 외에...
#### 오버로딩(Overloading)
- 오버로딩
- 이름은 같지만 매개변수가 다른 여러 메소드를 가질 수 있도록 하는 기능
- 메서드가 오버로드되면 Java는 전달된 인수의 수와 유형에 따라 실행할 메서드를 결정한다
- 입력에 따라 다른 작업을 수행할 수 있는 보다 유연하고 재사용 가능한 코드를 만들 수 있게 하는 기능
> 오버로딩 : 동일한 이름을 가진 여러 메서드를 클래스에 정의할 수 있는 기능
> 오버라이딩 : 부코 클래스의 함수를 재정의 하는 기능
---
#### 늦은 바인딩 / 이른 바인딩
- 늦은 바인딩
- 동적 바인딩이라고도 한다.
- 호출되는 메서드 구현이 프로그램 실행 중에 결정된다
- 다형성은 늦은 바인딩이다.
- 이른 바인딩
- 정적 바인딩이라도 한다
- C에서 배웠던 함수의 호출 방식이다
- 어떤 함수 구현을 호출해야 할지가 빌드 중에 결정된다.
- 따라서 함수 호출문을 바로 jmp 명령어로 교체 가능하다
- jmp하는 주소는 그 함수의 어셈블리어 코드가 시장되는 메모리 주소
- C에서 이게 가능한 이유는 다형성을 지원하지 않기 때문이다.
> 사실 C에 없는 기능은 하드웨어에 없다. 그렇기 때문에 사실 C에도 늦은 바인딩이 있다. 바로 **함수 포인터**다. 함수 포인터를 직접 전달하는 방식으로 늦은 바인딩을 구현할 수 있다.
>
> Java는 이 함수포인터 같은 기능을 편하게 사용할 수 있도록 해준것이다. Java에만 있는 기능은 C의 기능들을 조합해서 만든 것이기 때문에 C에서 이 기능이 아주 없다고 단정짓지 않아야 한다. (마찬가지로 Java도 final을 사용하여 이른바인딩이 가능하다.)
>
> 
---
#### Object의 다형적 메서드
- Java의 클래스는 모두 Object로부터 상속을 받는다
- 따라서 Object에 있는 메서드들은 어떤 클래스에서도 오버라이딩이 가능하다. 대표적으로 toString()이 있다.
**`toString()`**
- 사람이 읽기 편하게 해당 개체를 문자열로 표현
- Object클래스 안의 기본 구현
- `getClass().getName() + "@" + Integer.toHexString(hashCode())`
- Java 공식 문서는 모든 클래스에서 이 메서드를 오버라이딩 하라고 권장한다 (하지만 잘 하지 않는 편이다)
**`hashCode()`**
- 어떤 개체를 비교하는 해시값을 32 비트 정수로 반환한다
- 동치인 두 개체는 해시값이 같다
- 동치가 아닌 두 개체도 해시값이 같을 수 있다 (해시 충돌)
- Java의 Object클래스 안의 기본 구현은 개체의 주소를 반환하도록 되어있다.
- 주 목적은 Java가 자체 제공하는 HashMap클래스에서 사용하기 위함이다
- 키(key) 로 사용하는 개체의 해시값이 필요하기 때문
- 덕분에 빠른 비교용으로 사용 가능하다
- 단 두 개체가 같지 않다는 것만 빠르게 판단 가능하다 (해시코드가 같아도 두 개체는 다를 수 있기 때문)
**`equals()`**
- 동치 비교를 위한 메서드
- 아무런 구현이 없다면 단순한 주소를 비교한다
- 일일이 데이터를 비교하지 않는다
- 클래스마다 같다는 의미가 다를 수 있기 때문이다.
- 클래스 속 데이터를 비교해야한다면 오버라이딩이 필요하다
- 이때 hashCode()도 반드시 같이 오버라이딩 해야 한다
- **equals 와 hashCode 메서드는 재정의를 같이 하거나 둘 다 하지 않거나 해야한다**
> equals 와 hashCode 메서드 둘 중 하나만 오버라이딩 한 경우 발생할 수 있는 문제
> ```java
> Person p1 = new Person("John", "940629-1234567");
> Person p2 = new Person("John", "940629-1234567");
>
> HashSet<Person> set = new HashSet<>();
> set.add(p1);
>
> boolean containsP2 = set.contains(p2);
> System.out.println("Contains p2? " + containsP2);
> ```
---
### 패키지

#### 기존 패키지 시스템의 한계
- 어플리케이션이 사용하는 클래스 목록을 찾는 공식적인 방법이 없음
- 누락된 클래스가 있다면 실행 중에 그것을 사용하려 할 때 오류 발생
- 따라서 사용중인 패키지에 있는 모든 클래스를 같이 배포하는게 일반적
- 문제점
- Java버전이 증가함에 따라 Java 자체 제공 라이브러리의 크기가 커짐
- 안 사용하는 클래스까지 같이 배포할 경우 쓸데없이 용량이 커짐
- 패키지 안에 있는 모든 public 클래스를 아무나 사용할 수 있음
- 때로는 그 중 일부만 외부에 노출하고 싶은데 그럴 수 없음
---
### 과제 : 야구 게임
**요구사항**
- 1~9 사이의 중복되지 않는 3개의 정수를 생성한다.
- 사용자는 3개의 숫자를 입력한다.
- 생성된 3개의 숫자를 맞추는데 위치까지 정확히 맞춰야 한다.
- 숫자와 숫자의 위치까지 일치하면 strike 로 판정한다.
- 숫자는 맞지만 위치가 틀렸다면 ball 로 판정한다.
- 숫자3개가 모두 일치하지 않으면 out 으로 판정한다.
- 3 strike 가 되면 유저가 이기고 게임이 종료된다.
- 유저가 10번 안에 맞추지 못하면 패배하고 게임이 종료한다
###### tags: `과외(하희영)`