[2017/2/2] Project Java 9일차: CH 6. 객체지향 프로그래밍 I

CH 6. 객체지향 프로그래밍 I
1. 객체지향언어
1.1. 객제지향언어의 역사
-실제 세계와 유사한 가상 세계를 컴퓨터 속에 구현하고자 노력 –> 객체지향이론
-실제 세계는 사물로 이루어져 있으며, 발생하는 모든 사건들은 사물간의 상호작용이다.
-실제 사물을 데이터(변수)와 함수로 정의함
-현재 프로그래밍 언어의 주류

1.2. 객체지향언어
-코드 간에 관계를 맺어줌으로써 유기적으로 프로그램 구성
● 코드의 재사용성이 높다.
● 코드의 관리가 용이하다.
● 신뢰성이 높은 프로그래밍이 가능하게 한다.
-재사용성과 유지보수, 중복된 코드의 제거
-처음부터 객체지향 개념을 적용하지 말고 일단 프로그램 기능적으로 완성한 이후 어떻게 하면 보다 객체지향적으로 코드를 개선할 수 있을지를 고민하여 점차 개선해 나가자.

2. 클래스와 객체
2.1. 클래스와 객체의 정의와 용도
-클래스의 정의: 객체를 정의해 놓은 것.
-클래스의 용도: 객체를 생성하는 데 사용.
-객체의 정의: 실제로 존재하는 것, 사물 또는 개념.
-객체의 용도: 객체가 가지고 있는 기능과 속성에 따라 다름.
-유형의 객체: 책상, 의자, 자동차
-무형의 객체: 수학공식, 프로그램 에러와 같은 논리나 개념.
-클래스와 객체의 관계는 설계도와 제품의 관계
-클래스는 단지 객체를 생성하는데 사용할 뿐, 객체 그 자체는 아니다.
-먼저 클래스 작성한 다음, 클래스로부터 객체 생성하여 사용한다.
-왜 클래스를 만드는가?: 하나의 설계도만 잘 만들어 놓으면 매번 객체를 생성할 때마다 어떻게 객체를 만들어야 할지를 고민하지 않아도 된다.
-JDK는 많은 수의 유용한 클래스를 기본 제공하고 있다.

2.2. 객체와 인스턴스
-클래스로부터 객체 만드는 과정을 ‘인스턴스화’
-클래스로부터 만들어진 객체를 ‘인스턴스’
-결국 인스턴스와 객체는 같은 개념
-but 객체는 모든 인스턴스를 대표하는 포괄적인 의미, 인스턴스는 어떤 클래스로부터 만들어진 것인지를 강조하는 보다 구체적인 의미.

2.3. 객체의 구성요소 – 속성과 기능
-객체는 속성과 기능의 집합
-객체는 다수의 속성과 기능을 갖고 있으며, 속성과 기능을 객체의 멤버라 한다.
-속성: 멤버변수, 특성, 필드, 상태
-기능: 메서드, 함수, 행위
-일반적으로 메서드보다는 멤버변수를 먼저 선언하고 멤버변수는 멤버변수끼리, 메서드는 메서드끼리 모아 놓는다.

2.4. 인스턴스의 생성과 사용
-TV 인스턴스를 생성해야 TV 제품을 사용할 수 있다.
-클래스명 변수명;
변수명 = new 클래스명();

Tv t;
t = new Tv();
t.channel = 7;
t.channelDown();

-인스턴스와 참조변수의 관계는 TV와 TV 리모컨의 관계와 같다. TV리모컨을 사용하여 TV를 다룰 수 있기 때문.
-각 인스턴스의 속성은 서로 다른 값을 유지할 수 있으며 메서드의 내용은 모든 인스턴스에 대해 동일하다.

Tv t1 = new Tv();
Tv t2 = new Tv();
t2 = t1; //t1이 저장하고 있는 값(주소)를 t2에 저장한다.

-둘 이상의 참조변수가 하나의 인스턴스를 가리키는 것은 가능하지만, 하나의 참조변수로 여러 개의 인스턴스를 가리키는 것은 가능하지 않다.

2.5. 객체 배열
-많은 수의 객체를 다뤄야 할 때 참조변수들을 하나로 묶은 참조 변수 배열 –> 객체 배열

Tv[] tvArr = new Tv[3]; //길이가 3인 Tv 타입의 참조변수 배열

-그저 객체를 다루기 위한 참조 변수들이 만들어진 것일 뿐, 아직 객체가 저장되지 않았다.

tvArr[0] = new Tv(); //객체를 생성해서 배열의 각 요소에 저장해야 한다.

-배열의 초기화 블록을 사용하면 한 줄로 줄일 수 있다.

Tv[] tvArr = { new Tv(), new Tv(), new Tv()};

-객체 배열도 같은 타입의 객체만 저장할 수 있다.
-다형성을 이용하면 하나의 배열로 여러 종류의 객체를 다룰 수 있다.

2.6. 클래스의 또 다른 정의
-프로그래밍적인 관점에서 살펴본 클래스의 정의와 의미.
1) 클래스 – 데이터와 함수의 결합
● 변수: 하나의 데이터를 저장할 수 있는 공간
● 배열: 같은 종류의 여러 데이터를 하나의 집합으로 저장할 수 있는 공간
● 구조체: 서로 관련된 여러 데이터를 종류에 관계없이 하나의 집합으로 저장할 수 있는 공간
● 클래스: 데이터와 함수의 결합 (구조체 + 함수)
-서로 관계가 깊은 변수와 함수들을 함께 다룰 수 있게 한다.
2) 클래스 – 사용자정의 타입
-참조형의 개수가 정해져 있지 않은 이유.
-제어자와 메서드를 이용해서 이러한 조건들을 코드에 쉽게 반영할 수 있다.

3. 변수와 메서드
3.1. 선언위치에 따른 변수의 종류
-변수의 선언된 위치에 따라 ‘클래스 변수’, ‘인스턴스 변수’, ‘지역변수’ 세 종류
● 클래스 변수(cv): 클래스 영역에서 선언, 클래스가 메모리에 올라갈 때 생성
● 인스턴스 변수(iv): 클래스 영역에서 선언, 인스턴스가 생성되었을 때 생성
● 지역변수(lv): 클래스 영역 이외의 영역 (메서드, 생성자, 초기화 블록 내부), 변수 선언문이 수행되었을 때 생성

1) 인스턴스 변수
-독립적인 저장공간을 가지므로 서로 다른 값을 가질 수 있다.

2) 클래스 변수
-선언시 인스턴스 변수 앞에 static 붙이면 됨.
-모든 인스턴스가 공통된 저장공간을 공유하게 된다.
-인스턴스를 생성하지 않고도 언제라도 바로 사용할 수 있다.
-클래스이름.클래스변수의 형식으로 사용
-클래스가 메모리에 로딩될 때 생성되어 프로그램이 종료될 때까지 유지.
-public이 앞에 붙으면 전역변수의 성격을 갖는다.
-클래스의 정보가 필요할 때 클래스는 메모리에 로딩된다.

3) 지역변수
-메서드 내에 선언되어 메서드 내에서만 사용 가능.
-지역변수가 선언된 블록 {} 내에서만 사용 가능.

3.2. 클래스 변수와 인스턴스 변수
-카드놀이 카드에서 무늬와 숫자는 인스턴스 변수, 폭과 높이는 클래스 변수.
-인스턴스 c1의 폭을 바꾸면 인스턴스 c2의 폭도 바뀐다.
-클래스 변수를 사용할 때는 클래스이름.클래스변수의 형태로 하자.

3.3. 메서드
-메서드: 특정 작업을 수행하는 일련의 문장들을 하나로 묶은 것.
-입력값 또는 출력값이 없을 수 있으며 모두 없을 수 있다.
-블랙박스: 메서드가 내부적으로 어떤 과정을 거쳐 결과를 만들어내는지 전혀 몰라도 된다.
-메서드를 사용하는 이유
● 높은 재사용성
● 중복된 코드의 제거
● 프로그램의 구조화: 문장들을 작업단위로 나눠서 여러 개의 메서드에 담아 프로그램의 구조를 단순화시킨다.

3.4. 메서드의 선언과 구현
-선언부와 구현부로 이루어짐.
-메서드 선언부: 메서드의 이름, 매개변수 선언, 반환타입으로 구성. 후에 변경되지 않게 조심
-매개변수 선언:
● 메서드가 작업을 수행하는데 필요한 값들을 제공받기 위한 것.
● 두 변수의 타입이 같아도 변수의 타입을 생략할 수 없다.
● 입력해야 할 값의 개수가 많은 경우에는 배열이나 참조변수 사용.
● 매개변수도 지역변수
-메서드의 이름: 동사인 경우가 많으며 함축적이면서도 의미있는 이름으로
-반환타입: 반환값이 없는 경우 void로.
-메서드의 구현부: {} 안
-return 문
● 반환 타입이 void가 아닌 한 return 반환값; 무조건 있어야 한다.
● 반환 타입과 일치하거나 적어도 자동 형변환이 가능한 타입이어야 함.
● 단 하나의 값만 반환 가능.
● 배열이나 인스턴스 이용하면 둘 이상의 값을 반환하는 것과 같은 효과 얻을 수 있다.
-지역변수: 서로 다른 메서드라면 같은 이름의 변수를 선언해도 된다.

3.5. 메서드의 호출
-호출되지 않으면 아무 일도 일어나지 않는다.
-main 메서드는 프로그램 실행 시 OS에 의해 자동적으로 호출된다.
-인자와 매개변수: 인자의 개수와 순서는 호출된 메서드에 선언된 매개변수와 일치해야 한다.
–> 아니면 컴파일러가 경고 발생시킴
-반환값 저장하지 않아도 문제가 되지 않는다.

3.6. return문
-현재 실행중인 메서드를 종료하고 호출된 메서드로 되돌아간다.
원래는 반환값의 유무와 상관없이 return문 있어야 했으나 컴파일러가 반환 타입이 void인 경우 return; 자동적으로 추가해준다.
-반환값이 있을 때 return문 없으면 컴파일 에러 발생.
-반환값에는 변수 외에도 수식이 올 수 있음. 수식 계산한 결과가 반환됨.
-매개변수의 유효성 검사로 매개변수의 값이 적절한 것인지 확인해야 한다.

3.7. JVM의 메모리구조
-JVM은 시스템으로부터 프로그램을 수행하는데 필요한 메모리를 할당받고 이 메모리를 용도에 따라 여러 영역으로 나누어 관리한다.
1) 메서드 영역: 실행 중 어떤 클래스가 사용되면 클래스 파일 읽어서 클래스에 대한 정보를 저장. 클래스 변수도 이곳에 생성.
2) 힙: 인스턴스가 생성되는 공간. 인스턴스 변수도 생성.
3) 호출 스택: 메서드의 작업에 필요한 메모리 공간을 제공한다. 지역변수와 연산의 중간결과 등을 저장하는데 사용.
● 메서드가 수행을 마치고 나면 사용했던 메모리 반환하고 스택에서 제거
● 호출 스택의 제일 위에 있는 메서드가 현재 실행중인 메서드
● 아래에 있는 메서드가 바로 위의 메서드를 호출한 메서드

3.8. 기본형 매개변수와 참조형 배개변수
-메서드를 호출할 때 매개변수로 지정한 값을 메서드의 매개변수에 복사해서 넘겨준다.
-기본형일 경우 기본형 값이 복사, 변수의 값을 읽기만 할 수 있다.
-참조형일 경우 인스턴스의 주소가 복사, 변수의 값을 읽고 변경할 수 있다.
-기본형의 경우 원본이 아닌 복사본이 변경되기 때문에 원본에는 아무런 영향을 미치지 못한다.

int[] x = {10};
change(x);

static void change(int[] x){
x[0] = 1000;
}

-배열도 객체와 같이 참조변수를 통해 데이터가 저장된 공간에 접근하므로 참조형 매개변수와 같은 결과를 얻을 수 있다.
-참조형 매개변수를 활용하면 반환값이 없어도 메서드의 실행결과를 얻어올 수 있다.
-여러 개의 값을 반환받는 것과 같은 효과를 얻을 수 있다.

3.9. 참조형 반환타입
-반환타입도 참조형이 될 수 있다.
-객체의 주소값이 반환되는 것일 뿐

static Data copy (Data d){
Data tmp = new Data();
tmp.x = d.x;
return tmp; //복사한 객체의 주소를 반환한다.
}

Data d2 = copy(d1); // 새로운 객체의 주소를 반환해줘야 한다.

3.10. 재귀호출
-메서드의 내부에서 메서드 자신을 다시 호출하는 것
-값에 의한 호출을 통해, 원래의 값이 아닌 복사된 값으로 작업하기 때문에 호출한 메서드와 관계없이 독립적인 작업수행이 가능하다.
-재귀호출도 조건문이 필수적으로 따라다닌다.
-대부분의 재귀호출은 반복문으로 작성하는 것이 가능하다.
-반복문보다 수행시간이 더 오래 걸린다.
-비효율적이더라도 알아보기 쉽게 작성하는 것이 논리적 오류가 발생할 확률도 줄어들고 나중에 수정하기도 좋다.
-스택오버플로우 에러 발생 가능성 –> 매개변수의 유효성 검사 중요.

3.11. 클래스 메서드와 인스턴스 메서드
-메서드 앞에 static이 붙어 있으면 클래스 메서드.
-클래스 메서드는 객체 생성하지 않고도 클래스이름.메서드이름의 형식으로 호출 가능하다.
-인스턴스 메서드는 인스턴스 변수를 필요로 하는 메서드
-클래스 메서드는 인스턴스와 관계없는 메서드
● 클래스를 설계할 때, 멤버 변수 중 모든 인스턴스에 공통적으로 사용해야 하는 것에 static을 붙인다.
● 클래스 변수는 인스턴스를 생성하지 않아도 사용할 수 있다.
● 클래스 메서드는 인스턴스 변수를 사용할 수 없다.
● 메서드 내에서 인스턴스 변수를 사용하지 않는다면, static 붙이는 것을 고려한다. –> 메서드 호출 시간이 짧아지므로 성능이 향상된다.

3.12. 클래스 멤버와 인스턴스 멤버간의 참조와 호출
-같은 클래스에 속한 멤버들 간에는 별도의 인스턴스를 생성하지 않고도 서로 참조 또는 호출이 가능하다.
-단, 클래스멤버가 인스턴스 멤버를 참조 또는 호출하고자 하는 경우에는 인스턴스를 생성해야 한다.
-실제로는 같은 클래스 내에서 클래스 멤버가 인스턴스 멤버를 참조 또는 호출해야 하는 경우는 드물다.

int result = new MemberCall().instanceMethod1();
-이런 식의 경우 참조변수를 선언하지 않았기 때문에 더 이상 사용할 수 없다.
4. 오버로딩
4.1. 오버로딩이란?
-같은 클래스 내에서 서로 구별될 수 있어야 하기 때문에 각기 다른 이름을 가져야 한다.
but 매개변수의 개수 또는 타입이 다르면, 같은 이름을 사용해서 메서드를 정의할 수 있어야 한다.

4.2. 오버로딩의 조건
● 메서드 이름이 같아야 한다.
● 매개변수의 개수 또는 타입이 달라야 한다.
-반환 타입은 오버로딩 구현에 아무런 영향을 주지 않는다.

4.3. 오버로딩의 예
-println 메서드

4.4. 오버로딩의 장점
-기억하기도 쉽고 이름도 짧게 할 수 있어서 오류의 가능성 줄일 수 있다.
-쉽게 예측 가능
-메서드의 이름 절약 가능

4.5. 가변인자(varargs)와 오버로딩
-동적으로 매개변수 지정 가능 –> 가변인자
-타입… 변수명의 형식

public PrintStream printf(String format, Object… args){}

-가변인자를 매개변수 중에서 제일 마지막에 선언해야 한다.

String concatenate (String… str) { … }

-인자의 개수를 가변적으로 할 수 있다. 인자가 아예 없어도 되고, 배열도 인자가 될 수 있다.
-내부적으로 배열을 이용함 –> 메서드 호출할 때마다 배열 새로 생성 –> 다소 비효율적

concatenate(“-”, new strArr[]{“100”, “200”, “300”});은 되지만
concatenate(“-”, {“100”, “200”, “300”});은 안 된다.

-가능하면 가변인자를 사용한 메서드는 오버로딩하지 않는 것이 좋다.

5. 생성자
5.1. 생성자란?
-생성자: 인스턴스가 생성될 때 호출되는 ‘인스턴스 초기화 메서드’
-인스턴스 변수의 초기화 작업에 주로 사용되며, 인스턴스 생성 시에 실행되어야 할 작업 위해서도 사용.
-리턴값 없으며, 아무 것도 적지 않는다.
-조건
● 생성자의 이름은 클래스의 이름과 같아야 한다.
● 생성자는 리턴 값이 없다.
-생성자도 오버로딩이 가능하다.
-연산자 new가 인스턴스를 생성하는 것이지 생성자가 인스턴스를 생성하는 것이 아니다.
-인스턴스 생성 위해 사용했던 클래스이름()이 바로 생성자

5.2. 기본 생성자
-사실 모든 클래스에는 반드시 생성자가 정의돼야 한다.
-컴파일러가 제공하는 기본 생성자
-매개변수도 없고 아무 내용도 없는 간단한 생성자
-특별히 인스턴스 초기화 작업이 요구되지 않는다면 걍 기본 생성자 쓰는 것도 not bad
-기본 생성자가 컴파일러에 의해 추가되는 경우는 클래스에 정의된 생성자가 하나도 없을 때 뿐이다.

5.3. 매개변수가 있는 생성자
-호출 시 값을 넘겨받아서 인스턴스의 초기화 작업에 사용할 수 있다.
-매개변수 사용한 초기화에 유용

Car (String c, String g, int d){ .. }

-다양한 생성자를 제공함으로써 별도로 초기화를 하지 않아도 되도록 하는 것이 바람직하다.

5.4. 생성자에서 다른 생성자 호출하기 – this, this()
-생성자 간에도 서로 호출이 가능하다
● 생성자의 이름으로 클래스 이름 대신 this를 사용한다.
● 한 생성자에서 다른 생성자를 호출할 때는 반드시 첫줄에서만 호출이 가능하다.
-왜 첫줄에서만? : 다른 생성자를 호출하기 이전의 초기화 작업이 무의미해질 수 있기 때문

Car() {
this(“white”, “auto”, 4);
}

Car(String color){
this(color, “auto”, 4);
}

Car(String color, String gearType, int door){
this.color = color;
this.gearType = gearType;
this.door = door;
}

-같은 클래스 내의 생성자들은 일반적으로 서로 관계가 깊은 경우가 많아서 유기적으로 연결해주면 더 좋은 코드를 얻을 수 있다.
-생성자의 매개변수로 인스턴스 변수들의 초기값 제공받는 경우 this 사용해서 구별되도록 하는 것이 의미가 더 명확하고 이해하기 쉽다.
-this는 참조변수로 인스턴스 자신을 가리킴
-this는 static 메서드에서는 사용 불가.
-모든 인스턴스 메서드에는 자신이 관련된 인스턴스를 가리키는 참조변수 this가 지역변수로 숨겨진 채로 존재한다.
● this: 인스턴스 자신을 가리키는 참보녀수. 인스턴스 주소 저장됨
● this(): 생성자, 같은 클래스의 다른 생성자 호출할 때 사용.

5.5. 생성자를 이용한 인스턴스의 복사
-현재 사용하고 있는 인스턴스와 같은 상태를 갖는 인스턴스를 하나 더 만들고자 할 때.
-인스턴스 변수가 동일한 값 가지도록 함

Car(Car c){
color = c.color;
gearType = c.gearType;
door = c.door;
}

Car c1 = new Car();
Car c2 = new Car(c1);

-Car 클래스의 참조변수를 매개변수로 선언한 생성자.
-서로 독립적으로 메모리공간에 존재하는 별도의 인스턴스.

6. 변수의 초기화
6.1. 변수의 초기화
-변수를 선언하고 처음으로 값을 저장하는 것.
-가능하면 선언과 동시에 적절한 값으로 초기화 하는 것이 바람직하다.

class InitTest{
int x;
int y = x; //인스턴스 변수는 초기화 해주지 않아도 자동적으로 기본값으로 초기화

void method1(){
int i;
int j = i; //에러. 지역변수의 초기화는 필수적
}
}

-멤버변수의 초기화 방법
● 명시적 초기화
● 생성자
● 초기화 블록
● 인스턴스 초기화 블록
● 클래스 초기화 블록

6.2. 명시적 초기화
-가장 기본적이면서도 간단한 초기화 방법
-가장 우선적으로 고려돼야 함.

6.3. 초기화 블록
-인스턴스 초기화 블록: 클래스 내에 블록 {} 만들고 코드 작성하면 끝
-클래스 초기화 블록: 인스턴스 초기화 블록 앞에 static 붙이면 된다.
-클래스 초기화 블록은 클래스가 메모리에 처음 로딩될 때 한번만 수행.
-인스턴스 초기화 블록은 생성자와 같이 인스턴스 생성할 때마다 수행됨.
-생성자보다 인스턴스 초기화 블록이 먼저 수행된다.
-인스턴스 초기화 블록은 보통 생성자에 공통적으로 사용되는 코드 넣는데 사용된다.

6.4. 멤버변수의 초기화 시기와 순서
-클래스 변수의 초기화 시점: 클래스가 처음 로딩될 때 한번
-인스턴스 변수의 초기화 시점: 인스턴스가 생성될 때마다 각 인스턴스별로 초기화
-클래스 변수의 초기화 순서: 기본값 –> 명시적 초기화 –> 클래스 초기화 블록
-인스턴스 변수의 초기화 순서: 기본값 –> 명시적 초기화 –> 인스턴스 초기화 블록 –> 생성자

출처 <자바의 정석 3판>

댓글 남기기