Java/혼공자

Chapter 06 클래스

yeooniyeoon 2023. 1. 10. 21:33
728x90
SMALL

06 - 1 객체 지향 프로그래밍

 

객체 지향 프로그래밍

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

객체는 속성과 동작으로 구성되어 있으며 자바에선 속성 - 필드, 동작 - 메소드라고 한다.

 

 

객체와 클래스

클래스 = 설계도,

인스턴스 = 클래스로부터 만들어진 객체,

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

 

 

클래스 선언

클래스명은 다른 클래스와 식별할 목적으로 사용되므로 식별자 작성 규칙을 따른다.

 

식별자 작성 규칙

- 하나 이상의 문자로 이뤄짐

- '$', '_' 이외의 특수문자 사용 X

- 첫 글자 숫자 X

- 자바 키워드 사용 X

 

소스파일명과 클래스명은 대소문자까지 일치하도록 해야 함.

 

클래스 선언 시에는 public class 키워드를 사용해야 함.

 

한 소스 파일에 여러 클래스를 선언할 수 있다.

그러나 컴파일 시 바이트 코드 파일은 클래스 선언 갯수만큼 생기게 된다.

 

객체 생성과 클래스 변수

클래스로부터 객체 생성 시엔 new 연산자를 사용한다.

new 연산자는 힙 영역에 객체 생성 후 객체 번지를 리턴한다.

 

클래스 객체 생성 시

클래스 변수 선언 후 -> new 연산자로 객체 생성 뒤 객체 번지 리턴

 

 

클래스의 구성 멤버

클래스는 필드, 생성자, 메소드로 구성되어 있다.

 

필드

객체의 고유 데이터가 저장되는 곳.

생성자와 메소드 전체에서 사용되며, 객체가 소멸되지 않는 한 객체와 함께 존재함.

 

생성자

객체 생성시 초기화 담당.

메소드와 비슷하나 클래스 이름으로 되어 있고 리턴 타입이 없음.

 

메소드

객체의 동작에 해당하는 중괄호 블록.

객체 간의 데이터를 전달하는 수단.

 

 

06 - 2 필드

필드 선언

필드 선언은 클래스 중괄호 블록 내부 어디든 존재할 수 있다.

하지만 생성자/메소드 중괄호 블록 내부에 선언 시 로컬 변수가 되어버리므로 선언할 수 없다.

 

필드 선언시 초기값은 생략 가능하며, 생략 시 기본값으로 초기화된다.

 

 

필드 사용

클래스 내부에서 필드 사용 시엔 단순히 필드 이름으로 읽고 변경하면 되지만

클래스 외부에서 필드를 사용하기 위해선 객체를 생성해야 한다.

객체 생성 후 객체 접근 연산자인 도트(.) 연산자를 사용해 필드에 접근할 수 있다.

 

Car 클래스

public class Car {
	int speed;
}

 

Main 클래스

pubic void main(String[] args) {
	Car car = new Car();
    car.speed = 60;
}

위와 같이 car 객체를 먼저 생성한 후 도트 연산자를 사용하여 car 객체의 speed 필드에 접근이 가능하다.

 

 

06 - 3 생성자

생성자는 객체의 초기화를 담당하며, 객체의 초기화란 필드를 초기화하거나 메소드를 호출해 객체를 사용할 준비를 하는 것이다.

 

 

기본 생성자

모든 클래스는 생성자가 반드시 존재하며, 생성자를 하나 이상 가질 수 있다.

 

클래스 내부에 생성자 선언 생략 시 컴파일러가 기본 생성자를 자동으로 추가하며

클래스에 명시적으로 선언한 생성자가 있을 경우 컴파일러가 기본 생성자를 추가하지 않는다.

 

명시적으로 생성자를 선언하는 이유는

보다 더 다양한 값으로 객체를 초기화하기 위함이다.

 

 

생성자 선언

생성자는 다음과 같이 선언하면 된다.

클래스명 (매개변수) {
	객체 초기화 코드
}

 

생성자는 리턴 타입이 없으며 클래스명과 동일하다.

매개변수는 외부 값을 생성자 블록 내부로 전달하는 역할을 한다.

 

클래스에 명시적으로 생성자가 선언되어 있을 경우

반드시 선언된 생성자를 호출하여 객체를 생성해야 한다.

 

 

필드 초기화

필드를 초기화하는 두 가지 방법

 

1. 필드 선언 시에 초기값 주는 방법

이 방법 사용 시 동일 클래스로 생성되는 객체들은 모두 같은 값을 가진다.

 

2. 생성자에서 초기값 주는 방법

외부에서 받은 다양한 값들로 초기화 해야 할 경우 생성자에서 초기화를 해야 한다.

 

 

필드와 매개변수명이 동일한 경우, 생성자 내부에서 필드에 접근이 불가하다.

매개변수가 사용 우선순위가 높기 때문이다.

이런 경우 객체 자신의 참조인 this를 붙여 사용하면 된다.

public class Car {
	int speed;
    
    public Car(int speed) {
    	this.speed = speed;
    }
}

위 코드에서 this.speed는 Car 객체 내부 필드를 의미하고

this.speed에 전달된 값 speed는 생성자에서 매개변수로 전달받은 값을 의미한다.

 

 

생성자 오버로딩

자바에선 외부에서 제공되는 데이터에 따라 다양한 방법으로 객체를 생성할 수 있도록 하기 위해

생성자 오버로딩을 제공한다.

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

 

생성자가 오버로딩 되어 있는 경우,

생성자 호출 시 제공되는 매개값의 타입과 수에 의해 호출될 생성자가 결정된다.

public class Car {
	int speed;
    String model;
    
    Car () {
    }
    
    Car (int speed) {
    	this.speed = speed;
    }
    
    Car (int speed, String model) {
    	this.speed = speed;
        this.model = model;
    }
}
public void main (String[] args) {
    Car car = new Car(60, "sonata");
}

위의 경우 car 객체 생성 시 int형 매개변수와 String형 변수를 전달했으므로

Car 클래스의 세번째 생성자를 통해 객체가 생성된다.

 

 

다른 생성자 호출 : this()

this()는 생성자 내에서 다른 생성자를 호출하는 코드로 생성자의 첫 줄에서만 허용된다.

 

this()의 매개값은 호출되는 생성자의 매개변수에 맞게 제공해야 한다.

 

 

06-4 메소드

메소드 선언은 선언부와 실행 블록으로 구성된다.

메소드 선언부를 메소드 시그니처라고 하며, 선언부와 실행 블록에는 리턴 타입, 메소드 이름, 매개 변수 선언, 메소드 실행 블록이 포함된다.

 

 

메소드 선언

메소드 선언은 리턴 타입, 메소드 이름, 매개변수 선언, 실행 블록으로 구성된다.

 

리턴 타입

리턴 값이란 메소드를 실행한 후의 결과값을 말한다.

메소드는 리턴값이 있을 수도 없을 수도 있으며 리턴 값이 없을 경우 void, 있을 경우 리턴 타입이 명시되어야 한다.

 

메소드 이름

메소드 이름은 자바 식별자 규칙에 맞게 작성한다.

- 숫자로 시작 X, 특수문자 '$', '_'만 사용 가능

- 메소드 명은 소문자로 작성

- 단어 혼합 시 두번째 단어부터 첫글자를 대문자로 작성.

 

메소드명의 길이는 프로그램 실행과 무관하다.

 

매개변수 선언

매개변수는 메소드가 실행할 때 필요한 데이터를 외부로부터 받기 위해 사용된다.

 

매개값과 매개 변수의 타입은 일치해야 하지만

매개변수가 int형이고 매개값으로 byte형을 전달했을 경우

byte는 int로 자동 변환되기 때문에 컴파일 에러가 일어나지 않는다.

 

매개 변수의 개수를 모를 경우

1. 매개 변수를 배열로 선언

int sum(int[] values) { }

하지만 이 경우 메소드를 호출하기 전 배열을 생성해야하는 어려움이 있다.

 

2. ... 을 사용해 값의 목록만 넘겨주는 방법

int sum(int ... values) {}

...을 사용해서 매개변수를 선언해도 자바에서는 이 values 변수를 배열타입으로 간주하기 때문에

배열을 직접 매개값으로도 사용 가능하다. 

 

 

리턴문

리턴값이 있는 메소드

메소드 선언에 리턴 타입이 있는 메소드는 반드시 리턴문을 사용해 리턴값을 지정해줘야 한다.

return문의 리턴값은 리턴타입/리턴타입으로 변환 가능해야 한다.

ex) 리턴 타입이 int인 메소드의 경우

      --> byte, shrot, int 값이 리턴될 수 있다. byte, short는 int로 자동 타입 변환되어 리턴되기 때문

 

리턴값이 없는 메소드

리턴 값이 없는 메소드는 리턴 타입으로 void를 사용한다.

void로 선언된 메소드에서 return문 사용 시 이는 메소드 실행을 강제 종료시키는 역할을 한다.

void sum(int ... values) {
	return;		// 리턴문 실행 시 메소드 즉시 종료
}

 

 

메소드 호출

객체 내부에서 호출 시

메소드(매개값, ...);

리턴값이 필요한 경우

타입 변수 = 메소드(매개값, ...);

이때 변수 타입은 메소드 리턴타입과 동일하거나 자동 타입 변환이 될 수 있어야 한다.

 

객체 외부에서 호출 시

객체 외부에서 호출 시 클래스로부터 객체를 생성해야 한다.

객체 생성 뒤 참조변수와 함께 도트 연산자를 사용해 메소드 호출이 가능하다.

public class Car {
	int speed;
    
    int getSpeed() {
    	return speed;
    }
}
public void main(String[] args) {
	Car car = new Car();
    int speed = car.getSpeed();
}

 

 

메소드 오버로딩

메소드 오버로딩이란 같은 이름으로 메소드를 여러 개 선언하는 것을 말한다.

이는 매개값을 다양하게 받아 처리할 수 있도록 하기 위함이다.

 

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

 

오버로딩 된 메소드 호출 시 JVM은 매개값의 타입을 보고 메소드를 선택한다.

매개변수 타입이 일치하지 않을 경우 자동 타입 변환이 가능한지를 검사한다.

 

리턴 타입만 다른 경우는 메소드 오버로딩에 해당하지 않는다.

 

 

06-5 인스턴스 멤버와 정적 멤버

클래스 멤버는 인스턴스 멤버와 정적 멤버로 나눌 수 있다.

인스턴스 멤버 : 객체마다 갖고 있는 멤버

정적 멤버 : 클래스에 위치시키고 객체들이 공유하는 멤버

 

 

인스턴스 멤버와 this

인스턴스 멤버란 객체 생성 후 사용이 가능한 필드, 메소드를 말한다.

이들은 객체 소속 멤버이므로 객체 없이는 사용할 수 없다.\

 

인스턴스 멤버 선언

외부 클래스에서 인스턴스 멤버 접근 시 객체 생성 후 참조변수로 접근이 가능하다.

 

this

객체 내부에서도 인스턴스 멤버에 접근하기 위해 this를 사용할 수 있다.

this는 객체 자신을 가르킨다.

 

 

정적 멤버와 static

정적 멤버는 클래스에 고정된 멤버로서 객체 생성 없이 사용가능한 필드, 메소드를 말한다.

 

정적 멤버 선언

정적 필드, 메소드 선언 시 static 키워드를 붙여주면 된다.

public class 클래스 {
	static 타입 필드;
    
    static 리턴타입 메소드(매개변수, ...) { }
}

 

정적 멤버는 클래스에 고정된 멤버이르모 메소드 메모리 영역에 클래스별로 관리된다.

따라서 클래스의 로딩이 끝나면 바로 사용이 가능해진다~!!!

 

인스턴스/정적 필드 판단 기준

객체마다 갖고 있어야 할 데이터 --> 인스턴스 필드

객체마다 있을 필요 없는 공용 데이터  --> 정적 필드

 

인스턴스/정적 메소드 판단 기준

인스턴스 필드 포함 시 --> 인스턴스 메소드

인스턴스 필드 미포함 시 --> 정적 메소드

 

정적 멤버 사용

클래스가 메모리로 로딩되면 정적 멤버 사용이 가능하다.

클래스명과 도트 연산자로 다음과 같이 접근하면 된다.

클래스.필드;

클래스.메소드(매개값, ...);

원칙적으로는 클래스명으로 접근해야 하짐나 객체 참조 변수로도 접근이 가능하다.

하지만 정적 요소는 클래스명으로 접근하는 것이 좋다!!!

 

정적 메소드 선언 시 주의할 점

정적 메소드 선언 시 인스턴스 필드/메소드는 사용이 불가하며

객체 자신의 참조인 this 키워드도 객체가 생성되지 않았으므로 사용할 수 없다.

 

--> 인스턴스 멤버를 사용하려면 객체 생성 후 참조변수로 접근해야 한다.

 

 

싱글톤

전체 프로그램에서 단 하나의 객체만 만들도록 보장해야 하는 경우가 있는데,

이 객체를 싱글톤이라고 한다.

 

싱글톤을 만들려면

- 클래스 외부에서 new 연산자로 생성자 호출을 못하도록 막아야 하기 때문에

생성자 앞에 private 접근 제한자를 붙인다.

 

- 자신의 탕비인 정적 필드 선언 후 자신 객체를 생성해 초기화한다.

정적 필드도 priavte를 붙여 외부에서 필드값을 변경하지 못하도록 막는다.

(클래스 내부에선 new 연산자로 생성자 호출 가능)

 

- 외부에서 호출 가능한 정적 메소드인 getInstance() 선언 후 정적 필드에서 참조하는 자신의 객체를 리턴한다.

 

싱글톤에서 객체를 얻는 유일한 방법은 getInstance() 메소드 호출 뿐이다.

getInstace()는 단 하나의 객체만 리턴하므로 다른 클래스 변수에서 getInstace() 호출 시 해당 변수들은 모두 같은 객체를 참조한다.

 

 

final 필드와 상수

 

final 필드

final 필드는 초기값이 최종값이 되어 프로그램 실행 도중 수정할 수 없다.

 

final 필드에 초기값주는 두가지 방법

1. 필드 선언 시

-> 단순값일 경우 필드 선언 시 초기값 지정

2. 생성자에서 초기값 지정

-> 복잡한 초기화 코드 / 외부 데이터로 초기화 해야할 경우

 

생성자는 final 필드의 최종 초기화를 마쳐야 하는데, 초기화되지 않은 final 필드를 그래도 냅두면 컴파일 에러가 발생한다.

 

상수

상수는 불변의 값을 저장하는 필드로, static이면서 final이어야 한다.

static final 필드는 객체마다 존재하지 않으며, 클래스에만 존재한다.

한번 초기값을 저장하면 변경할 수 없다.

 

상수 != final 필드인 이유

상수는 객체마다 저장할 필요가 없지만 final 필드는 객체마자 저장해야 하며

상수는 여러 값으로 초기화되지 않지만 final 필드는 매개값을 받아 여러값으로 초기화된다.

 

상수명은 모두 대문자로 작성하는 것이 관례이며, 단어 혼합 시 언더바(_)로 연결한다.

 

 

06-6 패키지와 접근 제한자

패키지 선언

패키지 선언 : 해당 클래스가 어떤 패키지에 속할 것인지 선언하는 것.

선언 방법은 다음과 같다

pakage 상위패키지.하위패키지;

패키지는 클래스의 일부이기 때문에 클래스만 따로 복사하여 이동 시 해당 클래스는 사용이 불가하다.

따라서 클래스를 이동해야 할 경우 패키지 전체를 이동해야 한다.

 

패키지명 작성 규칙

- 숫자로 시작할 수 없고, 특수문자는 '$', '_' 만 사용 가능하다.

- 모두 소문자로 작성한다.

- java로 시작하는 패키지는 자바 표준 API에서만 사용하므로 사용해선 안된다.

 

패키지 이름의 중복 방지 방법

여러 회사가 참여하는 대규모 프로젝트나 다른 회사의 패키지를 사용할 경우 패키지 이름이 중복될 가능성이 있다.

그래서 중복을 방지하기 위해 회사의 도메인으로 패키지를 만든다.

도메인 이름 역순으로 패키지 이름을 주는데

이는 포괄적인 이름이 상위 패키지가 되도록 하기 위함이다.

com.samsung.projectname
com.hyundai.projectname

 

import문

다른 패키지에 속한 클래스/인터페이스 사용 시 import문으로 컴파일러에게 해당 클래스/인터페이스를 사용할 것임을 알려야 함.

 

import문은 패키시 선언과 클래스 선언 사이에 작성한다.

import문은 개수 제한이 없으며 얼마든지 추가할 수 있다.

 

상위 패키지를 import 했다고해서 하위 패키지까지 import 되는 것은 아니다.

자바는 패키지 전체 이름으로 패키지를 식별하기 때문에 서로 다른 패키지로 인식한다.

com.hankook.*;
com.hankook.project.*;

위처럼 상위,하위 패키지를 각각 import 해줘야 한다.

 

 

접근 제한자

접근 제한자 : 클래스/인터페이스, 이들이 가진 멤버의 접근을 제한하기 위해 사용.

- 클래스/인터페이스를 다른 패키지에서 사용하지 못하도록 할 때

- 생성자 호출/필드/메소드 사용을 막아야 할 경우

에 접근 제한자를 사용한다.

 

클래스의 접근 제한

클래스는 public, default 접근 제한을 갖는다.

 

default 접근 제한

클래스 선언 시 public을 생략하면 클래스는 default 접근 제한을 가진다.

같은 패키지에선 제한 없이 사용이 가능하며

다른 패키지에선 사용할 수 없다.

 

public 접근 제한

같은 패키지, 다른 패키지에서 모두 제한없이 사용이 가능하다.

라이브러리 클래스 개발 시 public 접근 제한을 갖도록 해야 한다.

인터넷으로 배포된 라이브러리 클래스도 모두 public 접근 제한을 가진다.

 

 

생성자의 접근 제한

컴파일러에 의해 자동 추가된 기본 생성자는 해당 클래스의 접근 제한과 동일하다.

클래스가 default면 기본 생성자도 default,

클래스가 public이면 기본 생성자도 public 접근 제한을 갖는다.

 

public : 모든 패키지에서 제한없이 호출 가능

protected : 같은 패키지에 속한 클래스이거나 해당 클래스의 자식 클래스에서 호출 가능

default : 같은 패키지면 호출 가능, 다른 패키지면 호출 X

private : 동일/다른 패키지 상관없이 호출할 수 없음. 클래스 내부에서만 생성자 호출/객체 생성 가능.

 

 

필드와 메소드의 접근 제한

public : 모든 패키지에서 제한없이 사용 가능

protected : 같은 패키지에 속한 클래스이거나 해당 클래스의 자식 클래스면 사용 가능

default : 같은 패키지면 사용 가능, 다른 패키지면 사용 X

private : 동일/다른 패키지 상관없이 사용 불가. 오로지 클래스 내부에서만 사용 가능

 

 

Getter와 Setter 메소드

일반적으로 객체 지향 프로그램에서는 객체의 필드를 객체 외부에서 직접적으로 접근하는 것을 막는다.

외부에서 접근 가능할 경우 객체의 무결성이 깨질 수 있기 때문이다.

 

이런 문제 해결을 위해 객체 지향 프로그래밍에서는 메소드를 통해 필드를 변경하는 방법을 선호한다.

필드는 외부에서 접근할 수 없고 메소드를 공개해 메소드를 통해 필드에 접근하도록 한다.

메소드는 매개값을 검증해 유효값만 객체의 필드로 저장할 수 있기 때문이다.

이런 역할을 하는 메소드가 바로 Setter이다.

void setSpeed(double speed) {
	if(speed < 0) {
    	this.speed = 0;
        return;
    } else {
    	this.speed = speed;
    }
}

 

외부에서 객체의 데이터를 읽을 때도 메소드를 사용하는 것이 좋다.

메소드로 필드값을 가공한 후 외부로 전달하면 되는데 이런 메소드가 Getter이다.

double getSpeed() {
	double km = speed * 1.6;
    return km;
}

위처럼 속도를 마일에서 km 단위로 환산하여 리턴하는 메소드가 getter 이다.

다음과 같이 필드 타입이 boolean일 경우 Getter는 get이 아닌 is로 시작하는 것이 관례이다.

public isStop() {
	return stop;
}

 

클래스 선언 시 되도록이면

필드를 private으로 선언해 외부로부터 보호하고, 필드에 대한 Setter와 Getter 메소드를 작성해 필드값을 안전하게 변경/사용하는 것이 좋다.

728x90
반응형
SMALL

'Java > 혼공자' 카테고리의 다른 글

Chapter 07 상속  (0) 2023.01.16
[혼공학습단 9기] 혼공자 2주차  (0) 2023.01.15
[혼공학습단 9기] 혼공자 1주차  (0) 2023.01.08
Chapter 05 참조 타입  (0) 2022.12.29
Chapter 01 자바 시작하기 ~ 04 조건문과 반복문  (0) 2022.12.28