ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Java-29] 자바 패키지, 접근제한자 & 클래스패스
    Java 2020. 12. 31. 01:06

    Java Package, 접근제한자, classPath, 환경 변수

    package 와 접근제한자, 클래스패스 에 관하여

    우리가 실제로 개발하면서, 정말 많은 클래스를 만들거나, 이미 만들어져 있는 클래스를 사용할 수 있다. 패키지란 이 클래스들을 정리하는 폴더 라고 생각하면 된다. 뒤죽박죽 되어 있는 파일들을 정리하기 위해서 폴더가 필요하 듯이, 정리가 되어있지 않은 클래스들을 정리하기 위해서는 패키지 를 사용하여야 한다.

    단 한가지 다른점이 있다면, 패키지는 폴더 그 이상의 역할을 한다. 모든 자바의 클래스는, package 이름.class이름 이다.

    package org.example.test1; //tset1 패키지
    public class Foo { }
    package org.example.test2; //tset2 패키지
    public class Foo { }
        @Test
        public void testClassName(){
            Foo foo1 = new Foo();
            org.example.test1.Foo foo2 = new org.example.test1.Foo();
            System.out.println(foo1.getClass().getName() + "     " + foo2.getClass().getName());
            assertEquals(foo1.getClass().getName(), foo2.getClass().getName());
        }
    //Expected :org.example.test2.Foo
    //Actual   :org.example.test1.Foo

    다음과 같은 클래스 2개가 있다고 가정하자, 전부 Foo 라는 동일한 이름을 가진 클래스 이다. 자/바에서 이 2개를 어떻게 서로 다른 클래스라고 생각을 할까? 바로 클래스 이름에 패키지 이름도 포함되어있기 때문이다.

    그러면 클래스를 복사해서 사용하는건 가능할까?

    • 만약에 test2 패키지 선언된 Apple.java 가 있다고 가정하자, 이 클래스를 그대로 복사해서, test1 패키지 Apple.java에 복사한다고 가정할 때 빨간줄 에러가 발생할 것이다.

      package org.example.test2; // 빨간줄 
      

    public class Apple {
    }

    ### pacakge 키워드 
    
    * 패키지는 클래스를 컴파일 하는 과정에서 자동적으로 생성되는 폴더이지만, 컴파일러는 클래스파일을 패키지 선언을 보고 그 경로에 따라 클래스 파일이  파일시스템을 통해 생성된다. 
    
      ![image](https://user-images.githubusercontent.com/70433341/103213123-1694e480-4950-11eb-91af-de7b8b75905a.png)
    
    #### pacakge 선언하는 방법과 주의할점
    
    * 패키지를 선언하는 방법은 간단하다. **package org.example.xxx** 과 같이 자바 코드 내에서 다음과 같이 선언해주면 된다. 
    
    ```java
    package org.example.test2;

    하지만 패키지를 선언하면서 주의할 점이 있다.

    1. 숫자로 시작하면 안됨, _. $를 제외한 특수 문자를 사용하면 안된다.
    • 만약 org.example.123test 라는 패키지를 만든 다음에 이 패키지 안에다가 자바 파일을 옮기려고 한다면, 다음과 같은 에러가 발생할 것이다 .

    image

    1. java로 시작하는 패키지는 자바 표준 API에서만 사용하므로 사용해서는 안된다.
    • 자바 패키지 안에 작성하는게 가능은 하지만, 사용하지 않는걸 권장한다.

    image

    1. 모두 소문자로 작성하는 것이 관례이다.

    패키지 선언이 포함된 클래스를 컴파일 하는 방법

    javac Apple.java  현재 클래스에서 App.class를 생성함 (pacakge org.example을 신경쓰지 않음)

    image

    javac -d . Apple.java 현재 폴더안에서 package.org.xxx 폴더를 직접 만들어서 넣어줌

    image

    javac -d ..\bin Apple.java 현재 폴더와 같은 위치의 bin 폴더에 생성

    image

    javac -d C:\test Apple.java 원하는 곳에 패키지 생성

    image

    import 키워드

    우리가 개발을 하면서 다른 클래스를 사용할 때, 2가지 방법을 사용할 수 있다. 예를들어

    package org.example.test2;
    
    public class Apple {
    }
    
    import org.example.test2.Apple;
    
    public static void main(String args[]){
        Apple apple = new Apple(); // 1. import 키워드를 사용
        org.example.test2.Apple apple2 = new Apple(); // 2. 패키지와 클래스를 모두 기술
    }
        @Test
        public void test1(){
            Apple useImport = new Apple();
            org.example.test2.Apple usePath = new Apple();
            System.out.println(useImport.getClass().getName());
            System.out.println( usePath.getClass().getName());
            assertEquals(useImport.getClass().getName(),usePath.getClass().getName());
        }

    이렇게 2가지 방법이 있다. 물론 우리는 이 2가지 방법중에 첫번째 방법인 import 방법을 주로 사용할 것이다. intellij 나 eclipse 를 사용하면 자동으로 package 패스를 작성해주긴 하지만, import 문을 직접 작성 하려면 어떻게 작성해야 하는지 알아보자,

    import 패키지명.클래스이름

    이런식으로 작성이 가능하다.

    • import 문이 작성되는 위치는 패키지 선언가 클래스 선언의 사이이다.
    • 만약 그 패키지안에 있는 모든 클래스를 한거번에 import 해서 사용하고 싶다면 ' * ' 를 사용하도록 하자 .
    package org.example; //패키지 선언
    
    import org.example.test2.Apple; // import 선언 Apple 클래스 사용
    import org.example.test2.*; // import 선언 test2 안에 있는 모든 클래스 사용
    
    public class App // 클래스 선언
    {
        Apple apple = new Apple();
    }

    접근 제한자

    우리가 main() 메소드가 있지 않은 대부분의 클래스는 라이브러리의 역할로, 외부 클래스를 끌어다 사용할 목적으로 만들어질 것이다. 이런 외부클래스를 설계할 때에는 클래스의 필드, 메소드 의 사용을 제한하고, 이를 설계할 필요가 있다.

    우리가 클래스를 선언할 때, 고려할 사항은 같은 패키지 내에서만 사용할 것인지, 아니면 다른 패키지에서도 사용할 수 있도록 할 것인지에 대한 결정을 해야한다.

    접근제한자는 크게 4가지가 있는데 public, protected, default, private 가 있다.

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

    defualt 접근 제한

    • 클래스를 선언할 때, 기본적으로 public class 키워드를 붙여주지만, 만약 *public 이 없다면 어떻게 될까? *
    package org.example.test2;
    
    class Apple{ // default 생략 
    }
    
    package org.example;
    
    import org.example.test2.Apple;
    
    public class App
    {
        public static void main(String[] args) {
            Apple apple = new Apple();
        }
    }
    
    java: org.example.test2.Apple is not public in org.example.test2; cannot be accessed from outside package

    이는 default 가 생략 되어있는 상태로, 다른 패키지에 소속된 클래스가 Apple 클래스를 사용할 수 없다.

    public 접근 제한

    • 보통 라이브러리를 개발한다라고 했을 때, 대두분의 라이브러리 클래스들의 접근제한은 public일 가능성이 높다. public 클래스는 ㅍ프로젝트 내에서 , 언제든지 그 클래스를 끌어다 사용할 수 있다.
    package org.example.test2;
    
    public class Apple {
    
    }
    package org.example;
    
    import org.example.test2.Apple;
    
    public class App
    {
        public static void main(String[] args) {
            Apple apple = new Apple();
        }
    }

    Getter 와 Setter 가 필요한 이유

    우리가 클래스를 작성을 하면, 또는 깃헙이나 다른 분들이 작성된 자바코드를 보면, 외부 클래스를 작성할 때, 필드(맴버 변수)에 대부분의 접근제한자가 private 를 붙이고는 한다. 이는 해당 클래스를 제외하고는 클래스의 맴버변수에 접근할 수 없다. 라는 것을 의미한다. 그리고 이런 필드에 접근하기 위해서, Getter, Setter의 public한 메소드를 이용해서 맴버변수에 접근한다.

    그럼 왜 클래스 변수 에다가 귀찮게 private이라는 접근제한자를 작성하는가?

    • 객체의 무결성 : 클래스를 맘대로 사용할 수 있다고 한들, 객체의 값들을 함부로 집어넣는 특히 행위는 객체를 무결성을 깨트릴 수 있다. 예를들어보자

    사람(Person) 이라는 클래스 내에서, 그사람의 신체 정보를 담고 있는 객체를 생성한다고 가정해보자,

    package org.example.test2;
    
    public class Person {
        public String name; // 성함
        public Gender gender; // 성별
        public int weight; // 몸무게
        public int height; // 키 
        public int age; // 나이
    
        enum Gender{
            MALE,FEMALE;
        }
    }

    다음과 같은 클래스가 존재한다고 가정을 해보자

        public static void main(String[] args) {
            Person person1 = new Person();
    
            person1.name = "이마루";
            person1.gender = Gender.MALE;
            person1.weight = 70;
            person1.height = 177;
            person1.age = 25;
        }

    우리가 다음과 같이 클래스를 사용한다면 더할 나위 없이 좋지만, 만약 양의 정수가 들어가야 하는 weight, height, age 에 음의 중수가 들어가면 어떨까?

        public static void main(String[] args) {
            Person person1 = new Person();
    
            person1.name = "이마루";
            person1.gender = Gender.MALE;
            person1.weight = -70;
            person1.height = -177;
            person1.age = -25;
        }

    다음과 같이, 몸무게, 키, 나이가 음의 정수인 사람은 아무도 없을 것이다. 이런 무분별하게 클래스 맴버변수에 접근을 하게 되면, 다음과 같은 데이터의 무결성이 깨질 수 있을 것이다. 그래서 먼저 클래스의 맴버변수들을 private 으로 지정한다.

    그런다음 각자 저번에 블로그에 포스팅한 this. 키워드를 이용해서 setter 를 작성하도록 한다.

    public class Person {
        private String name;
        private Gender gender;
        private int weight;
        private int height;
        private int age;
    
        enum Gender{
            MALE,FEMALE;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public void setGender(Gender gender) {
            this.gender = gender;
        }
    
        public void setWeight(int weight) {
            this.weight = weight;
        }
    
        public void setHeight(int height) {
            this.height = height;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    }
        public static void main(String[] args) {
            Person person1 = new Person();
    
            person1.setName("이마루");
            person1.setGender(Gender.MALE);
            person1.setAge(25);
            person1.setHeight(177);
            person1.setWeight(70);
        }

    만약 여기서 음의 정수를 받지 않는 Setter 를 만들고 싶다면, 다음과 같이 작성할 수 있을 것이다.

    
        public void setAge(int age) {
            if(age < 0){
                System.out.println("0보다 적은 나이를 입력할 수 없습니다");
                age = 0;
                //또는 오류를 발생시키는 것도 좋은 방법이다. 
            }
            this.age = age;
        }

    클래스 패스

    • 클래스 패스를 알아보기 전에 다음 코드를 먼저 보도록 하자. Apple 이라는
    package org.example.test2;
    
    public class Apple {
        public static void main(String[] args) {
            System.out.println("Hello Apple");
        }
    }

    이 클래스를 intelliJ, eclipse 와 같은 IDE 가 아닌, 맥의 iterm, 또는 cmd 화면에서 구동을 해보자,

    image

    그냥 바탕화면에서 java Apple.java 를 진행할 경우 다음과 같은 에러가 발생하는 것이다. 얘기의 요점은 클래스패스란, 결국 자바컴파일러가 클래스를 찾기위한 경로를 말하는 것이다. 그럼 클래스패스를 옵션을 사용해서, 자바 명령어를 실행시켜 보도록하자

    java -classpath ".;test2" Apple

    -classpath 명령어

    이 옵션은 결국에, Apple 클래스를 찾아주는 경로를 옵션으로 붙혀주는 것이다. 그럼 test2 패키지 앞에 ' . ; ' 반점과 새미콜론은 무엇을 의미하는 것일까?

    • 마침표 . 는 현재의 패키지(폴더)라는 뜻이고, 새미콜론은 그리고 라는 뜻이다
    • 즉, 현재폴더 안에서, 그리고 test2 라는 패키지 내부에서 Apple 클래스를 찾아서 java를 실행시켜라 라는 의미가 된다.
    • -classpath 는 -cp 로도 작성이 가능하다.
    • 우리가 classpath 를 계속 지정해줘야 하는 것일까?

    환경 변수

    앞서 클래스 패스에 대해서 알아보았다. 그런데, 이를 java 명령어를 실행시킬 때 마다 계속 진행해줘야하는 것인가? 사실 아니다. 우리가 자바를 사용하기 위해서, 환경 변수를 설정을 해 두었는데, 이를 통해서, 쉽게 해결이 가능하다.

    java -classpath "something" Test // 이를 환경변수를 이용하면
    java (-classpath "something") Test // 이를 클래스 패스를 생략이 가능해지는 것이다. 

    댓글

Designed by Tistory.