Java/Java

자바의 객체지향 프로그래밍 기초

마손리 2023. 2. 23. 21:35

타입 스크립트에서 OOP에 대한 내용을 포스팅하였지만 작동원리와 사용법이 조금 달라 다시 작성하게 되었다.

 

객체지향 프로그래밍 (Object Oriented Programming, OOP)

객체지향 프로그래밍은 어떠한 객체의 속성과 기능을 분석한 후, 이것을 프로그래밍의 변수와 함수로 정의하여 객체를 만들고 이 객체들 간의 상호작용을 이용한 프로그래밍이다.

객체들은 하나의 완전하고 독립적인 기능을 가지기 때문에 코드간의 결합도를 낮춰 그 자체로도 유용하고 코드의 수정 또한 용이한 장점이 있다.

 

 

클래스와 객체

클래스란 객체를 정의한 설계도 즉 객체를 찍어내기 위한 공장 같은 개념이다. 클래스는 그저 객체의 속성과 기능을 기술해 놓은 것이며 이에 맞게 객체가 생성된다.

 

객체클래스에서 생성된 제품이라고 할수 있다. 클래스에서 설계한 대로 객체는 여러 정보를 담고 있을수 있으며 필요한 동작 또한 가능하다. 이렇게 클래스를 통해 생성된 객체를 인스턴스(instance)라 부르며 그 과정을 인스턴스화(instantiate) 라고한다.

 

 

클래스의 구성요소

public class ExampleClass {
	int x; // (1)필드, 필드 선언
    ExampleClass(int x) { // (2)생성자
    	this.x = x;
    }
    
	void printX() { // (3)메서드
    	System.out.print()
    } 

	class ExampleClass2 {...} // (4)이너 클래스
}

클래스는 크게 4가지 요소로 구성되 있는데 각각 필드(field), 생성자(constructor), 메서드(method), 이너 클래스(inner class)이며 생성자를 제외한 나머지 3가지 요소를 멤버(member)라 한다.

 

 

객체 생성, 인스턴스화(Instantiate)

ExampleClass instantiate = new ExampleClass(1);

클래스명 참조변수명 = new 생성자(), 이 형식을 통해 객체를 생성하며 객체의 실제 데이터는 힙 메모리에 들어 있고 참조 변수는 객체의 실제 데이터가 들어있는 힙 메모리의 주소값을 가리킨다.

 

클래스 영역

클래스의 메서드 저장
스택 영역

참조변수 저장
(객체의 주소값)
힙 메모리 영역

멤버들을 포함한 객체 저장
(필드, 메서드의 주소값, 이너클래스)

클래스를 생성하면 클래스의 정보와 메서드들클래스 영역에 저장된다. 이후 인스턴스화를 하면 클래스의 규칙대로 객체를 생성한뒤 스택 영역에 객체의 주소값을 담은 참조변수를 저장하고, 생성된 객체힙 메모리영역에 저장된다. 이때 해당 객체에 직접적으로 메서드가 저장되는것이 아닌 클래스 영역에 있는 메서드의 주소값을 저장하며 필요에 따라 호출하여 사용한다.

 

 

필드(Field)

자바의 변수는 클래스 변수, 인스턴스 변수, 지역 변수로 구분되며 필드라 부르는 것은 클래스 변수인스턴스 변수이다. 

class Example { // => 클래스 영역
	int instanceVariable; // 인스턴스 변수
	static int classVariable; // 클래스 변수(static 변수, 공유변수)

	Example(inputInstanceVariable){	// => 생성자 메소드
    	this.instanceVariable = inputInstanceVariable; // 생성자를 통한 인스턴스 변수 할당
    }
    
	void method() { // => 메서드 영역
		int localVariable = 0; // 지역 변수. {}블록 안에서만 유효
	}
}
// 지역변수의 경우 직접적으로 초기값을 넣어 초기화를 해줘야 하지만
// 인스턴스, 클래스 변수는 자동으로 초기화 된다.

 

인스턴스 변수는 객체가 가지는 각각의 고유한 정보를 저장하기 위한 변수로 클래스내에서 생성자를 통해 인스턴스화 단계에서 인스턴스 변수에 데이터를 할당해주거나, 객체 생성후 포인트 연산자( . )를 통해 할당해 줄수 있다.

 

 

반대로 클래스 변수는 클래스내의 모든 인스턴스공통적으로 적용되는 정보이다. 따라서 클래스 변수에 데이터를 할당해주면 모든 인스턴스에 같은 값이 적용이 된다.

Example instantiate = new Example(100); // 생성자를 통한 인스턴스 변수 할당

instantiate.inputInstanceVariable = 10; // 연산자 포인터를 통한 인스턴스 변수 할당

Example.classVariable = 200; // 연산자 포인터를 통한 클래스 변수 할당

// instantiate.classVariable = 100;
// 인스턴스를 통해 클래스 변수 할당도 가능하지만 클래스 변수는 보통 클래스를 통해 할당

변수 뿐만아니라 인스턴스 메서드, 클래스 메서드로 나뉘며 그 성질 또한 변수와 같다. 정리하자면,

  • 인스턴스 메서드 : 인스턴스 변수와 클래스 변수, 인스턴스 메서드와 클래스메서드 모두 접근 가능.
  • 클래스 메서드 : 클래스 변수 및 클래스 메서드만 접근가능, 인스턴스 생성이 먼저 이루어 저야 인스턴스 변수와 메서드에 접근가능하다.

이러한 특징이 생겨난 이유는, 클래스 변수와 메서드는 해당 클래스가 생성된 시점에 클래스 영역에 이미 데이터가 저장되어 사용이 가능하지만 인스턴스 변수와 메서드는 해당 클래스가 생성이 되있더라도 인스턴스화를 진행해야 인스턴스의 정보가 힙메모리 영역에 저장되기 때문이다.

 

 

메서드

자바제어자 static 반환타입 메서드명(매개 변수) { // 메서드 시그니처
	메서드 내용 // 메서드 바디
}

public static int add(int x, int y) { // 메서드 시그니처
	int result = x + y; // 메서드 바디
	return result;
}

메서드란 객체의 특정한 작업을 수행하기 위한 기능을 정의한 것이다. 위와 같은 형태로 정의 할수 있으며 만약 인스턴스 메소드가 필요하다면 static은 없애준다. 

 

 

메서드 오버로딩

메서드 오버로딩이란 하나의 클래스 안에 같은 이름의 메서드를 여러개 정의하는것이다. 메서드 오버로딩의 경우 해당 메서드의 매개변수의 차이에 따라 성립이 된다.

 

public static String toString(int x) { 
	return String.format("%d", x);
}
public static String toString(double x) { 
	return String.format("%f", x);
}
public static String toString(char x) { 
	return String.format("%c", x);
}
public static String toString(boolean x) { 
	return String.format("%b", x);
}

위와 같이 메서드의 이름은 같지만 매개변수의 타입이 다르거나 개수가 다를경우에만 성립된다.

 

 

생성자

객체를 생성하는 역할을 하는 클래스의 구성 요소이다. 인스턴스가 생성될때 호출되는, 인스턴스 초기화 메서드라 정의 할수 있다.

인스턴스화 할때  new 키워드를 사용하면 일단 new 키워드는 인스턴스(객체)를 생성 한뒤 생성자라는 메서드가 인스턴스의 변수값들초기화하는 방식이다.

 

생성자는 메서드와 비슷하지만 두가지 다른 특징이 있는데

  1. 생성자의 이름은 반드시 클래스의 이름과 같다.
  2. 생성자는 리턴 타입이 없다. 생성자는 void 타입 밖에 없기 때문에 생성자 메소드의 타입을 정의 해줄 필요가 없다.

또한 일반 메서드와 마찬가지로 생성자 또한 오버로딩이 가능하며 그 특징은 일반 메서드들과 같다.

class Constructor {
    Constructor() { // (1) 생성자 오버로딩
        System.out.println("1번 생성자");
    }

    Constructor(String str) { // (2) 
        System.out.println("2번 생성자");
    }

    Constructor(int a, int b) { // (3) 
        System.out.println("3번 생성자");
    }
}

 

this()

같은 클래스 안에서 다른 메서드들끼리 서로 호출 할수 있던것 처럼 생성자 또한 상호 호출이 가능하며 이때 사용하는 것이 this()이다.

정리하자면 this()는 자신이 속한 클래스에 서로 다른 생성자를 호출할수 있는 메서드이며 이를 사용하기 위해선 두가지 문법 요소를 충족 시켜야 한다.

  1. this() 메서드는 반드시 생성자의 내부에서만 사용 가능하다.
  2. this() 메서드는 반드시 생성자의 첫줄에 위치해야한다.
public class Main {
    public static void main(String[] args) {
    
    Constructor first = new Constructor("firstArg");

}

class Constructor {
    String firstArg;
    String secondArg;

    Constructor(String a){	//첫번째 생성자
        this(a,"secondArg");	//this()메서드
        this.firstArg = a;
        this.secondArg = "";
        System.out.println("첫번째 생성자: "+this.firstArg +" "+ this.secondArg);
    }
    Constructor(String a, String b){	//두번째 생성자
         this.firstArg = a;
        this.secondArg = b;
        System.out.println("두번째 생성자: "+this.firstArg +" "+ this.secondArg);
    }
}

// 출력결과
두번째 생성자: firstArg secondArg
첫번째 생성자: firstArg

위처럼 인스턴스를 생성해주게 되었을때 하나의 매개변수만이 정의된, 첫번째 생성자를 먼저 호출한뒤 this()메서드두번째 생성자가 호출된다. 두번째 생성자의 코드가 모두 처리된뒤 this()메소드에서 빠저나와 첫번째 생성자의 코드가 진행되어 출력결과는 두번째 생성자가 먼저 나오게된다.

 

 

this

앞서 알아본 바에 의하면 인스턴스 변수는 각 인스턴스 고유의 정보를 담고 있다. 이때 각 인스턴스의 고유한 정보를 접근하는 방식으로 this가 사용된다. this인스턴스 자신을 뜻하며 위의 코드처럼 this를 통해서 인스턴스 변수인스턴스 메소드접근할 수있다.