Skip to content

Latest commit

 

History

History
483 lines (390 loc) · 18.4 KB

7주차.md

File metadata and controls

483 lines (390 loc) · 18.4 KB

7주차 과제

자바의 패키지에 대해 학습

목차

package

자바는 클래스를 체계적으로 관리하기 위해 패키지(package)를 사용하는데 패키지는 파일 시스템의 폴더와 비슷한 개념이며 물리적으로는 폴더 형태
패키지는 클래스의 일부분으로 취급하고, 클래스를 유일하게 만들어주는 식별자 역할

빌트-인 패키지(Built-in Package)

자바가 제공하는 패키지 중 가장 자주 쓰이는 패키지는 java.langjava.util

이 중 java.lang은 아주 기본적인 것들이기 때문에 import하지 않아도 자바가 알아서 java.lang의 클래스를 불러옴

Top

package 키워드

package 키워드는 자바 파일의 최상단에 작성하며, 클래스가 속해있는 패키지를 선언하기 위해 사용

클래스의 전체 이름은 패키지명 + 클래스명 (FQCN, Fully Qualified Class Name) 이며 계층 구조의 패키지를 점(.)을 사용해 구분

├── src                            # src 하위부터 패키지 경로 #
│   ├── Main.class                 # 속해있는 package가 없으면 미표시
│   ├── dami
│   │   ├── Solution.class         # package dami;
│   │   ├── hackerrank
│   │   │   ├── HackerRank.class   # package dami.hackerrank;
│   │   │   └── medium
│   │   │       ├── A.class        # package dami.hackerrank.medium;
│   │   │       ├── B.class        # package dami.hackerrank.medium;
│   │   │       └── C.class        # package dami.hackerrank.medium;
│   │   ├── leetcode
│   │   │   ├── Leetcode.class     # package dami.leetcode;
│   │   │   ├── easy
│   │   │   │   ├── A.class        # package dami.leetcode.easy;
│   │   │   │   ├── D.class        # package dami.leetcode.easy;
│   │   │   │   ├── D$InnerD.class # package dami.leetcode.easy;
package dami.leetcode.easy;

public class D {
  . . .

  public class InnerD {
    . . .
  }
}
  • D 클래스의 전체 이름은 dami.leetcode.easy.D가 되고, 파일 시스템 디렉토리 구조는 'dami\leetcode\easy\D.class'
  • 내부 클래스는 $ 기호 사용해서 연결

패키지 특징

  • 패키지가 다를 경우 중복 클래스명을 구분해서 사용 가능
  • 클래스 파일만 복사해서 다른 경로로 이동 시 클래스 사용이 불가능하기 때문에 이동하려면 반드시 '패키지 + 클래스' 전체를 이동

선언 규칙

  • 숫자로 시작하면 안되고, _와 $를 제외한 특수문자 사용 불가능
  • java로 시작하는 패키지명은 자바 표준 API에서만 사용 가능하기 때문에 불가능
  • 모두 소문자로 작성하는 것이 관례
  • 대규모 프로젝트의 경우 패키지명이 중복되지 않도록 회사 도메인 이름으로 패키지 생성하고 마지막은 프로젝트명
    com.samsung.projectname
    org.apache.projectname
    

패키지 선언이 포함된 클래스 컴파일

패키지 선언이 포함된 클래스를 명령 프롬프트에서 정상적으로 컴파일하려면 -d 옵션 사용 필요
옵션 미사용 시 패키지 미생성

javac ClassName.java                # 패키지 폴더 미생성하고 .class 파일 생성
javac -d . ClassName.java           # 현재 위치에 패키지 폴더 생성 후 생성된 가장 하위 경로에 .class 파일 생성
javac -d ..\bin ClassName.java      # 현재 폴더와 같은 뎁스 위치에 패키지 폴더 생성 후 생성된 가장 하위 경로에 .class 파일 생성
javac -d C:\Temp\bin ClassName.java # 입력한 절대경로에 패키지 폴더 생성 후 생성된 가장 하위 경로에 .class 파일 생성

또한, 패키지가 있는 클래스를 실행하려면 생성한 바이트코드가 있는 폴더가 아닌 패키지가 시작하는 최상위 폴더에서 java 명령어로 실행 (패키지는 클래스명의 일부이기 때문에)

IDE 사용 시 컴파일하면 IDE가 알아서 지정된 경로에 패키지 포함 클래스파일 생성

Top

import 키워드

다른 패키지에 존재하는 클래스를 사용하기 위해 import 키워드를 사용해 다른 클래스를 불러오는 것

다른 패키지의 클래스를 사용하는 방법

  1. 사용할 때 패키지와 클래스를 모두 작성하는 방법
    package com.mycompany;
    
    public class Car {
      com.hankook.Tire tire = new com.hankook.Tire();
    }
  2. import 사용해 클래스를 불러온 후 클래스 이름만 작성하는 방법
    package com.mycompany;
    
    //////////////////////////
    import com.hankook.Tire;
    // 또는
    import com.hankook.*;
    //////////////////////////
    
    public class Car {
      Tire tire = new Tire();
    }
    • 전체 코드가 복잡해질 수 있는 1번 방법에 비해 훨씬 간결한 코드 작성 가능

import 사용 규칙

  • 패키지 선언과 클래스 선언 사이에 작성
  • 특정 패키지 내 다수의 클래스를 사용해야 한다면 * 기호 사용 가능
    • * : 패키지에 속하는 모든 클래스
  • import 개수는 제한이 없으며 필요한 패키지가 있다면 얼마든지 추가 가능
  • import로 지정된 패키지의 하위 패키지는 import 대상에 미포함
    • 하위 패키지의 클래스가 필요하면 import 추가
    import com.mycompany.*;
    import com.mycompany.project.*;

단, 경우에 따라 다른 패키지의 클래스를 사용하기 위해 1번 방법 필요

  • 서로 다른 패키지에 동일한 클래스명이 존재하고, 두 패키지의 모든 클래스가 import되어있을 때
├── src                           
│   ├── dami
│   │   ├── hackerrank
│   │   │   └── medium
│   │   │       ├── A.class        
│   │   │       ├── B.class        
│   │   │       └── C.class        
│   │   ├── leetcode
│   │   │   ├── easy
│   │   │   │   ├── A.class        
│   │   │   │   ├── D.class        
│   │   │   │   ├── D$InnerD.class
import dami.hackerrank.medium.*;
import dami.leetcode.easy.*;

public class Ex {
  public static void main(String[] args) {
    // 컴파일러는 dami.hackerrank.medium의 A 클래스와 dami.leetcode.easy 패키지의 A 클래스 중 어떤 클래스를 로딩해야하는지 모름
    A error = new A(); // 컴파일 에러

    // 1번 방법으로 직접 전체 패키지 + 클래스명 작성해서 사용
    dami.hackerrank.medium.A success1 = new dami.hackerrank.medium.A();
    dami.leetcode.easy.A success2 = new dami.leetcode.easy.A();
  }
}
  • 컴파일 에러
    • Reference to 'A' is ambiguous, both 'dami.hackerrank.medium.A' and 'dami.leetcode.easy.A' match

Top

클래스패스

클래스를 찾기 위한 경로
JVM이 프로그램을 실행하기 위해 클래스파일(.class)을 찾는 기준이 되는 경로
.java로 끝나는 소스 코드 파일을 컴파일하면 .class로 끝나는 바이트코드가 생성되고, JVM은 이 바이트코드 명령을 실행하여 프로그램 실행
클래스패스는 .class 파일이 포함된 디렉토리와 파일을 콜론(:)으로 구분한 목록이며, 자바 실행 명령 시(java 또는 jre) 클래스패스에 지정된 경로를 모두 탐색해서 특정 클래스에 대한 코드가 포함된 .class 파일 검색

  • MacOS 클래스패스 설정
    • 터미널에서 vi /etc/profile 입력해서 CLASSPATH=. . . 입력
    • 콜론(:)으로 구분
  • Windows
    • 시스템 환경변수 설정 창에서 설정
    • 세미콜론(;)으로 구분
  • 단, IDE 사용 시 툴이 알아서 클래스패스를 추가해주기 때문에 환경변수 직접 지정할 필요 없음

기본적으로 패키지에 포함되지 않은 클래스 파일에 대해 클래스패스 설정

클래스패스에 사용 가능한 값 유형

  • /Users/mongzza/java/classes 와 같은 디렉토리
  • zip 파일
  • jar 파일

클래스패스를 지정하는 두 가지 방법

  • CLASSPATH 환경변수 설정
  • 자바 실행 명령 시 -classpath 옵션 사용

Top

CLASSPATH 환경변수

/Users/mongzza/java/classes 경로에 있는 MyClass.java 파일을 컴파일
(현재 경로 : /Users/mongzza/java/classes)

javac MyClass.java        # 패키지 없는 경우
javac -d . MyClass.java   # 패키지 있는 경우(package : kr.mycompany.project)
  • 패키지 없는 경우 : /Users/mongzza/java/classes 경로에 MyClass.class 파일 생성
  • 패키지 있는 경우 : /Users/mongzza/java/classes/kr/mycompany/project 경로에 MyClass.class 파일 생성

MyClass 프로그램 실행 전에 클래스패스 설정(하위 패키지 디렉토리들의 최상위 루트 디렉토리를 지정)

CLASSPATH=/Users/mongzza/java/classes

다른 경로에 추가할 클래스 파일이 더 있다면 다음과 같이 콜론(:)을 사용해 여러개 설정

CLASSPATH=/Users/mongzza/java/classes:/Users/mongzza/java/classes/util

클래스패스 지정 후 자바 실행 명령

java MyClass                      # 패키지 없는 경우
java kr.mycompany.project.MyClass # 패키지 있는 경우

Top

-classpath 옵션

javac 또는 java 명령 시 클래스패스를 지정하는 옵션. -cp로 단축 사용 가능

-cp <디렉토리 및 zip/jar 파일의 클래스 검색 경로>
-classpath <디렉토리 및 zip/jar 파일의 클래스 검색 경로>
javac -classpath <클래스패스> <자바 소스 파일 경로>

Maven이나 Gradle 등 빌드툴 사용 시 추가한 의존성들을 IDE가 javac, java 명령 시 직접 classpath 옵션에 추가해주는 것

예시

/Users/mongzza/java/classes 경로에 있는 MyClass.java 파일을 클래스패스 지정해서 컴파일
(현재 경로 : /)

javac -classpath /Users/mongzza/java/classes /Users/mongzza/java/classes/MyClass.java

다른 경로에 추가할 클래스 파일이 더 있다면 콜론(:) 사용해 여러개 설정

javac -cp /Users/mongzza/java/classes:/Users/valid.jar /Users/mongzza/java/classes/MyClass.java

Top

접근지시자

클래스 및 클래스의 구성 멤버에 대한 접근을 제한하는 역할. 라이브러리 클래스 설계 시 접근지시자로 외부에서 접근 가능/불가능한 멤버를 구분하는 것이 필요

  • 다른 패키지에서 클래스를 사용하지 못하도록 제한(클래스 제한)
  • 클래스로부터 객체를 생성하지 못하도록 제한(생성자 제한)
  • 특정 필드나 메소드를 숨김 처리(필드, 메소드 제한)
종류 범위 적용 가능 대상
private 선언된 클래스 내에서만 접근 가능 필드, 생성자, 메소드
(default) 접근 제어자를 별도로 지정하지 않은 경우
선언된 클래스와 같은 패키지 내에서만 접근 가능
클래스, 필드, 생성자, 메소드
protected 선언된 클래스와 같은 패키지에 있거나 선언된 클래스를 상속받는 자식 클래스 내에서만 접근 가능 필드, 생성자, 메소드
public 모든 클래스에서 접근 가능 클래스, 필드, 생성자, 메소드

클래스의 접근 제한

클래스에 적용 가능한 지시자는 publicdefault

default 제한

선언된 클래스와 같은 패키지 내에서만 사용 가능하며 다른 패키지에서 사용 불가능(하위 패키지 또한 다른 패키지로 취급)

package dami.access;

class AccessModifier {
  . . .
}
package dami.access;

public class Possible {
  public void method() {
    AccessModifier modifier = new AccessModifier(); // 성공
  }
}
package dami.access.other;

public class Impossible {
  public void method() {
    AccessModifier modifier = new AccessModifier(); // 컴파일 에러
  }
}
  • 컴파일 에러 발생
    • 'dami.access.AccessModifier' is not public in 'dami.access'. Cannot be accessed from outside package

public 제한

모든 패키지에서 제한 없이 접근 가능

package dami.access;

public class AccessModifier {
  . . .
}
  • 아무 클래스에서나 AccessModifier의 객체 생성 가능

생성자의 접근 제한

생성자에 적용 가능한 지시자는 public, projected, default, private

클래스에 생성자를 작성하지 않을 경우 자동으로 추가되는 생성자는 클래스 접근지시자와 같은 레벨
또한, 클래스 제한 수준보다 생성자 제한 수준이 더 낮아도 생성자는 클래스 내부에 포함되기 때문에 클래스의 제한 수준까지 접근 가능

  • 예를 들어 default 클래스에 public 생성자가 있다면, 생성자는 클래스에 의해 강제로 default 수준의 생성자가 되는 것과 마찬가지

public 제한

모든 패키지의 모든 클래스 내부에서 제한 없이 생성자 호출 가능

protected 제한

선언된 클래스와 같은 패키지에 있거나 상속받은 클래스 내부에서 생성자 호출 가능

default 제한

선언된 클래스와 같은 패키지에 있는 클래스 내부에서 생성자 호출 가능

private 제한

선언된 클래스 내부에서만 생성자를 호출할 수 있고, 객체 생성 가능. 싱글톤 패턴에서 사용

다양한 접근지시자 생성자를 선언한 클래스

package dami.access;

public class AccessModifier {

  public AccessModifier(boolean b) { . . . }

  protected AccessModifier(int i) { . . . }

  AccessModifier(double d) { . . . }

  private AccessModifier(char c) { . . . }

  public void method() {
    AccessModifier am = new AccessModifier(false);
    AccessModifier am = new AccessModifier(1);
    AccessModifier am = new AccessModifier(1.0);
    AccessModifier am = new AccessModifier('A');
  }

}
  • 모든 생성자 호출 가능

같은 패키지의 다른 클래스

package dami.access;

public Other1 {
  public void method() {
    AccessModifier am = new AccessModifier(false);
    AccessModifier am = new AccessModifier(1);
    AccessModifier am = new AccessModifier(1.0);
    // AccessModifier am = new AccessModifier('A');
  }
}
  • private 생성자 컴파일 에러

다른 패키지의 클래스

package dami.access;

public Other2 {
  public void method() {
    AccessModifier am = new AccessModifier(false);
    // AccessModifier am = new AccessModifier(1);
    // AccessModifier am = new AccessModifier(1.0);
    // AccessModifier am = new AccessModifier('A');
  }
}
  • public 생성자만 접근 가능

필드, 메소드의 접근 제한

필드와 메소드에 적용 가능한 지시자는 public, projected, default, private

public 제한

모든 패키지의 모든 클래스 내부에서 제한 없이 필드와 메소드 호출 가능

protected 제한

선언된 클래스와 같은 패키지에 있거나 상속받은 클래스 내부에서 필드와 메소드 호출 가능

default 제한

선언된 클래스와 같은 패키지에 있는 클래스 내부에서 필드와 메소드 호출 가능

private 제한

선언된 클래스 내부에서만 필드와 메소드를 호출 가능

다양한 접근지시자 필드와 메소드를 선언한 클래스

package dami.access;

public class AccessModifier {

  public int publicField;
  protected int protectedField;
  int defaultField;
  private int privateField;

  public void publicMethod() { . . . }

  protected void protectedMethod() { . . . }

  void defaultMethod() { . . . }

  private void privateMethod() { . . . }

  public void method() {
    publicField = 1;
    protectedField = 2;
    defaultField = 3;
    privateField = 4;

    publicMethod();
    protectedMethod();
    defaultMethod();
    privateMethod();
  }

}
  • 모든 멤버 호출 가능

같은 패키지의 다른 클래스

package dami.access;

public Other1 {
  public void method() {
    AccessModifier am = new AccessModifier();

    am.publicField = 1;
    am.protectedField = 2;
    am.defaultField = 3;
    //am.privateField = 4;

    publicMethod();
    protectedMethod();
    defaultMethod();
    //privateMethod();
  }
}
  • private 멤버 컴파일 에러

다른 패키지의 클래스

package dami.access;

public Other2 {
  public void method() {
    AccessModifier am = new AccessModifier();

    am.publicField = 1;
    //am.protectedField = 2;
    //am.defaultField = 3;
    //am.privateField = 4;

    publicMethod();
    //protectedMethod();
    //defaultMethod();
    //privateMethod();
  }
}
  • public 멤버만 접근 가능

이 외, static, final 키워드 관련 내용은 5주차 - 클래스 정의 방법 참고

일반적으로 객체 지향 프로그래밍은 객체의 데이터를 외부에서 직접 접근하지 못하게 제한하고, 메소드를 통해 데이터를 변경하는 방법 선호.
따라서, 필드는 주로 private 지정자를 사용하고 SetterGetter를 사용해 필드 값을 변경하도록 설계

Top

+ 스터디 수업 내용

자바에서 상수를 정의할 땐 Interface나 Enum을 사용하는 대신 일반 클래스에 static final 필드 선언 후 private 생성자를 정의하는 것이 적절한 방법

Reference