공부/Java

[Java-07] 객체지향프로그래밍Ⅱ - 패키지, import문, 제어자

줭♪(´▽`) 2021. 2. 24. 11:16

1. 패키지(package)

1) 패키지(package)

- 서로 관련된 클래스들끼리 그룹 단위로 묶어 놓음(패키지)으로써 클래스를 효율적으로 관리할 수 있음

- 클래스 또는 인터페이스를 포함시킬 수 있음

 

- 하나의 소스파일에는 첫 번째 문장으로 단 한 번의 패키지 선언만을 허용
- 모든 클래스는 반드시 하나의 패키지에 속해야 함
- 패키지는 점(.)을 구분자로 하여 계층구조로 구성할 수 있음
- 패키지는 물리적으로 클래스 파일(.class)을 포함하는 하나의 디렉토리

 

2) 패키지의 선언

 

package 패키지명;

 

- 대소문자를 모두 허용하지만 클래스명과 구분하기 위해 소문자로 하는 것을 원칙으로 함

- 이름없는 패키지(unnamed package) : 소스파일에 자신이 속할 패키지를 지정하지 않은 클래스들이 자동적으로 속하는 패키지

- 큰 프로젝트나 Java API와 같은 클래스 라이브러리를 작성하는 경우, 미리 패키지를 구성하여 적용해야 함

 

package com.codechobo.book;

class PackageTest {
    public static void main(String[] args) {
    	System.out.println("Hello World");
    }
}

 

C:\jdk1.8\work>javac -d . PackageTest.java

-d :
소스파일에 지정된 경로를 통해 패키지의 위치를 찾아서 클래스파일을 생성
     (만약 지정된 패키지와 일치하는 디렉토리가 존재하지 않으면 자동적으로 생성)

 

 

2. import문

1) import문

- 클래스의 코드를 작성하기 전 import문으로 사용하고자 하는 클래스의 패키지를 미리 명시하여 소스코드에 사용되는 클래스이름에서 패키지명을 생략할 수 있음

 

2) import문의 선언

일반적인 소스파일(*.java)의 구성
1. package문
2. import문
3. 클래스 선언

 

import 패키지명.클래스명;
   또는
import 패키지명.*;

단, *을 사용하는 것은 하위 패키지의 클래스까지 포함하는 것은 아님

 

import java.text.SimpleDateFormat;
import java.util.Date;

class ImportTest {
    public static void main(String[] args) {
    	Date today = new Date();
        
        SimpleDateFormat date = new SimpleDateFormat("yyyy/MM/dd");
        
        System.out.println(date.format(today));	// 결과 : 2021/02/24
    }
}

 

 

- 모든 소스파일에는 묵시적으로 java.lang 패키지에 대한 import문이 선언되어 있음

 

import java.lang.*;

 

- 따라서 java.lang 패키지의 System, String 등을 패키지명 없이 사용할 수 있음

 

 

3) static import문

- static 멤버를 호출할 때 클래스 이름을 생략할 수 있음

- 특정 클래스의 static 멤버를 자주 사용할 때 편리하며 코드도 간결해짐

 

import static java.lang.System.out;
import static java.lang.Math.*;

class StaticImportEx1 {
    public static void main(String[] args) {
    	// System.out.println(Math.random());
        out.println(random());
        
        // System.out.println(Math.PI);
        out.println(PI);
    }

}

 

 

3. 제어자(modifier)

1) 제어자란?

- 클래스, 변수, 메서드의 선언부와 함께 사용되어 부가적인 의미를 부여함

- 하나의 대상에 대해서 여러 제어자를 조합하여 사용할 수 있음

- 단, 접근 제어자는 네 가지 중 하나만 선택해서 사용할 수 있음 (ex) public private 동시 사용 불가

 

접근 제어자  public, protected, default, private
그         외  static, final, abstract, native, transient, synchronized, volatile strictfp

 

2) static - 클래스의, 공통적인

- 클래스변수(static 멤버변수)는 인스턴스에 관계없이 같은 값을 가짐

- 하나의 변수를 모든 인스턴스가 공유하기 때문

- static이 사용될 수 있는 곳 : 멤버변수, 메서드, 초기화 블럭

 

제어자 대상 의미
static 멤버변수 - 모든 인스턴스에 공통적으로 사용되는 클래스변수가 됨
- 클래스 변수는 인스턴스를 생성하지 않고도 사용 가능
- 클래스가 메모리에 로드될 때 사용
메서드 - 인스턴스를 생성하지 않고도 호출이 가능한 static 메서드가 됨
- static 메서드 내에서는 인스턴스멤버들을 직접 사용할 수 없음

 

class StaticTest {
    static int width = 200;	// static변수
    static int height = 100; // static변수
    
    static {
    	// static변수의 복잡한 초기화 수행
    }
   
    static int max(int a, int b) {  // static메서드
    	return a > b ? a : b;
    }
}

 

 

3) final - 마지막의, 변경될 수 없는

- final이 사용될 수 있는 곳 : 클래스, 메서드, 멤버변수, 지역변수

 

제어자 대상 의미
final 클래스 - 변경될 수 없는 클래스, 확장될 수 없는 클래스가 됨
- final로 지정된 클래스는 다른 클래스의 조상이 될 수 없음
메서드 - 변경될 수 없는 메서드
- final로 지정된 메서드는 오버라이딩을 통해 재정의될 수 없음
멤버변수 - 변수 앞에 final이 붙으면 값을 변경할 수 없는 상수가 됨
지역변수

 

final class FinalTest {			 // 조상이 될 수 없는 클래스
    final int MAX_SIZE = 10;	 // 값을 변경할 수 없는 멤버변수(상수)
    
    final void getMaxSize() {	 // 오버라이딩할 수 없는 메서드(변경불가)
    	final int LV = MAX_SIZE; // 값을 변경할 수 없는 지역변수(상수)
        return MAX_SIZE;
    }
}

 

※ 생성자를 이용한 final멤버 변수의 초기화

- final이 붙은 변수는 상수이므로 일반적으로 선언과 동시에 초기화

- 하지만 인스턴스변수의 경우 생성자에서 초기화할 수 있음

 

class Card {
    final int NUMBER;
    final String KIND;
    static int w = 100;
    static int h = 250;
    
    Card(String kind, int num) {
    	KIND = kind;
        NUMBER = num;
    }
    
    Card() {
    	this("HEART", 1);
    }
}

class FinalCardTest {
    public static void main(String[] args) {
    	Card c = new Card("HEART", 10);
        // c.NUMBER = 5;		// Error! final이 붙은 변수는 변경할 수 없음
        System.out.println(c.Kind);	// HEART
        System.out.println(c.NUMBER);	// 10
    }
}

 

 

4) abstract - 추상의, 미완성의

- 메서드의 선언부만 작성하고 실제 수행내용은 구현하지 않은 추상 메서드를 선언하는데 사용

- abstract가 사용될 수 있는 곳 : 클래스, 메서드

 

제어자 대상 의미
abstract 클래스 - 클래스 내에 추상 메서드가 선언되어 있음을 의미함
메서드 - 선언부만 작성하고 구현부는 작성하지 않은 추상 메서드임을 알림

 

abstract class AbstractTest {	// 추상 클래스 (추상 메서드를 포함한 클래스)
    abstract void move();	// 추상 메서드 (구현부가 없는 메서드)
}

 

 

5) 접근 제어자(access modifier)

- 해당되는 멤버 또는 클래스를 외부에서 접근하지 못하도록 제한함

- 접근 제어자가 지정되어 있지 않다면 접근 제어자는 default

- 접근 제어자가 사용될 수 있는 곳 : 클래스, 멤버변수, 메서드, 생성자

 

private      같은 클래스 내에서만 접근 가능
default      같은 패키지 내에서만 접근 가능
protected   같은 패키지 내에서, 그리고 다른 패키지의 자손클래스에서 접근 가능
public        접근 제한이 전혀 없음 

 

접근 제어자별 가능한 범위

 

접근범위가 넓은 쪽에서 좁은 쪽의 순으로 나열

 

 

대상 사용 가능한 접근 제어자
클래스 public, (default)
메서드 public, protected, (default), private
멤버변수
지역변수 없 음

 

 

※ 접근 제어자를 사용하는 이유

- 외부로부터 데이터를 보호하기 위해

- 외부에는 불필요한, 내부적으로만 사용되는 부분을 감추기 위해

- 객체지향개념의 캡슐화(encapsulation)에 해당하는 데이터 감추기(data hiding)

 

public class Time {
    private int hour;		// 접근제어자가 private로 지정된 변수
  	
    Time(int hour) {
    	this.hour = hour;
    }
    
    public int getHour() { return hour; }
    public void setHour(int hour) {
    	if (hour < 0 || hour > 23) return;
        this.hour = hour;
    }
}


public class TimeTest {
    public static void main(String[] args) {
    	Time t = new Time(10);
        // t.hour = 13;		// Error! 변수 hour의 접근 제어자가 private이므로 접근불가
        t.setHour(12);
    }
}
        

-> private를 사용하여 멤버변수로의 직접적인 접근을 허가하지 않고 메서드를 통한 접근만 허용하여 변수의 잘못된 변경을 예방함

 

 

※ 생성자의 접근 제어자

- 생성자에 접근 제어자를 사용함으로써 인스턴스의 생성을 제한할 수 있음

- 생성자의 접근 제어자는 클래스의 접근 제어자와 다르게 지정할 수도 있음

 

ex) 생성자의 접근 제어자가 private

-> 클래스 외부에선 인스턴스 생성 불가능, 클래스 내부에선 인스턴스 생성 가능

-> 대신 인스턴스를 생성해서 반환해주는 public메서드를 제공해야 함 (단, static이어야 함)

class Singleton {
    ...
    private static Singleton s = new Singleton();
    
    private Singleton() {
    	...
    }
    
    // 인스턴스를 생성하지 않고도 호출할 수 있어야 하므로 static이어야 함
    public static Singleton getInstance() {
    	return s;
    }
}

 

 

6) 제어자(modifier)의 조합

- 대상에 따라 사용할 수 있는 제어자

 

대상 사용 가능한 제어자
클래스 public, (default), final, abstract
메서드 모든 접근 제어자, final, abstract, static
멤버변수 모든 접근 제어자, final, static
지역변수 final

 

제어자를 조합해서 사용할 때 주의할 점

1. 메서드에는 static과 abstract를 함께 사용할 수 없음
 static메서드는 몸통이 있는 메서드에만 사용할 수 있기 때문

2. 클래스에 abstract와 final을 동시에 사용할 수 없음
 확장할 수 없는 final과 상속을 통해 완성되는 abstract의 의미가 서로 모순되기 때문

3. abstract 메서드의 접근 제어자가 private일 수 없음
 abstract는 자손클래스에서 구현해야하는데 private 메서드는 자손클래스에서 접근할 수 없기 때문

4. 메서드에 private와 final을 같이 사용할 필요는 없음
 접근 제어자인 private인 메서드는 어차피 오버라이딩될 수 없기 때문

 

 

 

 

 

 

 

 

참고 - Java의 정석 3rd Edition (저자 : 남궁성, 출판 : 도우출판)