객체 지향, 클래스와 인스턴스

홍예석·2023년 1월 20일
0

기초 학습

목록 보기
3/5

객체지향 part.1

클래스와 객체

  • 클래스가 무엇인가요?
    클래스는 객체를 정의해 놓은 일종의 틀입니다. 이 클래스는 객체를 생성하는 것이 목적입니다.

  • 그렇다면 객체는 무엇인가요?
    클래스라는 틀을 이용해 생성되는 사물 혹은 개념 등으로, 클래스가 갖고 있는 속성과 기능을 실제로 활용할 수 있는 대상입니다.

  • 인스턴스는 무엇인가요?
    일단은 객체가 조금 더 일반적인 개념이고 인스턴스가 조금 더 구체적인 개념이라고 할 수 있습니다. 클래스를 통해 생성되는 것을 객체를 생성한다고 하는 것과 인스턴스를 생성한다고 하는 것에 맥락적으로 큰 차이를 갖지 않기 때문에 이 둘을 명확히 구분하지는 않아도 괜찮습니다.

객체의 생성과 배열

객체를 출력해 보면 이상한 주소가 나오는데, 이에 대해 고민해 본 결과 이러한 결론을 냈습니다.

new 생성자는 해당 클래스가 요구하는 만큼의 메모리를 생성하여 주소를 할당하고, 그 메모리에
객체를 집어 넣는 것입니다.
GitBook - 객체의 생성과 배열 - 객체 배열 예시

    Tv1_2[] tvArr = new Tv1_2[3];
    tvArr[0] = new Tv1_2();
    tvArr[1] = new Tv1_2();
    tvArr[2] = new Tv1_2();

tvArr[0]안에 새로운 Tv1_2객체 그 자체가 들어가는 것이 아니라 이를 가리키는 새로운 주소가 들어가는 것입니다.
그런데 그냥 Tv1_2객체가 들어간다고 일반적으로 표현하는 것입니다.
조금 더 구체적으로, tvArr의 주소와 tvArr[0]의 주소를 출력해 봅시다.

tvArr 출력 : [Lprogrammers.Tv1_2;@6a5fc7f7
tvArr[0] 출력 : programmers.Tv1_2@3b6eb2ec

tvArr배열은 programmers파일(폴더)의 Tv1_2라는 파일(클래스로부터 생성된 인스턴스가 담겨 있는 주소 6a5fc7f7를 가리키고 있는 것입니다.
tvArr[0]는 programmers파일(폴더)의 Tv1_2라는 파일(클래스)로부터 생성된 인스턴스가
담겨 있는 주소 3b6eb2ec를 가리키고 있는 것이다.
다만 배열의 경우 앞에 [L이 추가되는데 제가 생각한 [L이 추가되는 이유는

  1. 해당 인스턴스는 객체 배열임을 표현하기 위해 추가(이 경우 경로가 달라져서 아닌 것 같습니다)
  2. [Lprogrammers라는 programmers와 같은 공간에 JVM이 가상 폴더를 생성

그렇다면 참조 배열이 아닌 기본 배열은 어떨까요?

    short[] shortArray = new short[1];
    int[] intArray = new int[1];
    double[] doubleArray = new double[1];
    char[] charArray = new char[1];
    String[] stringArray = new String[1];

shortArray 출력 : [S@5594a1b5
intArray 출력 : [I@5594a1b5
doubleArray 출력 : [D@6a5fc7f7
charArray 출력 : 인코딩에서 에러가 나는 건지 복사하면 공백이 나온다
stringArray 출력 : [Ljava.lang.String;@3b6eb2ec

물론 String은 기본형은 아니지만 기본형과 유사하게 활용되기에 기본형과 함께 다뤄보겠습니다.
참조 변수와 동일하게 주소가 출력되는 모습입니다.
다음으로 값을 넣은 후 출력해 봅시다.

	shortArray[0] = 1;
	intArray[0] = 1;
	doubleArray[0] = 1;
	charArray[0] = 'a';
	stringArray[0] = "string";

shortArray[0] 출력 : 1
intArray[0] 출력 : 1
doubleArray[0] 출력 : 1.0
charArray[0] 출력 : a
stringArray[0] 출력 : string

기본형 배열의 인덱스값을 출력하면 주소가 아닌 값이 즉시 등장합니다.
String도 기본형과 동일하게 값이 등장하는 모습을 볼 수 있습니다.

결론 : 기본 배열은 값 그 자체를 저장하지만 참조 배열은 주소를 저장한다.

클래스와 메서드

클래스

클래스는 데이터와 함수로 이루어져 있습니다.

클래스가 만들어지는 과정은

변수 : 하나의 데이터를 저장할 수 있는 공간
배열 : 같은 종류의 여러 데이터를 하나로 저장할 수 있는 공간
구조체 : 서로 연관된 여러 데이터(종류 관계 X) 를 하나로 저장할 수 있는 공간
클래스 : 데이터와 함수의 결합(구조체 + 함수)

클래스는 위와 같은 과정을 거쳐 만들어졌으며, 클래스는 곧 사용자가 정의한 하나의 타입이 된다고 할 수 있습니다. 그리고 이 타입을 이용해 객체를 생성하면 해당 객체는 클래스의 데이터와 함수를 내장한 채로 생성됩니다.

예시 - 사용자의 시간을 직접 관리하기 vs 시간 클래스를 만들어 객체로 관리하기

  1. 시간 직접 관리
class NoneClassTime {
    public static void main(String[] args) {
        // 총 3명 의 시간을 변수로 관리
        int hour1, hour2, hour3;
        int minute1, minute2, minute3;
        int second1, second2, second3;

        // 총 3명 의 시간을 배열로 관리
        int[] hour = new int[3];
        int[] minute = new int[3];
        int[] second = new int[3];
    }
}

이처럼 시간을 직접 관리하면, 문제되지는 않지만 직관적이지 않으며 데이터를 활용할 때 실수하기 쉽습니다. 또한 사람이 한 명 늘어날 때마다 변수의 갯수를 늘리거나 배열의 크기를 늘린 후 새로 추가해야 하는 등 코드의 유지 보수에 적합하지 않습니다.

  1. 시간 클래스를 만들어 객체로 관리하기
class Time3_1 {
    int hour;
    int minute;
    int second;
}
class Time3_1Main {
    public static void main(String[] args) {
        // 총 3명 의 시간을 객체로 관리
        Time3_1 userTime0 = new Time3_1();
        Time3_1 userTime1 = new Time3_1();
        Time3_1 userTime2 = new Time3_1();

        // 총 3명 의 시간을 객체 배열로 관리
        Time3_1[] userTimeArr = new Time3_1[3];
        userTimeArr[0] = new Time3_1();
        userTimeArr[1] = new Time3_1();
        userTimeArr[2] = new Time3_1();
    }
}

반면 이처럼 시간 클래스를 만들어 관리하는 경우 객체를 한 번 생성하면 시간, 분, 초 변수를 내장한 채로 생성되기 때문에 사용자가 늘어나거나 줄어도 관리하기가 용이합니다. 그리고 다른 점보다 직관적이고 관리하기가 간편해집니다.

매서드

앞에서는 시간 변수를 만들기만 하고 값을 넣지 않았습니다. 이제 직접 시간을 설정할 때와 클래스에서 메서드를 만들어 설정할 때의 차이를 통해 메서드에 대해 알아보겠습니다.

  1. 시간을 직접 관리할 때의 시간 설정
class NoneClassTime {
    public static void main(String[] args) {
        // 총 3명 의 시간을 변수로 관리
        int hour1, hour2, hour3;
        int minute1, minute2, minute3;
        int second1, second2, second3;
        
        hour1 = 1;
        minute1 = 19;
        second1 = 32;
        
        hour2 = 2;
        minute2 = 38;
        second2 = 34;
        
        hour3 = 3;
        minute3 = 50;
        second3 = 15;

        // 총 3명 의 시간을 배열로 관리
        int[] hour = new int[3];
        int[] minute = new int[3];
        int[] second = new int[3];
        // 변수로 관리할 때와 동일한 방법
    }
}

이처럼 매 번 시간,분,초를 따로따로 넣어야 하고 비효율적임을 알 수 있습니다.

  1. 시간 클래스를 만들어 객체로 관리할 때 메서드를 이용한 시간 설정
class Time3_1 {
    int hour;
    int minute;
    int second;
    void setTime(int hour, int minute, int second){
        this.hour = hour;
        this.minute = minute;
        this.second = second;
    }
}
class Time3_1Main {
    public static void main(String[] args) {
        // 총 3명 의 시간을 객체로 관리
        Time3_1 userTime0 = new Time3_1();
        Time3_1 userTime1 = new Time3_1();
        Time3_1 userTime2 = new Time3_1();
        
        userTime0.setTime(1,19,32);
        userTime1.setTime(2,38,34);
        userTime2.setTime(3,50,15);

        // 총 3명 의 시간을 객체 배열로 관리
        Time3_1[] userTimeArr = new Time3_1[3];
        userTimeArr[0] = new Time3_1();
        userTimeArr[1] = new Time3_1();
        userTimeArr[2] = new Time3_1();

        userTimeArr[0].setTime(1,19,32);
        userTimeArr[1].setTime(2,38,34);
        userTimeArr[2].setTime(3,50,15);
    }
}

이렇게 기본 변수 혹은 기본 배열로 시간을 관리할 때는 시간을 설정할 때 각각의 배열에 직접 시간,분,초를 대입해야 했고 이 경우 코드가 매우 길어지고 가독성이 떨어지게 된다. 반면 클래스 안에 메서드를 구현해 시간을 한 번에 설정하고 관리할 경우 코드가 간결해지고 가독성이 높아진다.

변수의 종류

이 부분은 2023-01-20 JAVA 공부 TIL 에 병합하였습니다.

static 메서드와 인스턴스 메서드

클래스이름.메서드(매개 변수))로 호출합니다
참조변수.메서드(매개변수)로 호출합니다.
매개 변수는 클래스에서 정의되어 있다면 넣고 없다면 비어 둘 수 있습니다.

앞의 time클래스를 보다 간결하게 이용해 static 메서드와 인스턴스 메서드를 비교해 보겠습니다.

class Time3_1 {
    int hour;
    int minute;
    int second;

    void setTime(int hour, int minute, int second){
        this.hour = hour;
        this.minute = minute;
        this.second = second;
    }
    
    //인스턴스 메서드
    void addHour(){
    //this를 통해 인스턴스 자신을 가리킨후, 해당 인스턴스의 변수인 hour의 값을 1 증가
        this.hour++;
    }
    
	//static 메서드
    public static void addHour(Time3_1 time){
    //메서드의 매개 변수로 들어온 참조 변수 time은 Time3_1의 인스턴스를 갖고 있고,
    //해당 인스턴스는 hour변수를 갖고 있다. 그 hour변수에 접근해 값을 1 증가
        time.hour++;
    }
}
public class Time3_1Main {
    public static void main(String[] args) {
        Time3_1 userTime0 = new Time3_1();
        userTime0.setTime(1,19,32);
        System.out.println(userTime0.hour);
        //참조 변수.메서드 이름()을 통해 인스턴스 메서드 호출
        userTime0.addHour();
        //인스턴스인 userTime0의 변수인 hour가 증가했음을 확인
        System.out.println(userTime0.hour);
        //클래스명.메서드이름();을 통해 static 메서드 호출 
        Time3_1.addHour(userTime0);
        //static 메서드의 매개 변수로 들어간 인스턴스 userTime0의 변수인 hour가 증가했음을 확인
        System.out.println(userTime0.hour);
    }
}

생성자

  • 생성자
    인스턴스가 생성될 때마다 호출되는 인스턴스 초기화 메서드
    • 생성 조건
      1. 이름이 클래스 이름과 같아야 합니다.
      2. return 값이 없습니다.
      3. void는 붙이지 않습니다.
      4. 오버로딩 가능합니다.

this와 this()

  • this
    인스턴스 자신을 가리키는 '참조 변수'입니다.
    인스턴스 메서드에서 사용되는 변수로, 클래스 메서드에서는 활용할 수 없습니다.
  • this()
    생성자 내에서 생성자를 호출할 때 활용합니다.
    생성자 안에서 클래스 자체의 생성자를 직접 호출하려고 하면 ERROR가 발생합니다.
    this()는 반드시 첫 줄에서만 사용할 수 있습니다.
class My_Tv{
	int channel;
    int volume;
    //channel을 매개변수로 받는 생성자
	My_Tv(int channel){
    	this.channel = channel;
    }
    //channel과 voulme을 모두 매개변수로 받는 생성자
    My_Tv(int channel, int volume){
    	//이 경우 this.channel = channel 대신 위의 생성자를 불러와서 메서드를 줄일 수 있다.
        //여기서는 생성자 안에 멤버가 한 줄뿐이라 효용성이 잘 느껴지지 않지만
        //두 생성자 간 겹치는 멤버가 많다면 코드를 훨씬 간결하게 만들 수 있다.
    	this(channel);
        this.volume = volume;
    }
}
profile
잘 읽어야 쓸 수 있고 잘 들어야 말할 수 있다

0개의 댓글