2026년 1월 8일 목요일 - 3주차
클래스 문법의 의도: 좋은 코드를 만들기 위해서. 좋은 코드는 이해하기 좋은 코드를 의미하며, 이를통해 유지보수 비용이 절감된다. 하지만, 생산비용은 올라간다.
영작(영어+작문)능력이 필요하다. 하지만, GPT의 도움으로 어느정도는 해결 가능하다.. 어느정도만.
객체지향 프로그래머(OOP): 역할을 잘 정의하고 코드를 잘 분리하자
하나의 배열에는 같은 개념(level)의 데이터를 묶는것이 핵심이다.
서로 다른 개념의 데이터를 묶고싶을때 클래스를 사용한다. 이때 클래스를 정의하는것에는 절차(순서)가 중요하지 않으며, 속성(형)을 정의할때 사용된다. 그렇기때문에 필드를 정의하는 영역에서는 System.out.println 과 같은 것들은 동작하지 않는다.
[P1]
// 클래스 정의 문법 (정의만을 한것이지 메모리를 생성하는것이 아님),
// 물론 똑같은 이름을 가지는 클래스는 중복 생성할 수 없다. (문법 오류)
class 클래스명 {
// 클래스명을 지을때는 파스칼 케이스. 첫번째 문자가 대문자
// 클래스 안에 들어가는 것: 속성 (attribute, 개념적 표현),
// 필드(field, 메모리적 표현), 멤버변수(문법적 표현)
}
프로그램이 실행된다는 것은 main(메서드)이 실행된다는 것을 의미함(JVM이 main을 찾아서 실행) 메서드는 기능을 정의하는 것. 그렇기에 절차가 중요하다. 클래스는 객체의 속성을 정의하는것이며, 클래스의 이름을 타입(사용자 정의 타입)으로 사용 할 수 있어서 클래스의 타입을 가지는 변수를 선언 할 수 있다. 직접 생성한 클래스 내부에서 선언되는 변수는 지역변수가 아닌 멤버변수이다. main의 내부에서 클래스명을 타입으로 가지고 선언되는 변수는 당연하게도 변수이기 때문에 stack에 생성되며, 그 변수의 값으로는 기본타입이 아닌, heap에 생성된 필드의 주소가 들어간다(참조변수) 이때 사용하는것이 new 키워드이며, heap에 인스턴스를 생성한다.
StudentInfo a = new StudentInfo(); // 위 코드의 구조: 참조변수 선언 문법 = 인스턴스 생성 문법. // 이러면 클래스에서 정의한 인스턴스의 메모리가 한번에 촤라락 생성된다. // StudentInfo 타입을 가지는 a 라는 이름의 참조변수를 stack에 생성하고, // StudentInfo 클래스의 생성자를 호출해서 new로 heap메모리에 인스턴스를 생성. // 그리고 그 후, 참조변수 a에는 heap에 생성된 인스턴스의 주소값을 넣는다. // ()가 붙으면 제어문과 같은 경우가 아닌 이상 무언가(메서드 등)를 호출하는 것. a.name = "홍길동"; // .은 접근 연산자 a.age = 27; a.score = 84;
public static void main(String[] args)가 가장 먼저 실행되며, 이 메서드를 가진 클래스가 프로그램의 시작점 역할을 한다. main 메서드가 없는 클래스는 단독으로 실행되지 않기 때문에,
main 메서드 안에서 new 키워드로 인스턴스를 생성하고 접근 연산자(.)를 통해 사용하게 된다.
[P2] - 생성자
public class P2 {
public static void main(String[] args) {
StudentInfo2 a = new StudentInfo2(1, 100);
// 괄호 안의 값은 아규먼트. 매개변수의 타입에 맞는 값을 넣어야 한다.
}
}
class StudentInfo2 {
// 속성
String name;
int age;
int score;
// 생성자 (constructor)
// 문법: 클래스명(매개변수1, 매개변수2, ... 매개변수n) { 로직 }
// 안에 정의된 로직은 인스턴스 생성 후 인스턴스별로 자동으로 한번씩 실행된다.
// 주요 사용 법(97%): 속성 값 초기화 목적.
// all argument contructor.
StudentInfo2(int nsme, int age, int score) { // 클래스 명을 쓴다.
// main메서드 안에서 인스턴스를 생성할때 생성자의 매개변수(parameter)의
// 타입에 맞는 아규먼트 값을 넣어야 하며, 생성자는 그렇게 받아온 값을
// 매개변수에 타입과 순서에 맞게 대입해서 필드를 생성한다.
// 절차 작성 구간 (순차작동)
this.name = name;
this.age = age;
this.score = score;
// "this."는 생성자가 실행되는 원인에 해당되는 주소값을 가르키는 것.
// 변수의 이름이 다르면 this 생략 가능한데, 사실 this가 있는데 단축된
// 것이다. 또한, 역기서 "."은 어떠한 값에 접근하는 접근 연산자이다.
// 메모리가 다 다르기 때문에 값이 다 다른데, name은 생성자의 매개변수고
// this.name은 클래스 속성 정의에 쓰인 멤버 변수 name이다
}
// 생성자를 2개 이상 정의 하는것이 생성자의 오버로딩. 오버로딩 중요함.
StudentInfo2() {
}
// 기능
}
클래스의 기능을 구현하기 위해서 메서드를 사용하는데, 목적은 코드들은 분류하고 재사용 하기 위해서이다.
메서드를 사용할 때는 this. 을 거의 무조건 써야 하는데, 지금 쓰려고 하고 있는 변수가 어디에 있는 변수인지 구분을 하기 위헤서 쓰인다.
메서드 정의 문법: 리턴 타입 메서드명(매개변수1, 매개변수2, ... 매개변수n) { 로직 구현 }
이때 메서드명은 camel case를 사용하며, 동사를 사용해야 한다. (변수는 명사)
메서드 호출 문법: 참조변수.메서드명(); (여기서 참조변수는 new 키워드로 생성한 인스턴스를 참조하는 변수)
public class P3 {
public static void main(String[] args) {
StudentInfo3 a = new StudentInfo3("민수", 0);
StudentInfo3 b = new StudentInfo3("철수", 0);
a.study(); // 메서드 호출
b.study(); // 필요할때 언제든지 여러번 호출 가능(로직 재사용)
a.study();
a.study();
a.study();
b.study();
// 이때 studyTime의 값은 무엇인가?
// ㄴ 정답: a의 studyTime은 4, b의 studyTime은 2. 메모리가 2개다.
a.eat();
b.eat();
System.out.println("프로그램 종료");
}
}
class StudentInfo3 {
// 속성
String name;
int studyTime;
// 생성자 - 없을때는 기본으로 빈 생성자가 생성됨. (초기화 하지 않겠다)
StudentInfo3(String name, int studyTime) {
this.name = name;
this.studyTime = studyTime;
}
// 기능(개념적 표현), 메서드(문법적인 표현)
// 메서드 정의 - 그냥 자동으로는 실행되지 않음
void study(/* 매개변수 자리 */) { // 지금 메모리를 생성하지는 않음
// 로직 구현
this.studyTime++;
// a.study(); 와 b.study(); 의 this 값이 다름.
// 다른 메모리에 접근하겠다는 뜻. 근데 여기서 중복되는 변수가 없으면
// this를 쓰지 않아도 됨. 무조건 this.study++ 라는 뜻이 되기 때문.
System.out.println("공부를 한다!!");
// this.이 없으면 응집도가 최악이 된다고 함.
System.out.println(name + "가 공부를 한다!!");
// (this.name + "") 랑 똑같은거.
}
void eat() {
System.out.println("밥을 먹는다..");
}
}