BACKEND/JAVA

[이것이 자바다] 6장 클래스

석태 2024. 1. 19. 16:18

객체(Object)

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

객체는 속성과 동작으로 구성되어 있다.

현실 세계의 객체를 소프트웨어 객체로 설계하는 것을 객체 모델링(Object Modeling)이라고 한다.

객체는 각각 독립적으로 존재하고, 다른 객체와 서로 상호작용하면서 동작한다.

메소드라는 수단을 이용하여 객체들 사이를 서로 상호작용한다.

객체가 다른 객체의 기능을 이용하는 것이 메소드 호출이다.

 

객체 간의 관계

구분 설명
집합 관계 완성품과 부품의 관계
사용 관계 다른 객체의 필드를 읽고 변경하거나 메소드를 호출하는 관계
상속 관계 부모와 자식 관계

 

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

1. 캡슐화(Encapsulation)

객체의 필드, 메소드를 하나로 묶고, 실제 구현 내용을 감추는 것을 말한다.

외부 객체는 객체 내부의 구조를 알지 못하며 객체가 노출해서 제공하는 필드와 메소드만 이용한다.

갭슐화를 이용하는 이유는 외부의 잘못된 사용으로 객체가 손상되지 않도록 하기 위해서다.

접근 제한자를 사용하여 객체의 필드와 메소드의 사용 범위를 제한함으로써 외부로부터 보호한다.

 

2. 상속(Inheritance)

부모 역할의 상위 객체와 자식 역할의 하위 객체가 존재한다. 여기서 상위 객체는 자기가 가지고 있는 필드와 메소드를 하위 객체에게 물려주어 하위 객체가 사용할 수 있도록 해주는 것이다.

상위 객체를 재사용해서 하위 객체를 쉽고 빨리 설계할 수 있도록 도와주고, 이미 잘 개발된 객체를 재사용해서 새로운 객체를 만들기 때문에 반복된 코드의 중복을 줄여준다.

 

3. 다형성(Polymorphism)

같은 타입이지만 실행결과가 다양한 객체를 이용할 수 있는 성질을 말한다.

코드에서는 하나의 타입에 여러 객체를 대입함으로써 다양한 기능을 이용할 수 있도록 해준다.

인터페이스 타입에는 모든 구현 객체가 대입될 수 있다. 다형성의 효과로 객체는 부품화가 가능해진다.

 

* 객체와 클래스

메모리에서 사용하고 싶은 객체가 있다면 설계도로 해당 객체를 만드는 작업이 필요하다.

설계도는 class에 해당하며 클래스에는 객체를 생성하기 위한 필드와 메소드가 정의되어 있다.

클래스로부터 만들어지 객체를 해당 클래스의 인스턴스라고 한다.

클래스로부터 객체를 만드는 과정을 인스턴스화라고 한다.

하나의 클래스로부터 여러 개의 인스턴스를 만들 수 있는데, 이것은 동일한 설계도로부터 여러 대의 자동차를만드는 것과 동일하다.

 

* 클래스 선언

객체 생성을 위한 설계도를 작성하는 작업이다.

하나의 소스 파일에 복수 개의 클래스를 선언할 때 소스 파일명과 동일한 클래스만 공개클래스로 선언할 수 있다.

클래스명은 첫 문자를 대문자로 하고 캐멀 스타일로 작성한다.

 

* 객체 생성과  클래스 변수

클래스를 선언한 다음, 컴파일을 했다면 객체를 생성할 설계도가 만들어진 것이다.

객체 생성 연산자인 new 연산자와 생성자 호출 코드인 '클래스()'를 통해 객체를 생성하고 객체 주소를 리턴한다.

new 클래스();
클래스 변수;
변수 = new 클래스()
클래스 변수 = new 클래스();

 

* 클래스의 구성 멤버

클래스의 구성 멤버

 

1. 필드

객체의 데이터를 저장하는 역할

변수는 생성자와 메소드 내에서만 사용되고 생성자와 메소드가 종료되면 자동 소멸되지만 필드는 생성자와 메소드 전체에 사용되며 객체가 소멸되지 않는 한 객체와 함께 존재한다.

 

2. 생성자

new 연산자로 호출되는 특별한 중괄호{}블록이다.

객체 생성 시 초기화를 담당한다.

생성자는 크래스 이름으로 되어 있고 리턴 타입이 없다.

 

3. 메소드

객체의 동작에 해당하는 중괄호 {} 블록을 말한다.

필드를 읽고 수정하는 역할도 하지만, 다른 객체를 생성해서 다양한 기능을 수행하기도 한다.

외부로부터 매개값을 받을 수 있고, 실행 후 어떤 값을 리턴할 수 있다.

 

 

* 필드 선언과 사용

초기값을 제공하지 않을 경우 필드는 객체 생성 시 자동으로 기본값으로 초기화된다.

public class Car {
    String model = "testModel";
    String color;
    int speed;
}
public class CarEx {
    public static void main(String[] args) {
        Car car = new Car();
        System.out.println(car.model);  // testModel
        System.out.println(car.color);  // null
        System.out.println(car.speed);  // 0
    }
}

클래스로부터 객체가 생성된 후 필드 값을 읽고 수정할 수 있다.

도트(.)으로 객체 접근 연산자로 필드를 읽고 수정할 수 있다.

 

* 생성자 선언과 호출

new 연산자는 객체를 생성한 후 생성자를 호출해 객체를 초기화하는 것이다.

객체 초기화란? 객체를 사용하기 위하여 준비를 하는 것

 

> 기본 생성자

모든 클래스는 기본 생성자가 존재하며, 생성자를 하나 이쌍 가질 수 있다.

클래스에 생성자 선언이 없을 경우, 컴파일러는 기본 생성자를 바이트 코드 파일에 자동으로 추가시켜 기본생성자를 생성시킨다.

하지만 개발자가 명시적으로 작성하여 선언한 생성자를 가질 경우에는 컴파일과정에서 기본 생성자를 추가하지 않는다.

 // 매개변수를 가지지 않는 기본 생성자
  public Car() {
      }
    
// 필드값을 모두 매개변수로 가지는 생성자
// 생성자의 매개변수명이 필드명과 동일하기 때문에 this 키워드를 필드명 앞에 붙여준다.
// this 는 현재 객체를 뜻한다.
public Car(String model, String color, int speed) {
        this.model = model;
        this.color = color;
        this.speed = speed;
    }

// 생성자는 리턴 타입이 없고 클래스 이름과 동일하다.

 위에 작성한 코드처럼 생성자는 매개변수를 달리하는 생성자를 여러 개 선언하는 것이 가능하기 떄문에 생성자 오버로딩이 가능하다.

하지만 매개변수의 타입과 개수, 순서가 동일한데 매개변수명만 변경하는 것은 오버로딩에 해당하지 않는다.

 

 

* 메소드 선언과 호출

메소드 선언은 객체의 동작을 실행 블록으로 정의하는 것이다.

메소드 호출은 실행 블록을 실제 실행하는 것이다.

메소드 명은 첫문자는 소문자로 시작해야 한다.

매개값을 가지기 위해 매개변수를 사용하며 전달할 매개값이 없다면 매개변수 생략 가능

메소드 이름은 같지만 매개변수의 타입, 개수, 순서가 다른 메서드를 여러개 선언하여 오버로딩이 가능하다. 오버로딩을 사용하는 목적은 다양한 매개값을 처ꈰ하기 위해서이다.

 

1. 리턴 타입

호출한 곳으로 전달하는 결과값의 타입을 의미

리턴 타입이 있는 메소드는 반드시 실행 블록 안에 return문을 지정하여 리턴을 지정해줘야 한다.

리턴 값이 없는 경우에는 void 작성(void로 작성하여도 return;을 사용할 수 있지만 해당 메소드를 종료하기를 원할 때 사용)

return문은 메소드의 실행을 강제 종료하고 호출한 곳으로 돌아간다는 의미를 가지고 있다. 메소드 선언시 return 타입이 있는 경우 return 문 뒤에 리턴 값을 지정해줘야 한다.

 

2. 메소드 호출

메소드는 객체가 존재하지 않으면 호출할 수 없다. → 클래스로부터 객체를 생성된 후 호출이 가능하다는 것을 뜻한다.

객체 내부에서는 메서드명으로만 호출 가능하지만 외부 객체에서는 참조 변수와 도트(.)연산자를 이용해 호출한다.

 

 

* 인스턴스 멤버

필드와 메소드는 선언 방법에 따라 인스턴스 멤버와 정적 멤버로 구분할 수 있다.

인스턴스 멤버 중 필드는 객체가 생성될 때마다 새로운 heap 영역에 생성이 되고, 메소드는 클래스가 로드될 때 method 영역에 할당되며 객체가 생성되면 객체에 소속되어 공유하여 사용한다. → 객체 없이는 사용할 수 없다는 것

> this 키워드

객체 내부에서 인스턴스 멤버에 접근하기 위해 this 키워드를 사용한다.

객체 자신을 this라고 하는 것

> this.
this는 자기 자신을 의미, 필드와 메소드 또는 생성자의 매개변수가 동일할 때 인스턴스 필드임을 명확하게 하기 위해서
ex)   this.a =a  // this.a : 필드 값 ::  a : 매개변수 a
> this()
- 자기 자신의 생성자를 호출할 때 사용하는 키워드(같은 클래스내에서 다른 생성자를 호출할 때 사용)
- this()는 생성자에서만 사용이 가능 
- this()는 같은 클래스에서 다른 생성자를 호출하는 것
- () -> 메소드 리턴값으로 가능

 

* 정적 멤버

static 변수 -> 처음 메모리에 올릴 때 초기화되는 대상

static 변수가 초기화 되는 시점은 JVM에 의해서 클래스가 메모리 공간에 올라가는 순간
→JVM : 자바를 실행하기 위한 가상 기계(자바는 OS에 종속되지 않아 OS에 종속받지 않고 실행하기위해서 JAVA를 실행시키기 위한 무언가가 필요한 것이 JVM)

> 인터페이스 상수는 static{ }블록으로 초기화할 수 없기 때문에 반드시 선언과 동시에 초기값을 지정해야 한다.

 static으로 선언된 변수는 메모리 공간에 하나만 존재하며, 어디서나 접근이 가능한 변수(public으로 선언시)

> 인스턴스 멤버 사용 불가하다.

정적 메소드나 정적 블록은 내부에 인스턴스 필드나 인스턴스 메소드를 사용할 수 없다.

객체 자신의 참조인 this도 사용 불가

main() 메서드도 정적 메서드이므로 객체 생성 없이 인스턴스 필드와 메소드를 바로 사용할 수 없다.

public class Bird {
    String color;

    void fly() {
        System.out.println("날다");
    }

    public static void main(String[] args) {
        // color = "red";  // 컴파일 에러 : non-static variable color cannot be referenced from a static context
        // fly();          // 컴파일 에러 : non-static method fly() cannot be referenced from a static context

        Bird bird = new Bird();
        bird.color = "red";
        System.out.println(bird.color);   // red
        bird.fly();                       // 날다
    }
}

 

* final 필드와 상수

final 키워드 : 변수를 상수화 시키는 키워드, 지역변수가 final로 선언되면 , 딱 한번 값의 초기화가 가능

final 수식자
     - final 클래스 : 기존 클래스로부터 새로운 서브(확장) 클래스를 만들 수 없음
     - final 메소드 : 서브 클래스에서 메소드 변경이 금지됨 (즉, 오버라이드를 금지하는 것)
     - final 필드    : 그 필드의 값이 변경되는 것을 금지(실행 도중에 수정 불가)

final 필드 초기값 주는 방법 2가지 1. 필드 선언 시 초기값 대입, 2. 생성자에서 초기값 대입

변경이 되지 않으면서, 참조의 용도로만 선언되는 변수 : static final로 선언 -> 외부에서 접근을 해도 변경이 되지 않기 때문에 private로 선언하지 않는다.

 

* 상수 선언

상수란? 블변의 값

상수는 static이면서 final 특정을 가져야 한다. → 상수는 객체마다 저장할 필요가 없고, 여러 개의 값을 가지면 안된다.

일반적으로 상수는 선언과 동시에 초기값을 선언하지만 정적 블록을 이용해 초기화할 수 있다.

 

 

* 접근 제한자

필드와 메서드가 외부로 노출되지 않도록 해서 객체의 무결성을 유지하기 위하여 사용한다.

dufault는 접근제한자가 붙지 않은 상태

 

> 접근 제한자를 사용하는 이유
- 클래스의 내부 상태에 접근하거나 수정하는 것을 방지하기 위한 안정성
- 안정적이고 구조화된 방식을 만들기 위해

접근 제한자 이미지로 표현

접근 제한자 제한 범위
public 모든 객체에 접근 가능
protected 클래스 멤버 함수 또는 클래스를 상속받은 클래스
default 같은 패키지에 있는 클래스만
private 같은 클래스 내 멤버 함수 또는 멤버 변수

 

private로 선언되면 변수가 선언된 클래스 외부에서는 접근이 불가능,private로 선언된 인스턴스 변수와 메소드는 선언된 클래스 내부에서만 접근이 가능
외부에서 private로 선언된 이 변수의 간접접근을 허용하기 위해 추가적인 메소드를 제공하는데 이걸 Access메소드라고 한다.
protected = default + 추가

 

 

* 어노테이션

컴파일 과정에서 어떻게 처리를 할 것인가를 표시하기 위한 것이다.

컴파일 과정과 실행 과정에서 코드를 어떻게 컴파일하고 처리할 것인지를 알려주는 정보

예시

@AnnotationName

@Override 

 

어노테이션 사용 용도

  • 컴파일러에게 코드 문법 에러를 체크하도록 정보를 제공
  • 소프트웨어 개발 툴이 빌드나 배치 시 코드를 자동으로 생성할 수 있도록 정보를 제공
  • 실행 시런타임 시) 특정 기능을 실행하도록 정보를 제공

 

import java.lang.annotation.*; // 어노테이션을 사용하기 위한 import

@Target({ElementType.METHOD}) // @Target 어노테이션을 이용해여 적용 대상 지정한다.
@Retention(RetentionPolicy.RUNTIME) // @Retention 어노테이션을 이용하여 유지 정책을 지정
public @interface testAnnotation{

}