Notice
Recent Posts
Recent Comments
Link
«   2024/11   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
Tags more
Archives
Today
Total
관리 메뉴

ultra_dev

이것이 자바다 CH 6-2(클래스) 본문

이것이 자바다

이것이 자바다 CH 6-2(클래스)

ultra_dev 2023. 1. 13. 00:32

CH 6 - 2(클래스)

☑️ 인스턴스 멤버

  • 인스턴스 멤버 : 객체에 소속된 멤버 ( 객체가 있어야 사용 가능)

↔정적(static) 멤버 : 클래스에 고정된 멤버(객체 없이도 사용 가능)

메소드를 객체마다 저장하면 중복 저장으로 메모리 효율이 떨어짐

→ 따라서 메소드는 메소드 영역에 둬서 공유해서 사용하되, 객체 없이 사용 못하도록 제한을 걸어둔 것

<참고: https://velog.io/@a_rubz/CH6.-클래스-2>

☑️ this

  • 객체는 자신을 this라고 함.
  • 생성자와 메소드의 매개변수명이 인스턴스 멤버인 필드명과 동일한 경우, 인스턴스 필드임을 강조하기 위해 this를 주로 사용

☑️ 정적(static) 멤버

  • 메소드 영역의 클래스에 고정적으로 위치하는 멤버
  • 객체를 생성할 필요 없이 클래스 통해 바로 사용 가능
  • 선언 하기 위해서는 앞에 static 붙히면 됨
  • 객체마다 가질 필요 없는 공용적인 필드는 정적 필드로 선언하는 것이 좋음
  • 인스턴스 필드를 이용하지 않는 메소드도 정적 메소드로 선언하는 것이 좋음
public Class Calculator{
	static double pi = 3.14159;
	static int plus(int x, int y) {...}
	static int minus(int x, int y) {...}
}
duble result1 = 10*10*Calculator.pi;
int result2 = Calculator.plus(10,5);

>이런 식으로 클래스에서 접근

☑️ 정적 블록

  • 정적 필드는 필드 선언과 동시에 초기 값을 주는 것이 일반적
static double pi = 3.14159; 
  • 만약 복잡한 초기화 작업이 필요하면 정적 블록 사용
static {
.
.
.
}

☑️ 정적 메소드와 정적 블록은 내부에 인스턴스 멤버(필드,메소드) 사용 불가

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

→ 만약 인스턴스멤버를 사용하고 싶으면 객체를 먼저 생성 후 참조 변수로 접근해야 함

안그러면 컴파일에러 뜸

public class ClassName{
//인스턴스 필드와 메소드 선언
int field;
void method1() {...}

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

//정적 블록 선언
static {
	field1 = 10; -> 컴파일에러
	method1(); -> 컴파일 에러
	field2 = 10;
	method2();
}
//정적 메소드 선언
static void Method3{
	this.field1 = 10; -> 컴파일에러
	this.method1(); -> 컴파일에러
	field2 = 10;
	method2();
}

-----옳은 접근(객체 생성 후 접근)-----

static void MEthod3(){
//객체생성
ClassName obj = new ClassName();
//인스턴스 멤버 사용
obj.field1 = 10;
obj.method1();
}

→ main()메소드도 동일한 규칙 적용.

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

public class car {
//인스턴스 필드 선언
int speed;
//인스턴스 메소드 선언
void run() {...}
//메인 메소드 선언
public static void main(String[] args){
	speed = 60; -> 컴파일 에러
	run(); -> 컴파일 에러

-----옳은 접근(객체 생성 후 접근)----
publci static void main(String[] args){
//객체 생성
Car myCar = new Car();
//인스턴스 멤버 사용
myCar.speed = 60;
myCar.run();
}

☑️ final(인스턴스 필드와 정적 필드는 언제든지 값 변경 가능하니 이걸 막기 위한 것)

  • final 필드는 초기값이 저장되면 이것이 최종값이 되어 프로그램 실행 도중 수정 불가능함
  • final 필드에 초기값을 줄 수 있는 방법은 2가지
    1. 필드 선언 시에 초기값 대입
    2. 생성자에서 초기값 대입 (만약 복잡한 초기화 코드가 필요하거나, 객체 생성시 외부에서 전달된 값으로 초기화 한다면 생성자에서 해야함)
  • 위 2가지 방법을 사용하지 않고 final 필드 그대로 놔두면 컴파일 에러 발생

☑️ 상수 : 불변의 값을 저장하는 필드

  • static이면서 final인 특성을 가짐 (상수는 객체마다 저장할 필요 없고 그래서도 안되기 때문에)
static final 타입 상수 [=초기값];
ex) static final double EARTH_RADIUS = 6400;
또는 

static final 타입 상수;
static {
	상수 = 초기값;
}

으로 초기화
  • 상수 이름은 대문자, 캐멀스타일 대신 연결은 언더바(_)로
  • 상수는 정적 필드이므로 클래스로 접근해서 읽기 가능
클래스명.상수

☑️ 패키지

  • 패키지는 클래스의 일부분이며 클래스를 식별하는 용도로 사용
  • 주로 개발 회사 도메인 이름의 역순
  • ex) mycompany.com 회사의 패키지는 com.mycompany 이런식으로 해야 com으로 묶어서 관리 가능
  • 상위 패키지 하위 패키지를 (.) 도트로 구분
  • 패키지는 클래스의 전체 이름에 포함됨
  • 패키지에 속한 바이트코드파일(~.class)는 따로 떼서 다른 디렉토리로 못옮김
  • → Car 클래스가 com.mycompany 패키지 소속이라면 다른 디렉토리에 Car.class를 옮겨 저장한다해도 Car클래스 사용 불가

☑️ 패키지 선언

  • 패키지 디렉토리는 클래스를 컴파일 하는 과정에서 자동으로 생성
  • 패키지는 모두 소문자 작성, 이름은 중복되지 않도록 회사 도메인 이름의 역순으로 작성 후 마지막에 프로젝트 이름을 붙여줌

☑️ import문

  • 다른 패키지에 있는 클래스 사용하려면 import문 사용해서 어떤 패키지의 클래스 사용하는지 명시, 만약 동일한 패키지에 포함된 다수의 클래스를 사용하려면 * 이용
import com.hankook.*
  • import문은 하위 패키지를 초함하지 않기 때문에 하위 패키지에 있는 클래스 사용하려면 다음과 같이 2개의 import문 필요
import com.hankook.*;
import com.hankook.project.*;
  • 만약 서로 다른 패키지에 동일한 클래스 이름이 존재한다면 어떤 패키지의 클래스를 사용할지 모르기 때문에 컴파일 에러가 발생한다

→ 따라서 클래스에 패키지이름까지 명시해줘야 함

com.hanko0ok.Tire tire = new com.hankook.Tire(); 이런식으로

☑️ 접근 제한자

  • 주로 필드와 메소드가 외부로 노출되지 않도록 객체의 무결성을 유지하는데 사용

→ 캡슐화와 연관

public
protected
private 
3가지의 접근제한자와 default(접근제한자가 붙지 않은 상태)가 존재한다.

접근제한자접근대상자제한범위

public 클래스, 필드, 생성자, 메소드 없음
protected 필드, 생성자, 메소드 같은 패키지 or 자식 객체
default 클래스, 필드, 생성자, 메소드 같은 패키지
private 필드, 생성자, 메소드 객체 내부

→ 클래스는 public과 default로만 선언 가능

  • 클래스는 같은 패키지에서만 사용할 건지(default), 다른 패키지에서도 사용 가능하게 할지(public)

☑️ Getter, Setter

  • 객체의 필드(데이터)를 외부에서 마음대로 읽고 변경하면 객체의 무결성이 깨짐 ex) 자동차의 속력은 음수가 될 수 없는데 외부에서 음수로 변경시 객체 무결성 깨짐 → ❗ 이러한 문제점 때문에 직접적인 필드 접근을 막고 메소드를 통해 필드에 접근하는 것 선호 📌 Setter 예시 (메소드를 통해 유효한 값만 필드에 저장) *사용 지양
private double speed; //private로 외부에서의 접근 막음

public void setSpeed(double speed){
	if(speed<0) {
		this.speed = 0;     //매개값이 음수일 경우 speed 필드에 0으로 저장하고 종료
		return;
}else {
	this.speed = speed;  //아니면 speed는 speed
	}
}

💥☢️Setter 사용은 지양하자!! 위험하다!!

  • Setter 메소드를 사용하면 값을 변경한 의도를 파악하기 힘듦
  • 객체의 일관성을 유지하기 어려움

📌 Getter 예시

private double speed;  // private로 외부 접근 막음. speed 단위는 마일로 가정

public double getSpeed(){   
	double km = speed * 1.6;  //필드 값인 마일을 km단위로 환산 후 리턴
	return km;
}

📌→ 필드 타입이 boolean일 경우 Getter은 get으로 시작하지 않고 is로 시작하는 것이 관례

☑️ 싱글톤 패턴

  • 애플리케이션 전체에서 단 한개의 객체만 생성해서 사용하는 패턴
private 클래스() {}

->생성자 private 접근 제한해서 외부에서 new 연산자로 생성자를 호출 할 수 없도록 막음

  • 생성자를 호출 할 수 없으니 외부에서 마음대로 객체 생성하는 것이 불가능해짐!
public class 클래스 {
//private 접근 제한 정적 필드 선언과 초기화
private static 클래스 singleton = new 클래스();    ---> 1번
//private 접근 제한 생성자 선언
private 클래스() {}
//public 접근 제한 정적 메소드 선언
public static 클래스 getInstance() {     --> 2번
	return singleton;
	}
}
  • 1번을 통해 자신의 타입으로 정적 필드 선언 + 미리 객체 생성해서 초기화 + private 통해서 외부에서 필드값 변경하지 못하게 막음
  • 2번을 통해 정적 필드값을 리턴하는 getInstance() 정적 메소드를 public으로 선언
  • → 외부에서 객체를 얻는 유일 한법은 getInstance() 메소드 호출하는 것 뿐!

📌getInstance()가 리턴하는 객체는 정적필드가 참조하는 싱글톤 객체

클래스 변수1 = 클래스.getInstance();
클래스 변수2 = 클래스.getInstance();

따라서 이 경우에 변수1과 변수2가 참조하는 개체는 동일한 객체다.

✔ 싱글톤 패턴은 언제 사용할까?

(예시1)

프로그램 내부에서 발생하는 이벤트들을 스케쥴링 하고 처리하는 객체

=> 모든 이벤트는 하나의 같은 스케쥴링 큐에 들어가서 처리되어야 함

=> 이벤트마다 스케쥴링 큐를 따로 생성하면 설계 의도와 어긋남

(예시2)

안내 음성

=> 안내 음성은 애플리케이션 내부에서 항상 한 번에 하나의 음성만 출력되어야 함

=> 동시에 여러 개의 음성이 겹쳐서 출력되지 않게 구현해야 함

=> 안내 음성을 관리하고 출력하는 객체가 프로그램 내부에서 단 하나만 존재하도록!

=> 기존에 음성이 나오는 상황에서 다른 음성이 나오는 기능을 동작할 경우

=> 기존의 음성을 정지하고 다른 음성이 나와야 함

[참고]https://injae-kim.github.io/dev/2020/08/06/singleton-pattern-usage.html

Comments