자바의 정석 6. 객체지향 프로그래밍
클래스와 객체
클래스와 객체의 정의와 용도
클래스란 객체를 정의해놓은 것
또는 클래스는 객체의 설계도 또는 틀
이라고 정의할 수 있다.
- 클래스의 정의 : 클래스란 객체를 정의해 놓은 것이다.
- 클래스의 용도 : 클래스는 객체를 생성하는데 사용된다.
예를 들자면 TV 설계도를 클래스라고 할 수 있고 TV를 객체라고 할 수 있다.
객체와 인스턴스
클래스로부터 객체를 만드는 과정을 클래스의 인스턴스화(instantiate)라고 하며, 어떤 클래스로부터 만들어진 객체를 그 클래스의 인스턴스(instance)라고 한다. 객체는 모든 인스턴스를 대표하는 포괄적인 의미를 가지고 있고 인스턴스는 어떤 클래스로부터 만들어진 것인지를 강조하는 보다 구체적인 의미를 지니고 있다.
ex)
- 책상은 인스턴스다 ❌ -> 책상은 객체다 ⭕️
- 책상은 책상 클래스의 객체다 ❌ -> 책상은 책상 클래스의 인스턴스다 ⭕️
인스턴스의 생성과 사용
1
2
3
4
5
6
7
8
9
10
11
12
13
class Tv {
String color; // 색깔
boolean power; // 전원상태(on/off)
int channel; // 채널
void power() { power = !power; } // 전원on/off
void channelUp( channel++;) // 채널 높이기
void channelDown {channel--;} // 채널 낮추기
}
Tv t; // Tv인스턴스를 참조하기 위한 변수 t를 선언
t = new Tv() // Tv 인스턴스 생성
t.channel = 7; // Tv 인스턴스의 멤버변수 channel의 값을 7로 한다.
t.channelDown(); // Tv 인스턴스의 메서드 channelDown()을 호출한다.
인스턴스를 생성하면 위와 같은 구조를 지닌다.
인스턴스는 참조변수를 통해서만 다룰 수 있으며, 참조변수의 타입은 인스턴스의 타입과 일치해야한다.
클래스의 또 다른 정의
클래스는 프로그래밍적인 관점에서 데이터와 함수를 묶는 것을 의미한다.
변수와 메서드
선언위치에 따른 변수의 종류
변수는 클래스 변수, 인스턴스 변수 지역 변수 모두 세 종류가 있다.
1
2
3
4
5
6
7
8
9
10
class Variables
{
int iv; // 인스턴스 변수
static int cv; // 클래스변수(static 변수, 공유 변수)
void method()
{
int lv = 0; // 지역 변수
}
}
인스턴스 변수(intance variable)
클래스 영역에 선언되며, 클래스의 인스턴스를 생성할 때 만들어진다.
인스턴스는 독립적인 저장공간을 가지므로 서로 다른 값을 가질 수 있다. 인스턴스마다 고유한 상태를 유지해야하는 속성의 경우, 인스턴스 변수로 선언한다.클래스 변수(class variable)
클래스 변수를 선언하는 방법은 인스턴스 변수 앞에
static
을 붙이기만 하면 된다.
클래스 변수는 모든 인스턴스가 공통된 저장공간(변수)을 공유하게 된다.
한 클래스의 모든 인스턴스들이 공통적인 값을 유지해야하는 속성의 경우, 클래스 변수로 선언해야 한다.
클래스이름.클래스변수
와 같은 형식으로 사용한다.지역 변수(local variable)
메서드 내에 선언되어 메서드 내에서만 사용 가능하며, 메서드가 종료되면 소멸되어 사용 할 수 없게 된다.
JVM의 메모리 구조
JVM은 시스템으로 부터 프로그램을 수행하는데 필요한 메모리를 할당받고 JVM은 이 메모리를 용도에 따라 여러 영역으로 나누어 관리한다.
메서드 영역(method area)
클래스 정보와 클래스 변수가 저장되는 곳
힙(Heap)
인스턴스가 생성되는 공간. new 연산자에 의해서 생성되는 배열과 객체는 모두 이곳에 생성된다.
호출스택(call stack or execution stack)
메서드의 작업공간. 메서드가 호출되면 메서드 수행에 필요한 메모리 공간을 할당받고 메서드가 종료되면 사용하던 메모리를 반환한다.
클래스 메서드(static 메서드)와 인스턴스 메서드
인스턴스 메서드는 인스턴스 변수와 관련된 작업을 하는, 즉 메서드의 작업을 수행하는데 인스턴스 변수를 필요로 하는 메서드이다.
클래스 메서드(static 메서드)는 인스턴스와 관계 없는 메서드이다.
- 클래스를 설계할 때, 멤버 변수 중 모든 인스턴스에 공통으로 사용하는 것에 static을 붙인다.
- 클래스 변수 (static 변수)는 인스턴스를 생성하지 않아도 사용할 수 있다.
- 클래스 메서드(static 메서드)는 인스턴스 변수를 사용할 수 없다.
- 메서드 내에서 인스턴스 변수를 사용하지 않는다면, 성능을 위해 static을 붙이는 것을 고려한다.
클래스 멤버와 인스턴스 멤버간의 참조와 호출
클래스 멤버는 클래스가 메모리에 올라갈 때 생성되고 인스턴스 멤버는 인스턴스가 생성되었을 때 생성된다.
즉, 클래스 멤버는 항상 인스턴스 멤버보다 먼저 생성되어있다.
따라서 클래스 멤버가 인스턴스 멤버를 참조하는 것은 안되지만 인스턴스 멤버가 클래스 멤버를 참조하는 것은 가능하다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class TestClass{
void instanceMethod() {} // 인스턴스 메서드
static void staticMethod() // static 메서드
void instanceMethod2 () { // 인스턴스 메서드
instanceMethod(); // 다른 인스턴스 메서드를 호출한다.
staticMethod(); // static 메서드를 호출한다.
}
static void staicMethod2() { // static 메서드
instanceMethod(); // 에러! 인스턴스 메서드를 호출 할 수 없다.
staticMethod(); // static 메서드는 호출 할 수 있다.
}
}
오버로딩(overloading)
오버로딩이란?
한 클래스 내에 같은 이름의 메서드를 여러 개 정의하는 것을 메서드 오버로딩, 간당히 오버로딩이라고 한다.
오버로딩 조건
- 메서드 이름이 같아야 한다.
- 매개변수의 개수 또는 타입이 달라야 한다.
반환 타입은 오버로딩을 구현하는데 아무런 영향을 주지 못한다.
오버로딩의 장점
동일한 동작을 하는 여러 메서드들이 하나의 이름으로 정의 될 수 있다. 메서드의 이름을 절약할 수 있다.
가변인자(varargs)와 오버로딩
1
public PrintStream printf(String format, Object... args){ ... }
타입... 변수명
과 같은 형식으로 선언하는 가변인자를 활용하면 여러 메서드를 하나로 간단히 대체할 수 있다. 가변인자는 항상 마지막 매개변수이어야 한다.
1
2
public PrintStream printf(String format, Object... args){ ... }
public PrintStream printf(Object... args){ ... }
두 메서드는 별 문자가 없어 보이지만 컴파일 에러가 발생한다. 그 이유는 컴파일러가 오버로딩된 메서드를 구분하지 않기 때문이다.
##생성자
생성자란?
생성자란 인스턴스 초기화 메서드이다. 인스턴스 생성시 실행되어야 할 작업을 위해서도 사용된다.
- 생성자의 이름은 클래스의 이름과 같아야 한다.
- 생성자는 리턴 값이 없다.
1
2
3
4
5
6
7
8
9
// 생성자도 overloading이 가능하다.
class Card{
Card(){
}
Card(String k, int num){
}
}
기본생성자
생성자 없이도 인스턴스를 생성할 수 있었던 이유는 컴파일러가 기본 생성자를 제공해주기 때문이다. 클래스에 생성자가 정의되지 않은 경우 컴파일러는 클래스 이름(){}
와 같이 기본 생성자를 만든다. 이는 매개변수도 없고 아무런 내용도 없다.
기본 생성자가 컴파일러에 의해서 추가되는 경우는 클래스에 정의된 생성자가 하나도 없을 때 뿐이다.
생성자에서 다른 생성자 호출하기 - this(), this
같은 클래스의 멤버들 간에 서로 호출할 수 있는 것처럼 생성자 간에도 아래 조건을 만족할 경우 서로 호출이 가능하다.
- 생성자의 이름으로 클래스이름 대신 this를 사용한다.
- 한 생성자에서 다른 생성자를 호출할 때는 반드시 첫 줄에서만 호출이 가능하다.
1
2
3
4
5
6
Car(String color){
door = 5;
Car(colo, "auto", 4);
// 에러1. 생성자의 두 번째 줄에서 다른 생성자 호출
// 에러2. this(color, "auto", 4);로 호출해야함
}
생성자에서 다른 생성자를 첫 줄에서만 호출이 가능하도록 한 이유는 생성자 내에서 초기화 작업도중에 다른 생성자를 호출하게 되면, 호출된 다른 생성자 내에서 초기화를 할 것이므로 다른 생성자를 호출하기 이전의 초기화 작업이 무의미해질 수 있기 때문이다.
this : 인스턴스 자신을 가리키는 참조변수, 인스턴스 주소가 저장되어 있다. 모든 인스턴스 메서드에 지역변수로 숨겨진 채로 존재한다. this(), this(매개변수) : 생성자, 같은 클래스의 다른 생성자를 호출할 때 사용한다.
this와 this()는 비슷하게 생겼을 뿐 완전히 다른 것이다. this는 ‘참조변수’이고 this()는 ‘생성자’이다.
변수의 초기화
초기화 블록
- 클래스 초기화 블록 : 클래스 변수의 복잡한 초기화에 사용
- 인스턴스 초기화 블록 : 인스턴스 변수의 복잡한 초기화에 사용된다.
1
2
3
4
5
6
7
class Car{
static {} // 클래스 초기화 블록
{} // 인스턴스 초기화 블록
Car(){}
}
- 클래스 변수의 초기화 순서 : 기본값 -> 명시적 초기화 -> 클래스 초기화 블록
- 인스턴스 변수의 초기화 순서 : 기본값 -> 명시적 초기화 -> 인스턴스 초기화 블록 -> 생성자