ultra_dev
이것이 자바다 CH 6-2(클래스) 본문
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가지
- 필드 선언 시에 초기값 대입
- 생성자에서 초기값 대입 (만약 복잡한 초기화 코드가 필요하거나, 객체 생성시 외부에서 전달된 값으로 초기화 한다면 생성자에서 해야함)
- 위 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
'이것이 자바다' 카테고리의 다른 글
이것이 자바다 CH 7-2(상속) (0) | 2023.01.13 |
---|---|
이것이 자바다 CH 7-1(상속) (0) | 2023.01.13 |
이것이 자바다 CH6-1(클래스) (0) | 2023.01.13 |
이것이 자바다 CH 5(참조타입) (0) | 2023.01.13 |
이것이 자바다 CH 3(연산자), CH 4(조건문과 반복문) (0) | 2023.01.13 |