JAVA는 객체 지향 언어다
자바는 1991년에 등장한 객체지향 언어다. 자바 이전에는 C 언어 등 절차지향 언어가 대세를 이루고 있었다. 하지만 지금은 '대한민국은 자바 공화국이다'라는 말이 나올 정도로 자바가 큰 사랑을 받고 있다. 이는 객체지향의 장점이 많이 드러나기 때문이다.
객체라는 게 무엇일까? '손님 객'자를 써서 주체가 아닌 개체를 이른다. 즉, 주체는 '나'이므로 나를 제외한 내가 사용하는 나머지 모든 것이 객체가 된다. 사물은 물론 논리와 같은 것도 포함된다. 그렇다면 객체지향은? 바로 '주변의 많은 것들을 객체화하라'라는 것.
간단한 예시를 들어보자. 철수는 영희에게 고백하기 위해 선물을 고민하고 있다. 그러던 중, 영희가 과일을 좋아한다는 사실을 알았다. 그래서 과일을 주며 고백하기로 결정했다. 철수가 만약 주체 지향적인 삶을 산다면 어떻게 영희에게 과일을 선물할까?
철수는 돈을 들여 직접 과일 나무 모종 몇가지를 구매했다. 과일 나무를 가꿀 경작도 몇 평 구입했고, 회사를 그만두고 직접 과수원에 상주하며 관리 했다. 그 결과 과일 몇 종류를 소량 얻을 수 있었다. 이제 영희에게 선물을 주며 마음을 전달하는 일만 남은 것이다. 하지만 문제가 생겼다. 과일을 가꾸는 과정에서 너무 오랜 시간이 걸려버렸고, 그 사이 영희는 애인이 생긴 것이다. 철수는 고백을 하지 못했고, 회사는 그만뒀으며 과일을 가꾸다 몸이 망가졌다. 이는 철수가 과일을 주체 지향적으로 얻었기 때문이다. 하지만, 만약 철수가 객체 지향적인 삶을 살아 과일을 구하는 과정을 객체에게 맡겼다면 어땠을까?
우선, 과일 가게에서 돈을 이용해 과일을 구매했으면 됐을 것이다. 과일 가게에서는 필요한 과일을 농부에게 발주하고, 별도의 과정 없이 철수에게 배송되었을 것. 즉, 과일 가게, 농부 모두 객체가 되는 것이다. 객체를 이용한 철수는 과일을 얻기 위해 몇 개월을 쓰지 않아도 되어 고백에 성공했을 것이다.
클래스
앞서 우리가 객체가 무엇인지 알아봤다. 그렇다면 이런 객체를 JAVA에서는 어떻게 구현할까?
많은 자료에서 클래스를 '설계도'라고 표현하는데, 더 쉽게 생각하면 '사람'과 '자동차'를 예를 들 수 있다.
편의상 사람과 자동차의 대표적인 특성 4가지를 꼽았다. 각 특징들은 전형적인 사람/자동차의 대표적인 특정이며, 대부분의 사람/자동차가 가지고 있다. 하지만 모두 같은 특성을 가졌다고 해서 다 획일적인 것은 아니다. 사람만 봐도 노인인지, 어린이인지, 여자/남자인지, 자동차는 세단인지, 경차인지, 스포츠카인지 알 수가 없다. 즉 아주 기본이 되는 틀이라고 생각하면 좋다. 그래서 설계도라고 표현하는 것이다.
아주 기본적인 틀을 가지고 여러가지를 만들어낼 수 있다. 가령, 모양과 재료가 똑같은 도자기라도 무늬를 다르게 찍으면 서로 다른 도자기가 되는 것이다. 이것을 상속과 다형성의 개념이라고 볼 수 있다.
JAVA에서 class
자바에서는 'class'라는 키워드로 클래스를 정의한다.
public class Person{
String name;
int age;
}
Person이라는 클래스를 한개 정의했다. 이 클래스가 가지는 특성은 name과 age다. 이제 부터 클래스 안의 특성은 '멤버 변수'(클래스 변수)라고 칭한다.
Person이라는 클래스를 정의하고 특성을 정의했으니, 이제 Person이 할 수 있는 행동 몇가지를 정의해보자. 사람이 할 수 있는 건 말하거나 먹기가 대표적으로 있다.
public class Person{
String name;
int age;
public void eat(){
System.out.println(name + "이 맛있게 먹어요.");
}
public void speak(){
System.out.println(name + "이 말 좀 할게요.");
}
}
eat과 speak는 각각 행동을 나타내는 텍스트를 출력한다. 이를 '메서드'라고 하는데, 클래스가 가진 메서드는 '멤버 메서드'라고 칭한다. 클래스와 멤버 변수, 멤버 메서드를 정의했으니 Kiky라는 사람을 하나 만들어보자.
Person Kiky = new Person();
Kiiky.name = "eun";
Kiky.age = 20;
Kiky.eat();
//결과: eun이 맛있게 먹어요.
Kiky라는 Person의 eat을 호출했다. 우리가 클래스를 정의할 때 선언했던 멤버 메서드를 사용할 수 있다.
지금은 멤버 변수가 name과 age 두개 뿐이지만, 대형 프로젝트에서는 무수히 많은 멤버 변수를 사용한다. 그럴 때마다 name과 age와 같이 한 줄 한 줄 써내려 가는 게 비효율적일 수 있다. 그래서 생성자를 사용한다.
static?
위에서 멤버 변수(클래스 변수)를 설명했다. 지금은 아주 단적인 예만 들었기 때문에 문제되진 않지만, 예를 들어
생성자
생성자란 객체를 생성할 때 호출하는 메서드 비슷한 것을 말하는데, 주로 일반 멤버 변수의 초기화나 객체를 생성할 때 실행하는 작업을 정리한다. 위에서 정의했던 name과 age를 생성자로 만들어보자.
public class Person{
Person(name, age){
this.name = name;
this.age = age;
}
public void eat(){
System.out.println("맛있게 먹어요.");
}
public void speak(){
System.out.println("말 좀 할게요.");
}
}
this 키워드가 보이는데, 이는 현재 Person 객체를 의미한다. 만약 this 키워드가 없었다면 생성자의 매개변수인 name과 Person의 name이 구별가지 않는다. 즉, [생성자의 name = 생성자의 name]이 되어버리기 때문에, [Person의 name은 생성자의 name]임을 가리킨다.
생성자는 클래스 이름과 동일하며 매개 변수를 받는 부분만 존재한다.
우리는 이제 이렇게 선언할 수 있다.
Person Kiky = new Person("eun",20);
원래라면 name과 age를 선언하는 2줄까지 포함해 3줄이었을 코드가 1줄로 줄어들었다. 간결해져 직관적이다.
상속과 다형성
앞서 클래스의 개념을 익힐 때 상속과 다형성에 대해 언급했다. 상속과 다형성 역시 객체 지향 언어의 특징 중 하나인데, 그림으로 먼저 살펴보자.
귀여운 고양이와 인간은 동물이다. 동물은 대표적인 3가지 메서드를 가지고 있다. 그리고 우리는 같은 동물이지만 서로 다르게 진화했다. (애초에 '종' 자체가 다르지만, 융통성있게 넘어가자.) 동물이 가지고 있는 특성은 고양이와 인간 모두 가지고 있지만, 우리는 고양이와 다르다. 이게 바로 다형성이다.
class Animal{
public void eat(){
System.out.println("먹어요");
}
public void sleep(){
System.out.println("자요");
}
public void speek(){
System.out.println("소리내요");
}
}
class Person extends Animal{
public void walk(){
System.out.println("걸어요.");
}
}
class cat extends Animal{
public void meow(){
System.out.println("야옹");
}
}
편의상 한 클래스에 정의했다. Animal이라는 클래스를 Peron과 cat이 상속 받고 있으므로 Animal의 멤버 메서드인 eat(), sleep(), speek()를 모두 사용할 수 있다. super()라는 메서드를 통해 부모 클래스(상속대상이 되는 클래스)의 변수를 그대로 상속받을 수도 있다.
접근 제한자
메서드를 정의할 때, 일련의 규칙이 존재한다.
접근 제한자는 멤버 등에 사용되며 해당 요소를 외부에서 사용할 수 있게 할 것인지를 제어한다.
접근 제한자의 사용 범위는 상기 표와 같다. protected와 private은 클래스에 사용할 수 없다. 그렇다면, 각각 접근을 제한하는 범위는 어떻게 될까?
먼저 같은 클래스 내에서는 모든 제한자들이 접근이 가능하다. 그러나 같은 패키지(일종의 '폴더'의 개념, 자바에서 src 아래 소스 코드의 모음집)가 되면 private은 접근이 불가능하다.
default는 접근 제한자를 지정하지 않았을 때 자동으로 부여되는 접근 제어다. 같은 패키지 내에서만 접근이 가능한 것이다. protected는 다른 패키지에 존재하는 자손(상속 받은) 클래스에서는 접근 가능하고, public은 전체에서 접근이 가능하다.
return type
말 그대로 반환하는 값의 type을 이야기한다. String, int, boolean 등... 다양한 것이 될 수 있으며 반환할 것이 없다면 'void'로 선언하면 된다. 반환 type을 객체로 선언할 수도 있다.
전역 변수와 지역 변수
전역 변수
전역 변수는 프로그램의 여러 부분에서 접근할 수 있는 변수를 말한다. 변수의 유효 범위는 프로그램 전체가 된다.
한 번 정의되면 어디서든지 사용할 수 있다는 장점이 있어 여러 부분에서 공유되어하는 데이터를 저장하기 위해 사용된다. 그러나 이런 장점 때문에 전역 변수를 남용하게 되면 참조하면 안 될 곳에서 참조하거나, 데이터를 바꿔버리는 일이 발생하기도 한다.
위에서 클래스에 선언된 변수들을 멤버 변수이자 클래스 변수라고 했다. 틀린 것은 아니지만 차이는 있다.
멤버 변수에 static 키워드가 선언된 변수를 클래스 변수라고 한다.
public class Person{
static String name = "eun";
}
클래스 변수는 클래스가 로딩되는 시점에 메모리에 적재된다. 그래서 개별 객체(인스턴스)의 생성과는 무관하다. 때문에 클래스 내에서 공유되는 변수라고 생각하면 편하다.
public class Person{
String name = "eun";
}
Person person1 = new Person();
Person person2 = new Person();
person1과 person2의 name은 별개가 된다.
🚫 static
static은 프로그램 시작부터 종료까지 메모리에 상주한다. 또한, 어떤 클래스에서도 static에 접근할 수가 있다.
일전에 개발자들이 빠른 프로그램 제작을 위해 static을 남용하는 때가 있었는데, 이 때문에 메모리 사용량이 많아져프로그램이 무거워지는 현상이 많았다. 따라서 특수한 경우 외에는 static 사용을 피하는 것이 좋다.
변수에 static을 쓸 수 있으며 선언하지 않으면 기본적으로 default가 적용된다.
지역 변수
반대로 지역 변수는 클래스 영역 이외의 모든 중괄호(블럭) 안에 선언되는 변수들을 말한다. 즉, 지역 변수는 메서드나 생성자의 내부 또는 파라미터, 그리고 초기화 블록의 내부에서 선언된 변수들이다.
public class Person{
static String name = "eun";
void call(){
int age = 20; //지역 변수
}
}
위와 같은 예시가 될 수 있다. (call은 접근 제한차를 생략했기 때문에 default로 설정된다.)
name은 클래스 변수, age는 지역 변수. 지역 변수는 변수가 선언된 행이 실행될 때 스레드별로 생성된 메모리의 스택 영역에 생성되고 해당 영역이 끝나면 자동 소멸된다. 주의할 점이라면 지역 변수는 클래스 변수와 다르게 자동 초기화가 불가능하다. 선언과 동시에 할당이 일어나야 한다.
'Programming > JAVA' 카테고리의 다른 글
[JAVA] 스레드 (0) | 2024.04.07 |
---|---|
[JAVA] 자바의 다형성 (0) | 2024.04.07 |
[JAVA] 자바 상속, super(), 메서드 오버라이딩 (0) | 2024.04.05 |