본문 바로가기

[책]명품 JAVA Programming

명품 자바 프로그래밍 4장 요약

세상 모든것이 객체

드디어 객체를 배우게 됐습니다. 객체는 쉽게 말하면 Obeject입니다. 게임을 할때 나오는 적 몬스터들은 각각이 객체이고 NPC들, user캐릭터들, 각종 아이템 모두 '객체'입니다. 게임내의 NPC들 각각 고유한 특성을 가지고 행동(대사)을 하죠? 객체는 모두 각각 존재하면서 서로 상호작용이 가능 합니다. 앞에서 Scanner sc = new Scanner(System.in)코드를 이용해서 입력을 받았습니다. 우리는 Scanner라는 객체를 생성해서 입력을 받는 용도로 사용한 것입니다.

아직 이해가 안 될수 있습니다. 좀 더 공부하다보면 이해 될겁니다 억지로 이해하려 하지 마세요! 어차피 이해됩니다.

 

객체 지향 언어의 특성

1. 캡슐화(Encapsulation)

객체를 캡슐로 싸서 객체 내의 코드를 보호하고 볼 수 없게 하는 것입니다. 가루약을 캡슐에 싸서 적절한 시기까지 녹지 않도록 보호하는게 알약입니다. 알약의 캡슐과 같이 사용자(프로그램 다운받아 쓰는 우리)들이 굳~~이 소스코드 까지 보지 않아도 버튼만 띡 누르면 딱 실행되게 하는것이 캡슐화 입니다. 잘 모르는 사용자가 소스코드를 건드려 오류가 날 수 있으니 그걸 방지하기도 하구요.

자바에서는 캡슐이 class입니다. 그안에 필드(변수), 메소드(함수)들이 있는 겁니다. 앞에서 Scanner sc = new Scanner(System.in)을 사용하기전에 import java.util.Scanner을 맨 앞줄 클래스 바깥에 작성 했습니다. 이는 자바의 util패키지 내에 있는 Scanner클래스를 지금 내 프로젝트에 사용할 수 있도록 import한다는 뜻인데 Scanner 클래스로 sc라는 객체를 만들어서 sc로 우리는 입력을 받았던 것입니다.

 

2. 상속(Inheritance)

상속의 개념을 나중에도 헷갈려 하는 친구들을 여럿 봤습니다. 객체개념과 캡슐화 개념은 자바를 사용하다보면 이해가 되지만 상속은 제대로 이해하고 가는게 중요할것 같습니다.

클래스를 부모클래스와 자식클래스로 나눕니다. 명시되어 있는것은 없고 상속받은 클래스가 상속을 준 부모클래스의 자식클래스가 되는 겁니다. 이는 다른말로 슈퍼클래스와 서브클래스 라고도 부릅니다.

무엇을 상속받나? 부모클래스 내에 있는 메소드와 변수를 모두 물려 받습니다. 여기서 자식클래스가 이를 모두 사용해도 되고 사용하지 않아도 됩니다. 재작성에 들어가는 시간을 줄이고자 하는 겁니다. 또한 자식클래스도 클래스이기 때문에 새로운 변수와 메소드를 작성할 수 있습니다. 그러면 자식클래스에서 작성한 변수나 메소드를 부모 클래스가 사용할 수 있을까요? 당연히 안되겠죠. 부모는 주기만 하고 받지는 않습니다. 자식의 자식클래스는 사용할 수 있습니다. 아래로 갈수록 코드가 다양해집니다.

 

3. 다형성(Polymorphism)

오버라이딩과 오버로딩이 있습니다. 자식클래스가 부모클래스로부터 상속받은 함수를 재구현 하는것을 오버라이딩 이라고 합니다.

오버로딩은 이름이 같은 함수가 여러개 존재하고 각각 다른 동작을 하게 만드는것이 오버로딩 이라고 합니다. 이는 다음장에서 자세히 다룰테니 이정도만 알고가도 될것 같습니다.

 

위의 3가지 특성은 자바 뿐만 아니라 객체지향 언어들이 가지고 있는 특성이므로 이를 잘 이해하고 이용하는것이 중요합니다. 객체 지향은 프로그램을 실제 세상에 가깝게 모델링한 것이기 때문에 각 객체간의 연결과 상호작용을 잘 파악한다면 코딩하는데 더욱 쉽게 다가갈 수 있을 겁니다.

 

클래스와 객체

클래스는 객체를 만드는 틀. 클래스는 객체를 만드는 설계도. 클래스는 객체를 만드는 재료.

객체는 클래스로 만들어내는 것입니다. 붕어빵틀로 붕어빵을 여러개 찍어낼 수 있고 팥을 생크림이나 초코크림, 치즈크림으로 바꿀 수 있죠? 클래스와 객체의 관계도 마찬가지 입니다. 클래스로 객체를 여러개 만들 수 있고 동일한 클래스로 생성한 객체를 다른 특성을 갖게 할 수 있습니다.

클래스는 public class Circle {} 와 같은 형식으로 만듭니다.

우리는 여태 여러 클래스를 만들었었죠. 손으로 써서 만들수도 있습니다. 

접근지정자 class 클래스명 {클래스내의 코드} 로 만들면 됩니다. 

객체는 Scanner 때처럼 new 연산자 이용하면 됩니다.

하다보면 익숙해지고 익숙해질 수 밖에 없으니 부담없이 해도 됩니다.

책에 있는 예시를 차근차근 따라해 보시고 이해 안되는건 댓글 달아주세요..!

 

클래스 생성자

클래스를 생성할때 이 클래스를 어떤식으로 이용하면 좋을지 생성자를 만들 수 있습니다. 클래스명과 똑같이 하며 매개변수 자리에 클래스 변수를 초기화 할 수 있도록 하는 경우가 대부분 입니다. 아무 생성자도 작성하지 않은 클래스는 디폴트 생성자로만 생성할 수 있습니다. 다른 생성자를 만들면 디폴트 생성자는 만들어지지 않으므로 필요하다면 직접 작성해야 한다.

 

This

this는 객체 자신에 대한 레퍼런스. 자바는 한줄씩 실행하므로 지금 실행되고 있는 클래스(객체)에 대한 레퍼런스 입니다.

this는 여러 역할이 있습니다. 생성자 내에서 this를 이용해서 변수를 설정하는것이 가장 기본적인 this 활용법 입니다.

예제 4-5의 littlePrince객체의 title변수는 어린왕자가 들어갔고 author 변수에는 색텍쥐페리 가 들어간겁니다. loveStory의 title에는 춘향전이 들어갔지만 author은 아무것도 정하지 않았습니다. 따라서 title 매개변수로만 클래스가 생성되었고 작자미상이 출력됩니다.

 

가비지 컬렉션

자바는 가비지컬렉션을 지원한다 라는 말을 어디선가 들은적 있을겁니다. 말그대로 프로그램 코드에서 더이상 아무 역할도 하지 않고 못하는 코드를 메모리에서 삭제해주는 겁니다. 내가 할 필요가 없으니 매우 편리한 기능이죠.

 

객체 배열

그냥 객체를 배열로 만든겁니다. 객체 circle 그러니까 동그라미를 여러개 만들어서 배열로 저장하겠다는 말입니다. 동그라미1 동그라미2 동그라미3 동그라미4 를 각각 만드는것 보다 동그라미1~4로 만드는게 더 쉽고 접근하기도 쉬울테니 말입니다. 여태까지 했던대로 만들면 됩니다. 레퍼런스 변수선언 -> 객체 생성.

Circle [] c;

c = new Circle[3]; // 동그라미 0~3 생성했습니다. (4개)

 

메소드

접근지정자 리턴타입 메소드명(매개변수){

                   내용

}

함수는 왜만들까요? 우선 편의를 위해 만듭니다. 함수를 하나 만들어 놓으면 동일한 코드를 여러번 작성할 필요없이 함수명만으로 여러번 사용할 수 있습니다. 둘째로 코드를 이쁘게 짤 수 있습니다. 이는 짜다보면 함수로 뺴는게 더 간결하겠다 그리고 자주 쓰이겠다 싶으면 함수로 빼면 됩니다. 함수 작성은 코딩에서 중요한 요소이기 때문에 잘 학습하길 바랍니다. 함수의 리턴타입에는 데이터타입이 될 수 있고 객체가 될 수 있습니다. 함수내에서 객체생성하고 이를 리턴하면 됩니다. 이때 클래스 타입이면 객체가 리턴되는게 아니라 객체의 레퍼런스 값이 리턴 됩니다..!

그니까 함수에서 매개변수로 객체를 넘길때 객체가 넘어가는것이 아니라 그 객체가 가리키는 레퍼런스 값이 전달되는 겁니다. 그럼 함수 내에서 그 레퍼런스 값을 변경시키는 겁니다. '화살표를 변경하는게 아니라 화살표가 가리키는 값을 변경합니다.' 이는 배열또한 마찬가지 입니다.

 

메소드 오버로딩

한 클래스 내에 이름이 같지만 배개변수의 타입이나 개수가 서로 다른 여러 개의 메소드를 중복 작성하는 것입니다.

자바 다형성의 하나의 특성이라고 배웠었죠..! 다른하나는 오버라이딩.

메소드 이름같아야 하며 매개변수의 개수나 타입달라야 합니다.

오버로딩 사례는 add라는 함수를 만들었을때 a+b처럼 두개만 더할 경우가 있고 a+b+c처럼 세개를 더하는 경우도 있습니다. 이때 오버로딩을 해 개수나 타입만 변경하면 동일한 메소드명으로 사용할 수 있습니다.

 

객체의 소멸

자바는 객체를 생성할때 new연산자로 해왔었죠? 하지만 객체를 지우는건 배운적 없습니다. 없어서요.

new는 객체를 메모리에 생성해줍니다. (자바는 메모리에 강점이 있고 그 이유가 뭔지 생각해봅시다.)

그러면 메모리에서 삭제할때는 어떻게 할까요? c에는 delete 연산자가 있습니다. c++에는 destructor함수가 있습니다. 그러면 자바는? 할필요가 없습니다.

안쓰면 알아서 없어지니까 골치아프게 신경쓸 필요가 없습니다. 가비지컬렉터가 알아서 없애줍니다!

 

접근 지정자

객체 지향 언어는 접근지정자를 두고 있습니다. public static void main(String[] args)를 예로 들면 public이 접근 지정자이고 패키지 상관없이 어떤 클래스에게도 사용이 허용 된다는 뜻입니다.

public int add(int a,int b){return a+b} 라는 add함수를 만들었을때 다른 패키지의 다른클래스도 이 add함수를 쓸 수 있습니다. (물론 저 add함수가 포함된 클래스를 임포트 해야합니다.)

 

접근 지정자에는 public, protected, default(아무것도 안씀), private 총 4개(디폴트제외 3개) 가 있습니다.

public은 모두 사용가능, protected는 같은 패키지만 가능하지만 다른 패키지의 자식클래스는 사용 가능합니다. default는 같은 패키지에서만 사용가능. private는 같은 클래스 내에서만 접근 가능합니다. protected는 비교적 자주 쓰이진 않습니다.

 

static

특별하게 static으로 선언된 멤버는 객체를 생성하지 않고도 사용할 수 있다..!! 클래스당 딱 하나만 생성되는 멤버로 동일한 클래스의 모든 객체들이 이를 공유해서 사용할 수 있다. static 멤버는 클래스당 딱 하나만 생성되므로 클래스 멤버라고 부르고 다른 멤버는 인스턴스 멤버 라고 부릅니다. static 멤버는 static 멤버가 작성된 클래스의 객체를 만들어 사용되기 이전에도 이미 생성되어 있는 상태 입니다. 그러므로 객체를 만들지 않고도 이에 접근해 여러 작업을 수행 할 수 있습니다. 객체 자체가 가비지 컬렉터에 의해 정리 되어도 static으로 선언된 멤버는 사라지지 않고 그대로 메모리에 남아 있습니다. 그러므로 쓸데없이 여러 변수를 static으로 선언하지 않는게 중요하겠죠.

static은 파이썬의 global 의 역할을 수행하네요. 파이썬에서 매우 유용하게 사용한 변수로 다른 모든 함수나 클래스에서 이 global로 선언된 변수에 접근할 수 있습니다. 자바의 static 설명과 동일 하네요.

 

정리하자면 static은

1. 객체 생성전에도 접근해서 사용할 수 있다.

2. 전역변수의 역할로 사용할 수 있다.

 

static 활용

코딩을 하다보면 Math라는 클래스에 있는 여러 함수들을 사용하는 경우가 많다. 특히 난수생성 할때 자주 사용하는 Math.random() 함수가 있는데 Math 클래스는 모든 멤버가 static이라고 한다. 그래서 우리가 Math 객체를 생성하지 않고도 random()함수를 접근해 사용할 수 있는것이다. 책의 요약 포스팅을 위해 복습 하면서 처음안 사실이다. 역시 복습이 중요한건가,,,

 

static 제약조건

static변수는 객체 생성 이전에도 생성되어 있다고 했다. non-static 변수는 객체가 생성되어야 비로소 이용할 수 있다. 그러면 static 변수가 non-static 변수에 접근할 때 문제가 생긴다. 접근하려는 non-static 변수가 생성되어 있을 수 있고 아닐수도 있기 때문에 static 변수는 static 변수만 사용할 수 있도록 했다. 반대로 non-static 변수는 이미 생성되어 있는 static 변수를 사용하는데 아무런 제약이 없기 때문에 non-static 변수는 static 변수에 접근 하고 사용할 수 있다.

static 으로 선언한 함수 내에서 this는 사용할 수 없다.

이 두가지 규칙만 잘 지키면서 static을 잘 사용하자!

 

final

final이 클래스 이름 앞에 사용되면 클래스를 상속받을 수 없다는 뜻이다.

final이 메소드 이름 앞에 사용되면 오버라이딩할 수 없는 메소드라는 뜻이다. 상속 받은 그대로 사용해야 한다.

final이 변수 앞에 선언되면 이는 상수가 되고 변경이 불가능하다.

final은 변경을 불가능하게 만드는 것이다!

본인이 게임을 만든다고 했을떄 int monster_damage = 10; 으로 선언해 놓았다고 하자. 이를 아래에서 user_damage를 수정해야 하는데 잘못해서 monster_damage = 1000; 으로 변경 한다면 난이도가 확 뛰지 않겠는가! 그러는걸 방지하기 위해서

final int monster_damage = 10; 으로 선언하면 아래에서 수정이 불가능 하다.