이것이 자바다 ch06 클래스(6.9~6.15)

dev·2022년 10월 18일
1

이것이 자바다

목록 보기
3/7

6.9 인스턴스 멤버 instance

필드/메소드는 선언방법에 따라 인스턴스멤버/정적멤버 로 분류된다.

  • 인스턴스(instance) 멤버: 객체에 소속된 멤버(객체 생성시에만 사용가능)
  • 정적(static) 멤버: 클래스에 고정된 멤버(객체 없이도 사용가능)

인스턴스 멤버 선언 및 사용

인스턴스 멤버

객체에 소속된 멤버
객체가 존재해야만 사용가능한 멤버

필드는 객체마다 따로 존재
메소드는 메소드 영역에 저장되고 공유(객체마다 존재하지 않음)

그렇다고 객체에 소속된게 아니라고 생각하면 안된다.
객체에 '포함'되어있는게 아니라 '소속'된거임.

메소드는 코드 덩어리이므로 객체마다 저장하면 중복 저장되어 메모리 효율 떨어진다.
따라서 메소드는 메소드 영역에 두고 공유해서 쓰고, 대신 객체 없이는 사용 못하게 해둔것임

this 키워드

객체내부에서는 인스턴스 멤버에 접근하기 위해 this를 사용할수있다.
우리가 자신을 '나'라고 하듯, 객체 자신을 'this'라고 한것이다.

생성자와 메소드의 매개변수명이 인스턴스 멤버인 필드명과 동일하면 인스턴스 필드임을 강조하려고 this를 쓴다.

package ch06.sec09;

public class CarExample {
    public static void main(String[] args) {
        Car myCar = new Car("포르쉐");
        Car yourCar = new Car("벤츠");

        myCar.run();//포르쉐가 달립니다. (시속100km/h)
        yourCar.run();//벤츠가 달립니다. (시속100km/h)
    }
}

6.10 정적 멤버 static

자바는 클래스 로더(loader)를 이용해 클래스를 메소드 영역에 저장하고 사용함
정적 멤버란 메소드 영역의 클래스에 고정적으로 위치하는 멤버.
그래서 정적 멤버는 객체 생성이 필요없이 클래스를 통해 바로 사용 가능

정적 멤버 선언

필드와 메소드는 모두 정적 멤버가 될수 있다.
static 키워드를 붙이면 된다.

public class 클래스 {
	static 타입 필드;
    static 리턴타입 메소드(매개변수, ...) {...};
}

객체마다 갖고있어야하는게 아닌 공용필드는 정적 필드를 쓰는게 좋다.
원 넓이, 둘레 구할때 필요한 파이(π)는 객체마다 각각 갖고있을 필요없고, 공통이기때문에 static으로 작성하는게 좋다.

인스턴스 필드 이용하지 않는 메소드는 정적 메소드로 선언하는게 좋다.
만약 외부에서 주어진 매개값으로 처리하는 메소드는 정적메소드로,
인스턴스 필드를 변경하는 setColor()와 같은 메소드는 인스턴스 메소드로 선언해야한다.

정적 멤버 사용

클래스가 메모리로 로딩되면 정적 멤버를 바로 사용가능
클래스 이름과 함께 도트(.)연산자 사용해서 접근하면 된다.
클래스이름.정적메소드();
Calculator.pi();

정적 필드/메소드는 객체 참조변수로도 접근 가능하지만,
정적 요소는 클래스 이름으로 접근하는게 정석

package ch06.sec10.exam01;

public class Calculator {
    static double pi = 3.14159;

    static int plus(int x, int y) {
        return x + y;
    }
    
    static int minus(int x, int y) {
        return x - y;
    }
}
package ch06.sec10.exam01;

public class CalculatorExample {
    public static void main(String[] args) {
        double result1 = 10 * 10 * Calculator.pi;
        int result2 = Calculator.plus(10, 5);
        int result3 = Calculator.minus(10, 5);

        System.out.println("result1 : " + result1);//result1 : 314.159
        System.out.println("result2 : " + result2);//result2 : 15
        System.out.println("result3 : " + result3);//result3 : 5
    }
}

정적 블록

정적 필드는 선언과 동시에 초기값 주는게 일반적임
static double pi = 3.14159;

복잡한 초기화 작업이 필요하면 정적 블록을 이용해야한다.
static { ... }

정적 블록은 클래스가 메모리에 로딩될때 자동 실행된다.
정적 블록이 여러개면 선언 순서대로 실행

정적 필드는 생성자에서 초기화 하지 않음
왜냐면 객체 생성이 되어야 생성자 실행되는데, 정적 필드는 객체 생성 안해도 사용가능하기 때문

package ch06.sec10.exam02;

public class Television {
    static String company = "MyCompany";
    static String model = "drone";
    static String info;

    static {
        info = company + "-" + model;
    }
}
package ch06.sec10.exam02;

public class TelevisionExample {
    public static void main(String[] args) {
        System.out.println(Television.info);//MyCompany-drone
    }
}

인스턴스 멤버 사용 불가

정적 메소드와 정정 블록은 객체 없이도 실행되기때문에 내부에 인스턴스 필드/메소드 사용 불가
또한 객체 자신의 참조인 this 키워드도 사용 불가

package ch06.sec10.exam03;

public class Car {
    //인스턴스 필드 선언
    int speed;

    //인스턴스 메소드 선언
    void run() {
        System.out.println(speed + "으로 달린다");
    }

    static void simulate() {
        //객체 생성
        Car myCar = new Car();
        //인스턴스 멤버 사용
        myCar.speed = 200;
        myCar.run();
    }

    public static void main(String[] args) {
        //정적 메소드 호출
        simulate(); //200으로 달린다.

        //객체 생성
        Car myCar = new Car();
        //인스턴스 멤버 사용
        myCar.speed = 60;
        myCar.run(); //60으로 달린다
    }
}

6.11 final 필드와 상수

인스턴스 필드와 정적 필드는 언제든 값 변경 가능하다. 그런데 값 변경 막아야한다면?

  • final필드/상수 : 값 변경을 막고 읽기만 허용해야할때 사용

final 필드 선언

final필드 초기값이 저장되면 최종적 값이 되어 프로그램 실행도중 수정할수 없다.
final 타입 필드;

final 필드에 초기값 주는 두가지 방법
1. 필드 선언시 초기값 대입 (고정값인 경우)
2. 생성자에서 초기값 대입 (복잡한 초기화 코드가 필요하거나, 객세 생성시 외부에서 전달된 값으로 초기화 해야하는 경우)

초기화 하지 않고 final필드를 그대로 남겨두면 컴파일 에러 발생

package ch06.sec11.exam01;

public class Korean {
    //인스턴스 final 필드 선언
    final String nation = "대한민국";
    final String ssn;

    //인스턴스 필드 선언
    String name;

    //생성자 선언
    public Korean(String ssn, String name) {
        this.ssn = ssn;
        this.name = name;
    }
}
package ch06.sec11.exam01;

public class KoreanExample {
    public static void main(String[] args) {
        //객체 생성시 주민번호와 이름 전달
        Korean k1 = new Korean("123456-1234567", "김자바");

        //필드값 읽기
        System.out.println(k1.nation);//대한민국
        System.out.println(k1.ssn);//123456-1234567
        System.out.println(k1.name);//김자바

        //final 필드는 값 변경 불가
        //k1.nation = "USA";
        //k1.ssn = "123-12-1345";

        //비 final 필드는 값 변경 가능
        k1.name = "박자바";
    }
}

상수 선언

상수 constant

불변의 값을 저장하는 필드
(원주율 파이, 지구의 무게, 지구 둘레.. 등)

상수는 객체마다 저장할 필요 없음
또한 여러개의 값을 가져서도 안됌
그래서 static이면서 final인 특성을 가져야함
static final 타입 상수이름;

초기값은 선언시 주는게 일반적이지만 복잡한 초기화 필요하면 정적 블록에서 초기화

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

상수 이름은 모두 대문자 로 작성하는게 관례이다.
서로 다른 단어가 혼합된 이름이라면 언더바(_)로 단어 연결

상수는 정적 필드이므로 클래스로 접근해야함
클래스명.상수;
Calculator.PI;

package ch06.sec11.exam02;

public class Earth {
    //상수 선언 및 초기화
    static final double EARTH_RADIUS = 6400;

    //상수 선언
    static final double EARTH_SURFACE_AREA;

    //정적 블록에서 상수 초기화
    static {
        EARTH_SURFACE_AREA = 4 * Math.PI * EARTH_RADIUS * EARTH_RADIUS; //Math.PI는 자바 제공 상수
    }
}
package ch06.sec11.exam02;

public class EarthExample {
    public static void main(String[] args) {
        //상수 읽기
        System.out.println("지구의 반지름 : " + Earth.EARTH_RADIUS + "km");//지구의 반지름 : 6400.0km
        System.out.println("지구의 표면적 : " + Earth.EARTH_SURFACE_AREA + "km^2");//지구의 표면적 : 5.147185403641517E8km^2
    }
}

6.12 패키지

패키지는 단순히 디렉토리가 아니고 클래스의 일부분이다.
클래스를 식별하는 용도로 사용된다.
패키지는 주로 개발회사 도메인 이름의 역순으로 만든다.

패키지 선언

패키지 이름
: 모두 소문자로 작성 ( com.samsung.projectname )

import문

다른 패키지에 있는 클래스를 사용해야할때 어느 패키지의 클래스인지 import해서 사용

만일 동일 패키지에 포함된 다수의 클래스를 사용한다면 클래스 이름 생략하고 * 로 표현가능
import com.hankook.*

서로 다른 패키지에 동일한 클래스 이름 Tire 를 가진다면
두 패키지 모두 import하면 그냥 Tire라고 썼을때 컴파일 에러 발생

그럴때는 클래스 이름 전체(패키지 이름 다 들어간)를 써야한다.
com.hankook.Tire tire = new com.hankook.Tire();

6.13 접근 제한자

객체의 필드를 외부에서 변경/호출 하지 못하도록 해야할때,
중요한 필드/메소드가 외부 노출되지 않도록 해 객체의 무결성을 유지하기위해 사용한다.

  • public :
    • 제한대상(클래스, 필드, 생성자, 메소드)
    • 제한범위(없음)
  • protected :
    • 제한대상(필드, 생성자, 메소드)
    • 제한범위(같은 패키지거나 자식 객체만 사용가능)
  • (default) :
    • 제한대상(클래스, 필드, 생성자, 메소드)
    • 제한범위(같은 패키지)
  • private :
    • 제한대상(필드, 생성자, 메소드)
    • 제한범위(객체 내부)

클래스의 접근제한

클래스 선언시 접근제한자를 생략하면 default 접근 제한을 가진다.
같은 패키지 내에서는 제한없이 클래스를 사용할수있고, 다른 패키지에선 사용불가

생성자의 접근 제한

생성자 접근제한

  • public : 모든 패키지에서 생성자 호출(객체 생성) 가능
  • default : 같은 패키지에서만 생성자 호출(객체 생성) 가능
  • private : 클래스 내부에서만 생성자 호출(객체 생성) 가능

필드/메소드 접근 제한

필드/메소드 접근제한

  • public : 모든 패키지에서 필드 읽기/변경 가능, 메소드 호출 가능
  • default : 같은 패키지에서만 필드 읽기/변경 가능, 메소드 호출 가능
  • private : 클래스 내부에서만 필드 읽기/변경 가능, 메소드 호출 가능

6.14 Getter / Setter

객체의 필드(데이터)를 외부에서 맘대로 읽고 변경하면 객체의 무결성이 깨질수있다.
예를 들어 사람의 키나 나이는 음수가 될수 없는데, 외부에서 음수로 변경하면 객체의 무결성이 깨진다.

그래서 외부에서 필드에 직접 접근하는것을 막고, 메소드를 통한 필드 접근만을 허용한다. 메소드로 데이터의 유효성을 검증하고 유효한 값만 필드에 저장할 수 있도록 할 수 있기때문이다. 그런 역할을 하는게 setter 메소드이다.

하지만 setter도 그다지 많이 쓰지 않는다고 한다. 여기 저기서 setter를 통해 변경을 해버리면 별로 좋지 않다고 함

getter/setter 작성법

  • 접근제한자 : 둘다 public
  • 리턴타입 : getter는 필드타입, setter는 void
    get + 필드이름(첫글자 대문자)
    set + 필드이름(첫글자 대문자)
    getter는 필드값을 리턴 , stter는 필드타입의 매개변수를 받는다.

boolean 타입의 필드는 get으로 시작하지 않고 is로 시작함

이클립스에는 getter/setter 메소드 자동 생성하는 기능이 있음

6.15 싱글톤 패턴 singleton

애플리케이션 전체에서 단 한개의 객체만 생성해서 사용하는것

생성자를 private으로 제한해서 외부에서 new 연산자로 생성자 호출할 수 없도록 하는것, 외부에서 객체 생성 불가함
대신 싱글톤 패턴이 제공하는 정적 메소드를 통해 간접적으로 객체를 얻을수있다.

출처: 이것이 자바다 (개정판) : JAVA 프로그래밍의 기본서 - 신용권, 임경균 저

profile
hello world!

0개의 댓글