클래스 - 2

Bummy·2023년 7월 18일
0

JAVA

목록 보기
6/11
post-thumbnail

6.9 인스턴스 멤버와 this

  • 인스턴스(instance) 멤버란 객체(인스턴스)를 생성한 후 사용할 수 있는 필드와 메소드를 의미한다.
  • 이들을 각각 인스턴스 필드, 인스턴스 메소드라고 부른다.
  • 인스턴스 필드와 메소드는 객체에 소속된 멤버이기에 객체 없이는 사용할 수 없다.
💡 new 연산자로 각각 객체를 생성하게 되면 인스턴스 필드는 객체마다 따로 존재하게 되고, 인스턴스 메소드는 객체마다 존재하지 않고 메소드 영역에 저장되고 공유된다.

6.10 정적 멤버와 static

  • 정적(static)은 ‘고정된’이란 의미를 가지고 있다.
  • 정적 멤버는 클래스에 고정된 멤버로서 객체를 생성하지 않고 사용할 수 있는 필드와 메소드를 말한다.

6.10.1 정적 멤버 선언

  • 정적 필드와 정적 메소드를 선언하는 방법은 필드와 메소드 선언 시 static 키워드를 추가적으로 붙이면 된다.
public class 클래스{
	//정적 필드
	static 타입 필드 {= 초기값};

	//정적 메소드
	static 리턴 타입 메소드(매개변수선언, ...){...};
}
  • 정적 필드와 정적 메소드는 클래스 로더가 클래스를 로딩해서 메소드 메모리 영역에 적재할 때 클래스별로 관리된다.
  • 즉 클래스의 로딩이 끝나면 바로 사용할 수 있다.ㄹ상
💡 객체마다 가지고 있어야 할 데이터라면 인스턴스 필드로 선언하고, 객체마다 가지고 있을 필요성이 없는 공용적인 데이터라면 정적 필드로 선언하는 것이 좋다. 💡 인스턴스 필드를 이용해서 실행해야 한다면 인스턴스 메소드로 선언하고, 인스턴스 필드를 이용하지 않는다면 정적 메소드로 선언한다.

6.10.2 정적 멤버 사용

  • 클래스가 메모리로 로딩되면 정적 멤버를 사용할 수 있는데, 클래스 이름과 함께 도트(.) 연산자로 접근한다.
public class Calculator{
	static double pi = 3.14159;
	static int plus(int x, int y){...};
	static int minus(int x, int y){...};
}

double result1 = 10 * 10 * Calculator.pi;
int result2 = Calculator.plus(10, 5);
int result3 = Calculator.minus(10, 5);

 ⚠️ 객체 참조 변수로도 접근이 가능하지만 정적 요소는 클래스 이름으로 접근하는 것이 좋다.

6.10.3 정적 초기화 블록

  • 정적 필드는 필드 선언과 동시에 초기값을 주는 것이 보통이다.
  • 인스턴스 필드는 생성자에서 초기화하지만, 정적 필드는 객체 생성 없이도 사용해야 하므로 생성자에서 초기화 작업을 할 수 없다.
  • 정적 필드의 초기화 작업을 위해서 정적 블록(static block)을 제공한다.

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

  • 정적 메소드와 정적 블록을 선언할 때 주의할 점은 객체가 없어도 실행된다는 특징 때문에, 이들 내부에 인스턴스 필드나 인스턴스 메소드를 사용할 수 없다.
  • 객체 자기 자신의 참조인 this 키워드도 사용이 불가능하다.
public class ClassName{
	//인스턴스 필드와 메소드
	int field1;
	void method1(){...}

	//정적 필드와 메소드
	static int field2;
	static void method2(){...}

	//정적 블록
	static{
		field1 = 10; (X)
		method1();   (X)
		field2 = 10; (O)
		method2();   (O)
	}

	//정적 메소드
	static void Method3{
		this.field1 = 10; (X)
		this.method1();   (X)
		field2 = 10;      (O)
		method2();        (O) 
	}
}
  • 정적 메소드와 정적 블록에서 인스턴스 멤버를 사용하고 싶다면 객체를 먼저 생성하고 참조 변수로 접근해야한다.

6.10.5 싱글톤(Singleton)

  • 전체 프로그램에서 단 하나의 객체만 만들도록 보장해야 하는 경우를 싱글톤(Singleton)이라고 한다.
  • 싱글톤을 만들려면 클래스 외부에서 new 연산자로 생성자를 호출할 수 없도록 생성자 앞에 private 접근 제한자를 붙여주면 된다.
  • 생성자가 여러 차례 호출되더라도 실제로 생성되는 객체는 하나이고 최초 생성 이후에 호출된 생성자는 최초의 생성자가 생성한 객체를 리턴한다.
  • 주로 공통된 객체를 여러 개 생성해서 사용하는 DBCP(DataBase Connection Pool)과 같은 상황에서 많이 사용된다.

(+)싱글톤의 이점

  1. 메모리 측면의 이점
    1. 싱글톤 패턴을 사용하게 된다면 한개의 인스턴스만을 고정 메모리 영역에 생성하고 추후 해당 객체를 접근할 때 메모리 낭비를 방지할 수 있다.
  2. 속도 측면의 이점
    1. 생성된 인스턴스를 사용할 때는 이미 생성된 인스턴스를 활용하여 속도 측면에 이점이 있다.
  3. 데이터 공유가 쉽다.
    1. 전역으로 사용하는 인스턴스이기 때문에 다른 여러 클래스에서 데이터를 공유하며 사용할 수 있다. 하지만 동시성 문제가 발생할 수 있어 이 점은 유의하여 설계해야 한다.
public class 클래스{
	//정적 필드
	private static 클래스 singleton = new 클래스();

	//생성자
	private 클래스() {}

	//정적 메소드
	static 클래스 getInstance(){
		return singleton;
	}
}
  • 댜음은 싱글톤을 만드는 코드이다.
💡 1. 외부에서 생성자 호출을 막기 위해 private를 붙여준다. 2. 자신의 타입인 정적 필드를 하나 선언하고 자신의 객체를 생성해 초기화한다. 3. 정적 필드도 private 접근 제한자를 붙여 외부에서 필드값을 변경하지 못하도록 막는다. 4. 대신 외부에서 호출할 수 있는 정적 메소드인 getInstance()를 선언하고 정적 필드에서 참조하고 있는 자신의 객체를 리턴해준다.
  • 외부에서 객체를 얻는 유일한 방법은 getInstatnce() 메소드를 호출하는 방법이다.
클래스 변수1 = 클래스.getInstance();
클래스 변수2 = 클래스.getInstance();

→ getInstance() 메소드는 단 하나의 객체만 리턴하기 때문에 아래 코드에서 변수1과 변수2는 동일한 객체를

참조한다.

6.11 final 필드와 상수

6.11.1 final 필드

  • final의 의미는 최종적이라는 뜻
  • final 필드는 초기값이 저장되면 이것이 최종적인 값이 되어서 프로그램 실행 도중에 수정할 수 없다.

final 필드 생성 방법

final 타입 필드 [= 초기갑];
  • final 필드에 초기값을 주는 방법에는 필드 선언 시에 줄 수 있는 방법과 생성자에서 주는 방법이 있다.
  • 단순 값이라면 필드 선언 시에 주는 것이 제일 간단하다.
  • 복잡하거나 객체 생성 시에 외부 데이터로 초기화해야 한다면 생성자에서 초기값을 지정해 줄 수 있다.
  • 만약 final 필드를 그대로 남겨두면 컴파일 에러가 발생한다.

6.11.2 상수(static final)

  • 일반적으로 불변의 값을 상수라고 부른다.
💡 final 필드가 한번 초기화하면 수정할 수 없는 필드라고 했으니 상수로 쓸 수 있지 않을까?

→ 불변의 값은 객체마다 저장할 필요가 없는 공용성을 띄고 있으며, 여러 가지 값으로 초기화될 수 없기에 final 필드를 상수라 부르지 않는다.
final 필드는 객체마다 저장되고, 생성자의 매개값을 통해서 여러 가지 값을 가질 수 있기 때문에 상수가 될 수 없다.

  • 상수는 다음과 같이 선언할 수 있다.
static final 타입 상수 [= 초기값];
  • 초기값이 단순 값이라면 선언 시에 주는 것이 일반적이지만, 복잡한 초기화일 경우 정적 블록에서도 가능하다.
static final 타입 상수;
static {
	상수 = 초기값;
}
💡 상수 이름은 대문자로 작성하는 것이 관례이며, 혼합된 단어라면 언더바(_)로 연결해준다.

6.12 패키지

  • 클래스를 체계적으로 관리하기 위해 사용하는 것
  • 폴더를 만들어 파일을 저장하고 관리하듯이 패키지를 만들어 클래스를 저장 관리한다.
  • 단순히 파일 시스템의 폴더 기능만 하는 것이 아닌 클래스를 유일하게 만들어주는 식별자 역할을 한다.
상위패키지.하위패키지.클래스

6.12.1 패키지 선언

  • 패키지는 클래스를 컴파일하는 과정에서 자동적으로 생성되는 폴더
  • 클래스에 포함되어 있는 패키지 선언을 보고, 파일 시스템의 폴더로 자동 생성시킨다.
💡 회사들 간에 패키지가 서로 중복되지 않도록 흔히 회사의 도메인 이름으로 패키지를 만든다. 도메인은 유일한 이름이 되도록 검증되었기 때문 도메인 이름 역순으로 패키지 이름을 지어주는데, 포괄적인 이름이 상위 패키지가 되도록 하기 위해서이다.

6.12.3 패키지 선언이 포함된 클래스 컴파일

  • 패키지 선언이 포함된 클래스를 명령 프롬프트에서 컴파일할 경우 javac 명령어 다음에 - d 옵션을 추가하고 패키지가 생성될 경로를 지정해줘야한다.

6.12.4 import 문

  • 같은 패키지에 속하는 클래스는 아무런 조건 없이 다른 클래스를 사용할 수 있다.
  • 다른 패키지에 속하는 클래스를 사용하려면 패키지와 클래스를 모두 기술하여 호출하거나, import문을 사용해 패키지를 선언하고 클래스를 사용할 때에는 패키지를 생략하면 된다.

6.13 접근 제한자

  • 대부분의 클래스는 외부 클래스에서 이용할 목적으로 설계된 라이브러리 클래스이다.
  • 라이브러리 클래스를 설계할 때는 외부 클래스에서 접근할 수 있는 멤버와 접근 할 수 없는 멤버로 구분해서 필드, 생성자, 메소드를 설계하는 것이 바람직하다.
  • 객체의 특정 데이터를 보호하기 위해 해당 필드에 접근하지 못하도록 막아야 하는데, 자바는 이러한 기능을 구현하기 위해 접근 제한자(Access Modifier)를 제공하고 있다.
접근 제한적용 대상접근할 수 없는 클래스
Public클래스, 필드, 생성자, 메소드없음
protected필드, 생성자, 메소드자식 클래스가 아닌 다른 패키지에 소속된 클래스
default클래스, 필드, 생성자, 메소드다른 패키지에 소속된 클래스
private필드, 생성자, 메소드모든 외부 클래스

6.13.1 클래스의 접근 제한

default 접근 제한

  • 클래스를 선언할 때 public을 생략했다면 클래스는 default 접근 제한을 가진다.
  • default 접근 제한을 가지게 되면 같은 패키지에서는 아무런 제한 없이 사용할 수 있지만 다른 패키지에서는 사용할 수 없도록 제한된다.

public 접근 제한

  • 클래스가 public 접근 제한을 가지게 되면 같은 패키지뿐만 아니라 다른 패키지에서도 아무런 제한 없이 사용할 수 있다.

6.13.2 생성자의 접근 제한

  • 클래스에 생성자를 선언하지 않으면 컴파일러에 의해 자동적으로 기본 생성자가 추가된다.
  • 자동으로 생성되는 접근 제한자는 클래스의 접근 제한과 동일하다.
접근 제한자생성자설명
public클래스(…)모든 패키지에서 아무런 제한 없이 생성자를 호출할 수 있도록 한다.
protected클래스(…)default 접근 제한자와 마찬가지로 같은 패키지에 속하는 클래스에서 생성자를 호출할 수 있도록 한다. 차이점은 다른 패키지에 속한 클래스가 해당 클래스의 자식 클래스라면 생성자를 호출할 수 있다.
default클래스(…)같은 패키지에서는 아무런 제한 없이 생성자를 호출할 수 있으나, 다른 패키지에서는 생성자를 호출할 수 없도록 한다.
private클래스(…)동일 패키지건 다른 패키지건 상관 없이 생성자를 호출하지 못하도록 제한한다. 클래스 외부에서 new 연산자로 객체를 만들 수 없으며 오로지 클래스 내부에서만 생성자를 호출할 수 있다.

6.13.3 필드와 메소드의 접근 제한

접근 제한자생성자설명
public클래스(…)필드와 메소드가 public 접근 제한을 가질 경우 클래스도 public 접근 제한을 가져야 한다. 클래스가 default 접근 제한을 가지게 되면 같은 패키지 안에서만 클래스가 사용되기 때문이다.
protected클래스(…)default 접근 제한자와 마찬가지로 같은 패키지에 속하는 클래스에서 생성자를 호출할 수 있도록 한다. 차이점은 다른 패키지에 속한 클래스가 해당 클래스의 자식 클래스라면 생성자를 호출할 수 있다.
default클래스(…)같은 패키지에서는 아무런 제한 없이 생성자를 호출할 수 있으나, 다른 패키지에서는 생성자를 호출할 수 없도록 한다
private클래스(…)동일 패키지건 다른 패키지건 상관 없이 생성자를 호출하지 못하도록 제한한다. 클래스 외부에서 new 연산자로 객체를 만들 수 없으며 오로지 클래스 내부에서만 생성자를 호출할 수 있다.

6.14 Getter와 Setter 메소드

  • 객체 지향 프로그래밍에서 객체의 데이터는 객체 외부에서 직접적으로 접근하는 것을 막는다. 객체의 무결성이 깨어질 수 있기 때문
  • 객체 지향 프로그래밍에서는 메소드를 통해서 데이터를 변경하는 방법을 선호한다.
  • 외부에서 메소드를 접근할 때 매개값을 검증해서 유효한 값만 데이터로 저장할 수 있게 하는 역할이 Setter
  • 객체 내부의 값을 보낼 때는 Getter를 사용해서 필드값을 가공한 후 외부로 전달한다.
💡 클래스를 선언할 때 가능하다면 필드를 private로 선언해서 외부로부터 보호하고, 필드에 해단 Setter와 Getter 메소드를 작성해서 필드값을 안전하게 변경/사용 하는 것이 좋다.

6.15 어노테이션

  • 어노테이션(Annotation)은 메타데이터(metadata)라고 볼 수 있다.
  • 메타데이터란 애플리케이션이 처리해야할 데이터가 아니라, 컴파일 과정 혹은 실행 과정에서 코드를 어떻게 컴파일하고 처리해야할지 알려주는 정보이다.
@AnnotationName
//어노테이션 작성 방법
💡 **어노테이션 용도** - 컴파일러에게 코드 문법 에러를 체크하도록 정보를 제공 - 소프트웨어 개발 툴이 빌드나 배치 시 코드를 자동으로 생성할 수 있도록 정보를 제공 - 실행 시(런타임 시) 특정 기능을 실행하도록 정보를 제공

예) @Override 어노테이션은 메소드 선언 시에 사용되는데 메소드가 오버라이드(재정의)된 것임을 컴파일러에게 알려주어 컴파일러가 오버라이드 검사를 하도록 해준다.

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

  • 어노테이션 타입을 정의하는 방법은 인터페이스를 정의하는 방법과 유사하다
public @interface AnnotationName{
}
  • 정의한 어노테이션은 코드에서 다음과 같이 사용한다.
@AnnotationName
  • 어노테이션은 엘리먼트(element)를 멤버로 가질 수 있으며 각 엘리먼트는 타입과 이름으로 구성된다.
public @interface AnnotationName{
	타입 elementName() [default];
	//엘리먼트 선언
	...
}
  • 엘리먼트의 타입으로는 int나 double, String, 열거 타입, Class 타입, 배열 타입 등을 사용할 수 있다.
public @interface AnnotationName{
	String elementName1();
	int elementName2() default 5;
}
  • 정의한 어노테이션을 코드에서 적용할 때는 다음과 같이 기술한다.
@AnnotationName(elementName1="값", elementName2=3);
또는
@AnnotationName(elementName1 = "값");
-> elementName1은 디폴트 값이 없기에 반드시 값을 기술해야하며, elementName2는 디폴트 값이
있기에 생략할 수 있다.

6.15.3 어노테이션 유지 정책

  • 사용 용도에 따라 어노테이션을 어느 범위까지 유지할 것인지 지정해야한다.
RetentoinPolicy 열거 상수설명
SOURCE소스상에서만 어노테이션 정보를 유지한다. 소스 코드를 분석할 때만 의미가 있으며 바이트 코드 파일에는 정보가 남지 않는다.
CLASS바이트 코드 파일까지 어노테이션 정보를 유지한다. 하지만 리플렉션을 이용해서 어노테이션 정보를 얻을 수는 없다.
RUNTIME바이트 코드 파일까지 어노테이션 정보를 유지하면서 리플렉션을 이용해서 런타임 시에 어노테이션 정보를 얻을 수 있다.
💡 **리플렉션(Reflection)**이란 런타임 시에 클래스의 메타 정보를 얻는 기능을 말한다. 클래스가 가지고 있는 필드가 무엇인지, 어떤 생성자를 갖고 있는지, 어떤 메소드를 가지고 있는지, 적용된 어노테이션이 무엇인지 알아내는 것이 리플렉션이다.

1개의 댓글

comment-user-thumbnail
2023년 7월 18일

아주 유익한 내용이네요!

답글 달기