220526~0530_혼.공.자 - Ch.6 클래스 1 (객체 지향 프로그래밍, 필드, 생성자)

창고·2022년 10월 14일
0

티스토리에 저장했던 글을 옮겼습니다.
https://mrcocoball.tistory.com/66
https://mrcocoball.tistory.com/68
https://mrcocoball.tistory.com/75

Chapter 6. 클래스

1. 객체 지향 프로그래밍

(1) 객체란

  • 객체(object) : 물리적으로 존재하거나 추상적으로 생각할 수 있는 것 중에서
    자신의 속성을 가지고 있으면서 식별 가능한 것
  • 객체는 속성과 동작으로 구성 되어 있음
  • 속성을 필드(field), 동작을 메소드(method) 라고 부름
  • 현실 세계와 자바의 객체 비교
    [현실 세계]
    사람
    ㄱ. 속성 : 이름, 나이
    ㄴ. 동작 : 웃다, 먹다

    [자바]
    사람 객체
    ㄱ. 필드 : 이름, 나이
    ㄴ. 메소드 : 웃다, 먹다
  • 객체 모델링 (object modeling) : 현실 세계의 객체를 소프트웨어 객체로 설계하는 것
    현실 세계 객체의 속성과 동작을 추려내어 소프트웨어 객체의 필드와 메소드로 정의하는 과정
  • 객체 지향 프로그래밍 (OOP : Object-Oriented Programming) :
    객체를 생성하고 이러한 객체를 하나씩 조립해서 완성된 프로그램을 만드는 기법

(2) 객체의 상호 작용

  • 소프트웨어에서 객체들은 각각 독립적으로 존재하고 다른 객체와 서로 상호작용하면서 동작
  • 객체들 사이의 상호작용 수단은 메소드이며, 객체가 다른 객체의 기능을 이용하는 것이 메소드 호출
  • 예시
    사람 객체
    ㄱ. 필드 : 이름, 나이
    ㄴ. 메소드 : 웃다, 먹는다
    계산기 객체
    ㄱ. 필드 : 제조사
    ㄴ. 메소드 : 더하기, 빼기
    에서 사람 객체가 계산기 측으로 메소드 호출, 메소드는 결과를 리턴
리턴값 = 계산기객체.메소드(매개값1, 매개값2, ...);
int result = Calculator.add(10,20);

// 객체에 도트(.) 연산자를 붙이고 메소드 이름을 기술, 도트 연산자는 객체의 필드와 메소드에 접근 시 사용
// 매개값은 메소드를 실행하기 위해 필요한 데이터, 예를 들면 10 + 20 계산 필요 시 10, 20이 더하기 기능의 매개값
// 리턴값은 메소드가 실행되고 난 후 호출한 곳으로 돌려주는 값

(3) 객체 간의 관계

1) 집합 관계

  • 집합 관계에 있는 객체는 하나는 부품이고 하나는 완성품에 해당
    예) 자동차는 엔진, 타이어, 핸들 등으로 구성되므로 자동차와 이 부품들은 집합 관계

2) 사용 관계

  • 객체 간의 상호 작용을 의미하며, 객체는 다른 객체의 메소드를 호출하여 원하는 결과를 얻어냄
    예) 사람 객체는 자동차를 사용하므로 두 객체는 사용 관계라고 할 수 있으며 사람은 자동차를 사용할 때 자동차의 '달린다' '멈춘다' 등의 메소드를 호출함

3) 상속 관계

  • 상위(부모) 객체를 기반으로 하위(자식) 객체를 생성하는 관계
  • 일반적으로 상위 객체는 종류를 의미하고 하위 객체는 구체적인 사물에 해당
    예) '자동차는 기계의 한 종류이다' 에서 기계(상위)와 자동차(하위)는 상속 관계

(4) 객체와 클래스

  • 객체는 설계도를 통해 제작되며 메모리에서 사용하고 싶은 객체가 있다면 우선 설계도로 해당 객체를 만드는 작업이 필요
  • 클래스(class) : 자바에서의 설계도이며, 객체를 생성하기 위한 필드와 메소드가 정의되어 있음
  • 인스턴스(instance) : 클래스로부터 만들어진 객체를 해당 클래스의 인스턴스라고 함
    (자동차 객체 = 자동차 클래스의 인스턴스)
  • 클래스로부터 객체를 만드는 과정을 인스턴스화라고 함
    개발자 -> 설계 -> 클래스(설계도) -> 인스턴스화 -> 인스턴스(객체)
  • 객체 지향 프로그래밍 개발의 단계
    • 클래스를 설계
    • 설계된 클래스를 가지고 사용할 객체를 생성
    • 생성된 객체를 이용

(5) 클래스 선언

  • 사용하는 객체를 구상했다면 그 객체의 대표 이름을 결정하고 이것을 클래스 이름으로 지정
  • 식별자 작성 규칙
    • 하나 이상의 문자로 이루어져야 함
    • 첫 글자에는 숫자가 올 수 없음
    • $, _ 외의 특수문자 사용 불가
    • 자바 키워드 사용 불가 int(x), for(x) 등
    • 대소문자를 구분하며, 단어의 첫글자를 대문자로 하고 나머지를 소문자로 작성
      Calulatior, Car, Member, SchoolIdol, Web_Browser
  • 클래스 이름 지정 후 '클래스 이름.java' 로 소스 파일 생성 필요
    (클래스 이름과 대소문자가 같아야 함)
  • 소스 파일 내부에서 다음과 같이 클래스 선언
public class 클래스 이름 {

}
  • 일반적으로 소스 파일 하나 당 하나의 클래스를 선언하는데 2개 이상의 클래스 선언도 가능
public class Idol {
}

class Aqours {
}
  • 2개 이상의 클래스가 선언된 소스 파일을 컴파일하면 바이트 코드 파일(.class)은 클래스를 선언한 개수만큼 생성
    즉, 소스 파일은 클래스 선언을 담고 있는 저장 단위일 뿐 클래스 자체가 아님
    -> 위의 경우 Idol.class와 Aqours.class가 각각 생성됨
  • public 접근 제한자 : 파일 이름과 동일한 이름의 클래스 선언에만 붙일 수 있음
    소스 파일 생성 시 꼭 기억해야 하는 내용!!

(6) 객체 생성과 클래스 변수

  • 클래스 선언 후 컴파일 진행(이클립스에서는 저장) 후 객체를 성생할 설계도가 만들어짐
  • 클래스로부터 객체를 생성하려면 다음과 같이 new 연산자 사용
new 클래스();
  • new 연산자 뒤에는 생성자가 오는데 생성자는 클래스() 형태를 지님
  • new 연산자로 생성된 객체는 매모리 힙 영역에 생성
  • 힙 영역에 객체 생성 후 객체의 번지를 리턴하도록 되어 있음
  • 참조 타입인 클래스 변수에 저장해두면 변수를 통해 객체를 사용할 수 있음
클래스 변수;
변수 = new 클래스();
Idol idol;
idol = new Idol();

// 혹은

클래스 변수 = new 클래스();
Idol idol = new Idol();
  • new 연산자로 객체를 생성하고 리턴된 객체의 번지를 변수 (idol)에 저장하면 변수가 객체를 참조하게 됨
[클래스 선언 - Idol.java]
public class Idol {

}
[클래스로부터 객체 생성 - IdolExample.java]
public class IdolExample {

	Idol i1 = new Idol();
    Idol i2 = new Idol();

}
  • IdolExample을 실행하면 메모리에 클래스 변수와 객체가 생성됨.

  • Idol 클래스는 하나지만 new 연산자를 사용한 만큼 객체가 메모리에 생성됨

  • 이러한 객체들이 바로 클래스의 인스턴스이며 같은 클래스로부터 생성되었으나
    각 객체는 자신만의 고유 데이터를 가지며 메모리에서 활동하게 됨

  • 클래스의 용도

    • 라이브러리(API : Application Program Interface) 용 : 다른 클래스에서 이용할 목적으로 설계
    • 실행용 : 프로그램의 실행 진입점인 main() 메소드를 제공하는 역할
    • 프로그램 전체에서 사용되는 클래스가 100개라면 99개는 라이브러리 클래스이고 단 하나가 실행 클러스
    • 위의 예시에서 Idol은 라이브러리 클래스, IdolExample은 실행 클래스
    • 대부분의 객체 지향 프로그램은 라이브러리 (부품 객체 및 완성 객체) 와 실행 클래스가 분리되어 있음

(7) 클래스의 구성 멤버

  • 클래스에는 객체가 가져야 할 구성 멤버가 선언되며 이 구성 멤버들을 생략되거나 복수의 개수로 작성될 수 있음
public class Classname {

  //필드
  int fieldname;

 // 생성자
 Classname() { ... }

 // 메소드
 void mathodName()

1) 필드 (Field)

  • 객체의 고유 데이터, 부품 객체, 상태 정보를 저장하는 곳.
  • 선언 형태는 변수와 비슷하나 변수라고 부르지는 않음
  • 생성자와 메소드 내에서만 사용되며 생성자와 메소드가 실행 종료 되면 자동 소멸되는 변수와 달리
    생성자와 메소드 전체에서 사용되며 객체가 소멸되지 않는 한 객체와 함께 존재

2) 생성자 (Constructor)

  • new 연산자로 호출되는 특별한 중괄호 블록
  • 객체 생성 시 초기화를 담당, 필드를 초기화하거나 메소드를 호출해서 객체를 사용할 준비를 함
  • 리턴 타입이 없음

3) 메소드 (Method)

  • 객체의 동작에 해당하는 중괄호 블록
  • 중괄호 블록 이름이 메소드 이름
  • 메소드 호출 시 중괄호 블록에 있는 모든 코드들이 일괄적으로 실행
  • 필드를 읽고 수정하는 역할도 하나 다른 객체를 생성해서 다양한 기능을 수행하기도 함
  • 객체 간의 데이터를 전달하는 수단
  • 외부(호출한 곳)로부터 매개 값을 받아 실행에 이용하고 실행 후 결과 값을 외부(호출한 곳)로 리턴할 수도 있음

2. 필드

(1) 필드란?

  • 필드(Field) : 객체의 고유 데이터, 객체가 가져야 할 부품, 객체의 현재 상태 데이터를 저장하는 곳
  • 예시
자동차 객체자동차 클래스 public class Car {
[고유 데이터]
제작회사
모델
색깔
최고 속도

String company;
String model;
String color;
int maxSpeed
[상태 데이터]
현재 속도
엔진 회전수

int speed;
int rpm;
[부품]
차체
엔진
타이어

Body body;
Engine engine;
Tire tire;

(2) 필드 선언

  • 필드 선언은 클래스 중괄호 {} 블록 어디서든 존재할 수 있음
  • 생성자 선언, 메소드 선언 앞 뒤 어떤 곳에서 선언이 가능
  • 다만 생성자, 메소드 중괄호 {} 블록 내부에는 선언될 수 없음 (모두 로컬 변수가 되어버림)
  • 필드 선언은 변수의 선언 형태와 비슷함
타입 필드 = 초기값 ;
String company = "현대자동차";
  • 타입은 필드에 저장할 데이터의 종류를 결정하며 기본 타입, 참조 타입 모두 올 수 있음
  • 필드 초기값은 필드 선언 시 주어질 수도 있고 생략될 수 있으며, 초기값이 지정되지 않은 필드는
    객체 생성 시 자동으로 기본 초기값으로 설정됨
    (참조 타입의 경우 객체를 참조하지 않고 있는 null 로 초기화됨)

(3) 필드 사용

  • 필드를 사용한다는 것은 필드 값을 읽고 변경하는 작업을 의미
  • 클래스 내부의 생성자/메소드에서 사용 시 단순히 필드 이름으로 읽고 변경이 가능
  • 클래스 외부에서 사용할 경우 클래스로부터 객체를 생성한 뒤 필드를 사용해야 함
    (필드가 객체에 소속된 데이터이기 때문에 객체가 존재하지 않으면 필드도 존재하지 않음)
  • 예시

[Person 클래스]

void method() {

 // Car 객체 생성
 Car myCar = new Car();
 // 필드 사용
 myCar.speed=60;

}

// → Car 클래스를 통해 Car 객체를 생성한 뒤 myCar 변수가 Car를 참조
// 여기서 speed 초기값이 0인 상태인데 myCar.speed=60; 을 통해
// 생성자 내부의 speed와 메소드 내부의 speed 값이 60으로 변경됨

[Car 클래스]

// 필드
int speed;

// 생성자
Car() {
 speed=0; // myCar.speed=60; 을 통해 60으로 변경
}

// 메소드
void method(...) {
 speed=10; // myCar.speed=60; 을 통해 60으로 변경
}

[Car 클래스 필드 선언]

public class Car {

	String company = "현대자동차";
    String model = "그랜저";
    String color = "검정";
    int maxSpeed = 350;
    int speed;

}

[외부 클래스에서 Car 필드값 읽기와 변경]

public class CarExample {

	public static void main(String[] args) {
    
    	// 객체 생성
        Car myCar = new Car();
        
        // 필드값 읽기
        System.out.println("제작회사 : " + myCar.company);
        System.out.println("모델명 : " + myCar.model);
        System.out.println("색깔 : " + myCar.color);
        System.out.println("최고속도 : " + myCar.maxSpeed);
        System.out.println("현재회사 : " + myCar.speed);
        
        // 필드값 변경
        myCar.speed = 60;
        System.out.println("수정된 속도 : " + myCar.speed);  
    }
}

3. 생성자

(1) 생성자란?

  • 생성자(Constructor) : new 연산자로 클래스로부터 객체를 생성할 때 호출되어 객체의 초기화 담당
  • 객체 초기화 : 필드를 초기화하거나 메소드를 호출, 객체를 사용할 준비를 하는 것
  • 생성자를 실행하지 않고는 클래스로부터 객체를 만들 수 없음
  • new 연산자에 의해 생성자가 성공적으로 실행되면 힙 영역에 객체가 생성되고 객체의 번치가 리턴됨
    또한 리턴된 객체의 번지는 클래스 변수에 저장됨

(2) 기본 생성자

  • 모든 클래스는 생성자가 반드시 존재, 하나 이상 가질 수 있음
  • 기본 생성자 (Default Constructor) : 클래스 내부에 생성자 선언 생략 시 컴파일러가 자동으로 바이트 코드에 추가함
[public] 클래스() {}

(3) 생성자 선언

  • 생성자를 명시적으로 선언하려면 다음과 같은 형태로 작성
클래스(매개 변수 선언, ...) {
 // 객체의 초기화 코드
}
  • 매개 변수는 new 연산자로 생성자를 호출할 때 외부의 값을 생성자 블록 내부로 전달하는 역할
  • 매개 변수 선언은 생략도 가능하고 여러 개를 선언하여도 됨
  • 예시
// Car 생성자 호출 시 3개의 매개값을 제공한다고 가정하면
// Car myCar = new Car("그랜저", "검정", 300);
// 일 때 생성자는 3개의 매개값을 받기 위해 매개 변수를 선언해야 함

public class Car {

 // 생성자
 Car(String model, String color, int maxSpeed) {...}
  • 클래스에 생성자가 명시적으로 선언되어 있을 경우에는 반드시 선언된 생성자를 호출해서 객체를 생성해야 함
    (필수 입력 조건이라고 생각하면 될듯)
    -> 위의 상황에서는 Car myCar = new Car("차 모델", "색깔", "속도"); 로 호출해야 생성자가 생성되며 Car myCar = new Car(); 로는 생성되지 않음

(4) 필드 초기화

  • 클래스로부터 객체가 생성될 때 필드는 기본 초기값으로 자동 설정됨
  • 만약 다른 값으로 초기화를 하고 싶다면 두 가지 방법이 있음
    • 필드를 선언할 때 초기값을 주는 방법
    • 생성자에서 초기값을 주는 방법
  • 객체 생성 시점에 외부에서 제공되는 다양한 값들로 초기화하여야 한다면 생성자에서 초기화가 필요
  • 예시
public class Korean {

 	// 필드
 	String nation = "대한민국"; // 초기값
 	String name;
 	String ssn;

 	// 생성자
 	public Korean(String n, String s) {
 		name = n; // 매개 변수 n으로 이름 지정
  		ssn = s; // 매개 변수 s로 번호 지정
    }
}

// 지정 시

Korean k1 = new Korean("최쾅쾅", "990615-1123456")
Korean k2 = new Korean("최큥큥", "990616-1123564")

// 로 호출하면 필드의 name, ssn에 초기값 지정 가능
  • 변수가 중복될 우려가 있기 때문에 this (객체 자신 참조) 를 사용하여 표현하면
public class Korean {

 	// 필드
 	String nation = "대한민국"; // 초기값
 	String name;
 	String ssn;

 	// 생성자
 	public Korean(String name, String ssn) {
  		this.name = name; // 매개 변수 n으로 이름 지정
  		this.ssn = ssn; // 매개 변수 s로 번호 지정
 	}
}

// 으로 수정 가능
//※ Python의 self와 유사 self.name = name
  • 객체의 필드는 하나가 아닌 여러 개가 있고 이 필드들을 모두 생성자에서 초기화할 경우 생성자의 매개 변수 수는 객체의 필드 수 만큼 선언되어야 함
  • 그러나 실제로는 중요한 몇 개의 필드만 매개 변수를 통해 초기화되고 나머지는 필드 선언 시, 혹은 생성자 내부에서 임의의 값 / 계산된 값으로 초기화되거나
    객체 생성 후 필드값을 별도로 저장하는 편임

(5) 생성자 오버로딩

  • 생성자 오버로딩(overloading) : 매개 변수를 달리하는 생성자를 여러 개 선언하는 것
  • 외부에서 제공되는 다양한 데이터들을 이용해서 객체를 초기화하려면 생성자 역시 다양화되어야 하기 때문에
    생성자 오버로딩이 필수
  • 예시
// Car 클래스를 예시로 하면

public class Car {

	Car() {...}
    Car(String model) {...}
    Car(String model, String color) {...}
    Car(String model, String color, int maxSpeed) {...}
    
}
  • 주의점 : 매개 변수의 타입과 개수, 선언된 순서가 똑같을 경우 매개 변수 이름만 바꾸는 것은 오버로딩이 아님
Car(String model, String color) {...}
Car(String color, String model) {...} // 오버로딩 아님
  • 생성자 오버로딩되어 있을 경우 new 연산자로 생성자 호출 시 제공되는 매개값의 타입과 수에 의해 호출될 생성자가 결정

(6) 다른 생성자 호출 : this()

  • 생성자 오버로딩이 많아질 경우 생성자 간 중복된 코드가 발생할 가능성 높음
  • 매개 변수의 수만 달리하고 필드 초기화 내용이 비슷한 경우 이러한 현상 多
  • 필드 초기화 내용은 한 생성자에만 집중적으로 작성하고 나머지 생성자는 초기화 내용을 가지고 있는 생성자를 호출하는 방법으로 개선
  • 생성자에서 다른 생성자 호출 시 this 코드 사용
  • 예시
클래스 ( [매개 변수, ...] ) {
	this (매개 변수, ...,, ...); // 클래스의 다른 생성자 호출
 	실행문;
}
  • this()는 자신의 다른 생성자를 호출하는 코드로 반드시 생성자의 첫 줄에서만 허용
  • this()의 매개값은 호출되는 생성자의 매개 변수에 맞게 제공해야 함
  • this() 다음에 추가적인 실행문이 올 수 있음. 이느 호출되는 생성자의 실행이 끝나면 원래 생성자로 돌와와서 다음 실행문을 진행
  • 중복 코드 예시
Car(String model) {
	this.model = model;
    this.color = "은색";
    this.maxSpeed = 250;
}

Car(String model, String color) {
	this.model = model;
    this.color = color;
    this.maxSpeed = 250;
}

Car(String model, String color, int maxSpeed) {
	this.model = model;
    this.color = color;
 	this.maxSpeed = maxSpeed;
}

// -> 3개의 생성자 내용이 비슷하므로 앞의 2개의 생성자에서 this()를 사용, 마지막 생성자를 호출하도록 수정

Car(String model) {
	this(model, "은색", 250); // 밑의 세번째 생성자로 호출됨

Car(String model, String color) {
	this(model, color, 250); // 밑의 세번째 생성자로 호출됨

Car(String model, String color, int maxSpeed) {
	this.model = model;
 	this.color = color;
 	this.maxSpeed = maxSpeed; // model~maxSpeed 해당 블록이 공통 실행 코드
}
profile
공부했던 내용들을 모아둔 창고입니다.

0개의 댓글