java

Java_Chapter06_클래스와 객체

강용민 2022. 9. 14. 12:54

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

자바 이전에는 C 언어 등 절차지향 언어가 대세를 이루고 있었지만, 자바가 C 언어를 제치고 부상할 수 있었던 이유는 객체지향 언어로서의 장점이 많이 드러나기 때문이다.

객체 지향 프로그램이란 프로그램에서 필요한 요소들을 객체로 만들고 이것들을 이용해 프로그래밍하는 것을 말한다.

 

다음은 객체지향의 특징들에 대해 살펴보겠다.

 

객체지향과 모듈화

객체지향의 큰 특징 중 하나로 모듈화를 들 수 있다.

모듈화가 되면 쉽게 부분을 교체하거나 재사용할 수 있다.

예를 들어 머리 모듈이 맘에 안 들면 머리 모듈만 다시 만들어서 결합하면된다. 또는 꼬리 모듈이 정말 잘 만들어진 경우는 다른 작품에서 그 꼬리 모듈을 재사용할 수도 있다.

모듈화된 프로그램은 다음과 같은 특징을 가질 수 있다.

  • 추가/ 수정/ 삭제가 쉽다.
  • 재사용이 가능하다.
  • 이미 검증된 모듈을 사용할 경우 신뢰도가 높다.소프

 

좋은 객체와 올바른 사용법

객체지향 프로그래밍이란 객체를 모듈화해서 작업하는 프로그램이다. 그럼 객체지향적으로 코드를 작성하기 위해서는 어떻게 해야하는가.

좋은 객체란 자신이 가진 정보를 활용하는 기능이 다양한 객체이다. 기능이 많을수록 그 객체의 활용도가 높아진다.

주체가 객체의 정보를 가져와서 사용하게되면 주체에서 처리해야 할 일들이 너무 많아지므로 복잡도는 올라가고 유지보수성은 떨어지게 된다. 

정보를 가져오지 말고 정보를 가진 객체에게 일을 시키는 것이 올바른 객체의 사용법이다.

 

클래스와 객체

앞서 객체에 대해 알아봤는데, 그런 객체를 어떻게 만들 수 있는지 살펴보자. 객체지향 언어의 특징 중 추상화(Abstraction)에 대한 부분이다.

다음 두 개의 문장은 클래스의 구성과 객체와의 관계를 나타낸다.

  • 현실의 객체가 갖는 속성과 기능은 추상화 과정을 거쳐 클래스로 정의된다.
  • 클래스는 구체화 과정을 거쳐 프로그램의 객체가 된다.

클래스란

  • 객체를 만들어 내기 위한 설계도

객체란

  • 소프트웨어 세계에 구현할 대상
  • 클래스의 인스턴스(instance)이다.

 

클래스와 객체가 만들어지는 과정

모든 객체를 분석하면 속성과 기능을 찾아볼 수 있다.

속성은 객체가 가지는 정보, 즉 데이터로 변수,상태 같은 말로 사용된다.

기능은 메서드(method), 함수(function)로 불리며 객체가 할 수 있는 작업을 나타낸다.

고양이를 예를들면 이름, 품종, 몸무게 등의 정보를 가지고 있으며, 할퀴거나 울음소리를 낼 수 있는 기능이 있다.

이를 코드로 구현하면 다음과 같이 할 수 있다.

class Cat{
	/*필드(상태) 영역 */
    String name;
    String breeds;
    double weight;
   	
    void claw() {
        System.out.println("할퀴기");
    }
    
    void meow(){
    	System.out.println("야옹");
    }
}

이제 해당 클래스를 이용해 객체를 만들고 사용할 주체를 만들어보자.

객체를 생성할 때는 new 키워드를 이용해서 생성자라는 것을 호출한다.

생성자를 호출하면 메모리에 객체가 생성되고 이것을 사용하기 위해서는 그 객체를 참조할 수 있는 참조값(주소값 비슷한 것)을 해당 클래스 타입의 변수에 할당해야 한다.

//클래스명 변수명 = new 클래스명();
Cat catObject = new Cat();
catObject.name = "야옹이";
catObject.breeds = "샴";
catObject.weight = 10;

System.out.println(catObject.name);
catObject.meow();

 

객체와 메모리

객체와 참조변수의 관계를 설명하면서 메모리 어딘가 생성되는 객체를 변수로 참조한다고 했는데 이부분에 대해서 더 알아보자.

먼저 JVM의 메모리 구조에 대해서 살펴볼 필요가 있다. JVM의 메모리는 크게 3부분으로 나눌 수 있다.

  • 클래스 영역(Class area)
    • Method Area, Static Area로도 불리운다.
    • 붕어빵 장사를 하기 위해서는 일단 붕어빵 틀을 가지고 나가야 한는 것처럼 클래스를 사용하려면 원판 클래스가 메모리에 올라가야 한다.
      이 원판이 등록되는 곳이 클래스 영역이다. 클래스 영역에서는 Field 정보, Method 정보, 타입에 대한 정보, 상수 풀이 저장된다.
      • Field 정보 : 멤버 변수의 이름, 데이터 타입 접근 제어자에 대한 정보
      • Method 정보 : 메서드 이름, 리턴 타입, 파라미터, 접근 제어자에 대한 정보
      • Type 정보 : 타입의 속성이 클래스인지 인터페이스인지, 타입의 전체 이름, 상위 클래스 이름, 접근 제어자 정보 등
      • 상푸 풀(Contant Pool) : 타입(클래스 또는 인터페이스)에서 사용된 상수가 저장되는 곳으로 문자 상수도 상수 풀에 저장
  • 스택 영역(Stack Area)
    • 스택은 메서드 호출 시마다 로컬 변수들이 쌓이는 영역으로 스레드(thread)별로 별도의 공간을 가진다.
    • 이 영역은 LIFO(Last In First Out)구조로 나중에 메모리에 생성된 데이터가 먼저 소멸된다.
    • 로컬 변수란 호출된 메서드의 매개변수나 메서드 내부에 선언된 변수 등으로 객체의 멤버 변수가 아닌 변수를 말한다.
  • 힙 영역(Heap Area)
    • 힙은 new 키워드로 생성된 객체가 저장되는 공간으로 스택과 달리 여러 스레드가 공유한다. 
    • 이 영역에 생성된 객체들은 프로그램 코드로 임의로 삭제할 수 없고 자동으로 가비지컬렉션이 실행될 때 삭제되어 사용하던 메모리 공간이 반환된다.

 

변수의 종류

선언 위치에 따른 변수의 종류

클래스에서 사용되는 변수는 선언 위치에 따라 크게 멤버 변수와 로컬 변수로 나뉜다.

멤버 변수

  • 클래스의 구성 요소로 클래스 영역에 선언된 변수
    • 클래스 멤버 변수 : static 키워드 사용 시
    • 인스턴스 멤버 변수 : static 키워드 미 사용 시

로컬 변수

  • 메서드나 생성자, 초기화 블록처럼 클래스 영역 내에 있는 또 다른 블록 내부에 변수
    • 로컬 변수
    • 파라미터 변수 : 메서드 등의 선언부에 파라미터로 선언된 변수
public class VariableTypes{
    int instanceVariable;	//인스턴스 멤버 변수
    static int classCariable;	//클래스 멤버 변수
    
    public static void main(String[] args){	//파라미터 변수
    	int localVariable = 10;	//로컬 변수
        for(int i = 0; i < 100; i++){	//로컬 변수
            System.out.println(i);
        }
    }
}

 

인스턴스 멤버 변수의 특징

인스턴스 멤버 변수는 클래스 영역에 static 키워드 없이 선언된 변수이다.

  • 인스턴스 멤버 변수는 객체가 만들어질 때 객체별로 생성된다.
  • 생성되는 위치는 객체 내부이기 때문에 힙 영역이다.
  • 인스턴스 멤버 면수는 생성 시점에 타입별로 초기화가 이뤄진다. 이 초기값은 배열에서각 요소의 타입별 초기값과 동일하다.
  • 인스턴스 멤버 변수는 객체 내부에 생성되기 때문에 각 객체의 소속이다. 따라서 이 변수를 사용하기 위해서는 먼저 객체를 생성해서 메모리에 올린 후 객체 이름을 통해서 접근해야 한다.
  • 인스턴스 멤버 변수의 값은 객체마다 생성되기 때문에 객체별로 고유한 값, 즉 상태를 가질 수 있다.
  • 인스턴스 멤버 변수가 메모리에서 삭제되는 시점은 객체가 없어지는 가비지 컬렉션이 일어나는 시점이다. 따라서 프로그램에서 이 변수를 임의로 삭제할 수는 없다.

 

클래스 멤버 변수의 특징

클래스 멤버 변수는 인스턴스 멤버 변수와 마찬가지로 클래스의 영역에 선언되는 변수 중 static키워드가 선언된 변수를 말한다.

따라서 클래스 멤버 변수는 static 멤버 변수라고도 한다.

  • 클래스 멤버 변수는 클래스 영역에 클래스가 로딩되는 시점에 메모리에 생성되므로 개별 객체의 생성과는 무관하다. 즉 이 변수는 소속이 객체가 아니라 클래스이다.
  • 결과적으로 클래스 멤버 면수는 같은 클래스를 통해 만들어진 모든 객체가 공유하게 되므로 공유 변수라고도 불린다.
  • 클래스 멤버 변수에 접근할 때 변수가 개별 객체와 무관하기 떄문에 객체가 아닌 클래스 이름으로 접근해야 한다.
  • 클래스 영역에 등록된 클래스 자체는 가비지 컬렉션 대상이 아니며, ㅍ로그램과 라이프사이클이 같다. 따라서 static 멤버 변수가 소멸하는 시점은 프로그램이 종료되는 시점이다.

 

로컬 변수(지역 변수)의 특징

로컬 변수는 클래스의 영역 이외의 모든 중괄호 안에 선언되는 변수들을 말한다. 즉 로컬 변수는 메서드나 생성자의 내부 또는 파라미터 그리고 초기화 블록의 내부에서 선언된 변수들이다.

  • 로컬 변수는 변수가 선언된 행이 실행될 때 스레드별로 생성된 메모리의 스택 영역에서 생성되고 해당 영역(중괄호)이 끝나면 자동으로 삭제된다.
  • 자바의 로컬 변수는 멤버 변수와 달리 자동으로초기화가 이뤄지지 않는다. 따라서 로컬 변수는 사용하기 저네 반드시 명시적인 초기화가 필요하다.

메서드

객체가 하는 동작을 메서드라고 부르며, 어떤 작업을 수행하는 명령문의 집합으로 표현한다.

메서드를 작성하는 이유는 반복적으로 사용되는 코드의 중복을 방지하고 코드의 양을 줄여 유지 보수성을 향상하기 위함이다.

 

메서드 호출

메서드의 이름과 파라미터를 알고 있다 해서 언제나 메서드를 호출할 수 있는 거은 아니다.

멤버 변수를 호출할 때와 마찬가지로 호출하려는 메서드가 메모리에 구성돼 있을 경우만 호출 가능한데 이때도 큰 차이점은 static 키워드에 있다.

static 키워드가 붙은 메서드는 개별 객체와 관련 없이 언제나 메모리에 있다. 따라서 메서드 호출 시 특별히 객체에 대한 레퍼런스가 필요 없이 바로 클래스 이름을 이용해서 호출할 수 있다.

 

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

변수에서 그랬던 것과 같이, 메서드 앞에 static이 붙어 있으면 클래스메서드이고 붙어 있지 않으면 인스턴스 메서드이다.

 

차이점

클래스는 '데이터(변수)와 데이터에 관련된 메서드의 집합'이므로, 같은 클래스 내에 있는 메서드와 멤버변수는 아주 밀접한 관계가 있다.

  • 인스턴스 메서드
    • 인스턴스 변수와 관련된 작업을 하는, 즉 메서드의 작업을 수행하는데 인스턴스 변수를 필요로 하는 메서드이다.
    • 반드시 객체를 생서해야만 호출할 수 있다.
  • 클래스 메서드
    • 인스턴스와 관계없는(인스턴스 변수나 인스턴스 메서드를 사용하지 않는) 메서드를 클래스 메서드(static메서드)로 정의한다.
    • 클래스변수처럼, 객체를 생성하지 않고도 '클래스이름.메서드이름(매개변수)'와 같은 식으로 호출이 가능하다.
class MyMath{
	long a,b;
    
    //인스턴스 변수 a,b만을 이용하므로 매개변수가 필요없다.
    long add()	{return a+b;}
    long substract() {return a-b}
    
    //인스턴스변수와 관계없이 매개변수만으로 잘업이 가능하다.
    static long add(long a, long b)	{return a+b;}
    static long subtract(long a, long b)	{return a-b;}
}

참조와 호출

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

단, 클래스멤버가 인스턴스 멤버를 참조 또는 호출하고자 하는 경우에는 인스턴스를 생성해야 한다.

그 이유는 인스턴스 멤버가 존재하는 시점에 클래스 멤버는 항상 존재하지만, 클래스멤버가 존재하는 시점에 인스턴스 멤버가 존재하지 않을 수도 있기 때문이다.

class TestClass{
	void instanceMethod() {}
    static void staticMethod() {}
    
    void instanceMethod2() {
    	instanceMethod();
        staticMethod();
    }
    
    
    static void staticMethod2(){
    	//에러!! 인스턴스메서드를 호출 할 수 없다.
        instanceMethod();
        staticMethod();
    }
}

 

 

메서드 오버로딩

메서드 오버로딩의 단순한 의미는 동일한 기능을 수행하는 메서드를 추가로 작성한다는 이야기다.

메서드 오버로딩은 뒤에 다룰 메서드 오버라이딩과 혼선이 올 수 있으므로 그 의미를 정확히 기억해야 한다.

public class WalkTestGood{
	void walk(){
    	walk(100,"cm");
    }
    
    void walk(int distance){
    	walk(distance,"cm");
   	}
   	
    void walk(int distance,String unit){
    	switch(unit){
        	case "cm":
            	break;
            case "inch":
            	distance *= 2.54;
                break;
            default:
            	System.out.println("unknown");
                distance = 0;
        }
        System.out.println(distance + "cm 이동");
    }
	
    
    public static void main(String[] args){
    	WalkTestGood wtg = new WalkTestGood();
        wtg.walk();
        wtg.walk(100);
        wtg.walk(100,"cm");

}

위 코드와 같이 함수 이름이 같은 walk라는 함수다. 다른점은 인자값의 개수가 다르다.

기존의 메서드를 호출함으로써 코드를 재사용할 수 있다.

 

생성자

생성자란 객체를 생성할 때 호출하는 메서드 비슷한 것을 말하는데 new 키워드와 함께 호출했던 것을 말한다.

새성자에서는 주로 일반 멤버 변수의 초기화나 객체를 생성할 때 실행하는 작업을 정리한다.

 

생성자의 종류

public class Person{
	String name;
    int age;
    boolean isHungry;
    
    //public DefaultPerson(){}	생략된 기본 생성자
    
    public Person(String name,int age, boolean isHungry){
    	this.name = name;
        this.age = age;
        this.isHungry = isHungry;
    }
    
    public static void main(String[] args){
    	Person person1 = new Person();
        person1.name = "홍길동";
        person1.age = 26;
        person1.isHungry = false;
        
        Person person2 = new Person("홍길동",26,false);

기본 생성자

기본 생성자(default constructor)는 파라미터가 없고 구현부가 비어 있는 형태로 잊까지 예제에서 사용했던 생성자이다.

기본 생성자는 우리가 별도의 생성자를 만들지 않았을 때 컴파일러에 의해 컴파일 타임에 자동으로 삽입된다.

 

파라미터 생성자

파라미터 생성자는 생성자가 파라미터를 받는 형태이다. 생성자에 전달될 파라미터는 주로 멤버  변수의 초기화에 사용된다.

 

생성자에서 다른 생성자 호출하기

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

단 다음의 두 조건을 만족시켜야 한다.

  • 생성자의 이름으로 클래스이름 대신 this를 사용한다.
  • 한 생성자에서 다른 생성자를 호출할 때는 반드시 첫 줄에서만 호출이 가능하다.

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

 

 

'java' 카테고리의 다른 글

Java_Chapter08_객체 지향 4대 요소  (0) 2022.10.09
Java_Chapter?_builder pattern  (0) 2022.10.06
Java_Chapter07_클래스의 관계  (0) 2022.09.15
Java_Chapter05_배열  (0) 2022.09.06
Java_Chapter01~02_자바특징 및 변수  (0) 2022.09.05