본문 바로가기

2022 여름방학 자바 스터디

[이것이 자바다] Ch06. 클래스

6.1. 객체 지향 프로그래밍

→ 부품 객체를 먼저 만들고 이것들을 하나씩 조립해서 완성된 프로그램을 만드는 기법

6.1.1. 객체란?

→ 속성과 동작을 가지는 모든 것

→ 필드와 메소드로 구성 

6.1.2. 객체의 상호 작용

→ 객체들은 서로 간에 기능을 이용하고 데이터를 주고 받음

→ 자바의 객체도 서로 간의 메소드(동작)를 호출하고 결과를 받음

6.1.3. 객체 간의 관계

→ 객체는 다른 객체와 관계를 맺고 있음

1. 집합 관계

: 하나는 부품이고 하나는 완성품에 해당

2. 사용 관계

객체 간의 상호작용

3. 상속 관계

: 상위(부모) 객체를 기반으로 하위(자식) 객체를 생성하는 관계

6.1.4. 객체 지향 프로그래밍의 특징

캡슐화

→ 객체의 필드와 메소드를 감싸줌, 실제 구현 내용을 외부에 감추는 것

→ 외부 객체는 객체 내부의 구조를 알지 못하며, 객체가 노출해서 제공하는 필드와 메소드만 이용할 수 있음

→ 필드와 메소드를 캡슐화 하는 이유? 외부의 잘못된 사용으로 객체가 손상되지 않도록 하기 위함

→ 자바는 이를 결접하기 위해 접근 제한자를 사용함

 

상속

→ 상위 객체는 자기가 가지고 있는 필드와 메소드를 하위 객체에게 물려주어 하위 객체가 사용할 수 있도록 함

→ 상속의 효과?

: 상위 객체를 재사용해서 하위 객체를 빨리 개발할 수 있음

: 반복된 코드의 중복을 줄여줌

: 유지 보수의 편리성 제공

: 객체의 다형성 구현

 

 

다형성

→ 같은 타입이지만, 실행 결과가 다양한 객체를 대입할 수 있는 성질

: 부모 타입에는 모든 자식 객체가 대입되는 경우

: 인터페이스 타입에는 모든 구현 객체가 대입되는 경우

→ 효과?

: 객체를 부품화시킬 수 있음

: 유지보수 용이

6.2. 객체와 클래스

→ 클래스에는 객체를 생성하기 위한 필드와 메소드가 정의되어 있음

→ 클래스로부터 만들어진 객체를 해당 클래스의 인스턴스라고 함

→ 하나의 클래스로부터 여러 개의 인스턴스 만들 수 있음

6.3. 클래스 선언

클래스 이름

→ 자바 식별자 작성 규칙에 따라야 함

→ 한글 이름도 가능하지만, 영어 이름으로 작성

→ 알파벳 대소문자 구분

→ 첫자와 연결된 다른 단어의 첫자는 대문자로 작성하는 것이 관례, 예) Car, ChatServer

 

클래스 선언과 컴파일

→ 소스 파일 생성

→ 소스 파일당 하나의 클래스를 선언하는 것이 관례지만, 두 개 이상의 클래스도 선언이 가능함

→ 선언한 개수만큼 바이트 코드 파일이 생성됨

6.4. 객체 생성과 클래스 변수

new 연산자

→ 객체를 생성하는 역할

: 클래스()는 생성자를 호출하는 코드

: 생성된 객체는 힙 메모리 영역에 생성됨

→ new 연산자는 객체를 생성한 후, 객체 생성 번지를 리턴함

 

클래스 변수

→ new 연산자에 의해 리턴된 객체의 번지를 저장하는 변수(참조 타입 변수)

→ 힙 영역의 객체를 사용하기 위해 사용됨

 

클래스의 용도

→ 라이브러리(API)용

: 자체적으로 실행되지 않으나, 다른 클래스에서 이용할 목적으로 만든 클래스

→ 실행용

: main() 메소드를 가지고 있는 클래스로, 실행할 목적으로 만든 클래스

⇒ 1개의 애플리케이션 = (1개의 실행 클래스) + (n개의 라이브러리 클래스)

6.5. 클래스의 구성 멤버

필드

→ 객체의 데이터가 저장되는 곳

→ 선언 형태는 변수와 비슷하지만 변수라고 부르지 않음

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

생성자

→ 객체 생성시 초기화 역할 담당

→ 생성자는 클래스 이름과 반드시 동일해야 함

→ 리턴 타입이 없음

메소드

→ 객체의 동작에 해당되는 실행 블록

→ 리턴 타입이 반드시 지정이 되어야 함, 없는 경우 void

6.6. 필드

필드의 내용

→ 객체의 고유 데이터

→ 객체가 가져야 할 부품 객체

→ 객체의 현재 상태 데이터

6.6.1. 필드 선언

→ 클래스 중괄호 어디서든 존재할 수 있음

→ 생성자와 메소드 중괄호 블록 내부에는 선언될 수 없음 (이것은 모두 로컬 변수)

→ 초기값이 지정되지 않은 필드들은 객체 생성 시 자동으로 기본 초기값으로 설정됨

: 정수 타입 필드는 0, 실수 타입 필드는 0.0, boolean 필드는 false로, 참조 타입은 null로 초기화

6.6.2. 필드 사용

→ 필드값을 읽고, 변경하는 작업

→ 필드 사용 위치

: 객체 내부? "필드이름"으로 바로 접근

: 객체 외부? "변수.필드이름"으로 접근 (참조 변수 이용)

6.7. 생성자

→ new 연산자에 의해 호출되어 객체의 초기화 담당

: 필드의 값을 설정하거나 메소드를 호출해서 객체를 사용할 수 있도록 준비하는 역할

6.7.1. 기본 생성자

→ 모든 클래스는 생성자가 반드시 존재, 하나 이상 가질 수 있음

→ 생성자 선언을 생략하면 컴파일러는 다음과 같은 기본 생성자를 추가시킴

: public은 있을 수도 있고 없을 수도 있는데, 

언제 있나? 클래스 선언 시 public을 붙였다면 자동으로 추가

6.7.2. 생성자 선언

→ 디폴트 생성자 대신 개발자가 직접 선언을 하는 경우

→ 개발자가 선언한 생성자가 이미 있는 경우

: 컴파일러는 기본 생성자를 추가하지 않음

→ new 연산자로 객체 생성 시 개발자가 선언한 생성자를 반드시 사용해야 한다.

6.7.3. 필드 초기화

→ 초기값 없이 선언된 필드는 객체가 생성될 때 기본값으로 자동 설정됨

→ 다른 값으로 필드를 초기화하는 방법

: 필드를 선언할 때 초기값을 설정

생성자의 매개값으로 초기값을 설정

 

생성자의 매개변수와 필드명이 동일할 경우

→ 관례적으로 매개변수의 값을 필드값으로 설정할 경우, 매개변수와 필드명을 동일하게 줌

→ 생성자 내부에서는 매개변수가 우선순위를 가짐

→ 필드임을 명시하기 위해 this.필드명을 사용함

: this는 객체 자신의 참조로서 자신의 번지를 가지고 있음

6.7.4. 생성자 오버로딩

생성자를 다양화해야 하는 이유?

→ 객체를 생성할 때 외부의 값으로 객체를 초기화할 필요가 있음

→ 외부의 값이 어떤 타입으로 몇 개가 제공될 지 모르기 때문에 생성자도 다양화될 필요가 있음

 

생성자 오버로딩

→ 매개변수의 타입, 개수, 순서가 다른 생성자를 여러 개 선언하는 것

→ 주의할 점

: 매개 변수의 타입과 개수 그리고 선언된 순서가 똑같을 경우,

  매개변수 이름만 바꾸는 것은 생성자 오버로딩이라고 할 수 없음

6.7.5. 다른 생성자 호출 (this)

→ 생성자가 오버로딩되면 생성자 간의 중복된 코드가 발생할 수 있음

→ 초기화 내용이 비슷한 생성자들에서 이러한 현상을 많이 볼 수 있음

→ this()

:자신의 다른 생성자를 호출하겠다는 의미

 

⇒ 해결 방법?

→ 초기화 내용을 한 생성자에 몰아서 작성

→ 다른 생성자는 초기화 내용을 작성한 생성자를 this로 호출

→ this()는 반드시 생성자의 첫줄에서만 허용됨

6.8. 메소드

메소드란?

→ 객체의 동작(기능)을 말하며, 호출해서 실행할 수 있는 중괄호 블록을 말함

→ 중괄호 블록은 이름을 가지고 있는데, 이것이 바로 메소드 이름

→ 메소드를 호출하면 중괄호 블록에 있는 모든 코드들이 일괄적으로 실행됨

6.8.1. 메소드 선언

→ 메소드 선언은 선언부(리턴 타입, 메소드이름, 매개변수선언)와 실행 블록으로 구성

→ 메소드 선언부는 메소드 시그니처라고도 함

 

리턴타입

→ 메소드가 실행 후 리턴하는 값의 타입

→ 메소드는 리턴 값이 있을 수도, 없을 수도 있음

→ void는 리턴 값 없이 실행만하고 끝남

 

메소드 이름

→ 숫자로 시작하면 안되고, $와 _ 외 기호 사용 불가

→ 관례적으로 메소드명은 소문자로 시작

→ 서로 다른 단어가 혼합된 이름일 때 뒤이어 오는 단어의 첫 머리 글자는 대문자로 작성함

 

매개변수 선언

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

→ 필요한 경우가 있고 필요가 없는 경우도 있음 (옵션)

 

매개변수의 수를 모를 경우

→ 방법 1

: 배열된 수로 매개변수를 지정

 

→ 방법 2

: ... 사용하기

 

⇒ 차이?

1번 방법은 호출을 할 때, 배열을 만들어서 값을 줘야하지만, 2번은 값 목록을 바로 주면 됨!

6.8.2. 리턴문

→ 메소드의 실행을 중지하고 리턴값을 지정하는 역할

 

리턴값이 있는 메소드

→ 반드시 리턴문을 사용해서 리턴값을 지정해야 함

→ return 문 뒤에는 실행문이 올 수 없음

 

리턴값이 없는 메소드 (void)

→ return문은 옵션이며, return 문의 용도는 메소드 실행을 단지 중지함

→ return 대신에 break;를 사용하게 되는 경우도 있음! (while문 사용시)

6.8.3. 메소드 호출

→ 메소드는 클래스 내/외부의 호출에 의해 실행됨

: 클래스 내부? 메소드 이름으로 호출

: 클래스 외부? 객체 생성 후, 참조 변수를 이용해서 호출

 

객체 내부에서 호출

→ 메소드 이름으로 호출하되, 매개변수의 타입과 수에 맞게 매개값을 제공해야 함

→ 리턴값이 없는 메소드 호출

→ 주의할 점

: 변수 타입은 메소드 리턴 타입과 동일하거나, 타입 변환이 될 수 있어야 함

 

객체 외부에서 호출 

→ 반드시 객체가 먼저 생성되어 있어야 함

→ 클래스로부터 객체를 생성하고 클래스 변수가 참조하도록 함

→ 참조변수와 함께 도트연산자를 이용해서 메소드를 호출함

: 도트연산자는 객체접근 연산자로 객체가 가지고 있는 필드나 메소드에 접근함

6.8.4. 메소드 오버로딩

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

→ 하나의 메소드 이름으로 다양한 매개값을 받기 위해 메소드를 오버로딩함

→ 오버로딩의 조건

: 매개변수의 타입, 개수, 순서가 달라야 함

6.9. 인스턴스 멤버와 this

인스턴스 멤버란?

→ 객체(인스턴스)마다 가지고 있는 필드와 메소드를 말함

: 이들을 각각 인스턴스 필드, 인스턴스 메소드라고 부름

→ 인스턴스 멤버는 객체에 소속된 멤버이기 때문에 객체가 없이는 사용 불가

 

this

→ 객체 자신의 참조(번지)를 가지고 있는 키워드

→ 객체 내부에서 인스턴스 멤버임을 명확하기 위해 this.를 붙일 수 있음

→ 주로 매개변수와 필드명이 동일할 경우 인스턴스 필드임을 명확히하기 위해 붙임

6.10. 정적 멤버와 static

정적 멤버란?

→ 클래스에 고정된 필드와 메소드

: 각각 정적 필드, 정적 메소드라고 부름

→ 정적 멤버는 클래스에 소속된 멤버로 객체 내부에 존재하지 않고, 메소드 영역에 존재함

정적 멤버는 객체를 생성하지 않고 클래스로 바로 접근해서 사용함

6.10.1. 정적 멤버 선언

→ 필드 또는 메소드를 선언할 때 static 키워드를 붙이면 됨

6.10.2. 정적 멤버 사용

→ 클래스 이름과 함께 도트 연산자로 접근 가능

 

인스턴스 멤버 선언 vs. 정적 멤버 선언의 기준

⇒ 필드

: 객체마다 가지고 있어야 할 데이터 → 인스턴스 필드

: 공용적인 데이터 → 정적 필드 (예: pi)

 

⇒ 메소드

: 인스턴스 필드로 작업해야 할 메소드 → 인스턴스 메소드

: 인스턴스 필드로 작업하지 않는 메소드 → 정적 메소드

→ static 안에서 this는 절대 사용하면 안됨!

6.10.3. 정적 초기화 블록

→ 클래스가 메소드 영역으로 로딩될 때 자동으로 실행하는 블록

→ 정적 필드의 복잡한 초기화 작업과 정적 메소드를 호출할 수 있음/ 인스턴스는 X

→ 클래스 내부에 여러 개가 선언되어도 상관없음, 선언된 순서대로 실행됨

6.10.4. 정적 메소드와 블록 선언 시 주의할 점

→ 객체가 없어도 실행할 수 있기 때문에, 이들 블록 내부에 인스턴스 메소드를 사용할 수 없음

→ 객체 자신의 참조인 this를 사용할 수 없음

→ 사용하고 싶다면 객체를 생성해서 사용하는 방법밖에는 없음

 

→ main() 메소드도 정적 메소드

6.10.5. 싱글톤(Singleton)

→ 하나의 어플리케이션 내에서 단 하나만 생성되는 객체를 말함

 

싱글톤을 만드는 방법?

→ 외부에서 new 연산자로 생성자를 호출할 수 없도록 막음

: 생성자 앞에 private 접근 제한자를 붙임 (클래스 내부에서만 사용할 수 있도록 함)

→ 클래스 자신의 타입으로 정적 필드를 선언하고, 자신의 객체를 생성해 초기화 함

→ 외부에서 호출할 수 있는 정적 메소드인 getInstance()를 선언

: 정적 필드에서 참조하고 있는 자신의 객체를 리턴하도록 함

6.11. final 필드와 상수

6.11.1. final 필드

→ final 필드 = 최종적인 값을 갖고 있는 필드 = 값을 변경할 수 없는 필드

→ final 필드의 딱 한번의 초기값 지정 방법

: 필드 선언시에 주는 방법

: 생성자에서 주는 방법

6.11.2. 상수 (static final)

상수?

→ 정적 final 필드

→ 상수도 한 번 값이 저장이 되면 수정이 불가함

 

! final 필드
객체마다 가지는 불변의 인스턴스 필드 
! 상수 (static final)
객체마다 가지고 있지 않고, 메소드 영역에 클래스 별로 관리되는 불변의 정적 필드
공용 데이터로서 사용

 

상수 선언과 초기화

→ 상수 이름

: 전부 대문자로 작성하는 것이 관례

: 다른 단어가 결합되면, _로 연결되는 것이 관례

 

→ 초기화 방법

6.12. 패키지

패키지란?

→ 클래스를 기능별로 묶어서 그룹 이름을 붙여 놓은 것

: 폴더(디렉토리)와 비슷한 개념

: 물리적인 형태는 파일 시스템의 폴더

→ 클래스 이름의 일부

: 전체 클래스 이름 -  상위패키지.하위패키지.클래스

: 클래스 명이 같아도 패키지 명이 다르면 다른 클래스로 취급

→ 클래스를 선언할 때 패키지가 결정됨

: 클래스를 선언할 때 포함될 패키지를 선언해야 함

: 클래스 파일은 선언된 패키지와 동일한 폴더안에서만 동작함

: 클래스 파일은 다른 폴더 안에 넣으면 동작하지 않음

 

패키지 선언

→  패키지 선언은 클래스 선언 첫 줄

→ 상위 패키지와 하위 패키지는 도트로 구분

→ 패키지 이름 규칙

: 전부 알파벳 소문자로 작성하는 것이 관례

: 숫자로 시작해서는 안됨

: _, $를 제외한 특수 분자 사용 불가

: 회사 도메인의 역순으로 패키지 이름을 보통 만듦 ex) com.samsung.projectname

 

import문

→ 패키지 내에 같이 포함된 클래스 간에는 클래스 이름으로 사용이 가능함

→ 패키지가 다른 클래스를 사용해야 할 경우, 2가지 방법이 있음

1. 패키지명이 포함된 전체 클래스 이름으로 사용 (조금 귀찮고 복잡)

2. import 문으로 패키지를 지정하고 사용 (보통 사용하는 방법)

단축키 : cmd + shift + O

6.13. 접근 제한자

→ 클래스 및 클래스의 구성 멤버에 대한 접근을 제한하는 역할

: 클래스 제한, 생성자 제한, 필드와 메소드 제한

→ 접근 제한자의 종류, 아래로 내려갈수록 접근 제한이 강화됨

6.13.1. 클래스의 접근 제한

default 접근 제한

다른 패키지의 외부 클래스에서 사용 불가능

 

public 접근 제한

다른 패키지의 외부 클래스에서 사용 가능

6.13.2. 생성자 접근 제한

→ public

: 어디서든 사용 가능

→ protected

: 상속을 배우고 난 다음 배움

→ default

: 같은 패키지에서만 사용 가능

→ private

: 해당 클래스에서만 사용 가능

6.14. Getter와 Setter 메소드

 

클래스를 선언할 때 필드는 일반적으로 provate 접근 제한을 함

 

: field의 값을 보호하기 위해!

→ 읽기 전용 필드가 있을 수 있음 (Getter의 필요성)

→ 외부에서 엉뚱한 값으로 변경할 수 없도록 함 (Setter의 필요성)

 

Getter

 

→ private 필드의 값을 리턴하는 역할

: 필요할 경우 필드의 값을 가공해서 리턴

 

→ getFieldName() 또는 isFieldName() 메소드를 말함

: 필드 타입이 boolean 일 경우 isFieldName()

 

Setter

 

→ 외부에서 주어진 값을 필드값으로 수정함

: 필요할 경우 외부의 값의 유효성을 검사함 (올바른 값이 주어진 경우에만 필드값 수정)

 

→ setFieldName(타입 변수) 메소드를 말함

: 매개 변수 타입은 필드의 타입과 동일함

 

이클립스에서 게터와 세터를 자동으로 만들어주는 기능

source → generate getters and setters

 

6.15. 어노테이션

: 프로그램에게 추가적인 정보를 제공해주는 메타데이터

 

용도?

: 컴파일러에게 코드 작성 문법 에러를 체크하도록 정보를 제공

: 소프트웨어 개발 툴이 빌드나 배치시 코드를 자동으로 생성할 수 있도록 정보를 제공

: 실행시(런타임시) 특정 기능을 실행하도록 정보를 제공

6.15.1. 어노테이션 타입 정의와 적용

// 어노테이션 타입 정의
public @interface AnnotationName {
}

// 어노테이션 타입 적용
@AnnotationName

6.15.2. 어노테이션 적용 대상

어노테이션 엘리먼트 멤버

: 어노테이션을 코드에 적용할 때 외부의 값을 입력받을 수 있도록 하는 역할

ElementType 열거 상수 적용 대상
TYPE 클래스, 인터페이스, 열거타입
ANNOTATION_TYPE 어노테이션
FIELD 필드
CONSTRUCTOR 생성자
METHOD 메소드
LOCAL_VARIABLE 로컬변수
PACKAGE 패키지
@AnnotationName
public class ClassName {
    @AnnotationName
    private String fieldName;
    
    // @AnnotationName (X)
    // Target에 CONSTRUCTOR가 없어 생성자는 적용 못함
    public ClassName() {}
    
    @AnnotationName
    public void methodName() {}
}

6.15.3. 어노테이션 유지 정책

→ 어노테이션 적용 코드가 유지되는 시점을 지정하는 것

→ 유지 정책 지정 방법

: @Retention 어노테이션으로 유지 정책을 지정

: @Retention의 기본 요소인 value의 타입은 RetentionPolicy

// RetentionPolicy 열거 상수
// RUNTIME: 바이트 코드 파일까지 어노테이션 정보를 유지하면서, 
// 리플렉션을 이용해서 런타임에 어노테이션 정보를 얻을 수 있음
// 리플렉션? 런테암에 클래스의 메타 정보를 얻는 기능

@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface AnnotationName {
}

6.15.4. 런타임 시 어노테이션 정보 사용하기

→ 클래스에 적용된 어노테이션 정보 얻기

: 클래스.class의 어노테이션 정보를 얻는 메소드를 이용

 

→ 필드, 생성자, 메소드에 적용된 어노테이션 정보 얻기

: 클래스.class의 다음 메소드를 이용해서, java.lang.reflect 패키지의 필드, 생성자, 메소드 클래스의 배열을 얻어냄

리턴 타입 메소드명(매개변수) 설명
Field[] getFields() 필드 정보를 Field 배열로 리턴
Constructor[] getConstructors() 생성자 정보를 Constructor 배열로 리턴
Method[] getDeclaredMethods() 메소드 정보를 Method 배열로 리턴

 

어노테이션 정보를 얻기 위한 메소드

리턴타입 메소드명(매개변수)
boolean isAnnotationPresent(Class<? extends Annotation> annotationClass)
지정한 어노테이션이 적용되었는지 여부로, Class에서 호출했을 때 상위 클래스에 적용된 경우에도 true를 리턴함
Annotation getAnnotation(Class<T> annotationClass)
지정한 어노테이션이 적용되어 있으면 어노테이션을 리턴하고 그렇지 않다면 null을 리턴함
Class에서 호출했을 때 상위 클래스에 적용된 경우에도 어노테이션을 리턴함
Annotation[] getAnnotations()
적용된 모든 어노테이션을 리턴함
Class에서 호출했을 때 상위 클래스에 적용된 어노테이션도 모두 포함하며, 적용된 어노테이션이 없을 경우 길이가 0인 배열을 리턴함
Annotation[] getDeclaredAnnotations()
직접 적용된 모든 어노테이션을 리턴함
Class에서 호출했을 때 상위 클래스에 적용된 어노테이션은 포함되지 않음