본문 바로가기
공부 자료/Java

[Java] 클래스

by 미노킴 2022. 11. 13.

1. 객체

객체란 물리적으로 존재하거나 추상적으로 생각할 수 있는 것 중에서 자신의 속성을 가지고 있으면서 식별 가능한 것을 의미한다. 객체는 속성과 동작으로 구성되어 있다. 자바에서 속성은 필드(field), 동작은 메소드(method)라고 부른다.

 

2. 클래스와 인스턴스

객체를 만들기 위한 설계도가 클래스(class), 그 클래스로 만든 객체를 해당 클래스의 인스턴스(instance)라고 한다. 클래스의 구성 멤버로는 필드, 생성자(Constructor), 메소드가 있다. 이 구성 멤버들은 복수로 작성될 수도 있고, 생략할 수도 있다.

 

public class ClassName{
	
    //필드, 객체의 데이터가 저장되는 곳
	int fieldname;
    
    //생성자, 객체 생성 시 초기화 역할 담당
    ClassName(){...}
    
    //메소드, 객체의 동작에 해당하는 실행 블록
    void methodName(){...}
    
}

 

2-1. 필드

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

 

필드 선언

필드는 클래스 블록 내에 어디서든 할 수 있지만 가독성을 위해 생성자와 메소드 전에 선언하는 것이 좋다. 필드 선언 방법은 다음과 같다.

 

String company = "현대자동차";
String model;
int maxSpeed = 300;
int currentSpeed;

 

타입 필드 = 초기값; 으로 선언하며 초기값을 주지 않고 선언만 할 수도 있다. 초기값을 주지 않을 경우 필드의 값은 기본 초기값으로 설정이 된다. 참조 타입은 모두 null, int 타입은 0, boolean 타입은 false가 기본 초기값이다.

 

정적(static) 필드는 클래스이름.필드이름; 으로 사용할 수 있고, 인스턴스 필드는 객체이름.필드이름;으로 사용할 수 있다. 정적필드와 인스턴스 필드에 대한 내용은 밑에서 추가로 설명한다.

 

final 필드는 첫 선언과 생성자 실행으로 값을 받은 이후에는 값을 변경할 수 없는 필드를 의미한다. 필드 선언시 필드 앞에 final을 붙여서 사용할 수 있다.

 

final과 static을 합친 필드를 상수라고 하며, static final 타입 상수 = 초기값; 으로 선언한다. 상수 이름은 모두 대문자로 작성하는 것이 관례이다. 서로 다른 단어가 혼합된 이름이라면 _ 를 이용해서 단어를 연결해준다.

 

static final double EARTH_RADIUS=6400;

 

2-2. 생성자

생성자는 new 연산자로 객체를 생성할때 호출되어 객체의 초기화를 담당한다. 모든 클래스는 생성자가 반드시 존재하며, 생성자를 여러 개 가질 수도 있다. 만약 클래스의 생성자가 없다면 컴파일러가 기본 생성자(Default Constructor)를 바이트 코드에 자동으로 추가한다. 기본 생성자의 접근 제한자는 class의 접근 제한자를 따라간다.

 

필드 초기화

클래스로부터 객체가 생성될 때 필드는 기본 초기값으로 자동 설정된다. 만약 다른 값으로 초기화 하고 싶다면 필드를 선언할 때 초기값을 주거나, 생성자에서 초기값을 주어야 한다. 클래스의 필드를 객체별로 다르게 하고 싶을 때 생성자에 매개변수를 설정하여 필드값을 초기화할 수 있다.

 

생성자의 선언은 다음과 같이 이루어진다.

 

클래스이름(매개변수타입 매개변수이름, ... ) { 초기화 내용 }

 

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

 

위 코드에서 생성자는 String 객체인 n과 s를 매개변수로 받고 name 필드에는 n을, ssn 필드에는 s을 넣어 필드를 초기화시킨다.

 

클래스에 생성자가 적혀있을 경우 컴파일 시 기본 생성자가 따로 생성되지 않는다. 또한 생성자에 매개변수가 있을 경우 객체를 선언할 때 생성자에 적힌 타입과 개수에 맞춰서 매개변수를 주지 않는다면 컴파일 에러가 난다.

 

일반적으로 생성자의 매개변수 이름은 필드의 이름을 그대로 가져온다. 그 경우 해당 생성자의 내부에서 필드는 this.필드이름, 매개변수는 그대로 매개변수 이름(=필드 이름) 으로 사용한다. 매개변수와 필드의 이름이 동일한 경우 매개변수가 사용 우선순위가 높기 때문이다.

 

public class Korean {
    //필드
    String nation = "대한민국";
    String name;
    String ssn;
    
    //생성자
    public Korean(String name, String ssn) {
    	this.name = name; // this.name은 필드, name은 매개변수
        this.ssn = ssn; // this.ssn은 필드, ssn은 매개변수
    }
}

 

생성자 오버로딩(constructor overloading) 

생성자 오버로딩이란 매개 변수를 달리하는 생성자를 여러 개 선언하는 것을 의미한다. 이때 매개 변수의 타입과 개수, 그리고 선언된 순서가 똑같을 경우 매개 변수의 이름만 바꾸는 것은 생성자 오버로딩이 아니다.

 

//오버로딩이 아닌 경우
Car(String model, String color) {...}
Car(String color, String model) {...}

//오버로딩
public class Car {
	Car(){...}
    Car(String model){...}
    Car(String model, String color){...}
    Car(String model, String color, int maxSpeed){...}
}

 

생성자 오버로딩이 많아지면 생성자 간의 중복된 코드가 발생할 수 있다. 이런 중복된 코드를 줄이기 위해서 사용하는 것이 this()이다. 생성자 내부에서 this()를 통해 클래스 내부의 다른 생성자를 호출할 수 있다. 단, this() 코드는 반드시 생성자의 첫 줄에서만 허용된다.

 

public class Car {
	//필드 선언
	...
    
	//생성자
	Car(){}
    
	Car(String model, String color, int maxSpeed) {
		this.model = model;
		this.color = color;
		this.maxSpeed = maxSpeed;
	}
    
	Car(String model) {
		this(model, "은색", 250);    // 8줄의 생성자 호출
	}
    
	Car(String model, String color) {
		this(model, color, 250);    // 8줄의 생성자 호출
	}

 

 

2-3. 메소드

메소드 구성

메소드 선언은 선언부와 실행 블록으로 구성된다. 메소드 선언부를 메소드 시그니처(signature)라고 하며, 선언부는 리턴타입, 메소드 이름, 매개 변수 선언 으로 이루어져 있다.

 

리턴타입 메소드이름 (매개변수 선언) {실행블록}

// 메소드 선언 예시
void powerOn(){...}
double divide( int x, int y){...}

 

메소드의 리턴값이 없으면 선언부에 void, 리턴값이 있다면 선언부에 해당 리턴 타입을 적어주면 된다. return문의 리턴값은 선언부에 적힌 리턴 타입이거나 해당 리턴 타입으로 변환될 수 있는 타입이어야 한다.

 

메소드의 실행 블록은 return이 나온다면 종료된다. void에서도 return; 을 사용하여 메소드를 강제 종료할 순 있지만, 자주 사용하진 않는다.

 

메소드 호출

메소드에서 클래스 내부의 다른 메소드를 호출할 때는 메소드이름(매개값)으로 호출하면 된다.

 

클래스 외부에서 메소드를 호출하는 방법은 메소드가 정적(static)메소드인지 인스턴스 메소드인지에 따라 갈린다.

 

public class Car{
	//필드
    ...
    
    //생성자
    ...
    
    //정적 메소드
    static void method1(){...}
    
    //인스턴스 메소드
    void method2(){...}
}

public class CarExample{
	public static void main(String[] args){
    	Car myCar = new Car();
        
        //인스턴스 메소드 사용
        myCar.method2()
        
        //정적 메소드 사용
        Car.method1()
    }
}

 

인스턴스 메소드는 인스턴스를 만들어서 '객체 이름.메소드 이름'  으로 호출해야 하고, 정적 메소드는 객체를 따로 만들지 않아도 사용할 수 있으며 '클래스 이름.메소드 이름' 으로 호출한다.

 

메소드 오버로딩(method overloading)

클래스 내에 같은 이름의 메소드를 여러 개 선언하는 것을 메소드 오버로딩이라 한다. 생성자 오버로딩처럼 매개 변수의 타입, 개수, 순서 중 하나가 달라야 한다.

 

3. 라이브러리 클래스와 실행 클래스

클래스는 두 가지 용도가 있다. 하나는 라이브러리(API: Application Program Interface)용이고 다른 하나는 실행용이다. 라이브러리 클래스는 다른 클래스에서 이용할 목적으로 설계된다. 프로그램 전체에서 사용하는 실행 클래스는 단 하나이고, 나머지는 모두 라이브러리 클래스이다. 실행 클래스는 프로그램의 실행 진입점인 main()메소드를 제공하는 역할을 한다.

 

한 클래스를 라이브러리인 동시에 실행 클래스로 만들 수도 있지만, 웬만해선 분리해서 사용하는 것이 좋다.

 

4. 인스턴스 멤버와 정적 멤버

인스턴스 멤버는 객체마다 가지고 있는 멤버(필드,메소드)를 의미하고, 정적 멤버는 클래스에 위치시키고 객체들이 공유하는 멤버를 말한다.

 

정적 멤버를 선언하려면 멤버를 선언할 때 앞에 static을 붙이면 된다. 정적 메소드는 메소드 내에서 인스턴스 필드나 인스턴스 메소드를 사용할 수 없으니 주의하자. 

 

정적 멤버는 객체를 만들지 않고도 사용할 수 있다. '클래스 이름.필드or메소드 이름' 으로 사용한다.

 

 

참고 서적: 혼자 공부하는 자바