# 구조체
- 데이터를 그룹으로 묶어 새로운 데이터형을 만드는 방법
- 이 그룹을 마치 하나의 '변수' 처럼 사용
## 구조체의 한계
- 여전히 데이터와 동작이 분리되어 있음
- 어떤 구조체가 어떤 함수랑 연관 있는지 찾기 복잡함
# 사람이 세상을 인지하는 법
- 사람은 세상을 물체(object)의 집합으로 인지
- 일반적인 이야기 (모든 것을 물체로 보지는 않음)
- object를 프로그래밍에서는 객체 또는 개체라 번역한다
- 물체는 상태를 가질 뿐만 아니라 동작도 할 수 있다.
## 상태와 동작
- 프로그래밍에서 변수는 상태를 저장
- 동작은 함수를 의미
- 즉, 개체란 상태(변수, 데이터) 외에 동작(함수)까지 포함한다.
# 클래스와 객체

**클래스(Class)**
- 객체를 만들어내기 위한 **설계도**
- 자바에서는 이러한 설계도를 가지고 여러 객체를 생성하여 사용하는 식으로 프로그래밍을 이어나간다
- 객체의 상태를 나타내는 필드(field)와 객체의 행동을 나타내는 메서드(method)로 구성된다.
- 필드(field)란 클래스에 포함된 변수를 의미한다
- 메서드(method)란 클래스에 포함된 함수를 의미한다
- Code영역에 위치한다
- 자바 파일은 클래스를 정의하는 것이기 때문에, 클래스 이름과 자바 파일 이름은 동일해야 한다

```java
public class Korean {
String name;
int age;
}
```
**객체(Object) / 인스턴스(Instance)**
- 객체 : 설계도를 바탕으로 소프트웨어 세계에 구현된 **구체적인 실체**
- 인스턴스 : 클래스에 의해 만들어진 객체
- 모든 객체는 참조 자료형이다
- Heap영역에 위치한다
- 객체를 생성 후 필드나 메서드에 접근하려면 `.` 연산자를 사용한다
```java
public class Korean {
String name;
int age;
}
```
```java
public class Main {
public static void main(String[] args) {
Korean p1 = new Korean();
p1.name = "김철수";
p1.age = 20;
System.out.println(p1.name); // 김철수
System.out.println(p1.age); // 20
}
}
```
## 필드
- 클래스에 포함된 변수
- 선언된 위치와 선언자에 따라 다음과 같이 구분된다
- 클래스 변수 (static variable)
- 모든 인스턴스가 공유해야 하는 값을 유지하기 위해 사용된다
- 인스턴스 생성 없이도 사용할 수 있다
- 인스턴스 변수 (instance variable)
- 지역 변수 (local variable)
- 각각의 변수는 생성/소멸 시기가 다르고, 메모리 공간과 사용 방법도 다르다.
|변수|생성 시기|소멸 시기| 저장 메모리 |
|---|---|---|--------|
|클래스 변수|클래스가 메모리에 올라갈 때|프로그램이 종료될 때| 데이터 영역 |
|인스턴스 변수|인스턴스가 생성될 때|인스턴스가 소멸할 때| 힙 영역 |
|지역 변수|블록 내에서 변수의 선언문이 실행될 때|블록을 벗어날 때| 스택 영역|
### 필드 사용 예시
```java
public class Korean {
static String NATION = "Korea"; // 클래스 변수
String name; // 인스턴스 변수
int age; // 인스턴스 변수
}
```
```java
public class Main {
public static void main(String[] args) {
Korean p1 = new Korean();
p1.name = "김철수";
p1.age = 20;
System.out.println(p1.name); // 김철수
System.out.println(p1.age); // 20
Korean p2 = new Korean();
System.out.println(p2.name); // null
System.out.println(p2.age); // 0
System.out.println(Korean.NATION); // Korea (클래스 변수 사용)
}
}
```
## 접근제어자
- 접근제어자란 클래스 외부에서 클래스의 멤버 변수, 메서드, 생성자를 사용할 수 있는지 여부를 지정하는 키워드다
- 접근 제어자는 크게 `public`, `protected`, `default`, `private` 4가지로 나뉜다
- `default`는 별도의 키워드가 없다
- 자바에서는 정보의 은닉을 위해서 접근 제어자를 사용한다
- 정보 은닉이란 외부에서 접근하지 못하도록 막는 것을 의미한다
- 외부에서 접근할 수 없으므로 외부에서 변수의 값을 변경할 수 없다
- 외부에서 접근할 수 없으므로 외부에서 잘못된 값을 설정할 수 없다
- 자바에서 접근 제어자의 접근 범위가 보다 많은 제어자부터 적은 제어자 순으로 나열하면 다음과 같다.
- public > protected > default > private
| 접근 제어자 | 같은 클래스의 멤버 | 같은 패키지의 멤버 | 자식 클래스의 멤버 | 그 외의 영역 |
| :---------: | :----------------: | :----------------: | :----------------: | :----------: |
| public | ○ | ○ | ○ | ○ |
| protected | ○ | ○ | ○ | X |
| default | ○ | ○ | X | X |
| private | ○ | X | X | X |
### 접근제어자 사용 예시
```java
public class Korean {
private static String NATION = "Korea"; // 클래스 변수
public String name; // 인스턴스 변수
private int age; // 인스턴스 변수
}
```
```java
public class Main {
public static void main(String[] args) {
Korean p1 = new Korean();
p1.name = "김철수"; // public 접근 제어자이므로 접근 가능
System.out.println(p1.name); // 김철수
// p1.age = 20; // 에러 발생
// System.out.println(Korean.NATION); // 에러 발생
}
}
```
## 메서드
- 클래스 안의 함수를 의미
- 다른 프로그래밍 언어에서 `함수` 라고 부르는 개념을 자바에서는 `메서드` 라고 표현한다. 하지만 혼용되서 사용되므로 크게 의미를 두지 않아도 된다.
- 메서드 정의
- 접근 제어자 : 해당 메서드가 접근할 수 있는 범위를 명시
- 반환 타입(return type) : 메서드가 모든 작업을 마치고 반환하는 데이터의 타입을 명시
- 메서드명 : 메서드를 호출하기 위한 이름을 명시
- 구현부 : 메서드의 고유 기능을 수행하는 집합. 중괄호 `{`, `}`안에 표현됨
- 클래스 메서드 : static 키워드를 가지는 메서드
- 클래스 메서드에서는 인스턴스 변수를 사용할 수 없다
- 인스턴스 메서드 : static 키워드를 가지지 않는 메서드
- 
### 메서드 사용 예시
```java
public class Korean {
private static String NATION = "Korea";
public String name;
public int age;
public void speak() {
System.out.println("안녕하세요. 저는 " + name + "입니다. 저는 " + NATION + "사람입니다.");
}
public void howOld() {
System.out.println("저는 " + age + "살 입니다.");
}
}
```
```java
public class Main {
public static void main(String[] args) {
Korean p1 = new Korean();
p1.name = "김철수";
p1.age = 20;
p1.speak(); // 안녕하세요. 저는 김철수입니다. 저는 Korea사람입니다.
p1.howOld(); // 저는 20살 입니다.
}
}
```
### static 메서드 사용 예시
```java
public class Korean {
private static String NATION = "Korea";
public String name;
public int age;
public void speak() {
System.out.println("안녕하세요. 저는 " + name + "입니다. 저는 " + NATION + "사람입니다.");
}
public void howOld() {
System.out.println("저는 " + age + "살 입니다.");
}
public static void changeNation() {
NATION = "대한민국";
}
}
```
```java
public class Main {
public static void main(String[] args) {
Korean p1 = new Korean();
p1.name = "김철수";
p1.age = 20;
p1.speak(); // 안녕하세요. 저는 김철수입니다. 저는 Korea사람입니다.
p1.howOld(); // 저는 20살 입니다.
Korean.changeNation();
p1.speak(); // 안녕하세요. 저는 김철수입니다. 저는 대한민국사람입니다.
}
}
```
### `getter`, `setter`
- 클래스 내부의 변수는 외부에서 직접 접근하는 것이 아니라 메서드를 통해 접근하는 것이 좋다
- 외부에서 직접 접근하게 되면 변수의 값을 잘못 설정할 수 있기 때문이다
- 클래스 내부의 변수를 막는 경우, 변수의 값을 읽어오거나 설정할 수 있는 메서드를 제공해야 한다
- 클래스 내부의 값을 읽는 메서드를 `getter` 메서드라고 한다
- 클래스 내부의 값을 설정하는 메서드를 `setter` 메서드라고 한다
- 일반적으로 `getter` 메서드는 `get`으로 시작하며 , setter 메서드는 `set`으로 시작한다
- 필드가 `boolean` 타입의 경우 `is`로 시작한다
```java
public class Korean {
private static String NATION = "Korea";
private String name;
private int age;
public void speak() {
System.out.println("안녕하세요. 저는 " + name + "입니다. 저는 " + NATION + "사람입니다.");
}
public void howOld() {
System.out.println("저는 " + age + "살 입니다.");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
```
```java
public class Main {
public static void main(String[] args) {
Korean p1 = new Korean();
// p1.name = "김철수"; // 에러 발생
// p1.age = 20; // 에러 발생
p1.setName("김철수");
p1.setAge(20);
p1.speak(); // 안녕하세요. 저는 김철수입니다. 저는 Korea사람입니다.
p1.howOld(); // 저는 20살 입니다.
}
}
```
#### `getter`, `setter` 사용 이유
- 외부에서 직접 접근하게 되면 변수의 값을 잘못 설정할 수 있다
- 내부 변수 값을 변경할 때, 추가적인 작업을 할 수 있다
- 예를 들어, 나이가 0보다 작거나 200보다 큰 경우, 잘못된 값이므로 0으로 설정하거나 에러를 발생시킬 수 있다
```java
public class Korean {
private static String NATION = "Korea";
private String name;
private int age;
public void speak() {
System.out.println("안녕하세요. 저는 " + name + "입니다. 저는 " + NATION + "사람입니다.");
}
public void howOld() {
System.out.println("저는 " + age + "살 입니다.");
}
public String getName() {
return name;
}
public void setName(String name) {
if (name.length() < 2 || name.length() > 5) {
System.out.println("잘못된 이름입니다.");
return;
}
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
if (age < 0 || age > 200) {
System.out.println("잘못된 나이입니다.");
return;
}
this.age = age;
}
}
```
```java
public class Main {
public static void main(String[] args) {
Korean p1 = new Korean();
p1.setName("김철수");
p1.setAge(20);
p1.speak(); // 안녕하세요. 저는 김철수입니다. 저는 Korea사람입니다.
p1.howOld(); // 저는 20살 입니다.
p1.setName("김"); // 잘못된 이름입니다.
p1.setAge(300); // 잘못된 나이입니다.
}
}
```
## 생성자
- 클래스의 인스턴스를 생성할 때 호출되는 특별한 메서드로, 주로 인스턴스 변수의 초기화 작업에 사용된다
- 주요 목적은 인스턴스의 초기 상태를 설정하는 것
- 생성자는 클래스 이름과 같은 이름을 가지며 반환 타입이 없다
- 자바의 모든 클래스는 반드시 하나 이상의 생성자가 정의되어 있어야 한다.
- 생성자를 명시적으로 정의하지 않으면 아무 인자도 받지 않고 아무 작업도 수행하지 않는 기본 생성자를 자동으로 만들어준다
- 생성자를 명시적으로 정의한 경우 기본 생성자는 만들어지지 않는다
> **초기화 시점**
>
> - 클래스 변수 : 클래스가 처음 로딩될 때 단 한번 초기화
> - 인스턴스 변수 : 인스턴스가 생성될 때마다 각 인스턴스별로 초기화
```java
public class Korean {
private static String NATION = "Korea";
private String name;
private int age;
public Korean() {
name = "이름없음";
age = 20;
}
public void speak() {
System.out.println("안녕하세요. 저는 " + name + "입니다. 저는 " + NATION + "사람입니다.");
}
public void howOld() {
System.out.println("저는 " + age + "살 입니다.");
}
public String getName() {
return name;
}
public void setName(String name) {
if (name.length() < 2 || name.length() > 5) {
System.out.println("잘못된 이름입니다.");
return;
}
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
if (age < 0 || age > 200) {
System.out.println("잘못된 나이입니다.");
return;
}
this.age = age;
}
}
```
```java
public class Main {
public static void main(String[] args) {
Korean p1 = new Korean();
p1.speak(); // 안녕하세요. 저는 이름없음입니다. 저는 Korea사람입니다.
p1.howOld(); // 저는 20살 입니다.
}
}
```
### this
- 인스턴스 자신을 가리키는 참조변수
- 지역변수와 인스턴스 변수를 구분할 때 사용한다
- 인스턴스 메서드 내에서만 사용 가능하다
- `this.필드` : 인스턴스 변수
- `this.메서드()` : 인스턴스 메서드
- `this()` : 생성자 메서드
```java
public class Korean {
private static String NATION = "Korea";
private String name;
private int age;
public Korean(String name, int age) {
this.name = name;
this.age = age;
}
public void speak() {
System.out.println("안녕하세요. 저는 " + name + "입니다. 저는 " + NATION + "사람입니다.");
}
public void howOld() {
System.out.println("저는 " + age + "살 입니다.");
}
public String getName() {
return name;
}
public void setName(String name) {
if (name.length() < 2 || name.length() > 5) {
System.out.println("잘못된 이름입니다.");
return;
}
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
if (age < 0 || age > 200) {
System.out.println("잘못된 나이입니다.");
return;
}
this.age = age;
}
}
```
```java
public class Main {
public static void main(String[] args) {
// Korean p1 = new Korean(); // 에러 발생
Korean p1 = new Korean("김철수", 20);
p1.speak(); // 안녕하세요. 저는 김철수입니다. 저는 Korea사람입니다.
p1.howOld(); // 저는 20살 입니다.
}
}
```
```java
public class Korean {
private static String NATION = "Korea";
private String name;
private int age;
public Korean() {
this("이름없음", 1); // Korean(String, int) 생성자 호출
}
public Korean(String name, int age) {
this.name = name;
this.age = age;
}
public void speak() {
System.out.println("안녕하세요. 저는 " + name + "입니다. 저는 " + NATION + "사람입니다.");
}
public void howOld() {
System.out.println("저는 " + age + "살 입니다.");
}
public String getName() {
return name;
}
public void setName(String name) {
if (name.length() < 2 || name.length() > 5) {
System.out.println("잘못된 이름입니다.");
return;
}
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
if (age < 0 || age > 200) {
System.out.println("잘못된 나이입니다.");
return;
}
this.age = age;
}
}
```
```java
public class Main {
public static void main(String[] args) {
Korean p1 = new Korean();
p1.speak(); // 안녕하세요. 저는 이름없음입니다. 저는 Korea사람입니다.
p1.howOld(); // 저는 1살 입니다.
Korean p2 = new Korean("김철수", 20);
p2.speak(); // 안녕하세요. 저는 김철수입니다. 저는 Korea사람입니다.
p2.howOld(); // 저는 20살 입니다.
}
}
```
# 클래스 설계 시의 Best Practice
- 실수 방지를 위해 초기화가 가능한 모든 변수는 초기화 할 수 있도록 생성자를 만든다
- 정보 은닉 및 잘못된 값 설정 방지를 위해 필드에 대한 접근제어자는 `private`으로 설정하고, `getter`, `setter`를 사용한다
- 클래스 메서드와 인스턴스 메서드를 적절히 구분하여 사용한다
- 메서드와 변수의 이름을 명확하고 의미 있게 작성한다
- 코드의 가독성과 유지보수성을 향상시키기 위해
- 프로그램의 안정성을 높이고 메모리 사용을 최적화하기 위해 상수 값은 `static final` 키워드를 사용하여 정의한다.
- 코드의 명확성과 안정성을 높이기 위해 `this` 키워드를 적절히 사용한다