Java #1. 클래스(1)

1. 객체 지향 프로그래밍

객체를 하나씩 조립해서 완성된 프로그램을 만드는 기법을 객체 지향 프로그래밍(OOP: Object-Oriented Programming)이라고 한다.

객체란 물리적으로 존재하거나 추상적으로 생각할 수 있는 것 중에서 자신의 속성을 가지고 있으면서 식별 가능한 것을 말한다.

  • 객체 : 속성과 동작으로 구성
  • 필드 : 속성
  • 메소드 : 동작

객체들은 각각 독립적으로 존재하고, 다른 객체와 서로 상호작용 하면서 동작한다.
객체들 사이의 상호작용 수단은 메소드이며, 객체가 다른 객체의 기능을 이용하는 것이 바로 메소드 호출이다.

int result = Calculator.add(10, 20);
// 리턴한 값을 int 변수에 저장

1-1.) 객체와 클래스

자바에서의 설계도를 클래스라고 하며 클래스에는 객체를 생성하기 위한 필드와 메소드가 정의되어 있다. 클래스에서 만들어진 객체를 해당 클래스의 인스턴스라고 한다.

소스파일을 생성하면 소스 파일을 열고 다음과 같이 클래스를 선언한다.

public class 클래스이름 {

}

생성된 클래스로부터 객체를 생성하려면 new 연산자를 사용하면 된다.

new 클래스(); 

new 연산자는 힙 영역에 객체를 생성시킨 후 객체의 번지 주소를 리턴한다.
이 번지 주소를 참조 타입인 클래스 변수에 저장해두면 변수를 통해 객체를 사용할 수 있다.

클래스 변수 = new 클래스();
<StudentExample.class>

public class Student {
}

public class StudentExample {
	pulic static void main(String[] args) {
    	Student s1 = new Student();
        Student s2 = new Student();
    }
}
  • Student 클래스는 하나이지만 new 연산자를 사용한 만큼 객체가 메모리에 저장된다.
  • s1과 s2가 참조하는 Student객체는 완전히 독립된 서로 다른 객체이다.

1-2.) 클래스의 구성 멤버

public class ClassName {
    // 필드
    int fieldname;
    
    // 생성자
    ClassName() { ... }
    
    // 메소드
    void method() { ... } 
}
  • 필드
    • 필드는 객체 고유의 데이터, 부품 객체, 상태 정보를 저장하는 곳.
    • 선언 형태는 변수(Variable)이나 필드를 변수라고 부르진 않는다.
    • 필드는 생성자와 메소드 전체에서 사용되며 객체가 소멸되지 않는 한 객체와 함께 존재한다.
  • 생성자
    • 객체 생성 시 초기화를 담당.
    • 필드를 초기화하거나 메소드를 호출해서 객체를 사용할 준비를 한다.
  • 메소드
    • 객체 간의 데이터를 전달하는 수단.
    • 외부로부터 매개값을 받아 실행에 이용하고, 실행 후 결과 값을 외부로 리턴.

2. 필드

필드는 객체의 고유 데이터, 객체가 가져야 할 부품, 객체의 현재 상태 데이터를 저장하는 곳

2-1.) 필드 선언

필드는 { } 블록 어디서든 존재할 수 있다.

타입 필드 [ = 초기값 ];

선언 형태는 변수와 비슷하지만, 필드를 변수라고 부르지는 않는다.

2-2.) 필드 사용

필드를 사용한다는 것은 필드값을 읽고 변경하는 작업을 말한다.

  • 클래스 내부에서 사용하는 경우
    • 필드 이름으로 읽고 변경.
  • 클래스 외부에서 사용하는 경우
    • 우선적으로 클래스로부터 객체를 생성한 뒤 필드 사용.(참조 변수를 사용)
    • 필드객체에 소속된 데이터이므로 객체가 존재하지 않으면 필드도 존재하지 않는다.
// 클래스 외부에서 필드 사용하기
<Car.class>
public class Car() {
	int speed;
}
<CarExample.class>
public class CarExample {
	public static void main(String[] args) {
    	Car myCar = new Car(); // 객체 생성 
        System.out.println("속도 : " + myCar.speed); // 필드 값 읽기
        
        myCar.speed = 60; // 필드값 변경 
        System.out.println("수정된 속도 : " + myCar.speed);

3. 생성자

생성자new 연산자로 클래스로부터 객체를 생성할 때 호출되어 객체의 초기화를 담당한다.

  • 객체 초기화란 필드를 초기화하거나 메소드를 호출해서 객체를 사용할 준비를 하는 것을 말한다.
  • new 연산자에 의해 생성자가 성공적으로 실행된다면 힙(Heap)영역에 객체가 생성되고 객체의 번지가 리턴된다.

3-1.) 기본 생성자

클래스 내부에 생성자 선언을 생략했다면 컴파일러가 { } 블록 내용이 비어있는 기본생성자를 바이트 코드에 자동으로 추가한다.

[public] 클래스 {}
  • 생성자를 선언하지 않아도 다음과 같이 new 연산자 뒤에 기본 생성자를 호출해서 객체를 생성할 수 있다.
Car myCar = new Car();

3-2.) 생성자 선언

<생성자 블록>
클래스( 매개변수 선언, ... ) {
	// 객체 초기화 코드
}
  • 생성자 블록 내부에는 객체 초기화 코드가 작성되는데, 일반적으로 필드에 초기값을 저장하거나 메소드를 호출하여 객체 사용 전에 필요한 준비를 한다.
public clas Car {
	// 생성자
    Car(String model, String color, int maxSpeed) { *** }
}

클래스에 생성자가 명시적으로 선언되어 있을 경우에는 반드시 선언된 생성자를 호출하여 객체를 생성해야만 한다.

3-3.) 필드 초기화

  • 클래스로부터 객체가 생성될 때 필드는 기본 초기값으로 자동 설정된다.
  • 다른 값으로 초기화 하고 싶으면 아래 두가지 방법이 있다.
    1. 필드를 선얼할 때 초기값을 주는 방법.
    2. 생성자에게 초기값을 주는 방법.

객체 생성 시점에서 외부에서 제공되는 다양한 값들로 초기화되어야 한다면 생성자에서 초기화 해야 한다.

public class Korean {
    // 필드
    String nation = "대한민국";
    String name;
    String ssn;
    
    // 생성자 
    public Korean(String n, String s) {
        name = n;
        ssn = s;
    }
}

3-4.) 생성자 오버로딩

생성자 오버로딩이란 매개 변수를 달리하는 생성자를 여러 개 선언하는 것을 말한다.

  • 생성자 오버로딩을 사용하는 이유는 외부에서 제공되는 다양한 데이터들을 이용해서 객체를 초기화하려면 생성자도 다양화될 필요가 있기 때문이다.
// 생성자 오버로딩
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) { ... }

3-5.) 다른 생성자 호출: this()

this()는 생성자에서 다른 생성자를 호출할 때 사용한다.

클래스( [매개변수, ...] ) {
    this( 매개변수, ... , 값, ... ); // 클래스의 다른 생성자 호출 
    실행문;
}
  • this()는 자신의 다른 생성자를 호출하는 코드로 반드시 생성자의 첫 줄에서만 허용된다.

4. 메소드

메소드 선언은 선언부와 실행 블록으로 구성된다. 메소드 선언부를 메소드 시그니처라고 하며, 선언부와 실행 블록에는 다음 요소를 포함한다.

  • 리턴 타입 : 메소드가 리턴하는 결과의 타입을 표시한다.
  • 메소드 이름 : 메소드의 기능이 드러나도록 식별자 규칙에 맞게 이름을 지어준다.
  • 매개 변수 선언 : 메소드를 실행할 때 필요한 데이터를 받기 위한 변수를 선언한다.
  • 메소드 실행 블록 : 실행할 코드를 작성한다.
리턴타입 메소드이름( [매개변수선언, ...] ) {
    실행할 코드를 작성하는 곳 // 메소드 실행 블록 
}

4-1.) 메소드 선언

  • 리턴 타입
    • 리턴 타입은 리턴값의 타입을 말한다.
    • 리턴값이 없을 수도 있으나 리턴값이 있을 경우 리턴 타입이 선언부에 명시되어야 한다.
void powerOn() { ... }
double divide(int x, int y) { ... }

리턴값이 없는 메소드는 리턴 타입에 void로 기술하며, divide() 메소드의 결과가 double값이라면 리턴 타입으로 double를 기술해야 한다.

리턴값이 있느냐 없느냐에 따라서 메소드를 호출하는 방법은 다르다. 위의 두 메소드는 다음과 같이 호출할 수 있다.

powerOn();
double result = divide(10, 20);
  • 매개 변수 선언
    • 매개 변수는 메소드가 실행할 때 필요한 데이터를 외부로부터 받기 위해 사용된다.
    • 매개 변수가 필요한 경우가 있고 필요 없는 경우가 있다.

다음은 매개 변수가 있는 divide() 메소드의 선언 예이다.

double divide(int x, int y) { ... }

이렇게 선언된 divide() 메소드를 호출할 때에는 반드시 2개의 int 값을 주어야 한다.

double result = divide(10, 20);
  • 매개 변수의 개수를 모를 경우
    • 해결 방법은 매개 변수를 배열 타입으로 선언하는 것이다.
int sum1(int[] value) { ... } 

배열의 항목 수는 함수를 호출할 때 결정된다.

4-2.) 메소드 호출

  • 클래스 내부의 다른 메소드에서 호출하는 경우 -> 메소드 이름으로 호출
  • 클래스 외부에서 호출할 경우 -> 클래스로부터 객체를 생성 후 참조 변수를 이용하여 메소드를 호출
클래스 참조변수 = new 클래스(매개값, ...);

객체가 생성되었다면 참조 변수와 함께 도트(.) 연산자를 사용해서 메소드를 호출할 수 있다.
도트(.) 연산자객체 접근 연산자로 객체가 가지고 있는 필드나 메소드에 접근할 때 사용된다.

참조변수.메소드(매개값, ...); // 리턴값이 없거나, 있어도 리턴값을 받지 않을 경우
타입 변수 = 참조변수.메소드(매개값, ...); // 리턴값이 있고, 리턴값을 받고 싶을 경우 

4-3.) 메소드 오버로딩

클래스 내에 같은 이름의 메소드를 여러 개 선언하는 것.

메소드 오버로딩의 조건은 매개 변수의 타입, 개수, 순서 중 하나가 달라야 한다.

메소드 오버로딩이 필요한 이유는 매개값을 다양하게 받아 처리할 수 있도록 하기 위해서이다.

메소드 오버로딩할 때 주의할 점은 매개 변수의 타입과 개수, 순서가 똑같을 경우 매개 변수 이름이 다르다고 해서 이것을 메소드 오버로딩이라고 하지 않는다는 것이다.

또한 리턴 타입만 다르고 매개 변수가 동일하다면 이것도 오버로딩이 아니다.

// 컴파일 에러 발생 
int divide(int x, int y) { ... }
double divide(int boonja, int boonma) { ... }

좋은 웹페이지 즐겨찾기