
생각해보니까 다음주부터 시험공부도 시작하면서
수요일에 발표과제도 해야하고
금요일에 C프 과제제출도 해야하고
주말에 알바도 가야하고
다음주까지 이팩티브 자바 읽어야하는데 그러기 위해서는 자바 지식을 좀 많이 쌓아놔야하고
매일 1303 백준도 해야하고
암튼 그래서 부지런히 자바책을 읽어야겠다.
클래스
바로 이전 게시글에서 클래스를 단순히 객체를 정의하고 생성하기 위한 존재라고 했는데, 다른 관점에서 클래스를 바라본다고 한다.
우선 클래스는 데이터와 함수(메서드)로 이루어진 존재다. 이전에는 변수, 그 뒤에는 여러 개의 변수를 저장하기 위한 배열이, 그리고 여러 종류의 데이터를 저장하기 위한 (배열은 한 종류 밖에 저장하지 못한다) 구조체가 등장했고, 이후 데이터와 메서드 간의 밀접한 관계를 인식하고 하나로 합친 것이 클래스다.
기본형 변수는 8개지만, 참조형 변수는 무한한데, 이는 사용자가 직접 정의한 클래스와 객체(사용자정의 타입)의 인스턴스 변수도 참조형 변수이기 때문이다.
변수
클래스변수, 인스턴스변수, 로컬변수가 있으며,
메서드 밖 클래스 영역에 생성되는 변수는 인스턴스 변수 (객체가 고유하게 가지는 값)
메서드 내에서 선언된 변수는 로컬 변수 (메서드가 종료되면 사라진다)
메서드 밖 클래스 영역에서 static으로 선언된 클래스변수 (같은 클래스에서 생성된 모든 객체가 공유 / 인스턴스 변수가 아니다.) 다른 말로 정적변수라고 한다. 난 정적변수가 익숙해. 접근법은 클래스명.정적변수명 (Member.time 이런 식으로)
메서드
특정 로직과 기능을 정의한 함수다. 여러 번 재사용되는 기능에 대하여 메서드에 선언시키면 높은 재사용성과 코드중복을 방지할 수 있다. 수정도 용이하고. 같은 기능을 100개의 클래스가 공유하고 있을 때, 그 기능에 수정이 필요하면 직접 100개의 클래스를 돌며 그 기능을 수정해야하지만, 메서드를 사용하면 메서드만 수정하면 그 메서드를 공유하는 100개의 클래스에 자동으로 변경사항이 들어간다.
매개변수와 인자로 전해준 값의 타입이 다르지만 호환가능할 때는 자동형변환이 일어난다. 매개변수로 double을 받는 메서드가 인자로 int나 long을 받았을 때 이런 일이 일어난다.
같은 클래스 내의 메서드는 서로 호출이 가능하지만 static 메서드는 호출이 불가능하다.
모든 메서드는 return이 필요하고, void로 선언된 메서드도 return;이 필요하나 컴파일러가 자동으로 추가해준다.
JVM 메모리 구조
자바가 실행되면 JVM은 프로그램 실행에 필요한 메모리를 할당받고 객체나 값들을 저장한다.
크게 힙 - 호출스택 - 메서드 영역이 있다.
메서드 영역은 클래스를 실행할 때 (또는 필요할 때) 클래스의 클래스파일을 읽고 그의 데이터를 저장한다. 이 때 정적변수도 이 때 생성된다.
힙은 객체와 인스턴스가 생성되는 공간이다. 만약 로컬변수로 객체 레퍼런스 변수를 선언하면 객체와 그의 인스턴스 변수들은 힙메모리에 저장되고, 그 객체의 주소(참조값)만이 호출스택의 로컬변수 영역에 저장된다.
호출스택은 메서드가 호출될 때 메서드에 필요한 메모리를 형성하고 로컬변수, 매개변수 등을 담는데 사용한다. 메서드가 종료되면 자동으로 반환된다. 호출스택에서 처음 호출된 메서드를 위한 공간(스택프레임)이 호출스택 맨 밑에 자리잡고, 이후 그 메서드가 다른 메서들을 호출하면 가장 위에 있는 스택프레임 위에 새 호출된 메서드를 위한 스택프레임이 형성된다. 호출스택은 가장 위에 있는 스택프레임 한 개만 실행한다.
즉, 메서드를 실행하다가 다른 메서드가 호출되면 호출된 메서드가 호출스택 맨 위에 올라가 그 메서드가 실행되고 호출한 메서드는 대기상태에 놓인다.
호출스택에서의 매개변수
메서드가 호출될 때 매개변수가 존재하면 매개변수로 지정한 값을 복사하여 스택프레임에 로컬변수로 넘겨준다. 기본형이면 값이 그대로 복사되고, 참조형이라면 참조값이 복사된다.


따라서 객체를 직접 넘기느냐와 객체의 인스턴스를 넘기느냐에 따라 차이가 생긴다.
넘겨진 매개변수가 기본형일 경우 값을 읽기만하고 변경할 수 없으나 참조형일 경우 수정할 수 있고 이것이 실제 객체에 영향을 끼친다.
이의 구분은 '기본형 변수를 넘기느냐'와 '객체 참조변수를 넘기느냐'의 차이다.
일례로 배열을 넘기더라도 배열의 각 요소는 객체의 참조값이 저장되므로(기본형으로 저장하지 않은경우) 배열의 값과 객체의 값이 수정될 수 있다.
따라서 메서드는 단 하나의 값만을 반환받을 수 있으나 배열을 적절히 응용하면 여러 개의 값을 반환받을 수 있다.
참조형 반환
참조형도 반환할 수 있으며 이 때는 객체의 주소값이 반환된다.
주소값을 반환할 때 이를 어떠한 변수에도 저장하지 않으면 해당 객체는 스택이 종료되면서 사라진다.
재귀호출
재귀호출이란 재귀함수와 동일하다. 메서드에서 자기자신을 호출하는 것을 뜻하며, 메서드의 호출이란 특정 메모리에 저장된 명령들을 수행하는 것 뿐이고, 호출된 메서드는 원래 값이 아닌 복사된 값으로 작업을 수행하기에 누가 호출했는지에는 영향을 끼치지 않는다. 로컬변수로 저장한 값이 재귀호출을 통하여 변경시키더라도 호출된 메서드의 복사된 로컬변수가 변하는 것이지 호출한 메서드의 로컬변수에는 영향을 끼치지 않는다.
다만 재귀호출은 조건문, 메서드, 복귀할 주소 저장 등 반복문보다 효율이 떨어지는 편이다. 하지만 간결하다. 대표적인 예시가 팩토리얼
int factorial(int n) {
int result = 1;
for (int i = 1; i <= n; i++) {
result *= i;
}
return result;
}
static int factorial(int n) {
if (n == 1) {
return 1;
}
return n * factorial(n - 1);
}
코드가 좀 더 간결하다.
물론 중괄호까지 지우면 거의 2줄짜리 코드를 만들 수도 있으니 (메서드 선언부 제외) 확실히 재귀함수가 더 간결한 편이다.
근데 만약 저 재귀함수 코드에서 n을 0으로 대입하면?
main - factorial(0) - factorial(-1) ... 이렇게 스택에 쌓이다가 어느순간 호출스택의 메모리를 벗어나게 된다. 이것을 스택오버플로우 에러라고하고, 굳이 0이 아니더라도 n이 매우크면 발생할 수 있다. 따라서 매개변수 유효성 검사가 필요하다. 또는 탈출조건 없이 무한호출에 빠져도 발생한다.


n이 1억일 때도 팩토리얼을 위한 스택이 1억개가 생기며 스택오버플로우가 일어난다.
이럴 땐 그냥 반복문을 써야한다.
정적메서드
메서드를 static으로 생성하면 정적메서드다. 헤드퍼스트 때 했던 것 같으니 간략하게 알아보자면
인스턴스 메서드는 인스턴스 변수과 관련된 메서드를 실행한다. 인스턴스 변수는 객체 인스턴스가 만들어져야하므로 객체가 생성되고 호출되어야만 실행할 수 있다.
정적 메서드는 인스턴스 변수와 무관하다. 따라서 굳이 객체가 생성되지 않고도 클래스명.메서드명 으로 접근할 수 있다. 대표적인 예시가 Math 클래스. 그렇기 때문에 정적메서드는 인스턴스 변수를 사용할 수 없다. 하지만 인스턴스 메서드가 정적메서드와 정적변수를 사용하는 것은 가능
추가로 메서드나 인스턴스 변수를 사용하지 않으면 static을 붙이는 것이 권장된다. 인스턴스 메서드는 실행 시 호출되어야할 메서드를 찾는 과정이 추가로 필요하기 때문에 시간이 더 걸린다. 정적메서드는 그냥 static 붙은 것을 찾아서 실행하면 되기 때문
메서드 - 인스턴스 변수
같은 클래스의 구성요소들은 서로 참조나 호출이 가능하다. 그냥 메서드이름만 붙이면 호출이된다.
하지만 정적요소(정적메서드, 정적변수 등)이 인스턴스요소를 호출할 때는 인스턴스를 생성해야한다. 왜냐하면 정적요소들은 클래스가 호출되는 순간 바로 생성되지만, 인스턴스요소들은 객체가 생성되지 않으면 생성되지 않기 때문.
따라서 클래스요소만 존재하고 인스턴스요소들은 존재하지 않을 수 있어 인스턴스 생성이 강제된다.
static 메서드가 static 메서드를 호출하고, 인스턴스 메서드가 인스턴스 메서드와 static 메서드를 자유롭게 호출할 수 있으나 static 메서드가 인스턴스 메서드를 그대로 호출할 수는 없다.
암튼 이렇게 클래스와 메서드에 대해서 다뤄봤다. 사실 6장이 아예 끝난 것은 아니고 한 절반정도 읽었는데 이 뒤로는 생성자와 관련된 얘기가 나와서 한 번 끊고 가려고한다. 백준도 해야하고
'언어공부 > Java | Kotlin' 카테고리의 다른 글
| [CS] 자바의 정석 독서 #8 - 생성자와 초기화 (0) | 2025.10.01 |
|---|---|
| [CS] 자바의 정석 독서 #7 - 메서드 오버로딩 (0) | 2025.10.01 |
| [CS] 자바의 정석 독서 #6 - 객체지향 프로그래밍, OOP란 무엇인가? (0) | 2025.09.24 |
| [CS] 자바의 정석 독서 #5 - 연산자 (0) | 2025.09.22 |
| [CS] 자바의 정석 독서 #4 - 형변환 (0) | 2025.09.21 |