자바 클래스를 모아놓은 디렉토리, 즉 폴더 개념이에요. 패키지에는 클래스 또는 인터페이스를 포함시킬 수 있고, 서로 관련된 클래스들끼리 그룹 단위로 묶어 놓아 효율적으로 관리할 수 있게 해요.
같은 이름의 클래스여도 폴더로 분리하여 서로 다른 패키지에 존재하는 것이 가능해, 자신만의 패키지 체계를 유지하면서 다른 개발자가 개발한 클래스 라이브러리의 클래스와 이름이 충돌하는 것을 피할 수 있어요.
패키지 선언은 아래처럼 해주면 되요.
package {패키지, 즉 해당 소스가 속한 폴더 이름};
굳이 이 키워드를 선언해야 하냐면 답은 Yes에요. 자바에서 클래스를 선언할 때 package
를 지정해주지 않으면 defaultunnamed
package로 여겨져요. 이렇게 생성된 클래스는 다른 package
에 있는 자바 파일에서 불러올import
수 없어요.
물론, 같은 default package
에 존재하면 가능해요.
💡import
자바 패키지 내 클래스를 불러오는 키워드에요..
을 통해 하위 경로로 이동이 가능해요. 선언 방식은 아래와 같아요.import {패키지명}[.{하위 패키지명}].클래스(또는 *);
./java/lang/Function
클래스를 불러오고 싶다면 아래처럼 선언해주시면 되요.import java.lang.Function; import java.lang.*; // 해당 패키지 내 모든 클래스를 불러와요. // 특정 클래스만 import 윗 구문과 성능상 차이는 전혀 '없어요'
주의할 점은,
import
문에서 클래스 이름대신*
을 사용하는게 하위 패키지의 클래스까지 포함하는 건 아니에요.
import static
이라는 문법이 존재해요. 이 방식으로 선언하면 클래스 사용할 때 따로 패키지를 기입할 필요가 없어요.import static java.lang.System.out; public class Example{ public static void main(String[] args){ // System.out.println("Hello"); out.println("Hello"); } }
import
는 C/C++ 개발한 입장에서 굳이 대입하자면, #include
와 동일한 방식으로 동작한다고 이해하시면 되요.
패키지 기본 속성에 대해서 설명드렸어요. 이제 패키지 선언에 있어 여러 상황을 알아보기 위해 패키지랑 자바 코드를 아래와 같이 구성해봤어요.
하나의 소스 파일엔 오직 하나의 package
만 선언이 가능해요. package
는 단순히 계층 구성을 표현하는게 아니라 컴파일 시점의 계층 구성을 정의하기 때문이에요. 그래서 package
를 다중 선언하게 되면 해당 소스 파일은 다수의 폴더에 존재한다는 의미가 되요.
같은 내용과 이름은 존재할 수 있지만 그 파일 자체가 여럿 존재할 수는 없어요. 그래서 package
는 소스 파일 당 하나만 선언이 가능해요.
예제를 통해 보여드릴게요. 저는 이 부분까지 학습하면서 위에 package
의 다중 선언이 불가하다는 이유를 깨닳았어요. 이걸 보시는 여러분들도 그랬으면 좋겠네요 ㅎㅎ.
코드에 대한 계층 설명
1. Main.java : 실제./src/Main.java
, 소속 패키지practice
2. HelloWorld.java : 실제./src/practice/HelloWorld.java
, 소속 패키지practice
해당 예제는 실제 폴더 구성 경로와 패키지 구성 경로를 다르게 해봤어요. javac -d ./out Main.java
를 수행하면 정상 동작해요. 다만 결과물에 대한 계층 구성이 좀 상이해요.
둘 다 ./out/practice
폴더에 구성이 되요. 이렇게 패키지 계층 구성과 실제 폴더 경로를 다르게 하면 결과가 기존 구성과 다르게 나온다는 것을 알 수 있어요.
그럼 여기서, CLI 환경에서 Main
을 실행시키려면 어떻게 해야할까요? 참고로 java -cp ./out/practice Main
은 안되요.
정답은 바로 java -cp ./out practice.Main
이에요. 이유는 아래에 있어요(그니까 좀 더 읽어줘요 ㅎㅎ).
C++에선 클래스를 단순히 이름으로만 구분했어요. 그래서 다른 경로나 소스 파일에 존재하더라도, 클래스 이름이 같으면 컴파일 에러를 발생시켜요. 자바는 이름이 같더라도 클래스의 실제 이름Full name
은 패키지 명도 포함되어 구분되요. ./Main
의 클래스는 Main
이고, ./MyFunction/Main
의 클래스의 실제 이름은 MyFunction.Main
인 거에요. 그래서 같은 이름의 클래스가 있어도 컴파일이 잘되요.
따라서 이렇게 같은 이름의 클래스가 존재해도 다른 패키지경로
에 있다면 정상적으로 컴파일되요.
위에 예제에선 java
를 통해 실행할 때 실제 이름Full name
으로 해줘야 하는 이유도 이것이에요.
💡참고로, 자바 언어 자체의 패키지들은
java.
나javax.
로 시작해요.
내용을 정리하자면,
package 특성
1.package
는 계층 구조로 클래스 라이브러리를 관리하기 위한 컴파일러 단에서 정의하기 위한 계층 구조 정의 키워드에요.
2.package
는 소스 당 하나만 선언 가능해요. 왜냐하면 파일은 오직 하나의 계층 구조를 가지기 때문이에요.
3. 그래서 유지보수를 위해서라도package
을 선언할 때 실제 폴더 구성 경로와 일치시켜야 해요.
4. 클래스의 실제 이름은{package 구성}.{클래스 이름}
이에요.
오라클에서 제공하는 내용을 기반으로 말씀드릴게요. 모든 패키지 이름은 클래스나 인터페이스와 혼동을 주지 않기 위해 소문자lower case
로 지정해요. 그리고 일반적인 기업에서는 com.회사이름.패키지 이름
식으로 명명해서 해당 패키지는 회사.com
의 개발자가 이 패키지를 만들었다고 명시해요. 특수적으로 지역도 표기하고 싶은 경우에는 com.회사이름.지역.패키지 이름
으로 하는 방법도 있어요.
이 방식이 항상 잘되란 법은 없어요. 만약 회사 이름에 -
이 포함되거나, 앞 자리가 숫자이거나, 또는 자바의 고유 keyword
일 수도 있죠(ex. int, boolean(이런 회사가 있나?)). 각 상황에 대한 대처 방식은 아래와 같아요.
크게 2가지로 나뉘어요.
- 빌트인
Built-in
패키지
- 자바 프로그래밍에 가장 기본이 되는 클래스들을 포함하고 있어요.
- 따로
import
를 선언하지 않아도 사용할 수 있어요.- 대표적인 예로
java.lang
패키지가 있어요.
- 유저 정의
User-defined
패키지
- 개발자가 직접 만든 패키지에요.
기본적인 자바 클래스 라이브러리들을 포함하는 빌트인 패키지인, java.lang
에 있는 클래스들을 나열해봤어요. 보다 자세한건 Oracle Java Reference를 참조하면서 알아가면 좋아요(저도 이렇게 알아가도록 할게요!).
모든 클래스의 최고 조상격인 클래스에요. 클래스를 선언하면 자동으로 상속되요. 그래서 Object
고유의 멤버들은 모든 클래스에서 사용이 가능해요.
문자열을 저장하고 이를 다루는데 필요한 메서드를 제공해요. C/C++을 해보신 분이라면 기존 문자열을 char
또는 string
같은 타입으로 문자열을 선언하는데, 자바는 String
이라는 클래스로 문자열을 다뤄요.
String
클래스는 인스턴스를 생성하면 지정된 문자열을 변경할 수가 없어요. 새로 초기화하던가 해야죠. StringBuffer
클래스는 이를 가능케 해요. 내부적으로 문자열 편집을 위한 버퍼buffer
를 가지고 있고, 인스턴스를 생성할 때 크기를 지정할 수도 있어요.
StringBuffer
는 멀티스레드에 안전Thread Safe
하도록 만들어졌어요. 그래서 데이터가 안전하도록 동기화Synchronization
작업을 거쳐서, 스레드를 사용하지 않으면 불필요한 처리로 인해 효율성이 떨어져요. 그래서 StringBuilder
가 나왔어요. StringBuilder
는 StringBuffer
와 완전 똑같은 기능을 제공하고, 동기화 작업을 거치지 않아서 보다 나은 성능을 보여요.
💡 여담이지만,
String
클래스는 내부적으로StringBuilder
를 사용해요. 그래서 저 같은 경우 문자열 관련 처리는StringBuilder
를 사용해서 퍼포먼스를 올려요 ㅎㅎㅎ.
기본적인 수학 계산에 유용한 메서드로 구성되어 있어요. 생성자 접근자modifier
가 private
이기 때문에 인스턴스로 생성을 할 수 없고 정적 메서드만 사용할 수 있어요. 왜냐하면 따로 인스턴스를 생성할 필요 없이 기능만 제공하기 때문이에요.
Math
클래스는 최대한의 성능을 위해 JVM
이 설치된 운영체제의 메서드System call
를 호출해서 사용해요. 즉, 운영체제에 의존적인 친구에요. 예를 들면 반올림 설정 방법이 운영체제마다 상이하기 때문에 어떠한 환경에서 구동하냐에 따라 결과가 다르게 나타날 수 있어요. 이런 차이를 없애기 위해 성능이 다소 떨어져도, 어떤 OS에서도 같은 결과를 얻도록 하는 것이 StrictMath
클래스에요.
라고 했는데, 일부 Math
클래스에선 StrictMath
를 쓰더라구요...어떻게 된건지는 계속 공부해 봐야 겠어요.
Wrapper
라는 이름을 가진 클래스가 아니에요 ㅎㅎ.
객체지향 개념에서는 모든 것을 객체로 다뤄야 하는데, 자바는 8개의 기본형을 객체로 다루지 않아요(ex. int, boolean, char,...). 이 때문에 자바는 완전한 객체지향 언어가 아니라는 이야길 들어요. 물론, 그대신 높은 성능을 얻을 수 있게 했죠(클래스랑 기본형 타입 선언 및 초기화는 속도가 다르죠).
때로는 이런 기본형 변수Primitive Variable
도 객체로 다뤄야하는 경우가 있어요.
기본형 변수를 객체로 다뤄야 하는 Case
1. 매개변수로 객체를 요구
자바는 기본형 변수가 call-by-value, 그 외 객체를 call-by-reference에요. 이때 필요하죠.
2. 기본형 값이 아닌 객체로 저장해야 할 때
3. 객체 간의 비교가 필요
이를 위해 사용되는 것이 Wrapper
클래스에요. 기본형 변수들을 대표하는 Wrapper 클래스가 있고, 이 클래스들로 기본형 값들을 객체로 다룰 수 있어요.
Wrapper 클래스 종류
1. Boolean boolean
2. Character char
3. Byte byte
4. Short short
5. Integer int
6. Long long
7. Float float
8. Double double
JDK1.5 이전에는 Wrapper 클래스와 기본형 변수간 연산이 불가능했지만, 그 이후 부터 가능해요.
java.awt
, java.io
, java.net
, java.sql
그리고 java.util
가 있어요. 여기에 다 적기엔 방대하니 기회가 되면 정리할게요 ㅎㅎ.
deftkang님 블로그
허쯔님 블로그
GYU님 블로그
Oracle Java Reference
리그캣님 블로그