객체지향 언어
1. 코드의 재사용성이 높다.
- 새로운 코드를 작성할 때 기존의 코드를 이용하여 쉽게 작성할 수 있다.
2. 코드의 관리가 용이하다.
- 코드간의 관계를 이용해서 더 적은 노력으로 쉽게 코드를 변경할 수 있다.
3. 신뢰성이 높은 프로그래밍을 가능하게 한다.
- 제어자와 메서드를 이용해서 데이터를 보호하고 올바른 값을 유지하도록 하며, 코드의 중복을
제거하여 코드의 불일치로 인한 오동작을 방지할 수 있다.
객체지향언어는 기존의 프로그래밍언어와 다른 전혀 새로운 것이 아니라, 기존의 프로그래밍 언어에 몇 가지 새로운 규칙을 추가한 보다 발전된 형태의 것이다. 이러한 규칙들을 이용해 코드 간의 관계를 맺어 줌으로써 보다 유기적으로 프로그램을 구성하는 것이 가능해진 것이다. 가장 큰 장점으로는 '코드의 재사용성이 높고 유지보수가 용이하다.'라는 것이다. 이러한 장점은 프로그램의 개발과 유지보수에 드는 시간과 비용을 많이 개선하였다.
클래스와 객체
클래스의 정의 - 클래스란 객체를 정의해 놓은 것
클래스의 용도 - 클래스는 객체를 생성하는데 사용
객체의 정의 - 실제로 존재하는것. 사물 또는 개념
객체의 용도 - 객체가 가지고 있는 기능과 속성에 따라 다름
클래스는 객체를 생성하는 데 사용되며, 객체는 클래스에 정의된 대로 생성한다. 이 관계를 예로 들면, 제품 설계도와 제품과의 관계라고 할 수 있다. TV로 예를 들어보자면 TV설계도(클래스)는 TV라는 제품(객체)을 정의한 것이며, TV(객체)를 만드는 데 사용된다. 또한 클래스는 객체를 생성하는 데 사용될 뿐, 객체 그 자체는 아니다. 우리가 TV를 보기 위해서는 TV(객체)가 필요하지 TV설계도(클래스)가 필요한 것은 아니기 때문이다.
객체의 구성요소 - 속성과 기능
속성 | 크기, 길이, 높이, 색상, 볼륨, 채널 등 |
기능 | 켜기, 끄기, 볼륨 높이기, 볼륨 낮추기, 채널 변경하기 등 |
객체는 속성, 기능 두 가지로 이루어져 있다. 일반적으로 객체는 다수의 속성과 다수의 기능을 갖는다. 객체는 속성과 기능의 집합이라고 말할 수 있다. 그리고 객체가 가지고 있는 속성과 기능을 그 객체의 멤버(구성원, member)라고 한다.
class Tv{
String color; // TV 색깔
boolean power; // 전원상태
int channel; // 채널
void power() { power = !power; }
void channelUp() { channel++; }
void channelDown() { channel--; }
위의 속성과 기능을 예로 들어 TV클래스를 만들어보면 위와 같이 만들 수 있다.
객체와 인스턴스
클래스 -----------> 인스턴스(객체)
(인스턴스화)
클래스로부터 객체를 만드는 과정을 클래스의 인스턴스화(instantiate)라고 한다. 그리고 어떤 클래스로부터 만들어진 객체를 그 클래스의 인스턴스(instance)라고 한다. 객체와 인스턴스는 같은 의미로 두 용어의 사용을 엄격히 구분할 필요는 없지만, 문맥에 따라 구별하여 사용하는 것이 좋다.
객체의 생성과 사용
클래스명 변수명;
변수명 = new 클래스명();
Tv t; // Tv클래스 타입의 참조변수 t를 선언
t = new tv(); // Tv인스턴스를 생성한 후, 생성된 Tv인스턴스의 주소를 t에 저장
일반적으로는 객체를 생성할 때 다음과 같이 사용한다.
인스턴스는 참조변수를 통해서만 다룰 수 있으며, 참조변수의 타입은 인스턴스의 타입과
일치해야 한다.
객체배열
Tv tv1, tv2, tv3; -----> Tv[] tvArr = new tv[3];
객체도 배열로 관리할 수 있다. 이를 '객체 배열'이라고 한다. 객체 배열은 참조변수들을 하나로 묶음 참조변수 배열이라고 생각하면 된다. 위 그림은 인스턴스 주소를 아직 저장해주지 않았다. 분명 헷갈릴 수 있는 부분이다. 참조변수들의 배열을 만들어줬으면 그 안에 초기화를 시켜줘야 한다.
Tv[] tvArr = new Tv[3]; // 참조변수 배열을 생성
tvArr[0] = new Tv();
tvArr[1] = new Tv();
tvArr[2] = new Tv();
다음과 같이 선언해 주어야 사용할 수 있다. 객체의 수가 많을 때는 for문을 사용하면 된다.
비객체지향적 코드, 객체지향적 코드
비객체지향적 코드 | 객체지향적 코드 |
int hour1, hour2, hour3; int minute1, minute2, minute3; float second1, second2, second3; |
TIme t1 = new TIme(); TIme t2 = new TIme(); TIme t3 = new TIme(); |
int[] hour = new int[3]; int[] minute = new int[3]; float[] second = new float[3]; |
Time[] t = new Time[3]; t[0] = new Time(); t[1] = new Time(); t[2] = new Time(); |
비객체지향적으로 코딩을 하면 위와 같이 시, 분, 초가 서로 분리되어 있기 때문에 데이터가 뒤섞일 가능성이 있다. 이런 경우에는 시, 분, 초를 하나로 묶는 사용자 정의타입, 즉 클래스를 정의하여 사용하여야 한다.
메서드
수학의 함수와 달리 메서드는 입력값 또는 출력값이 없을수도 있으며,
심지어 입력값과 출력값이 모두 없을 수도 있다.
'메서드(method)'는 특정 작업을 수행하는 일련의 문장들을 하나로 묶은 것이다. 기본적으로 수학의 함수와 유사하며, 어떤 값을 입력하면 이 값으로 작업을 수행해서 결과를 반환한다.
메서드의 선언부
int add(int x, int y){
int result = x + y;
return result;
}
위의 코드에서 첫 번째 줄의 int는 반환타입을 말한다. 예를 들어 메서드 내부의 리턴해주는 result의 자료형이 int타입이기 때문에 반환타입을 int로 선언해 주었다. 뒤의 add는 메서드의 이름이다. 괄호( ) 안의 변수 x, y는 값을 받아서 메서드 내부에서 사용하는 변수들이다. 매개변수이다. 메서드를 정의할 때 지정해 둔 매개변수의 타입과 개수는 일치하여야 한다. 다만 자동형 변환은 이루어진다.
return문
return문은 현재 실행중인 메서드를 종요하고 호출한 메서드로 되돌아간다.
지금까지 반환값이 있을때만 return문을 썼지만, 원래는 반환값의 유무에 관계없이
모든 메서드에느 적어도 하나의 return문이 있어야 한다. 메서드의 반환타입이 void인 경우
return문이 없어도 아무런 문제가 없는 이유는 컴파일러가 메서드의 마지막에 'return;'을
자동적으로 추가해주었기 때문이다.
호출스택(call stack)
호출스택은 메서드의 작업에 필요한 메모리 공간을 제공한다. 메서드가 호출되면,
호출스택에 호출된 메서드를 위해 메모리가 할당되며, 이 메모리는 메서드가 작업을 수행하는 동안
지역변수(매개변수 포함)들과 연산의 중간 결과를 저장하는데 사용된다.
메서드가 작업을 마치면 할당되었던 메모리 공간은 반환되어 비워진다.
- 메서드가 호출되면 수행에 필요한 만큼의 메모리를 스택에 할당받는다.
- 메서드가 수행을 마치고나면 사용했던 메모리를 반환하고 스택에서 제거된다.
- 호출스택의 제일 위에 있는 메서드가 현재 실행 중인 메서드이다.
- 아래에 있는 메서드가 바로 위의 메서드를 호출한 메서드이다.
static 메서드와 인스턴스 메서드
변수와 같이 메서드 앞에 static이 붙어 있으면 클래스메서드이고 붙어있지 않다면
인스턴스 메서드이다. 클래스 메서드도 클래스변수처럼, 객체를 생성하지 않고도
'클래스이름.메서드이름(매개변수)'와 같은 식으로 호출이 가능하다. 반면
인스턴스 메서드는 반드시 객체를 생성해야만 호출할수있다.
static을 언제 붙여야 할까?
1. 클래스를 설계할 때, 맴버변수 중 모든 인스턴스에 공통으로 사용하는 것에 static을 붙인다.
2. 클래스 변수(static 변수)는 인스턴스를 생성하지 않아도 사용할 수 있다.
3. 클래스 메서드(static 메서드)는 인스턴스 변수를 사용할 수 없다.
4. 메서드 내에서 인스턴스 변수를 사용하지 않는다면, static을 붙이는것을 고려한다.
오버로딩
1. 메서드 이름이 같아야 한다.
2. 매개변수의 개수 또는 타입이 달라야 한다.
3. 반환 타입은 관계없다.
메서드도 변수와 마찬가지로 같은 클래스 내에서 서로 구분이 되어야 하기 때문에 각기 다른 이름을 가져야 한다. 그러나 자바에서는 한 클래스 내에 이미 사용하려는 이름과 같은 이름을 가진 메서드가 있어도 매개변수의 개수 또는 타입이 다르면, 같은 이름을 사용해서 메서드를 정의할 수 있다. 이처럼 한 클래스 내에 같은 이름의 메서드를 여러 개 정의하는 것을 '메서드 오버로딩(Method overloading)' 또는 간단하게 '오버로딩(overloading)'이라고 한다. 같은 이름의 메서드를 정의한다고 무조건 오버로딩이 아니라 위와 같은 조건을 만족해야 한다.
생성자(constructor)
1. 생성자의 이름은 클래스의 이름과 같아야 한다.
2. 생성자는 리턴값이 없다.
생성자는 인스턴스가 생성될 때 호출되는 '인스턴스 초기화 메서드'이다. 따라서 인스턴스 변수의 초기화 작업에 주로 사용되며, 인스턴스 생성 시에 실행되어야 하는 작업을 위해서라도 사용된다. 생성자 역시 메서드처럼 클래스 내에 선언되며, 구조도 메서드와 유사하지만 리턴값이 없다는 점이 다르다. 그렇다고 해서 생성자 앞에 리턴값이 없음을 뜻하는 키워드 void를 사용하지 않고, 단지 아무것도 적지 않는다. 생성자의 조건은 위와 같다.
기본 생성자(default constructor)
클래스이름(){} // 기본 생성자
Point(){} // Point클래스의 기본 생성자
기본 생성자가 컴파일러에 의해서 추가되는 경우는
클래스에 정의된 생성자가 하나도 없을 때 뿐이다.
지금까지는 생성자를 몰라도 프로그래밍에는 문제가 없었다. 사실 모든 클래스에는 반드시 하나 이상의 생성자가 정의되어 있어야 한다. 그러나 지금까지 클래스에 생성자를 정의하지 않고도 인스턴스를 생성할 수 있었던 이유는 컴파일러가 제공하는 '기본 생성자(default constructor)' 덕분이다. 컴파일러가 자동으로 추가해 주는 경우는 '클래스 내에 생성자가 하나도 없을 때' 뿐이다.
매개변수가 있는 생성자
class Car{
String color; // 색상
String gearType; // 변속기 종류
int door; // 문의 개수
Car(){} // 기본 생성자
Car(String c, String g, int d){ // 생성자
color = c;
gearType = g;
door = d;
}
}
위와 같은 코드로 Car인스턴스를 생성할 때, 생성자 Car()를 사용한다면, 인스턴스를 생성한 다음에 인스턴스 변수들을 따로 초기화해주어야 한다. 하지만 매개변수가 있는 생성자 Car(String color, String gearType, int door)를 사용한다면 인스턴스를 생성하는 동시에 원하는 값으로 초기화를 할 수 있게 된다. 인스턴스를 생성한 다음에 인스턴스 변수의 값을 변경하는 것보다 매개변수를 갖는 생성자를 사용하는 것이 코드를 보다 간결하고 직관적으로 만든다.
Car c = new car();
c.color = "white";
c.gearType = "auto"
c.door = 4;
Car c = new Car("white", "auto", 4);
위의 코드를 아래와 같이 한 줄로 간결하고 직관적으로 만들 수 있는 것이다.
인스턴스를 생성할 때는 다음과 같은 2가지의 사항을 결정해야 한다.
1. 클래스 - 어떤 클래스의 인스턴스를 생성할 것인가?
2. 생성자 - 선택한 클래스의 어떤 생성자로 인스턴스를 생성할 것인가?
생성자에서 다른 생성자 호출하기 - this()
- 생성자의 이름으로 클래스이름 대신 this를 사용한다.
- 한 생성자에서 다른 생성자를 호출할 때는 반드시 첫 줄에서만 호출이 가능하다.
Car(String color){
door = 5;
Car(color, "auto", 4); // 에러
}
위와 같은 코드는 에러가 발생한다. 생성자 내에서 다른 생성자를 호출할 때는 클래스이름인 'Car'대신 'this'를 사용해야 한다. 그리고 생성자 호출은 첫 번째 줄에서만 가능하다.
객체 자신을 가리키는 참조변수 - this
Car(String c, String g, int d){
color = c;
gearType = g;
door = d;
}
Car(String color, String gearType, int door){
this.color = color;
this.gearType = gearType;
this.door = door;
}
첫 번째 코드의 'color = c;'는 생성자의 매개변수로 선언된 지역변수 c의 값을 인스턴스 변수 color에 저장한다. 이때 변수 color와 c는 이름으로 서로 구별이 된다. 그래서 아무런 문제가 없다.
두 번째 코드처럼 생성자의 매개변수로 선언된 변수의 이름이 color로 인스턴스 변수 color와 같을 경우에는 이름만으로는 두 변수가 서로 구별이 안된다. 이럴 때는 인스턴스변수 앞에 'this'를 사용하면 된다.
this
- 인스턴스 자신을 가리키는 참조변수, 인스턴스의 주소가 저장되어 있다.
모든 인스턴스메서드에 지역변수로 숨겨진 채로 존재한다.
this(), this(매개변수)
- 생성자, 같은 클래스의 다른 생성자를 호출할 때 사용한다.
this와 this()는 비슷하게 생겼을 뿐 완전히 다른 것이다. this는 '참조 변수'이고, this()는 '생성자'이다.
변수의 초기화
class InitTest{
int x; // 인스턴스변수
int y = x; // 인스턴스변수
void method1(){
int i; // 지역변수
int j = i; // 에러 지역변수를 초기화하지 않고 사용
}
}
변수를 선언하고 처음으로 값을 저장하는 것을 '변수의 초기화'라고 한다. 변수의 초기화는 경우에 따라서 필수적이기도 하고 선택적이기도 하지만, 가능하면 선언과 동시에 적절한 값으로 초기화하는 것이 바람직하다.
멤버변수는 초기화를 하지 않아도 자동적으로 변수의 자료형에 맞는 기본값으로 초기화가 이루어지므로 초기화하지 않고 사용해도 되지만, 지역변수는 사용하기 전에 반드시 초기화해야 한다.
위의 코드에서 인스턴스 변수 x는 초기화를 해주지 않아도 자동적으로 int형의 기본값인 0으로 초기화되므로 사용이 가능하다. 하지만 method1()의 지역변수 i는 자동적으로 초기화되지 않으므로, 초기화되지 않은 상태에서 변수 j를 초기화하는 데 사용될 수 없다. 멤버변수(클래스 변수와 인스턴스 변수)와 배열의 초기화는 선택이지만, 지역변수의 초기화는 필수이다.
CHAPTER 6. 연습문제는 다음게시글에서 풀이하도록 하겠다.
정리
이번 챕터는 나에게 부족한 부분을 채워주는 강의였다. 그동안 생성자를 쓰면서도 왜 쓰는지, 클래스이름과 동일한 생성자가 여러 개가 있고 그중 하나의 생성자는 선언하지 않아도 자동으로 실행이 되었다. 이 강의에서 정확히 알 수 있었다. 또한 생성자가 정의되어 있으면 컴파일러가 기본 생성자를 추가해주지 않는다는 것을 알게 되었다. 코딩을 할 때 생성자에 매개변수를 넣고 따로 생성자를 만들고 실행했더니 자꾸 컴파일러 오류가 뜨길래 어디서 뜨는지 궁금하였다. 이제 생성자의 개념을 어느 정도 알게 된 것 같다. 이번챕터는 시간을 많이 투자해서 집중해서 읽고, 게시글을 쓰면서 한번 더 읽으며 머리에 넣으려고 많이 노력했던 것 같다.
'Back-End > JAVA' 카테고리의 다른 글
[JAVA] 자바의 정석(기초편) 공부 - CHAPTER 6 . 객체지향 프로그래밍 I - 연습문제 (0) | 2023.06.02 |
---|---|
[JAVA] 자바의 정석(기초편) 공부 - CHAPTER 5 . 배열 (0) | 2023.06.02 |
[JAVA] 자바의 정석(기초편) 공부 - CHAPTER 4 . 조건문과 반복문 (0) | 2023.06.01 |
[JAVA] 자바의 정석(기초편) 공부 - CHAPTER 3 . 연산자 (0) | 2023.05.31 |
[JAVA] 자바의 정석(기초편) 공부 - CHAPTER 2 . 변수 (0) | 2023.05.31 |
남건욱's 공부기록