[Java] 자바의 정석 6장 회고(1) - 객체지향(Class)

토닉·2021년 8월 30일
0

Java

목록 보기
7/13
post-thumbnail

객체지향 언어를 배우는 이유, 개념

객체지향 언어 : 프로그래밍 언어 + 객체지향개념(규칙)

나타난 배경

기존의 절차적으로 작성하는 방법이 소프트웨어의 빠른 변화를 쫓아가지 못했다.
해결책으로 객체지향 언어를 도입(절차적 -> 객체지향)

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

특징

  1. 캡슐화
  2. 상속
  3. 추상화
  4. 다형성

장점

  1. 코드의 재사용성
  2. 유지보수
  3. 중복 코드 제거

객체지향적으로 프로그램을 어떻게 작성하나요?
우선 너무 객체지향개념에 사로잡혀 고민하는 것보다는 프로그램을 기능적으로 완성시킨 후 객체지향적으로 코드를 개선할 수 있을지를 고민하여 점점 개선해 나가는 방법이 좋습니다.

클래스와 객체(인스턴스)

자바에서는 class를 사용하여 객체라는 개념을 다룹니다.
정확히 정의하면

클래스의 정의: 객체를 정의해 놓은 것
클래스의 용도: 객체를 생성할 때 사용

객체의 정의: 실제로 존재하는 것
객체의 용도: 객체가 가지고 있는 기능과 속성을 사용

비유하자면 클래스는 설계도이고 객체는 실제 사물, 존재하는 것 입니다.
예를 들어 티비를 만들기 위한 설계도가 클래스이고 만들어진 티비를 객체라고 생각하면 됩니다.

객체는 2가지 구성 요소를 가지고 있습니다.
1. 속성
2. 기능
위 두 가지를 이 객체의 멤버라고 부르기도 합니다.
보통 클래스에서 속성은 변수로 표현하고, 기능은 메서드로 표현합니다.

객체, 인스턴스 다른점은 무엇인가요?
같은 의미여서 꼭 구분할 필요는 없지만 문맥에 따라 구별하여 사용됩니다.

  • 책상은 인스턴스다 (x) 책상은 객체이다 (o)
  • 책상은 책상 클래스의 객체이다 (x) 책상은 책상 클래스의 인스턴스이다(o)

인스턴스화 : 클래스로 부터 객체를 만드는 과정

자바에서 클래스 작성 방법

// Car.java
public class Car {}
class Bus {}

위 처럼 Car.java 라는 소스 파일에 코드를 작성할 수 있습니다.
단 규칙이 있습니다.

규칙
1. 일반적으로 하나의 소스파일에는 하나의 클래스를 작성하는 것이 이상적이다.
2. 둘 이상의 클래스를 정의하는 것도 가능하다.
3. 클래스 명의 시작 단어는 무조건 대문자!
4. public class Car {}처럼 앞에 public이 붙어 있다면 무조건 소스파일의 이름과 클래스 이름이 같아야 한다.
5. src 폴더안에 똑같은 이름의 클래스는 작성할 수 없다.

클래스와 객체 사용 방법

// CarMain.java
public class CarMain {
    public static void main(String[] args){
	// 객체 생성
        Car car1 = new Car();
        Car car2 = new Car();
    // car1 = car2;
	// 객체 사용
	car1.wheel = 5;
        System.out.println("car1.wheel = " + car1.wheel);
        System.out.println("car2.wheel = " + car2.wheel);
        car2.drive(10);
        System.out.println("car1.fuel = " + car1.fuel);
        System.out.println("car2.fuel = " + car2.fuel);
    }
}

// 클래스 작성
class Car {
    // 속성
    String color;
    int wheel = 4;
    int fuel = 100;
    // 기능
    void drive(int time){
        fuel -= time;
    }
}

>> car1.wheel = 5
>> car2.wheel = 4
>> car1.fuel = 100
>> car2.fuel = 90

만약 위의 car1 = car2;를 실행하면 어떻게 될까?
car2 참조변수에 저장된 객체의 주소가 car1 참조변수에 저장됩니다.
이렇게 되면 car1이 원래 갖고 있던 객체의 주소는 없어지면서 원래 car1이 였던 객체는 참조할 수 없는 상태가 되기 때문에 가비지컬렉터가 객체를 지워버립니다.
그리고 car2, car1 둘 다 같은 객체의 주소를 가르키고 바라보게 됩니다.

객체 배열

객체도 배열로 작성할 수 있습니다.

// 참조변수 배열(객체 배열)을 생성
Car[] carArr = new Car[3];
// 객체를 생성해서 배열의 각 요소에 저장 (중요!!!)
carArr[0] = new Car();
carArr[1] = new Car();
carArr[2] = new Car();

위 처럼 작성하면 되고 이 때 주의할 점은 객체 배열은 참조변수 배열이므로 각 참조변수에 객체를 연결시켜야 합니다.

// 초기화 블럭으로 한 줄로 간단히
Car[] carArr = {new Car(), new Car(), new Car()};
// 수가 많다면 for 문으로
Car[] carArr = new Car[100];
for (int i=0; i < carArr.length; i++){
	carArr[i] = new Car();
}

클래스란?

앞에서 클래스를 간단히 설명했지만 보다 자세하게 작성했습니다.
1. 설계도
2. 데이터와 함수
3. 사용자 정의 타입

데이터와 함수

변수 : 하나의 데이터를 저장할 수 있는 공간

배열: 같은 종류의 여러 데이터를 하나로 저장할 수 있는 공간

구조체 : 서로 관련된 여러 데이터(다른 타입)을 하나로 저장할 수 있는 공간

클래스: 데이터와 함수의 결합(구조체 + 함수)

사용자 정의 타입

원하는 타입을 직접 만들 수 있습니다. 아래는 시간이라는 타입을 만들어 본 예제입니다.

시간은 시,분,초 라는 속성이 있습니다. 만약 객체를 사용하지 않고 이를 사용하려면

// 변수 사용
int hour1, hour2, hour3;
int minute1, minute2, minute3;
int second1, second2, second3;
// 배열 사용
int[] hour = new int[3];
int[] minute = new minute[3];
int[] second = new second[3];

하나의 시간을 만들기 위해 시,분,초 3개의 변수를 작성해야 하고 이를 직접 구분도 해주어야 합니다.
또는 수가 많다면 위처럼 시,분,초 각각 배열로 작성해야 하는데 이는 서로 독립적이기에 관리가 편하지 않습니다.

// 객체 지향적인 코드
class Time{
	int hour;
	int minute;
	int second;
}

Time[] time = new Time[3];

time[0] = new Time();
time[1] = new Time();
time[2] = new Time();

class를 사용하여 시간이라는 객체를 만든다면 서로 다른 시간을 구분하기도 쉽고 관리하기도 편해집니다.

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

앞서 객체의 속성을 담당하는 것은 클래스의 변수라고 설명드렸습니다.
이 변수는 위치에 따라 그리고 앞에 static의 유무에 따라 종류가 다릅니다.

영역
클래스 영역 - iv(instance variable), cv(class variable)
메서드 영역 - lv(local variable)

클래스 영역은 클래스{} 안에 있고 메서드{} 밖에 있는 영역을 말합니다.
메서드 영역은 메서드{} 안에 있는 영역을 말합니다.

선언의 순서는 상관없지만 일반적으로 메서드보다 변수를 먼저 작성해줍니다.

인스턴스 변수(iv)와 클래스 변수(cv)의 다른점은 무엇인가요?
Q. 이 카드의 속성은 어떤 것들이 있나요?
A. 무늬, 숫자, 폭, 높이 등등
Q. 카드마다 개별적인 속성은 무엇인가요?
A. 무늬, 숫자
Q. 카드의 공통적인 속성은 무엇인가요?
A. 폭, 높이
무늬, 숫자 = iv(인스턴스 변수)
폭, 높이 = cv(클래스 변수)

public class TestMain {
    public static void main(String[] args){
        Card card1 = new Card();
        Card card2 = new Card();

        card1.kind = "heart";
        card1.number = 9;

        card2.kind = "spade";
        card2.number = 5;
				
        Card.width = 80; // card1.width = 80 도 가능은 하지만 권장 x
        Card.height = 150; // card2.height = 150 도 가능은 하지만 권장 x
        System.out.println("card | kind | number | width | height");
        System.out.printf("card1 | %s | %d | %d | %d \n", card1.kind, card1.number, card1.width, card1.height);
        System.out.printf("card2 | %s | %d | %d | %d ", card2.kind, card2.number, card2.width, card2.height);

    }
}

class Card {
	// 인스턴스 변수( 개별 속성 )
	String kind; // 무늬
	int number; // 숫자
	// 클래스 변수( 공통 속성 )
	static int width = 100; // 폭
	static int height = 250; // 높이
}

>> card | kind | number | width | height
>> card1 | heart | 9 | 80 | 150 
>> card2 | spade | 5 | 80 | 150

JVM 메모리 구조

우리가 프로그램을 실행하면 가장 먼저 main 메서드를 실행합니다.

이 main 메서드는 Call Stack에 쌓이고 main 메서드의 코드가 실행됩니다.

// CarMain.java
public class CarMain {
    public static void main(String[] args){
	// 객체 생성
        Car car1 = new Car00();
        Car car2 = new Car00();
    // car1 = car2;
	// 객체 사용
	car1.wheel = 5;
        System.out.println("car1.wheel = " + car1.wheel);
        System.out.println("car2.wheel = " + car2.wheel);
        car2.drive(10);
        System.out.println("car1.fuel = " + car1.fuel);
        System.out.println("car2.fuel = " + car2.fuel);
    }
}

이 코드를 예시로 들면 우선 객체를 생성하는 코드가 실행될 때 Car라는 클래스가 메서드 영역에 저장됩니다.
이 때 Car 클래스 영역에 static이 붙은 변수는 클래스와 같이 메서드 영역에 저장됩니다.
그리고 car1, car2는 인스턴스로 static이 붙지 않은 변수들이 인스턴스와 함께 Heap에 저장됩니다.
1. static이 붙으면 class와 함께 메서드 영역에 저장
2. static이 붙지 않으면 인스턴스와 함께 힙 영역에 저장

호출 스택(call stack)

스택: 밑이 막힌 상자, 위에 차곡차곡 쌓인다.
1. 메서드가 호출되면 수행에 필요한 만큼의 메모리를 스택에 할당받는다.
2. 메서드가 수행을 마치고 나면 사용했던 메모리를 반환하고 스택에서 제거된다.
3. 호출스택의 제일 위에 있는 메소드가 현재 실행 중인 메서드이다.
4. 아래에 있는 메서드가 바로 위의 메서드를 호출한 메서드이다.

profile
우아한테크코스 4기 교육생

0개의 댓글