[java] 객체지향 프로그래밍I 정리(클래스~초기화)

객체지향언어

+주요특징

- 코드의 재사용성이 높다 : 새로운 코드를 작성할 때 기존의 코드를 이용하여 쉽게 작성할 수 있다.

- 코드의 관리가 용이하다 : 코드간의 관계를 이용해서 적은 노력으로 쉽게 코드를 변경할 수 있다.

- 신뢰성이 높은 프로그래밍을 가능하게 한다 : 제어자와 메서드를 이용해서 데이터를 보호하고 올바른 값을 유지하도록 하며, 코드의 중복을 제거하여 코드의 불일치로 인한 오동작을 방지할 수 있다.

 

 

클래스와 객체

+클래스란?

- 객체를 정의해놓은 것, 객체의 설계도 또는 틀.

(메모리에는 클래스의 데이터(멤버변수)와 같은 모습으로 저장된다. 메소드는 상관 없다.)

+객체란?

- 실제로 존재하는 것, 객체지향이론에서는사물과 같은 유형적인 것뿐만 아니라, 개념이나 논리와 같은 무형적인 것들도 객체로 간주한다. 프로그래밍에서의 객체는 클래스에 정의된 내용대로 메모리에 생성된 것을 뜻한다.

+why? 왜 클래스를 정의하고 객체를 생성해?

└ 클래스를 정의하고 객체를 생성하는 이유는 설계도를 통해서 제품을 만드는 이유와 같다. 하나의 설계도만 잘 만들어 놓으면 제품을 만드는 일이 쉬워진다. 제품을 만들 때마다 매번 고민할 필요없이 설계도대로만 만들면 되기 때문이다.

 

객체와 인스턴스

- 클래스로부터 객체를 만드는 과정을 클래스의 인스턴스화(instantiate)라고 하며, 어떤 클래스로부터 만들어진 객체를 그 클래스의 인스턴스(instance)라고 한다.

- 결국 인스턴스는 객체와 같은 의미이지만, 객체는 모든 인스턴스를 대표하는 포괄적인 의미를 갖고 있으며, 인스턴스는 어떤 클래스로부터 만들어진 것인지를 강조하는 보다 구체적인 의미를 갖고 있다.

[[정리]]

클래스 ----(인스턴스화)----> 인스턴스(객체)

 

객체의 구성요소 - 속성과 기능

- 객체는 속성과 기능, 두 종류의 구성요소로 이루어져 있으며, 일반적으로 객체는 다수의 속성과 다수의 기능을 갖는다. 즉, 객체는 속성과 기능의 집합이다.

- 객체가 가지고 있는 속성과 기능을 그 객체의 멤버(구성원)라 한다.

 

인스턴스의 생성과 사용

[[정리]]

클래스명 변수명;  // 클래스의 객체를 참조하기 위한 참조변수를 선언한다.

Test t;

변수명 = new 클래스명();  // 클래스의 객체를 생성 후, 객체의 주소를 참조변수에 저장한다.

t = new Test();

- 인스턴스는 참조변수를 통해서만 다룰 수 있으며, 참조변수의 타입은 인스턴스의 타입과 일치해야한다.

- 같은 클래스로부터 생성되었을지라도 각 인스턴스의 속성(멤버변수)은 서로 다른 값을 유지할 수 있으며, 메서드의 내용은 모든 인스턴스에 대해 동일하다.

- 참조변수에는 하나의 값(주소)만이 저장될 수 있으므로 둘 이상의 참조변수가 하나의 인스턴스를 가리키는(참조하는) 것은 가능하지만 하나의 참조변수로 여러 개의 인스턴스를 가리키는 것은 가능하지 않다.

- 자신을 참조하고 있는 참조변수가 하나도 없는 인스턴스는 더 이상 사용되어질 수 없으므로 가비지 컬렉터(Garbage Collector)에 의해서 자동적으로 메모리에서 제거된다.

 

클래스의 또 다른 정의

1. 프로그래밍적인 관점에서 클래스의 정의와 의미 : 데이터와 함수의 결합.

- 데이터 처리를 위한 데이터 저장형태의 발전 과정

└ 변수(하나의 데이터를 저장) -> 배열(같은 종류의 데이터를 하나의 집합으로 저장) -> 구조체(서로 관련된 여러 데이터를 종류에 관계없이 하나의 집합으로 저장) -> 클래스(데이터와 함수의 결합)

- 서로 관련된 변수들을 정의하고 이들에 대한 작업을 수행하는 함수들을 함께 정의한 것이 클래스이다.

2. 사용자 정의 타입(user-defined type)

- 프로그래밍언어에서 제공하는 자료형외에 프로그래머가 서로 관련된 변수들을 묶어서 하나의 타입으로 새로 추가하는 것을 말한다.

 

 

변수와 메서드

변수

[[정리]]

변수의 종류  선언위치  생성시기 
클래스변수
(class variable)
클래스 영역 클래스가 메모리에 올라갈 때 
인스턴스 변수
(instance variable) 
인스턴스가 생성되었을 때 
지역변수
(local variable) 
클래스 영역 이외의 영역
(메서드, 생성자, 초기화 블럭 내부) 
변수 선언문이 수행되었을 때 

ex)--------------------------------------------------

class Test{

int i;  // 인스턴스 변수

static int j;  // 클래스 변수(static 변수, 공유변수)

// i,j는 Test클래스의 멤버변수이기도 하다.

 

void method(){

int a;  // 지역변수

}

};---------------------------------------------------

- 클래스변수 : 클래스 변수를 선언하는 방법은 인스턴스변수 앞에 static을 붙이기만 하면 된다. 인스턴스마다 독립적인 저장공간을 갖는 인스턴스변수와는 달리, 클래스변수는 모든 인스턴스가 공통된 저장공간(변수)을 공유하게 된다. 그래서 공유변수(shared variable)라고도 한다. 한 클래스의 모든 인스턴스들이 공통적으로 값을 유지해야하는 속성의 경우, 클래스변수로 선언해야 한다.

또한 클래스변수는 인스턴스를 생성하지 않고도 언제라도 바로 사용할 수 있다는 특징이 있으며 '클래스이름.클래스변수'와 같은 형식으로 사용한다.

 

매서드

- 어떤 작업을 수행하기 위한 명령문의 집합이다.

- 반복적으로 사용되는 코드를 줄이기 위해서 사용한다. 자주 사용되는 내용의 코드를 메서드로 작성해 놓고 필요한 곳에서 호출만 하면 된다. 코드의 양도 줄일 수 있고 코드를 한 곳에서만 관리하면 되므로 유지보수가 편리하다.

형태)-----------------------------------------------

리턴타입 메서드이름 (타입 변수명, , ,...)  // 선언부

{  // 구현부

// 메서드 호출시 수행될 코드  

}---------------------------------------------------

- 메서드의 괄호()에 선언된 매개변수는 지역변수로 간주되어 메서드 내에서만 사용될 수 있으며, 메서드가 종료되는 순간 메모리에서 제거되어 더 이상 사용할 수 없게 된다.

- 매개변수가 있는 경우, 본격적인 작업을 시작하기에 앞서 넘겨받은 매개변수의 값이 유효한 것인지 확인하는 것은 매우 중요하다.

 

return문

메서드가 정상적으로 종료되는 경우는 두 가지가 있다

└ 메서드의 블럭{}내의 마지막 문장까지 수행했을 때

└메서드의 블럭{}내에 있는 문장을 수행 중 return문을 만났을 때

- return문은 현재 실행 중인 메서드를 종료하고 호출한 메서드로 되돌아가게 한다.

형태)----------------------------------------------

1. 반환값이 없는 경우 : return문만 써주면 된다.

return;

2. 반환값이 있는 경우 : return문 뒤에 반환값을 지정해 주어야 한다.

return 반환값;  // 반환값이 있는 경우, 반환값은 메서드의 선언부에 정의된 반환타입과 일치하거나 반환타입으로 자동형변환이 가능한 것이어야 한다.

---------------------------------------------------

- 반환값이 있는 메서드에서는 어떠한 경우에도 return문에 의해 결과가 반환되어야한다.(조건문 주의) 또한 return문을 여러 번 쓰는 것보다 리턴값을 저장했다가 마지막에 한 번만 사용하는 것이 좋다.

 

메서드의 호출

형태)----------------------------------------------

참조변수.메서드이름();

참조변수.메서드이름(값1,값2...);

---------------------------------------------------

- 같은 클래스 내의 메서드끼리는 참조변수를 사용하지 않고도 서로 호출이 가능하지만 static메서드는 같은 클래스 내의 인스턴스 메서드를 호출할 수 없다.

 

JVM의 메모리구조

 

 

 

 

 

 

 

 

 Method Area






클래스
데이터

클래스
데이터
cv 



Call Stack 



 (main) lv



Heap 
 인스턴스 인스턴스  인스턴스 
iv 

(cv는 클래스변수, lv은 지역변수, iv는 인스턴스변수를 뜻한다.)

 

 

 

 

 

 

 

 

- 응용프로그램이 실행되면, JVM은 시스템으로부터 프로그램을 수행하는데 필요한 메모리를 할당받고 JVM은 이 메모리를 용도에 따라 여러 영역으로 나누어 관리한다.

└ 메서드영역 : 프로그램 실행 중 어떤 클래스가 사용되면, JVM은 해당 클래스의 클래스파일(*.class)을 읽어서 분석하여 클래스에 대한 정보(클래스 데이터)를 이곳에 저장한다. 이 때, 클래스의 클래스변수도 이 영역에 함께 생성된다.

└ 힙 : 인스턴스가 생성되는 공간, 프로그램 실행 중 생성되는 인스턴스는 모두 이곳에 생성된다. 즉, 인스턴수변수들이 생성되는 공간이다.

└ 호출스택(call stack or execution stack) : 호출스택은 메서드의 작업에 필요한 메모리 공간을 제공한다. 메서드가 호출되면, 호출스택에 호출된 메서드를 위한 메모리가 할당되며, 이 메모리는 메서드가 작업을 수행하는 동안 지역변수(매개변수 포함)들과 연산의 중간결과 등을 저장하는데 사용된다. 그리고 메서드가 작업을 마치면 할당되었던 메모리공간은 반환되어 비워진다. 반환타입이 있는 메서드는 종료되면서 결과값을 자신을 호출한 메서드에게 반환한다. 대기상태에 있던 호출한 메서드는 넘겨받은 반환값으로 수행을 계속 진행하게 된다.

└─호출스택의 특징

- 메서드가 호출되면 수행에 필요한 만큼의 메모리를 스택에 할당받는다.

- 메서드가 수행을 마치고나면 사용했던 메모리를 반환하고 스택에서 제거된다.

- 호출스택의 제일 위에 있는 메서드가 현재 실행 중인 메서드이다.

- 아래에 있는 메서드가 바로 위의 메서드를 호출한 메서드이다.

 

기본형 매개변수와 참조형 매개변수

- 메서드를 호출할 때 매개변수로 지정한 값을 메서드의 매개변수에 복사해서 넘겨준다.

- 매개변수의 타입이 기본형일 때는 기본형 값이 복사되겠지만, 참조형이면 인스턴스의 주소가 복사된다. 참조형으로 선언하면 값이 저장된 곳의 주소를 알 수 있기 때문에 값을 읽어 오는 것은 물론 값을 변경하는 것도 가능하다.

[[정리]]

기본형 매개변수 - 변수의 값을 읽기만 할 수 있다. read only

참조형 매개변수 - 변수의 값을 읽고 변경할 수 있다. read & write

 

 

 

재귀호출(recursive call)

- 메서드의 내부에서 메서드 자기 자신을 다시 호출하는 것을 재귀호출이라 한다.

- 반복적인 작업을 해야 하는 메서드에 반복문 대신 재귀호출을 이용하면, 메서드를 훨씬 간단하게 할 수 있는 경우가 있다.

- 하지만 재귀호출은 다소 효율이 떨어진다는 단점이 있다. 재귀호출은 반복적으로 메서드를 호출하는 것이기 때문에 메서드를 호출하는데 드는 비용이 추가적으로 발생하기 때문이다.

 

클래스메서드(static메서드)와 인스턴스메서드

- 메서드 앞에 static이 붙어 있으면 클래스메서드이고 붙어 있지 않으면 인스턴스메서드이다.

- 클래스메서드도 클래스변수처럼, 객체를 생성하지 않고도 '클래스이름.메서드이름(매개변수)'와 같은 식으로 호출이 가능하다.

+why? 그럼 어떤 경우에 클래스메서드로 정의해?

└ 인스턴스메서드는 인스턴스변수와 관련된 작업을 하는, 즉 메서드의 작업을 수행하는데 인스턴스변수를 필요로 하는 메서드이다. 반면에 메서드 중에서 인스턴스와 관계없는(인스턴스변수나 인스턴스메서드를 사용하지않는) 메서드를 클래스메서드(static메서드)로 정의한다.

- 클래스메서드는 인스턴스변수를 사용할 수 없다. 인스턴스변수는 인스턴스가 반드시 존재해야만 사용할 수 있는데, 클래스메서드는 인스턴스 생성 없이 호출가능하므로 클래스메서드가 호출되었을 때 인스턴스가 존재할 수도 있고 존재하지 않을 수도 있다. 그래서 클레스매서드에서 인스턴스변수의 사용을 금지한다. 인스턴스변수나 인스턴스메서드에서는 static이 붙은 멤버들을 사용하는 것이 언제나 가능하다. 인스턴스 변수가 존재한다는 것은 static이 붙은 변수가 이미 메모리에 존재한다는 것을 의미하기 때문이다.

- 메서드 내에서 인스턴스변수를 사용하지 않는다면, static을 붙이는 것을 고려한다. 메서드 호출시간이 짧아지기 때문에 효율이 높아진다.

 

클래스멤버와 인스턴스멤버간의 참조와 호출

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

 

메서드 오버로딩

- 한 클래스 내에 같은 이름의 메서드를 여러 개 정의하는 것을 메서드 오버로딩(method overloading) 또는 간단히 오버로딩이라 한다.

오버로딩의 조건

1. 메서드 이름이 같아야 한다.

2. 매개변수의 개수 또는 타입이 달라야 한다.

3. 매개변수는 같고 리턴타입이 다른 경우는 오버로딩이 성립되지 않는다.(리턴타입은 오버로딩을 구현하는데 아무런 영향을 주지 못한다.)

ex)----------------------------------------------

int add(int a, int b){ ... }

int add(float c, float d) { ... }

-------------------------------------------------

 

생성자(Constructor)

- 생성자는 인스터스가 생성될 때 호출되는 '인스턴스 초기화 메서드'이다.

- 클래스 내에 선언되며, 구조도 메서드와 유사하지만 리턴값이 없다는 점이 다르다. 생성자도 메서드이기 때문에 리턴값이 없다는 의미의 void를 적어야 하지만 모든 생성자가 리턴값이 없으므로 void를 생략하도록 한 것이라고 이해하라.

- 생성자의 이름은 클래스의 이름과 같아야 한다. 또한 생성자도 오버로딩이 가능하므로 하나의 클래스에 여러 개의 생성자가 있을 수 있다.

ex)----------------------------------------------

class Test{

Test(){ ... }  // 매개변수가 없는 생성자

Test(int i, int j) { ... }  // 매개변수가 있는 생성자

}------------------------------------------------

- 연산자 new가 인스턴스를 생성하는 것이지 생성자가 인스턴스를 생성하는 것은 아니다.

 

기본 생성자(default constructor)

- 모든 클래스에는 반드시 하나 이상의 생성자가 정의되어 있어야 한다.

- 컴파일 할 때, 소스파일(*.java)의 클래스에 생성자가 하나도 정의되지 않은 경우 컴파일러는 자동적으로 기본 생성자를 추가하여 컴파일 한다.

- 기본 생성자가 컴파일러에 의해서 추가되는 경우는 클래승에 정의된 생성자가 하나도 없을때 뿐이다.

 

매개변수가 있는 생성자

- 생성자도 메서드처럼 매개변수를 선언하여 호출 시 값을 넘겨받아서 인스턴스의 초기화 작업에 사용할 수 있다.

- 인스턴스를 생성한 다음에 인스턴스변수의 값을 변경하는 것보다 매개변수를 갖는 생성자를 사용하는 것이 코드를 보다 간결하고 직관적으로 만든다.

 

생성자에서 다른 생성자 호출하기 - this(), this

- 같은 클래스 멤버들 간에 서로 호출할 수 있는 것처럼 생성자 간에도 서로 호출이 가능하다.

└ 단, 생성자의 이름으로 클래스이름 대신 this를 사용한다.

└ 단, 한 생성자에서 다른 생성자를 호출할 때는 반드시 첫 줄에서만 호출이 가능하다.(생성자 내에서 초기화 작업도중에 다른 생성자를 호출하게 되면, 호출된 다른 생성자 내에서도 멤버변수들의 값을 초기화를 할 것이므로 다른 생성자를 호출하기 이전의 초기화 작업이 무의미해질 수 있기 때문이다.)

ex)====================================================

public class test {

String color;

String gearType;

int door;

test(){

this("white","auto",4);

System.out.println("test()");

}

test(String color){

this(color,"auto",4);

System.out.println("test(1)");

}

test(String color, String geartype, int door){

this.color = color;  // this.color 인스턴스 변수, color 매개변수로 정의된 지역변수

this.gearType = geartype;

this.door = door;

System.out.println("test(3)");

}

 

public static void main(String arg[]){

test t = new test();

test t2 = new test("blue");

 

System.out.println("t color = "+t.color+", gearType = "+t.gearType+"door = "+t.door);

System.out.println("t2 color = "+t2.color+", gearType = "+t2.gearType+"door = "+t2.door);

 

}

}

 

실행결과)------------------------------------------

test(3)

test()

test(3)

test(1)

t color = white, gearType = autodoor = 4

t2 color = blue, gearType = autodoor = 4

====================================================
- 같은 클래스 내의 생성자들은 일반적으로 서로 관계가 깊은 경우가 많아서 이처럼 서로 호출하도록 하여 유기적으로 연결해주면 더 좋은 코드를 얻을 수 있다.
- this는 참조변수로 인스턴스 자신을 가리킨다. 참조변수를 통해 인스턴스의 멤버에 접근할 수 있는 것처럼, this로 인스턴스변수에 접근할 수 있는 것이다.
- this를 사용할 수 있는 것은 인스턴스멤버뿐이다. static메서드(클래스메서드)에서는 인스턴스 멤버들을 사용할 수 없는 것처럼, this역시 사용할 수 없다.(static메서드가 호출된 시점에 인스턴스의 존재 여부가 불투명 하기 때문이다.)
- 생성자를 포함한 모든 인스턴스메서드에는 자신이 관련된 인스턴스를 가리키는 참조변수 this가 지역변수로 숨겨진 채로 존재한다.
 
생성자를 이용한 인스턴스의 복사
- 사용하고 있는 인스턴스와 같은 상태를 갖는 인스턴스를 하나 더 만들고자 할 때 생성자를 이용할 수 있다. 두 인스턴스가 같은 상태를 갖는다는 것은 두 인스턴스의 모든 인스턴스변수(상태)가 동일한 값을 갖고 있다는 것을 뜻한다.
ex)====================================================
public class test {
String color;
String gearType;
int door;
test(test t){
color = t.color;
gearType = t.gearType;
door = t.door;
System.out.println("test(test t)");
}
test(String color, String geartype, int door){
this.color = color;
this.gearType = geartype;
this.door = door;
System.out.println("test(3)");
}
 
public static void main(String arg[]){
test t = new test("blur","auto",4);
test t2 = new test(t);
 
System.out.println("t color = "+t.color+", gearType = "+t.gearType+", door = "+t.door);
System.out.println("t2 color = "+t2.color+", gearType = "+t2.gearType+", door = "+t2.door);
 
t.door = 100;
 
System.out.println("t color = "+t.color+", gearType = "+t.gearType+", door = "+t.door);
System.out.println("t2 color = "+t2.color+", gearType = "+t2.gearType+", door = "+t2.door);
}
}
실행결과)---------------------------------------------
test(3)
test(test t)
t color = blur, gearType = auto, door = 4
t2 color = blur, gearType = auto, door = 4
t color = blur, gearType = auto, door = 100
t2 color = blur, gearType = auto, door = 4
======================================================
- 복사된 인스턴스는 원본 인스턴스와 서로 같은 상태를 갖지만, 서로 독립적인 메모리공간에 존재하는 별도의 인스턴스이므로 원본의 값이 변경되어도 영향을 받지 않는다.
 
 
변수의 초기화
- 변수를 선언하고 처음으로 값을 저장하는 것을 말한다.
- 멤버변수는 초기화를 하지 않아도 자동적으로 변수의 자료형에 맞는 기본값으로 초기화가 이루어지므로 초기화하지 않고 사용해도 되지만 지역변수는 반드시 초기화를 해야 한다.
 
멤버변수의 초기화 방법
1. 명시적 초기화(explicit initialization)
 - 변수를 선언과 동시에 초기화 하는 것을 말한다. ex) int  i = 0; , Test t = new Test();
2. 생성자(constructor)
3. 초기화 블럭(initialization block)
- 인스턴스 초기화 블럭 : 인스턴스변수를 초기화 하는데 사용한다. ex) { ... }
- 클래스 내에 블럭'{}'을 만들고 그 안에 코드를 작성한다.
- tip! 코드의 의미상 모든 생성자에서 공통적으로 수행되어야하는 내용에는 인스턴스 초기화 블럭을 사용하는 것이 좋다.
- 클래스 초기화 블럭 : 클래스변수를 초기화 하는데 사용한다. ex) static { ... }
- 인스턴스 초기화 블럭 앞에 static을 덧붙인다.
- 클래스가 메모리에 처음 로딩될 때 한번만 수행되며, 인스턴스 초기화 블럭은 생성자와 같이 인스턴스를 생성할 때 마다 수행된다. 생성자 보다 인스턴스 초기화 블럭이 먼저 수행된다.(클래스가 처음 로딩될 때 클래스변수들이 자동적으롤 메모리에 만들어지고, 바로 클래스 초기화블럭이 클래스변수들을 초기화하게 되는 것이삳.)
 
멤버변수의 초기화 시기와 순서
초기화 시점
 - 클래스변수 : 클래스가 처음 로딩될 때 단 한번  초기화 된다.
 - 인스턴스변수 : 인스턴스가 생성될 때마다 각 인스턴스별로 초기화가 이루어진다.
초기화 순서
 - 클래스변수 : 기본값 -> 명시적초기화 -> 클래스 초기화 블럭
 - 인서턴스변수 : 기본값 -> 명시적초기화 -> 인스턴스 초기화 블럭 -> 생성자
 
- 프로그램 실행도중 클래스에 대한 정보가 요구되어질 때, 클래스는 메모리에 로딩된다. ex) 클래스 멤버를 사용했을 때, 인스턴스를 생설할 때 등. 그러나 클래스가 이미 로딩되어 있다면, 또다시 로딩하지 않는다. 물론 초기화도 다시 수행되지 않는다.
ex)===========================================
public class test {
static int cv = 1;
int iv = 1;
 
static{  // 클래스 초기화 블럭
cv = 2;
System.out.println("static cv = 2");
}
 
{  // 인스턴스 초기화 블럭
iv = 5;
System.out.println("instance iv = 5");
}
 
test(){  // 생성자
iv = 3;
System.out.println("test()");
}
 
public static void main(String arg[]){
test t = new test();
}
}
실행결과)---------------------------------------
// 1. cv가 메모리에 생성되고, int형의 기본값인 0이 cv에 저장된다.
// 2. 명시적 초기화에 의해 cv에 1이 저장된다.
static cv = 2  // 3. 클래스 초기화 블럭이 수행되어 저장된다.
// 4. main에서 인스턴스(객체) 생성, 멤버변수 iv가 메모리에 존재하게 되면서 기본값 0이 저장된다.
// 5. 명시적 초기화에 의해서 iv에 1이 저장된다.
instance iv = 5  // 6. 인스턴스 초기화에 의해서 iv에 5가 저장된다.
test()  // 7. 마지막으로 생성자가 수행되어 iv에는 3이 저장되고 "test()"가 출력된다.
================================================