[이것이자바다] chapter 6. 클래스(class) 3

chapter 6

클래스의 멤버 종류, 패키지, 접근 제한자.

멤버 종류

인스턴스 멤버와 this

public class Car{
  //필드
  int gas;

  //메소드
  void setSpeed(int speed){
    //...
	}
}

// 외부 클래스
Car myCar = new Car(); 
myCar.gas = 10;
myCar.setSpeed(60);

=> 인스턴드 필드 gas는 객체마다 따로 존재하고, 인스턴트 메소드 setSpeed()는 객체마다 존재하지 않고 메소드 영역에 저장되고 공유된다.

// 생성자
Car(String model){
  this.model = mode; // 필드, 매개변수
}

// 메소드
void setModel(String model){
  this.model = model;
}

정적 멤버와 static

public class Calculator {
    
    String color; //인스턴트 필드
    void setColor(String color){  //인스턴트 메소드
        this.color= color;
    }
    
    static int plus(int x , int y){  // 정적(static) 메소드
        return x+y;
    }
    
    static int minus(int x, int y){ // 정적 메소드
        return x-y;
    }
    // 인스턴트 필드를 사용할때는 인스턴트 메소드, 
    // 인스턴트 필드를 사용하지 않을 경우에는 정적(ststic) 메소드를 사용하자.
    // 그 이유는 인스턴트 필드를 사용하지 않을 경우, 굳이 객체를 생성하지 않고도 
    // 메소드를 부를 수 있기 때문이고 이게 적절하다.
}

정적 멤버 선언

정적 멤버 사용

//외부 클래스
double result1 = 10*10*Calurator.pi;
int result2 = Calculator.plus(10,5);
int result3 = Calculator.minus(10,5);

=> 사용은 위와 같이 객체를 생성해서 사용하는 것이 아니라 직접 클래스 이름으로 접근해야 하고 이것이 원칙이다. (객체참조 변수로도 접근이 가능하지만 이는 경고 표시(Warning)가 나므로 반드시 클래스 이름으로 접근하자.)

정적 초기화 블록

정적 필드는 다음과 같이 필드 선언과 동시에 초기값을 주는 것이 보통이다.

static double pi = 3.14159;

그러나 정적 필드가 계산이 필요한 초기화 작업이 있을 수 있고, 이런 경우 정적 필드는 new 연산자와 생성자를 통해 초기화할 수 없는데 어떻게 해야 할까? 해답은 정적 블록(static block)에 있다. 자바는 정적 필드 의 복잡한 초기화 작업을 위해서 정적 블록을 제공한다.

public class Television {
    static String company = "samsung";
    static String model = "LCD";
    static String info;

    static{
        info = company + "-" +model;
    }

    public static void main(String[] args) {
        System.out.println(Television.info);
    }
} 

=> 위와 같이 정적 필드 중 계산이 필요한 초기화 작업이 있는 경우 static 블록을 사용한다. 위의 경우 company = “samsung”, model = “LCD”이면 info는 “samsung-LCE”가 되고, company = “LG”, model = “LED” 이면 info는 “LG-LED”가 된다.

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

  • 정적 메소드와 정적 블록 선언시 객체가 없어도 실행된다는 특징 때문에, 이들 내부에 인스턴트 필드나 인스턴트 메소드를 사용할 수 없고, 객체 자신의 참조인 this 키워드도 사용 불가능하다. => 사용하면 컴파일에러! (=> 정적 메소드와 정적 블록 자체가 객체에 대한 참조가 없으므로 이는 당연한 얘기다. 인스턴트 필드는 특히 힙 영역에 할당되어있기에.)
 public class Calculator{
     
    String color; //인스턴트 필드
    static String mycolor;// 정적 필드

    static void abs(String color){ // 정적 메소드
        this.color = color; // 컴파일 에러
    }
    
    static {  // 정적 블록
        mycolor = this.color; // 컴파일 에러
    }

 }

싱글 톤

}

public class SingletonExample { public static void main(String[] args){ /* Singleton obj1 = new Singleton(); // 생성자 앞에 private 있으므로 사용 x, 컴파일 에러. Singleton obj2 = new Singleton(); */ Singleton obj1 = Singleton.getInstance(); Singleton obj2 = Singleton.getInstance();

    //obj1 과 obj2는 같은 객체를 참조.
    if(obj1 == obj2){
        System.out.println("obj1과 obj2는 같은 객체를 참조합니다.");
    }else{
        System.out.println("obj1과 obj2는 서로 다른 객체를 참조합니다.");
    }
    //obj1과 obj2는 같은 객체를 참조합니다.
} }

#### final 필드와 상수

> final 필드 
* final의 의미는 최종적이란 뜻을 가지고 있고, 따라서 final 필드는 초기값이 저장되면 이것이 최종적인 값이 되어서 프로그램 도중에 수정할 수 없는 필드라는 의미를 나타낸다. 
```java
final 타입 필드명 [=초기값];

final 필드의 초기값을 줄 수 있는 방법은 두가지 밖에 없다.

  1. 필드 선언시에 초기화하는 방법
  2. 생성자로 초기화하는 방법

=>따라서 메소드로 final 필드를 초기화할 수 없다.

public class Person {
    final String nation = "korea";
    final String ssn;
    String name;

    public Person(String name, String ssn){
        this.name = name;
        this.ssn = ssn;
    }
}

public class PersonExample{
    public static void main(String[] args) {
        Person person = new Person( "김도형","930928-1999999" ); //final 필드는 생성자로 초기화 가능 , 메소드 x!

        System.out.println(person.nation); //korea
        System.out.println(person.name); // 김도형
        System.out.println(person.ssn); // 930928-1999999
        
        //person.nation = "japan"; final 필드는 변경 불가능!
        //person.ssn = "930928-1111111"; final 필드는 변경 불가능!
        person.name = "옥자"; // 인스턴트 필드는 변경 가능.
    }
}

상수(static final)

  • 상수는 불변의 값으로 static final를 써서 사용한다. final 필드와 헷갈리지 말아야 하는데 final 필드는 초기화가 한번 필요한 필드인 것이고, 상수는 공용성을 띠고 있는 초기화가 필요없는 수이다. (final 필드는 여러 객체 생성시 한번 초기화하면 변하지 않는 값이지만 상수는 여러 객체에서 초기화 없이 사용할 수 있는 수이다. 개념 자체가 다르다.) => 상수는 주로 pi, 지구의 무게 및 둘레 처럼 변하지 않는 수의 개념이다.
static final 타입 상수1 [=초기값];

//선언만 하고 초기화 하지 않은 경우 static 블록 사용
static {
  static final 타입 상수2 = 상수1 * 2;
}

패키지

상위패키지.하위패키지.클래스
// 패키지는 클래스의 일부분이고 이는 클래스를 유일하게 만들어주는 **식별자 역할**을 한다.

패키지 선언

  • 패키지는 클래스를 컴파일하는 과정에서 자동적으로 생성되는 폴더이다. 컴파일러는 포함 되어 있는 패키지 선언을 보고, 파일 시스템의 폴더로 자동 생성시킨다. 다음은 패키지를 선언하는 방법이다. ```java package 상위패키지.하위패키지;

public class ClasssName {…}

> 패키지 이름의 규칙
1. 숫자로 시작해서는 안되고, _,$를 제외한 특수 문자를 사용해서는 안된다.
2. java로 시작하는 패키지는 자바 표준 API에서만 사용하므로 사용해서는 안된다.
3. **모두 소문자로 작성하는 것이 관례이다. 


> 패키지 선언이 포함된 클래스 컴파일
* 패키지 선언이 포함된 클래스를 터미널에서 컴파일할 경우, 단순히 javac ClassName.java로 컴파일 해선 안된다. 패키지 폴더가 자동으로 생성되려면 **javac 명령어 다음에 -d 옵션을 추가하고 패키지가 생성될 환경 변수를 지정하여 컴파일 하자.**


```java
javac -d . ClassName.java  //<- 현재 폴더를 기준으로 패키지 생성 ( 상대 경로 사용) 
javac -d /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin 
// <- 현재 폴더를 기준으로 패키지 생성 (절대 경로 사용) 

import문

같은 패키지에 속하는 클래스들은 아무런 조건 없이 다른 클래스를 사용할 수 있지만 다른 패키지에 속하는 클래스를 사용하려면 두 가지 방법 중 하나를 선택해야 한다.

  1. 첫번째 방법: 패키지와 클래스를 모두 기술한다. ```java package com.mycompany;

public class Car{ com.hankook.Tire tire = new com.hankook.Tire(); // com.mycompany와 다른 패키지인 com.hankook. } // 불편하므로 두번째 방법(import)을 이용한다.


2. 두번째 방법: import문 사용하기.
```java
package com.mycompany;

import com.hankook.Tire; // import를 사용하여 다른 패키지인 com.hankook의 Tire 클래스를 불러온다.

public class Car{
  Tire tire = new Tire(); // import를 선언 했으므로 패키지는 생략한다. 
}

접근 제한자 : public, protected, default, private

접근 제한자 적용 대상 접근 할수 없는 클래스
public 클래스, 필드, 생성자, 메소드 없음
protected 필드, 생성자, 메소드 자식 클래스가 아닌 다른 패키지에 소속된 클래스(자식이면 다른 패키지여도 가능!)
default 클래스, 필드, 생성자, 메소드 다른 패키지에 소속된 클래스
private 필드, 생성자, 메소드 모든 외부 클래스