Table of Contents
Intro
객체지향 언어
객체지향 언어는 절차지향 언어와 자주 비교되는 언어중 하나입니다. 절차지향 언어는 코드의 순서에 의미를 가지고 있고, 객체지향 언어는 코드의 관계에 중요한 의미를 가지고 있습니다.
객체지향 언어의 대표적인 장점은 코드의 재사용성이 높다는 것입니다. 코드의 재사용성이 높아짐으로써 불필요한 중복 코드가 제거되며, 개발과 유지보수에 들어가는 시간이 줄고, 확장성이 높아집니다.
위의 장점을 얻기 위해 코드를 작성하기 전에 어떤식으로 객체를 정의하고 관계를 지을지 계획하는 것이 굉장히 중요합니다. 하지만 너무 많은 시간을 계획에 몰두하는 것은 오히려 개발의 지연을 발생시키므로, 일단 프로그램을 기능적으로 완성한 후 어떻게 객체지향적으로 코드를 개선할 수 있을지 고민하며 점차 개선해 나가는 것이 좋습니다.
클래스와 객체
- 클래스: 실체를 만들기 전에 작성하는 설계도. 변수(속성)와 메서드(기능)를 바탕으로 정의
- 객체: 클래스에 정의된 내용을 바탕으로 메모리에 생성된 실체
객체 생성
클래스로부터 객체를 생성하는 방법은 다음과 같습니다.
// <참조변수의 타입> <참조변수> = new <클래스명>();
Person p = new Person();
어떤 클래스의 경우, 연산자 new
를 생략하기도 하고, 위의 코드를 두 줄로 나눠 적기도 하고, 매개변수를 전달하기도 하는 등 객체 생성의 몇가지 변형된 방법 또한 존재합니다.
객체 생성과 변수에 할당되는 과정은 다음과 같습니다.
ex)
1. Person p;
Person 클래스 타입의 참조변수 p를 선언하면, 메모리에 참조변수 p를 위한 공간이 마련
(아직 객체가 생성되지는 않았으므로 비어있음)
2. p = new Person();
연산자 new에 의해 Person클래스의 객체가 메모리의 빈 공간에 생성
(멤버변수는 각 자료형에 해당하는 기본값으로 초기화)
대입연산자(=)에 의해 객체의 주소갑이 참조변수 p에 저장
클래스 정의
객체를 생성하려면 생성하려는 객체의 타입인 클래스를 먼저 정의해야 합니다. 클래스는 크게 변수와 메서드로 구성되어 있습니다. 선언하는 위치와 제어자의 종류에 따라 변수와 메서드의 성질이 변하게 되는데 이에 대해서는 뒤에서 정리하도록 하며, 여기서는 가장 간단하게만 짚고 넘어가겠습니다.
class Person {
// 변수
String name;
int age;
// 메서드
void introduceMyself() {
System.out.println("My name is " + this.name)
}
}
변수
위에서 변수를 정의하는 코드를 간단하게 살펴봤습니다. 변수는 클래스의 어느 위치에서 선언하냐에 따라 종류가 달라지게 됩니다. 종류에는 클래스 변수, 인스턴스 변수, 지역 변수가 있습니다.
-
인스턴스 변수
인스턴스 변수는 인스턴스가 생성될 때 같이 생성되는 변수로, 인스턴스마다 별도의 저장공간을 가지기 때문에 서로 다른 값을 가질 수 있습니다. -
클래스 변수
클래스가 메모리에 올라가는 시점에 생성되는 변수로, 모든 인스턴스가 공통된 저장공간을 공유하게 됩니다. 클래스 변수는 앞에static
을 붙여주기만 하면 되고<클래스명>.<클래스 변수명>
으로 사용합니다.
메서드
메서드는 특정 작업을 수행하는 코드를 하나로 묶은 것입니다. 예를 들어 두 수의 덧셈, 게임 캐릭터의 공격과 같은 것들이 메서드에 해당합니다.
메서드는 크게 선언부와 구현부로 이루어져 있습니다.
class Person {
// 선언부
int add(int a, int b)
// 구현부
{
int result = a + b;
return result;
}
}
-
메서드 선언부
선언할 수 있는 매개변수의 개수는 거의 제한이 없지만, 입력해야 할 값의 개수가 많은 경우에는 배열이나 참조변수를 사용하면 됩니다.메서드 선언부 앞에 메서드 수행 후 반환할 결과값의 타입을 적습니다. 반환값이 없는 경우에는
void
라고 적습니다. -
메서드 구현부
메서드 내에 선언된 변수들은 그 메서드 내에서만 사용할 수 있는 지역변수 입니다.메서드에서 반환할 결과값을
return
문 뒤에 작성합니다. 결과값은 선언부에서 정의한 타입과 일치해야 합니다.
메서드의 매개변수
자바에서는 메서드를 호출할 때 인자 값을 메서드의 매개변수에 복사해서 넘겨줍니다. 넘겨준 인자의 타입이 기본형인지 참조형인지에 따라 복사되는 값이 다릅니다.
-
기본형 매개변수
매개변수가 기본형인 경우에는 기본형 값을 복사해 오기 때문에, 값을 읽을 수만 있고 수정할 수는 없습니다.class Person { public static void main(String[] args) { int a = 10; System.out.println(introduceMyself(a)); // 1000 System.out.println(a); // 10 (변경안됨) } // 기본형(정수) 매개변수 a static int introduceMyself( int a ) { a = 1000; return a; } }
-
참조형 매개변수
매개변수의 타입이 참조형인 경우에는 값이 아니라, 값을 가지고 있는 인스턴스의 주소를 복사해 옵니다. 주소가 복사되기 때문에 값을 읽어오는 것 뿐 아니라 변경할 수도 있게 됩니다.class Person { public static void main(String[] args) { Data d = new Data(); d.x = 10; System.out.println(introduceMyself(d)); // 1000 // introduceMyself 메서드 안에서 변경된 값이 반영됨 System.out.println(d.x); // 1000 } // 참조형(Data 타입) 매개변수 d static int introduceMyself( Data d ) { d.x = 1000; return d.x; } } class Data { int x; }
static
위의 예제에서 중간중간 등장했던 static
키워드에 대해 짚고 넘어가도록 하겠습니다. static
은 인스턴스 단위가 아닌 클래스 단위로 변수와 메서드를 정의하도록 해줍니다.
static
키워드를 붙였을 때의 특징은 다음과 같습니다.
- 인스턴스를 생성하지 않아도 사용할 수 있다
- 모든 인스턴스가 공유해서 사용한다
static
이 붙은 변수를 클래스 변수, 메서드를 클래스 메서드라고 한다- 클래스 메서드는 인스턴스 변수를 사용할 수 없다
static
은 다음과 같은 경우에 사용합니다.
- 멤버변수 중 모든 인스턴스에서 공통된 값을 가져야 하는 경우 또는 가져도 되는 경우(메모리 효율성)
- 메서드 중 인스턴스 변수나 인스턴스 메서드를 사용하지 않는 경우
표기법은 다음과 같습니다.
변수에 붙는 경우: static <변수 타입> <변수명>
ex) static int a = 5;
메서드에 붙는 경우: <접근 제어자> static <리턴 타입> <변수명>
ex) public static void main() {}
-> 타입 앞에 붙는다
메서드 오버로딩
한 클래스 내에 같은 이름의 메서드를 여러 개 정의하는 것을 메서드 오버로딩 또는 간단히 오버로딩이라고 합니다. 오버로딩이 성립하기 위해서는 다음과 같은 조건을 만족해야 합니다.
- 메서드 이름이 같아야 한다
- 매개변수의 개수 또는 타입이 달라야 한다
- 반환 타입은 관계없다
오버로딩을 사용하는 경우는 같은 기능을 하지만 매개변수의 개수나 타입이 달라질 때입니다.
인스턴스 초기화 메서드: 생성자
생성자는 인스턴스가 생성될 때 가장 먼저 호출되는 인스턴스 초기화 메서드입니다. 인스턴스 초기화는 인스턴스 변수들을 초기화하는 것을 뜻합니다.
생성자 역시 메서드처럼 클래스내에 선언되며, 구조도 메서드와 유사하지만 리턴값이 없다는 점이 특징입니다. 그렇다고 void를 사용하는 것은 아니고 그냥 아무것도 적지 않으면 됩니다.
생성자를 작성하는 규칙은 다음과 같습니다.
- 생성자의 이름은 클래스의 이름과 같아야 한다
- 생성자는 리턴 값이 없다
생성자의 특징은 다음과 같습니다.
- 생성자도 오버로딩이 가능하기 때문에 하나의 클래스에 여러개의 생성자가 존재할 수 있다
- 기본(default) 생성자가 있어서 정의하지 않아도 사용할 수 있다
- 새로운 생성자를 추가한 경우, 기본 생성자도 다시 정의해줘야 한다
생성자는 다음과 같은 경우에 정의합니다.
- 인스턴스 변수의 초기화가 필요한 경우
- 필요하지 않으면 컴파일러가 제공핳는 기본 생성자를 사용하면 된다
class Person {
// 인스턴스 변수
String name;
int age;
// 매개 변수가 없으면 기본 생성자
Person() {}
// 매개 변수가 있는 생성자
Person(String name, int age) {
this.name = name;
this.age = age;
}
public static void main(String[] args) {
Person p1 = new Person();
Person p2 = new Person("Peter", 25);
System.out.println("p1의 이름: " + p1.name + ", p1의 나이: " + p1.age); //p1의 이름: null, p1의 나이: 0
System.out.println("p2의 이름: " + p2.name + ", p2의 나이: " + p2.age); //p2의 이름: Peter, p2의 나이: 25
}
}
생성자에서 this()
를 사용해 다른 생성자를 호출할 수도 있습니다. 필수적인 것은 아니고 코드가 조금 더 간결해지기 때문에 사용합니다.
위의 예시에서 기본 생성자가 사용될 경우 이름이 String 객체의 기본값인 null
로 초기화되어 있는 것을 확인할 수 있습니다. 이렇게 매개변수가 없을 때 name
의 초기값을 no-name
으로 하고 싶다고 가정해보겠습니다.
Person() {
name = "no-name";
age = 0;
}
이런 식으로 작성할 수도 있지만 this()
를 사용해 조금 더 간결하게 나타낼 수 있습니다.
Person() {
// Person 클래스의 Person(String name, int age) 생성자를 this()로 호출
this("no-name", 0)
}