# 구조체 - 데이터를 그룹으로 묶어 새로운 데이터형을 만드는 방법 - 이 그룹을 마치 하나의 '변수' 처럼 사용 ## 구조체의 한계 - 여전히 데이터와 동작이 분리되어 있음 - 어떤 구조체가 어떤 함수랑 연관 있는지 찾기 복잡함 # 사람이 세상을 인지하는 법 - 사람은 세상을 물체(object)의 집합으로 인지 - 일반적인 이야기 (모든 것을 물체로 보지는 않음) - object를 프로그래밍에서는 객체 또는 개체라 번역한다 - 물체는 상태를 가질 뿐만 아니라 동작도 할 수 있다. ## 상태와 동작 - 프로그래밍에서 변수는 상태를 저장 - 동작은 함수를 의미 - 즉, 개체란 상태(변수, 데이터) 외에 동작(함수)까지 포함한다. # 클래스와 객체 ![](https://hackmd.io/_uploads/Hk_fMMlFa.png) **클래스(Class)** - 객체를 만들어내기 위한 **설계도** - 자바에서는 이러한 설계도를 가지고 여러 객체를 생성하여 사용하는 식으로 프로그래밍을 이어나간다 - 객체의 상태를 나타내는 필드(field)와 객체의 행동을 나타내는 메서드(method)로 구성된다. - 필드(field)란 클래스에 포함된 변수를 의미한다 - 메서드(method)란 클래스에 포함된 함수를 의미한다 - Code영역에 위치한다 - 자바 파일은 클래스를 정의하는 것이기 때문에, 클래스 이름과 자바 파일 이름은 동일해야 한다 ![](https://i.imgur.com/H2uXhc6.png) ```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 키워드를 가지지 않는 메서드 - ![](https://i.imgur.com/wu8OALn.png) ### 메서드 사용 예시 ```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` 키워드를 적절히 사용한다